├── .editorconfig ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE-MIT ├── README.md ├── fixtures └── empty.css ├── index.js ├── package.json └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "expr": true, 5 | "immed": true, 6 | "latedef": true, 7 | "noarg": true, 8 | "node": true, 9 | "noempty": true, 10 | "strict": true, 11 | "trailing": true, 12 | "undef": true, 13 | "unused": true 14 | } 15 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .editorconfig 2 | .gitignore 3 | .jshintrc 4 | .travis.yml 5 | test.js 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 'iojs' 4 | - '0.12' 5 | - '0.10' 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 2.0.0 2 | 3 | * Upgraded to PostCSS 5.0. 4 | 5 | # 1.2.0 6 | 7 | * Plugins consumed by `postcss-plugin-context` are now passed the `Result` 8 | from PostCSS as well as the CSS AST. 9 | 10 | # 1.1.0 11 | 12 | * Adds support for defining a context across a whole file. 13 | 14 | # 1.0.0 15 | 16 | * Initial release. 17 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) Ben Briggs (http://beneb.info) 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [postcss]-plugin-context [![Build Status](https://travis-ci.org/postcss/postcss-plugin-context.svg?branch=master)][ci] 2 | 3 | > Limit a PostCSS processor to a local stylesheet context. 4 | 5 | ## Install 6 | 7 | With [npm](https://npmjs.org/package/postcss-plugin-context) do: 8 | 9 | ``` 10 | npm install postcss-plugin-context --save 11 | ``` 12 | 13 | ## Example 14 | 15 | This plugin is useful should you need to contextualize another processor inside 16 | your CSS file. For example, [`postcss-map`] will allow you to specify a short 17 | syntax if only one file is specified. Using this module, we can limit the scope 18 | of the transform to be of our choice, rather than the module's. In this example, 19 | only the first ruleset is actually passed to postcss-map, and the rest of the 20 | CSS file is untouched. 21 | 22 | Simply define a `@context` block inside your CSS file, such as: 23 | 24 | ```css 25 | @context brandColors { 26 | h1 { 27 | color: map(primary); 28 | } 29 | } 30 | ``` 31 | 32 | Then, the plugin will work on the `brandColors` context. The `@context` block is 33 | removed automatically for you after the fact. 34 | 35 | ```js 36 | var postcss = require('postcss'); 37 | var map = require('postcss-map'); 38 | var context = require('postcss-plugin-context'); 39 | 40 | var css = '@context brandColors { h1 { color: map(primary) } } h2 { color: map(primary) }'; 41 | console.log(postcss([ 42 | context({ 43 | brandColors: map({ 44 | maps: ['brand.yml'] 45 | }) 46 | }) 47 | ]).process(css).css); 48 | 49 | // => 'h1 { color: red } h2 { color: map(primary) }' 50 | ``` 51 | 52 | Note that you can pass multiple processors to a single context block: 53 | 54 | ```css 55 | h1 { 56 | @context brandColors, size { 57 | color: map(primary); 58 | size: 100px; 59 | } 60 | } 61 | ``` 62 | 63 | Outputs: 64 | 65 | ```css 66 | h1 { 67 | color: red; 68 | width: 100px; 69 | height: 100px; 70 | } 71 | ``` 72 | 73 | Note that a context may also be defined across a whole file; if you specify 74 | `@context` without curly braces. For example: 75 | 76 | ```css 77 | @context size; 78 | 79 | h1 { 80 | size: 100px; 81 | } 82 | ``` 83 | 84 | Outputs: 85 | 86 | ```css 87 | h1 { 88 | width: 100px; 89 | height: 100px; 90 | } 91 | ``` 92 | 93 | ## API 94 | 95 | ### context(plugins) 96 | 97 | #### plugins 98 | 99 | Type: `object` 100 | *Required value*. 101 | 102 | Pass an object of processors to contextualize. 103 | 104 | ```js 105 | context({ 106 | brandColors: map({maps: ['brand.yml']}) 107 | size: require('postcss-size') 108 | }); 109 | ``` 110 | 111 | ## Contributing 112 | 113 | Pull requests are welcome. If you add functionality, then please add unit tests 114 | to cover it. 115 | 116 | ## License 117 | 118 | MIT © [Ben Briggs](http://beneb.info) 119 | 120 | [ci]: https://travis-ci.org/postcss/postcss-plugin-context 121 | [postcss]: https://github.com/postcss/postcss 122 | [`postcss-map`]: https://github.com/pascalduez/postcss-map 123 | -------------------------------------------------------------------------------- /fixtures/empty.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/postcss/postcss-plugin-context/4c3fef58b706eff831ac32897d7e45b417deb453/fixtures/empty.css -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var postcss = require('postcss'); 4 | var comma = postcss.list.comma; 5 | var plugin = 'postcss-plugin-context'; 6 | 7 | module.exports = postcss.plugin(plugin, function (plugins) { 8 | 9 | var getPlugin = function (name) { 10 | return plugins[Object.keys(plugins).filter(function (p) { 11 | return name === p; 12 | })[0]]; 13 | }; 14 | 15 | return function (css, result) { 16 | if (Object.prototype.toString.call(plugins) !== '[object Object]') { 17 | throw new Error(plugin + ' cannot be called on a non-object'); 18 | } 19 | css.walkAtRules('context', function (rule) { 20 | comma(rule.params).forEach(function (ctx) { 21 | var method = getPlugin(ctx); 22 | if (method.postcss) { 23 | method = method.postcss; 24 | } 25 | if (method) { 26 | if (rule.nodes) { 27 | method(rule, result); 28 | } else { 29 | method(css, result); 30 | } 31 | rule.each(function (r) { 32 | r.remove(); 33 | rule.parent.insertBefore(rule, r); 34 | }); 35 | } else { 36 | var err = 'No context was found for "' + ctx + '".'; 37 | throw rule.error(err, {plugin: plugin}); 38 | } 39 | }); 40 | rule.remove(); 41 | }); 42 | }; 43 | }); 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-plugin-context", 3 | "version": "2.0.0", 4 | "description": "Limit a PostCSS processor to a local stylesheet context.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tape test.js | tap-spec" 8 | }, 9 | "keywords": [ 10 | "css", 11 | "postcss", 12 | "postcss-plugin" 13 | ], 14 | "license": "MIT", 15 | "devDependencies": { 16 | "postcss-import": "^7.0.0", 17 | "postcss-map": "^0.4.1", 18 | "postcss-size": "^1.0.0", 19 | "postcss-svgo": "^2.0.0", 20 | "tap-spec": "^4.1.0", 21 | "tape": "^4.2.0" 22 | }, 23 | "homepage": "https://github.com/postcss/postcss-plugin-context", 24 | "author": { 25 | "name": "Ben Briggs", 26 | "email": "beneb.info@gmail.com", 27 | "url": "http://beneb.info" 28 | }, 29 | "repository": "postcss/postcss-plugin-context", 30 | "dependencies": { 31 | "postcss": "^5.0.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var test = require('tape'); 4 | var postcss = require('postcss'); 5 | var plugin = require('./'); 6 | var map = require('postcss-map'); 7 | var name = require('./package.json').name; 8 | 9 | var tests = [{ 10 | message: 'should contextualize a postcss processor', 11 | fixture: '@context foo{h1{bar:map(language)}}h1{baz:map(language)}', 12 | expected: 'h1{bar:node_js}h1{baz:map(language)}', 13 | options: {foo: map({maps: ['.travis.yml']})} 14 | }, { 15 | message: 'should contextualize with the postcss sugar syntax', 16 | fixture: '@context size{h1{size:100px}}h2{size:100px}', 17 | expected: 'h1{width:100px;height:100px}h2{size:100px}', 18 | options: {size: require('postcss-size')} 19 | }, { 20 | message: 'should contextualize across a whole file', 21 | fixture: '@context size;h1{size: 100px}h2{size: 100px}', 22 | expected: 'h1{width: 100px;height: 100px}h2{width: 100px;height: 100px}', 23 | options: {size: require('postcss-size')} 24 | }]; 25 | 26 | function process (css, options) { 27 | return postcss(plugin(options)).process(css).css; 28 | } 29 | 30 | function processAsync (css, options, cb) { 31 | return postcss(plugin(options)).process(css).then(cb); 32 | } 33 | 34 | test(name, function (t) { 35 | t.plan(tests.length); 36 | 37 | tests.forEach(function (test) { 38 | var options = test.options || {}; 39 | t.equal(process(test.fixture, options), test.expected, test.message); 40 | }); 41 | }); 42 | 43 | test('should throw on undefined processor', function (t) { 44 | t.plan(1); 45 | t.throws(function () { 46 | return process('@context bar { h1 { color: red } }', {}); 47 | }, 'should throw because the "bar" context is undefined'); 48 | }); 49 | 50 | test('should throw on invalid options', function (t) { 51 | t.plan(2); 52 | t.throws(function () { 53 | return process('@context foo { h1 { color: red }}'); 54 | }, 'should throw when no options were passed'); 55 | t.throws(function () { 56 | return process('@context foo { h1 { color: red }}', [map]); 57 | }, 'should throw when options were not typeof object'); 58 | }); 59 | 60 | test('should use the postcss plugin api', function (t) { 61 | t.plan(2); 62 | t.ok(plugin().postcssVersion, 'should be able to access version'); 63 | t.equal(plugin().postcssPlugin, name, 'should be able to access name'); 64 | }); 65 | 66 | test('should receive warnings from result object', function (t) { 67 | t.plan(2); 68 | var options = { 69 | inline: require('postcss-import')({ 70 | path: [__dirname] 71 | }) 72 | }; 73 | 74 | processAsync('@context inline { @import "./fixtures/empty"; }', options, function (result) { 75 | t.equal(result.css, '', 'should have empty output'); 76 | t.equal(result.warnings().length, 1, 'should have warning from postcss-import'); 77 | }); 78 | }); 79 | 80 | test('should work with async plugins', function (t) { 81 | t.plan(2); 82 | 83 | var options = { 84 | svgo: require('postcss-svgo') 85 | }; 86 | 87 | var fixture = '@context svgo{h1{background:url(\'data:image/svg+xml;utf-8,\')}}'; 88 | var fixture2 = 'h1{background:url(\'data:image/svg+xml;utf-8,\')}'; 89 | var expected = 'h1{background:url(\'data:image/svg+xml;utf-8,\')}'; 90 | 91 | processAsync(fixture, options, function (result) { 92 | t.equal(result.css, expected); 93 | }); 94 | 95 | processAsync(fixture2, options, function (result) { 96 | t.equal(result.css, fixture2); 97 | }); 98 | }); 99 | --------------------------------------------------------------------------------