├── CHANGELOG.md ├── .gitignore ├── .npmignore ├── test ├── fixtures │ ├── nested.css │ ├── nested.expected.css │ ├── class.css │ ├── id.css │ ├── class.expected.css │ ├── id.expected.css │ ├── keyframe.css │ ├── keyframe.expected.css │ ├── id-container.css │ ├── class-container.css │ ├── exisiting.css │ ├── exisiting.expected.css │ ├── id-container.expected.css │ ├── class-container.expected.css │ ├── at-rules.css │ └── at-rules.expected.css └── test.js ├── .travis.yml ├── .eslintrc ├── .github └── workflows │ └── node.js.yml ├── package.json ├── LICENSE ├── index.js └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | coverage/ 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | 3 | node_modules/ 4 | 5 | test/ 6 | .travis.yml 7 | 8 | gulpfile.js 9 | -------------------------------------------------------------------------------- /test/fixtures/nested.css: -------------------------------------------------------------------------------- 1 | /* LESS or SCSS syntax */ 2 | .boo .Component { 3 | .Subcomponent { 4 | box-sizing: border-box; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/nested.expected.css: -------------------------------------------------------------------------------- 1 | /* LESS or SCSS syntax */ 2 | .boo .Component { 3 | .Subcomponent { 4 | box-sizing: border-box; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/class.css: -------------------------------------------------------------------------------- 1 | .Component { 2 | color: green; 3 | } 4 | 5 | .Component.is-disabled .Subcomponent.is-done { 6 | box-sizing: border-box; 7 | } 8 | 9 | .Component.is-disabled, .Subcomponent.is-done > h1 { 10 | color: white; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/id.css: -------------------------------------------------------------------------------- 1 | .Component { 2 | color: green; 3 | } 4 | 5 | .Component.is-disabled .Subcomponent.is-done { 6 | box-sizing: border-box; 7 | } 8 | 9 | .Component.is-disabled, .Subcomponent.is-done > h1 { 10 | color: white; 11 | } 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | before_install: 4 | - "export DISPLAY=:99.0" 5 | 6 | node_js: 7 | - '4' 8 | 9 | before_script: 10 | # - 'npm run lint' 11 | 12 | after_script: 13 | - 'npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls' 14 | -------------------------------------------------------------------------------- /test/fixtures/class.expected.css: -------------------------------------------------------------------------------- 1 | .boo .Component { 2 | color: green; 3 | } 4 | 5 | .boo .Component.is-disabled .Subcomponent.is-done { 6 | box-sizing: border-box; 7 | } 8 | 9 | .boo .Component.is-disabled, .boo .Subcomponent.is-done > h1 { 10 | color: white; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/id.expected.css: -------------------------------------------------------------------------------- 1 | #foo .Component { 2 | color: green; 3 | } 4 | 5 | #foo .Component.is-disabled .Subcomponent.is-done { 6 | box-sizing: border-box; 7 | } 8 | 9 | #foo .Component.is-disabled, #foo .Subcomponent.is-done > h1 { 10 | color: white; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/keyframe.css: -------------------------------------------------------------------------------- 1 | /* Chrome, Safari, Opera */ 2 | @-webkit-keyframes mymove { 3 | 0% {top: 0px;} 4 | 25% {top: 200px;} 5 | 75% {top: 50px} 6 | 100% {top: 100px;} 7 | } 8 | 9 | /* Standard syntax */ 10 | @keyframes mymove { 11 | 0% {top: 0px;} 12 | 25% {top: 200px;} 13 | 75% {top: 50px} 14 | 100% {top: 100px;} 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/keyframe.expected.css: -------------------------------------------------------------------------------- 1 | /* Chrome, Safari, Opera */ 2 | @-webkit-keyframes mymove { 3 | 0% {top: 0px;} 4 | 25% {top: 200px;} 5 | 75% {top: 50px} 6 | 100% {top: 100px;} 7 | } 8 | 9 | /* Standard syntax */ 10 | @keyframes mymove { 11 | 0% {top: 0px;} 12 | 25% {top: 200px;} 13 | 75% {top: 50px} 14 | 100% {top: 100px;} 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/id-container.css: -------------------------------------------------------------------------------- 1 | .Component { 2 | color: green; 3 | } 4 | 5 | & { 6 | color: green; 7 | } 8 | 9 | & > .Component { 10 | color: red; 11 | } 12 | 13 | .Wrapper > & { 14 | color: red; 15 | } 16 | 17 | & & { 18 | color: pink; 19 | } 20 | 21 | &.Other { 22 | color: pink; 23 | } 24 | 25 | & .WithAmpersand, 26 | .Other, 27 | .Component { 28 | color: orange; 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/class-container.css: -------------------------------------------------------------------------------- 1 | .Component { 2 | color: green; 3 | } 4 | 5 | & { 6 | color: green; 7 | } 8 | 9 | & > .Component { 10 | color: red; 11 | } 12 | 13 | .Wrapper > & { 14 | color: red; 15 | } 16 | 17 | & & { 18 | color: pink; 19 | } 20 | 21 | &.Other { 22 | color: pink; 23 | } 24 | 25 | & .WithAmpersand, 26 | .Other, 27 | .Component { 28 | color: orange; 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/exisiting.css: -------------------------------------------------------------------------------- 1 | .boo .Component { 2 | color: green; 3 | } 4 | 5 | .boo .Component.is-disabled .Subcomponent.is-done { 6 | box-sizing: border-box; 7 | } 8 | 9 | .boo .Component.is-disabled, .boo .Subcomponent.is-done > h1 { 10 | color: white; 11 | } 12 | 13 | /* see issue https://github.com/pazams/postcss-scopify/issues/4 */ 14 | .boo2 .Component { 15 | color: green; 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/exisiting.expected.css: -------------------------------------------------------------------------------- 1 | .boo .Component { 2 | color: green; 3 | } 4 | 5 | .boo .Component.is-disabled .Subcomponent.is-done { 6 | box-sizing: border-box; 7 | } 8 | 9 | .boo .Component.is-disabled, .boo .Subcomponent.is-done > h1 { 10 | color: white; 11 | } 12 | 13 | /* see issue https://github.com/pazams/postcss-scopify/issues/4 */ 14 | .boo .boo2 .Component { 15 | color: green; 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtures/id-container.expected.css: -------------------------------------------------------------------------------- 1 | #boo .Component { 2 | color: green; 3 | } 4 | 5 | #boo { 6 | color: green; 7 | } 8 | 9 | #boo > .Component { 10 | color: red; 11 | } 12 | 13 | .Wrapper > #boo { 14 | color: red; 15 | } 16 | 17 | #boo #boo { 18 | color: pink; 19 | } 20 | 21 | #boo.Other { 22 | color: pink; 23 | } 24 | 25 | #boo .WithAmpersand, 26 | #boo .Other, 27 | #boo .Component { 28 | color: orange; 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/class-container.expected.css: -------------------------------------------------------------------------------- 1 | .boo .Component { 2 | color: green; 3 | } 4 | 5 | .boo { 6 | color: green; 7 | } 8 | 9 | .boo > .Component { 10 | color: red; 11 | } 12 | 13 | .Wrapper > .boo { 14 | color: red; 15 | } 16 | 17 | .boo .boo { 18 | color: pink; 19 | } 20 | 21 | .boo.Other { 22 | color: pink; 23 | } 24 | 25 | .boo .WithAmpersand, 26 | .boo .Other, 27 | .boo .Component { 28 | color: orange; 29 | } 30 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-unused-expressions": [0], 4 | "no-underscore-dangle": [0], 5 | "no-reserved-keys": [2], 6 | "no-multi-spaces": [0], 7 | "no-extra-parens": [2], 8 | "no-unused-vars": [2], 9 | "no-loop-func": [0], 10 | "key-spacing": [0], 11 | "max-len": [2], 12 | "strict": [0], 13 | "indent": [2], 14 | "quotes": [2, "single", "avoid-escape"], 15 | "curly": [0] 16 | }, 17 | "env": { 18 | "mocha": true, 19 | "node": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x, 16.x, 18.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm test 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-scopify", 3 | "version": "1.0.0", 4 | "description": "PostCSS plugin that adds a user input scope to each selector", 5 | "keywords": [ 6 | "postcss", 7 | "css", 8 | "postcss-plugin", 9 | "scope", 10 | "scoped css", 11 | "css post processing" 12 | ], 13 | "author": "pazams ", 14 | "license": "MIT", 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/pazams/postcss-scopify.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/pazams/postcss-scopify/issues" 21 | }, 22 | "homepage": "https://github.com/pazams/postcss-scopify", 23 | "dependencies": { 24 | "postcss-selector-parser": "^6.0.6" 25 | }, 26 | "devDependencies": { 27 | "istanbul": "^0.4.5", 28 | "mocha": "^8.4.0", 29 | "postcss": "^8.3.0" 30 | }, 31 | "peerDependencies": { 32 | "postcss": "^8.3.0" 33 | }, 34 | "scripts": { 35 | "test": "istanbul cover ./node_modules/.bin/mocha test/*.js" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2015 pazams laptop 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/fixtures/at-rules.css: -------------------------------------------------------------------------------- 1 | @media screen, print { 2 | .Component { line-height: 1.2 } 3 | } 4 | 5 | @supports (--foo: green) { 6 | .Component { 7 | color: green; 8 | } 9 | } 10 | 11 | @document url(http://www.w3.org/), 12 | url-prefix(http://www.w3.org/Style/), 13 | domain(mozilla.org), 14 | regexp("https:.*") 15 | { 16 | .Component { 17 | color: purple; 18 | } 19 | } 20 | 21 | @page { 22 | size: auto; 23 | margin: 10%; 24 | } 25 | 26 | @font-face { 27 | font-family: MyHelvetica; 28 | src: local("Helvetica Neue Bold"), 29 | local("HelveticaNeue-Bold"), 30 | url(MgOpenModernaBold.ttf); 31 | font-weight: bold; 32 | } 33 | 34 | @keyframes identifier { 35 | 0% { top: 0; left: 0; } 36 | 30% { top: 50px; } 37 | 68%, 72% { left: 50px; } 38 | 100% { top: 100px; left: 100%; } 39 | } 40 | 41 | @counter-style circled-alpha { 42 | system: fixed; 43 | symbols: Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ; 44 | suffix: " "; 45 | } 46 | 47 | 48 | @font-feature-values Font One { 49 | @styleset { 50 | nice-style: 12; 51 | } 52 | } 53 | 54 | @font-feature-values Font Two { 55 | @styleset { 56 | nice-style: 4; 57 | } 58 | } 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/fixtures/at-rules.expected.css: -------------------------------------------------------------------------------- 1 | @media screen, print { 2 | .boo .Component { line-height: 1.2 } 3 | } 4 | 5 | @supports (--foo: green) { 6 | .boo .Component { 7 | color: green; 8 | } 9 | } 10 | 11 | @document url(http://www.w3.org/), 12 | url-prefix(http://www.w3.org/Style/), 13 | domain(mozilla.org), 14 | regexp("https:.*") 15 | { 16 | .boo .Component { 17 | color: purple; 18 | } 19 | } 20 | 21 | @page { 22 | size: auto; 23 | margin: 10%; 24 | } 25 | 26 | @font-face { 27 | font-family: MyHelvetica; 28 | src: local("Helvetica Neue Bold"), 29 | local("HelveticaNeue-Bold"), 30 | url(MgOpenModernaBold.ttf); 31 | font-weight: bold; 32 | } 33 | 34 | @keyframes identifier { 35 | 0% { top: 0; left: 0; } 36 | 30% { top: 50px; } 37 | 68%, 72% { left: 50px; } 38 | 100% { top: 100px; left: 100%; } 39 | } 40 | 41 | @counter-style circled-alpha { 42 | system: fixed; 43 | symbols: Ⓐ Ⓑ Ⓒ Ⓓ Ⓔ Ⓕ Ⓖ Ⓗ Ⓘ Ⓙ Ⓚ Ⓛ Ⓜ Ⓝ Ⓞ Ⓟ Ⓠ Ⓡ Ⓢ Ⓣ Ⓤ Ⓥ Ⓦ Ⓧ Ⓨ Ⓩ; 44 | suffix: " "; 45 | } 46 | 47 | 48 | @font-feature-values Font One { 49 | @styleset { 50 | nice-style: 12; 51 | } 52 | } 53 | 54 | @font-feature-values Font Two { 55 | @styleset { 56 | nice-style: 4; 57 | } 58 | } 59 | 60 | 61 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const conditionalGroupRules = ['media','supports','document']; 4 | 5 | const isObject = item => { 6 | return item != null && typeof item === 'object' && Array.isArray(item) === false; 7 | } 8 | 9 | /** 10 | * Determine if selector is already scoped 11 | * 12 | * @param {string} selector 13 | * @param {string} scope 14 | */ 15 | const isScopeApplied = (selector, scope) => { 16 | const selectorTopScope = selector.split(" ",1)[0]; 17 | return selectorTopScope === scope; 18 | } 19 | 20 | /** 21 | * Determine if scope is valid 22 | * 23 | * @param {string} scope 24 | */ 25 | const isValidScope = scope => { 26 | if (!scope){ 27 | return false; 28 | } 29 | return scope.indexOf(',') === -1; 30 | } 31 | 32 | /** 33 | * Determine if rule should be scoped 34 | * 35 | * @param {Rule} rule 36 | */ 37 | const isRuleScopable = rule => { 38 | if(rule.parent.type !== 'root') { 39 | return rule.parent.type === 'atrule' && conditionalGroupRules.indexOf(rule.parent.name) > -1; 40 | } else { 41 | return true; 42 | } 43 | } 44 | 45 | /** 46 | * extract the scope from the input given by caller 47 | * 48 | * @param {string | Record} options 49 | */ 50 | const extractScope = (options) => { 51 | if (typeof options === 'string') { 52 | return options; 53 | } else if (isObject(options) && options.scope) { 54 | return options.scope 55 | } 56 | return null; 57 | } 58 | 59 | const scopify = (options) => { 60 | return { 61 | postcssPlugin: 'postcss-scopify', 62 | Once (root, { result }) { 63 | const scope = extractScope(options); 64 | // guard statment- allow only valid scopes 65 | if(!isValidScope(scope)){ 66 | throw root.error('invalid scope', { plugin: 'postcss-scopify' }); 67 | } 68 | root.walkRules(rule => { 69 | 70 | // skip scoping of special rules (certain At-rules, nested, etc') 71 | if(!isRuleScopable(rule)){ 72 | return rule; 73 | } 74 | 75 | rule.selectors = rule.selectors.map(selector => { 76 | if (isScopeApplied(selector,scope)) { 77 | return selector; 78 | } 79 | 80 | // special case for a top level '&' selector, resolves to scope 81 | if (selector.includes('&')) { 82 | return selector.replace(/&/g, scope); 83 | } 84 | 85 | return scope + ' ' + selector; 86 | 87 | }); 88 | }); 89 | }, 90 | }; 91 | } 92 | 93 | 94 | module.exports = scopify; 95 | module.exports.postcss = true; 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PostCSS Scopify 2 | 3 | [![Node.js CI](https://github.com/pazams/postcss-scopify/actions/workflows/node.js.yml/badge.svg)](https://github.com/pazams/postcss-scopify/actions/workflows/node.js.yml) 4 | [![Test coverage][coveralls-image]][coveralls-url] 5 | [![Downloads][downloads-image]][downloads-url] 6 | 7 | [PostCSS] plugin that adds a user input scope to each selector. 8 | for a command line interface, see [scopify-cli]. 9 | 10 | [`poststylus`]: https://github.com/seaneking/poststylus 11 | [PostCSS API]: https://github.com/postcss/postcss/blob/master/docs/api.md 12 | [Broccoli]: https://github.com/jeffjewiss/broccoli-postcss 13 | [CLI tool]: https://github.com/code42day/postcss-cli 14 | [webpack]: https://github.com/postcss/postcss-loader 15 | [Brunch]: https://github.com/iamvdo/postcss-brunch 16 | [Grunt]: https://github.com/nDmitry/grunt-postcss 17 | [Gulp]: https://github.com/postcss/gulp-postcss 18 | [ENB]: https://github.com/theprotein/enb-postcss 19 | 20 | [travis-image]: https://img.shields.io/travis/pazams/postcss-scopify.svg?style=flat-square 21 | [travis-url]: https://travis-ci.org/pazams/postcss-scopify 22 | [coveralls-image]: https://img.shields.io/coveralls/pazams/postcss-scopify.svg?style=flat-square 23 | [coveralls-url]: https://coveralls.io/r/pazams/postcss-scopify 24 | [downloads-image]: https://img.shields.io/npm/dm/postcss-scopify.svg?style=flat-square 25 | [downloads-url]: https://npmjs.org/package/postcss-scopify 26 | 27 | [scopify-cli]: https://github.com/pazams/scopify-cli 28 | [PostCSS]: https://github.com/postcss/postcss 29 | 30 | __Example input__ 31 | 32 | ```css 33 | .foo, .boo h1 { 34 | /* declarations */ 35 | } 36 | 37 | & { 38 | /* declarations */ 39 | } 40 | ``` 41 | __Example output__ 42 | `scopify('#scope')` or `scopify({scope: '#scope'})` 43 | ```css 44 | #scope .foo, #scope .boo h1 { 45 | /* declarations */ 46 | } 47 | 48 | #scope { 49 | /* declarations */ 50 | } 51 | ``` 52 | 53 | ## Installation 54 | 55 | ``` 56 | npm install postcss-scopify 57 | ``` 58 | 59 | ## Usage 60 | 61 | ```javascript 62 | var fs = require('fs'); 63 | var postcss = require('postcss'); 64 | var scopify = require('postcss-scopify'); 65 | 66 | var css = fs.readFileSync('css/my-file.css', 'utf8').toString(); 67 | var out = postcss() 68 | .use(scopify('#scope')) 69 | .process(css) 70 | .css; 71 | ``` 72 | 73 | You can use PostCSS with your build tool. 74 | Note there are plugins for [Grunt], [Gulp], [webpack], [Broccoli], 75 | [Brunch] and [ENB]. 76 | See [PostCSS] docs for examples for your environment. 77 | 78 | ## Change Log 79 | ### v1.0.0 80 | - update all deps to latest versions 81 | - update plugin syntax for postCss v8+ 82 | - allow pass scope as object (because postcss+webpack) 83 | 84 | ### v0.1.8 85 | closes [#10](https://github.com/pazams/postcss-scopify/issues/10) 86 | 87 | ### v0.1.7 88 | fixes [#7](https://github.com/pazams/postcss-scopify/issues/7) 89 | 90 | ### v0.1.5 91 | closes [#3](https://github.com/pazams/postcss-scopify/issues/3) 92 | 93 | ### v0.1.4 94 | fixes [#4](https://github.com/pazams/postcss-scopify/issues/4) 95 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const assert = require('assert'); 3 | const fs = require('fs'); 4 | const postcss = require('postcss'); 5 | const scopify = require('..'); 6 | 7 | function fixture(name) { 8 | return fs.readFileSync('test/fixtures/' + name, 'utf8').trim(); 9 | } 10 | 11 | describe('postcss-scopify', function() { 12 | it('scopes all selectors with an id', function() { 13 | const output = postcss() 14 | .use(scopify('#foo')) 15 | .process(fixture('id.css')).css; 16 | const expected = fixture('id.expected.css'); 17 | 18 | assert.strictEqual(output, expected); 19 | }); 20 | 21 | it('scopes all selectors with a class', function() { 22 | const output = postcss() 23 | .use(scopify('.boo')) 24 | .process(fixture('class.css')).css; 25 | const expected = fixture('class.expected.css'); 26 | 27 | assert.strictEqual(output, expected); 28 | }); 29 | 30 | it('replaces & selector with an id scope', function() { 31 | const output = postcss() 32 | .use(scopify('#boo')) 33 | .process(fixture('id-container.css')).css; 34 | const expected = fixture('id-container.expected.css'); 35 | 36 | assert.strictEqual(output, expected); 37 | }); 38 | 39 | it('replaces & selector with a class scope', function() { 40 | const output = postcss() 41 | .use(scopify('.boo')) 42 | .process(fixture('class-container.css')).css; 43 | const expected = fixture('class-container.expected.css'); 44 | 45 | assert.strictEqual(output, expected); 46 | }); 47 | 48 | it('does NOT adds a scope if it already exists', function() { 49 | const output = postcss() 50 | .use(scopify('.boo')) 51 | .process(fixture('exisiting.css')).css; 52 | const expected = fixture('exisiting.expected.css'); 53 | 54 | assert.strictEqual(output, expected); 55 | }); 56 | 57 | it('does not allow invliad scopes', function() { 58 | try 59 | { 60 | postcss() 61 | .use(scopify('#foo , #boo')) 62 | .process(fixture('id.css')).css; 63 | } 64 | catch(error){ 65 | assert.strictEqual(error.name+'.'+error.reason, 'CssSyntaxError.invalid scope'); 66 | } 67 | 68 | }); 69 | 70 | it('treats empty scopes as invalid', function() { 71 | try 72 | { 73 | postcss() 74 | .use(scopify('')) 75 | .process(fixture('id.css')).css; 76 | } 77 | catch(error){ 78 | assert.strictEqual(error.name+'.'+error.reason, 'CssSyntaxError.invalid scope'); 79 | } 80 | 81 | }); 82 | 83 | it('allows scope as object input', function() { 84 | const output = postcss() 85 | .use(scopify({scope: '#foo'})) 86 | .process(fixture('id.css')).css; 87 | const expected = fixture('id.expected.css'); 88 | 89 | assert.strictEqual(output, expected); 90 | }); 91 | 92 | it('does not allow scope as object input with wrong key', function() { 93 | try 94 | { 95 | postcss() 96 | .use(scopify({wrongKey: '#foo'})) 97 | .process(fixture('id.css')).css; 98 | } 99 | catch(error){ 100 | assert.strictEqual(error.name+'.'+error.reason, 'CssSyntaxError.invalid scope'); 101 | } 102 | }); 103 | 104 | // https://github.com/pazams/postcss-scopify/issues/7 105 | it('should not scope keyframe definitions', function() { 106 | const output = postcss() 107 | .use(scopify('#foo')) 108 | .process(fixture('keyframe.css')).css; 109 | const expected = fixture('keyframe.expected.css'); 110 | 111 | assert.strictEqual(output, expected); 112 | }); 113 | 114 | it('should not scope at-rules, but do scope their nested rules for conditional groups at-rules only', function() { 115 | const output = postcss() 116 | .use(scopify('.boo')) 117 | .process(fixture('at-rules.css')).css; 118 | const expected = fixture('at-rules.expected.css'); 119 | 120 | assert.strictEqual(output, expected); 121 | }); 122 | 123 | it('should not scope LESS/SASS style nested rules', function() { 124 | const output = postcss() 125 | .use(scopify('.boo')) 126 | .process(fixture('nested.css')).css; 127 | const expected = fixture('nested.expected.css'); 128 | 129 | assert.strictEqual(output, expected); 130 | }); 131 | 132 | 133 | }); 134 | --------------------------------------------------------------------------------