├── test ├── non-class.expected.json ├── non-class.noscope.expected.json ├── basic.expected.json ├── basic.noscope.expected.json ├── media-query.expected.json ├── media-query.noscope.expected.json ├── keyframes-with-decimal.expected.json ├── keyframes-with-decimal.noscope.expected.json ├── non-class.expected.css ├── basic.expected.css ├── basic.noscope.expected.css ├── not-pseudo.noscope.expected.json ├── comments.expected.json ├── keyframes-usage.noscope.expected.json ├── non-class.noscope.expected.css ├── not-pseudo.expected.json ├── comments.noscope.expected.json ├── keyframes-usage.expected.json ├── animation-as-pseudo.noscope.expected.json ├── dot-in-values.noscope.expected.json ├── animation-as-pseudo.expected.json ├── dot-in-values.expected.json ├── multiple-extensions.noscope.expected.json ├── keyframes.noscope.expected.json ├── basic.source.js ├── extensions.noscope.expected.json ├── non-class.source.js ├── dot-in-values.noscope.expected.css ├── keyframes.expected.json ├── multiple-extensions.expected.json ├── not-pseudo.noscope.expected.css ├── basic.noscope.source.js ├── non-class.noscope.source.js ├── not-pseudo.expected.css ├── dot-in-values.expected.css ├── extends-in-media-query.noscope.expected.json ├── keyframes-usage.noscope.expected.css ├── keyframes-with-decimal.expected.css ├── keyframes-with-decimal.noscope.expected.css ├── extensions.expected.json ├── media-query.noscope.expected.css ├── keyframes-usage.expected.css ├── media-query.expected.css ├── multiple-extensions.noscope.expected.css ├── dot-in-values.source.js ├── extends-in-media-query.expected.json ├── not-pseudo.source.js ├── multiple-extensions.expected.css ├── dot-in-values.noscope.source.js ├── not-pseudo.noscope.source.js ├── keyframes-with-decimal.source.js ├── media-query.source.js ├── extensions.noscope.expected.css ├── keyframes-with-decimal.noscope.source.js ├── media-query.noscope.source.js ├── animation-as-pseudo.noscope.expected.css ├── animation-as-pseudo.expected.css ├── extensions.expected.css ├── keyframes-usage.source.js ├── animation-as-pseudo.source.js ├── animation-as-pseudo.noscope.source.js ├── keyframes-usage.noscope.source.js ├── extensions.source.js ├── extensions.noscope.source.js ├── keyframes.noscope.expected.css ├── multiple-extensions.source.js ├── keyframes.expected.css ├── multiple-extensions.noscope.source.js ├── keyframes.source.js ├── keyframes.noscope.source.js ├── extends-in-media-query.noscope.expected.css ├── extends-in-media-query.expected.css ├── extends-in-media-query.source.js ├── extends-in-media-query.noscope.source.js ├── comments.noscope.expected.css ├── comments.expected.css ├── comments.source.js ├── comments.source.noscope.js └── index.js ├── .gitignore ├── csjs.js ├── get-css.js ├── .istanbul.yml ├── lib ├── get-css.js ├── css-key.js ├── scoped-name.js ├── hash-string.js ├── regex.js ├── base62-encode.js ├── extract-exports.js ├── replace-animations.js ├── scopeify.js ├── build-exports.js ├── css-extract-extends.js ├── composition.js └── csjs.js ├── index.js ├── .zuul.yml ├── docs ├── automatic-vendor-prefixing.md ├── comments.md └── keyframe-animations.md ├── LICENSE ├── package.json ├── .travis.yml └── README.md /test/non-class.expected.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/non-class.noscope.expected.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | npm-debug.log 4 | -------------------------------------------------------------------------------- /test/basic.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo_4xZqpC" 3 | } 4 | -------------------------------------------------------------------------------- /test/basic.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo" 3 | } 4 | -------------------------------------------------------------------------------- /test/media-query.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo_nYfJE" 3 | } 4 | -------------------------------------------------------------------------------- /test/media-query.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo" 3 | } 4 | -------------------------------------------------------------------------------- /csjs.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./lib/csjs'); 4 | -------------------------------------------------------------------------------- /get-css.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./lib/get-css'); 4 | -------------------------------------------------------------------------------- /test/keyframes-with-decimal.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "woot": "woot_3xZGg2" 3 | } 4 | -------------------------------------------------------------------------------- /test/keyframes-with-decimal.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "woot": "woot" 3 | } 4 | -------------------------------------------------------------------------------- /test/non-class.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | #foo { 4 | color: red; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /test/basic.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .foo_4xZqpC { 4 | color: red; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /test/basic.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .foo { 4 | color: red; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /test/not-pseudo.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo", 3 | "bar": "bar" 4 | } 5 | -------------------------------------------------------------------------------- /test/comments.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo_1LMZVZ", 3 | "wow": "wow_1LMZVZ" 4 | } 5 | -------------------------------------------------------------------------------- /test/keyframes-usage.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "boo": "boo", 3 | "woo": "woo" 4 | } 5 | -------------------------------------------------------------------------------- /test/non-class.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | #foo { 4 | color: red; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /test/not-pseudo.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo_25D7El", 3 | "bar": "bar_25D7El" 4 | } 5 | -------------------------------------------------------------------------------- /test/comments.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo_1LMZVZ", 3 | "wow": "wow_1LMZVZ" 4 | } 5 | -------------------------------------------------------------------------------- /test/keyframes-usage.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "boo": "boo_2MFmAf", 3 | "woo": "woo_2MFmAf" 4 | } 5 | -------------------------------------------------------------------------------- /.istanbul.yml: -------------------------------------------------------------------------------- 1 | instrumentation: 2 | excludes: 3 | - 'lib/base62-encode.js' 4 | - 'lib/hash-string.js' 5 | -------------------------------------------------------------------------------- /test/animation-as-pseudo.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "animation": "animation", 3 | "hover": "hover" 4 | } 5 | -------------------------------------------------------------------------------- /test/dot-in-values.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo", 3 | "baz": "baz", 4 | "bar": "bar" 5 | } 6 | -------------------------------------------------------------------------------- /test/animation-as-pseudo.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "animation": "animation_zz7qS", 3 | "hover": "hover_zz7qS" 4 | } 5 | -------------------------------------------------------------------------------- /test/dot-in-values.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo_1Veq6n", 3 | "baz": "baz_1Veq6n", 4 | "bar": "bar_1Veq6n" 5 | } 6 | -------------------------------------------------------------------------------- /test/multiple-extensions.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "lol": "lol", 3 | "baz": "baz lol bar foo", 4 | "fob": "fob foo" 5 | } 6 | -------------------------------------------------------------------------------- /test/keyframes.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "yolo": "yolo", 3 | "yoloYolo": "yoloYolo", 4 | "foo": "foo", 5 | "bar": "bar" 6 | } 7 | -------------------------------------------------------------------------------- /test/basic.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | .foo { 6 | color: red; 7 | } 8 | 9 | `; 10 | -------------------------------------------------------------------------------- /test/extensions.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo", 3 | "bar": "bar foo", 4 | "baz": "baz bar foo", 5 | "bazz": "bazz bar foo" 6 | } 7 | -------------------------------------------------------------------------------- /test/non-class.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | #foo { 6 | color: red; 7 | } 8 | 9 | `; 10 | -------------------------------------------------------------------------------- /lib/get-css.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var cssKey = require('./css-key'); 4 | 5 | module.exports = function getCss(csjs) { 6 | return csjs[cssKey]; 7 | }; 8 | -------------------------------------------------------------------------------- /test/dot-in-values.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .foo { 4 | font-size: 1.3em; 5 | } 6 | 7 | .bar { font-size: 12.5px; } .baz { width: 33.3% } 8 | 9 | -------------------------------------------------------------------------------- /test/keyframes.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "yolo": "yolo_4lrRRM", 3 | "yoloYolo": "yoloYolo_4lrRRM", 4 | "foo": "foo_4lrRRM", 5 | "bar": "bar_4lrRRM" 6 | } 7 | -------------------------------------------------------------------------------- /test/multiple-extensions.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "lol": "lol_IZbgA", 3 | "baz": "baz_IZbgA lol_IZbgA bar_g7Bft foo_g7Bft", 4 | "fob": "fob_IZbgA foo_g7Bft" 5 | } 6 | -------------------------------------------------------------------------------- /test/not-pseudo.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @media screen and (min-width: 769px) { 4 | .foo:not(.bar) { 5 | display: flex; 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/basic.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | .foo { 6 | color: red; 7 | } 8 | 9 | `; 10 | -------------------------------------------------------------------------------- /test/non-class.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | #foo { 6 | color: red; 7 | } 8 | 9 | `; 10 | -------------------------------------------------------------------------------- /test/not-pseudo.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @media screen and (min-width: 769px) { 4 | .foo_25D7El:not(.bar_25D7El) { 5 | display: flex; 6 | } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/dot-in-values.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .foo_1Veq6n { 4 | font-size: 1.3em; 5 | } 6 | 7 | .bar_1Veq6n { font-size: 12.5px; } .baz_1Veq6n { width: 33.3% } 8 | 9 | -------------------------------------------------------------------------------- /test/extends-in-media-query.noscope.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "hello", 3 | "underline": "underline", 4 | "woot": "woot foo underline hello yay", 5 | "yay": "yay" 6 | } 7 | -------------------------------------------------------------------------------- /lib/css-key.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * CSS identifiers with whitespace are invalid 5 | * Hence this key will not cause a collision 6 | */ 7 | 8 | module.exports = ' css '; 9 | -------------------------------------------------------------------------------- /test/keyframes-usage.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .woo { 4 | animation: yolo 5s infinite; 5 | } 6 | 7 | .boo { 8 | animation-name: yoloYolo; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /test/keyframes-with-decimal.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @keyframes woot_3xZGg2 { 4 | 0% { opacity: 0; } 5 | 33.3% { opacity: 0.333; } 6 | 100% { opacity: 1; } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/keyframes-with-decimal.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @keyframes woot { 4 | 0% { opacity: 0; } 5 | 33.3% { opacity: 0.333; } 6 | 100% { opacity: 1; } 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/extensions.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "foo_g7Bft", 3 | "bar": "bar_g7Bft foo_g7Bft", 4 | "baz": "baz_g7Bft bar_g7Bft foo_g7Bft", 5 | "bazz": "bazz_g7Bft bar_g7Bft foo_g7Bft" 6 | } 7 | -------------------------------------------------------------------------------- /test/media-query.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .foo { 4 | color: red; 5 | } 6 | 7 | @media (max-width: 480px) { 8 | .foo { 9 | color: blue; 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/keyframes-usage.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .woo_2MFmAf { 4 | animation: yolo_4lrRRM 5s infinite; 5 | } 6 | 7 | .boo_2MFmAf { 8 | animation-name: yoloYolo_4lrRRM; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /test/media-query.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .foo_nYfJE { 4 | color: red; 5 | } 6 | 7 | @media (max-width: 480px) { 8 | .foo_nYfJE { 9 | color: blue; 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/multiple-extensions.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .lol { 4 | font-family: serif; 5 | } 6 | 7 | .baz { 8 | font-size: 12px; 9 | } 10 | 11 | .fob { 12 | font-weight: 500; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /test/dot-in-values.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | .foo { 6 | font-size: 1.3em; 7 | } 8 | 9 | .bar { font-size: 12.5px; } .baz { width: 33.3% } 10 | 11 | `; 12 | -------------------------------------------------------------------------------- /test/extends-in-media-query.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello": "hello_2XWyPW", 3 | "underline": "underline_2XWyPW", 4 | "woot": "woot_2XWyPW foo_4xZqpC underline_2XWyPW hello_2XWyPW yay_2XWyPW", 5 | "yay": "yay_2XWyPW" 6 | } 7 | -------------------------------------------------------------------------------- /test/not-pseudo.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | @media screen and (min-width: 769px) { 6 | .foo:not(.bar) { 7 | display: flex; 8 | } 9 | } 10 | 11 | `; 12 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var csjs = require('./csjs'); 4 | 5 | module.exports = csjs(); 6 | module.exports.csjs = csjs; 7 | module.exports.noScope = csjs({ noscope: true }); 8 | module.exports.getCss = require('./get-css'); 9 | -------------------------------------------------------------------------------- /test/multiple-extensions.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .lol_IZbgA { 4 | font-family: serif; 5 | } 6 | 7 | .baz_IZbgA { 8 | font-size: 12px; 9 | } 10 | 11 | .fob_IZbgA { 12 | font-weight: 500; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /test/dot-in-values.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | .foo { 6 | font-size: 1.3em; 7 | } 8 | 9 | .bar { font-size: 12.5px; } .baz { width: 33.3% } 10 | 11 | `; 12 | -------------------------------------------------------------------------------- /test/not-pseudo.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | @media screen and (min-width: 769px) { 6 | .foo:not(.bar) { 7 | display: flex; 8 | } 9 | } 10 | 11 | `; 12 | -------------------------------------------------------------------------------- /test/keyframes-with-decimal.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | @keyframes woot { 6 | 0% { opacity: 0; } 7 | 33.3% { opacity: 0.333; } 8 | 100% { opacity: 1; } 9 | } 10 | 11 | `; 12 | -------------------------------------------------------------------------------- /test/media-query.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | .foo { 6 | color: red; 7 | } 8 | 9 | @media (max-width: 480px) { 10 | .foo { 11 | color: blue; 12 | } 13 | } 14 | 15 | `; 16 | -------------------------------------------------------------------------------- /test/extensions.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .foo { 4 | color: red; 5 | } 6 | 7 | .bar { 8 | background: blue; 9 | } 10 | 11 | .baz { 12 | text-transform: uppercase; 13 | } 14 | 15 | .bazz { 16 | color: white; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /test/keyframes-with-decimal.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | @keyframes woot { 6 | 0% { opacity: 0; } 7 | 33.3% { opacity: 0.333; } 8 | 100% { opacity: 1; } 9 | } 10 | 11 | `; 12 | -------------------------------------------------------------------------------- /test/media-query.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | .foo { 6 | color: red; 7 | } 8 | 9 | @media (max-width: 480px) { 10 | .foo { 11 | color: blue; 12 | } 13 | } 14 | 15 | `; 16 | -------------------------------------------------------------------------------- /test/animation-as-pseudo.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @keyframes hover { 4 | 0% { opacity: 0.0; } 5 | 100% { opacity: 0.5; } 6 | } 7 | 8 | @media (max-width: 480px) { 9 | .animation:hover { 10 | background: green; 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /test/animation-as-pseudo.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @keyframes hover_zz7qS { 4 | 0% { opacity: 0.0; } 5 | 100% { opacity: 0.5; } 6 | } 7 | 8 | @media (max-width: 480px) { 9 | .animation_zz7qS:hover { 10 | background: green; 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /test/extensions.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .foo_g7Bft { 4 | color: red; 5 | } 6 | 7 | .bar_g7Bft { 8 | background: blue; 9 | } 10 | 11 | .baz_g7Bft { 12 | text-transform: uppercase; 13 | } 14 | 15 | .bazz_g7Bft { 16 | color: white; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /test/keyframes-usage.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | const keyframes = require('./keyframes.source'); 4 | 5 | module.exports = csjs` 6 | 7 | .woo { 8 | animation: ${keyframes.yolo} 5s infinite; 9 | } 10 | 11 | .boo { 12 | animation-name: ${keyframes.yoloYolo}; 13 | } 14 | 15 | `; 16 | -------------------------------------------------------------------------------- /lib/scoped-name.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var encode = require('./base62-encode'); 4 | var hash = require('./hash-string'); 5 | 6 | module.exports = function fileScoper(fileSrc) { 7 | var suffix = encode(hash(fileSrc)); 8 | 9 | return function scopedName(name) { 10 | return name + '_' + suffix; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /test/animation-as-pseudo.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | @keyframes hover { 6 | 0% { opacity: 0.0; } 7 | 100% { opacity: 0.5; } 8 | } 9 | 10 | @media (max-width: 480px) { 11 | .animation:hover { 12 | background: green; 13 | } 14 | } 15 | 16 | `; 17 | -------------------------------------------------------------------------------- /test/animation-as-pseudo.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | @keyframes hover { 6 | 0% { opacity: 0.0; } 7 | 100% { opacity: 0.5; } 8 | } 9 | 10 | @media (max-width: 480px) { 11 | .animation:hover { 12 | background: green; 13 | } 14 | } 15 | 16 | `; 17 | -------------------------------------------------------------------------------- /test/keyframes-usage.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | const keyframes = require('./keyframes.noscope.source'); 4 | 5 | module.exports = csjs` 6 | 7 | .woo { 8 | animation: ${keyframes.yolo} 5s infinite; 9 | } 10 | 11 | .boo { 12 | animation-name: ${keyframes.yoloYolo}; 13 | } 14 | 15 | `; 16 | -------------------------------------------------------------------------------- /test/extensions.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | .foo { 6 | color: red; 7 | } 8 | 9 | .bar extends .foo { 10 | background: blue; 11 | } 12 | 13 | .baz extends .bar { 14 | text-transform: uppercase; 15 | } 16 | 17 | .bazz extends .bar{ 18 | color: white; 19 | } 20 | 21 | `; 22 | -------------------------------------------------------------------------------- /.zuul.yml: -------------------------------------------------------------------------------- 1 | ui: tape 2 | browsers: 3 | - name: chrome 4 | version: latest 5 | - name: firefox 6 | version: latest 7 | - name: safari 8 | version: latest 9 | - name: iphone 10 | version: latest 11 | - name: microsoftedge 12 | version: latest 13 | browserify: 14 | - transform: bulkify 15 | - transform: brfs 16 | - transform: folderify 17 | -------------------------------------------------------------------------------- /docs/automatic-vendor-prefixing.md: -------------------------------------------------------------------------------- 1 | # Automatic Vendor Prefixing 2 | 3 | ## Runtime solutions 4 | 5 | * http://leaverou.github.io/prefixfree/ 6 | 7 | ## Buildtime solutions 8 | 9 | * https://github.com/rtsao/babel-plugin-csjs-postcss w/ https://github.com/postcss/autoprefixer 10 | * https://github.com/rtsao/csjs-extractify w/ https://github.com/postcss/autoprefixer 11 | -------------------------------------------------------------------------------- /test/extensions.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | .foo { 6 | color: red; 7 | } 8 | 9 | .bar extends .foo { 10 | background: blue; 11 | } 12 | 13 | .baz extends .bar { 14 | text-transform: uppercase; 15 | } 16 | 17 | .bazz extends .bar{ 18 | color: white; 19 | } 20 | 21 | `; 22 | -------------------------------------------------------------------------------- /test/keyframes.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @keyframes yolo { 4 | 0% { opacity: 0; } 5 | 100% { opacity: 1; } 6 | } 7 | 8 | .foo { 9 | animation: yolo 5s infinite; 10 | } 11 | 12 | @keyframes yoloYolo { 13 | 0% { opacity: 0; } 14 | 100% { opacity: 1; } 15 | } 16 | 17 | .bar { 18 | animation: yoloYolo 5s infinite; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /lib/hash-string.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * djb2 string hash implementation based on string-hash module: 5 | * https://github.com/darkskyapp/string-hash 6 | */ 7 | 8 | module.exports = function hashStr(str) { 9 | var hash = 5381; 10 | var i = str.length; 11 | 12 | while (i) { 13 | hash = (hash * 33) ^ str.charCodeAt(--i) 14 | } 15 | return hash >>> 0; 16 | }; 17 | -------------------------------------------------------------------------------- /test/multiple-extensions.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | const external = require('./extensions.source'); 4 | 5 | module.exports = csjs` 6 | 7 | .lol { 8 | font-family: serif; 9 | } 10 | 11 | .baz extends .lol, ${external.bar} { 12 | font-size: 12px; 13 | } 14 | 15 | .fob extends ${external.foo} { 16 | font-weight: 500; 17 | } 18 | 19 | `; 20 | -------------------------------------------------------------------------------- /test/keyframes.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | @keyframes yolo_4lrRRM { 4 | 0% { opacity: 0; } 5 | 100% { opacity: 1; } 6 | } 7 | 8 | .foo_4lrRRM { 9 | animation: yolo_4lrRRM 5s infinite; 10 | } 11 | 12 | @keyframes yoloYolo_4lrRRM { 13 | 0% { opacity: 0; } 14 | 100% { opacity: 1; } 15 | } 16 | 17 | .bar_4lrRRM { 18 | animation: yoloYolo_4lrRRM 5s infinite; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /test/multiple-extensions.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | const external = require('./extensions.noscope.source'); 4 | 5 | module.exports = csjs` 6 | 7 | .lol { 8 | font-family: serif; 9 | } 10 | 11 | .baz extends .lol, ${external.bar} { 12 | font-size: 12px; 13 | } 14 | 15 | .fob extends ${external.foo} { 16 | font-weight: 500; 17 | } 18 | 19 | `; 20 | -------------------------------------------------------------------------------- /test/keyframes.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | module.exports = csjs` 4 | 5 | @keyframes yolo { 6 | 0% { opacity: 0; } 7 | 100% { opacity: 1; } 8 | } 9 | 10 | .foo { 11 | animation: yolo 5s infinite; 12 | } 13 | 14 | @keyframes yoloYolo { 15 | 0% { opacity: 0; } 16 | 100% { opacity: 1; } 17 | } 18 | 19 | .bar { 20 | animation: yoloYolo 5s infinite; 21 | } 22 | 23 | `; 24 | -------------------------------------------------------------------------------- /test/keyframes.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | module.exports = csjs` 4 | 5 | @keyframes yolo { 6 | 0% { opacity: 0; } 7 | 100% { opacity: 1; } 8 | } 9 | 10 | .foo { 11 | animation: yolo 5s infinite; 12 | } 13 | 14 | @keyframes yoloYolo { 15 | 0% { opacity: 0; } 16 | 100% { opacity: 1; } 17 | } 18 | 19 | .bar { 20 | animation: yoloYolo 5s infinite; 21 | } 22 | 23 | `; 24 | -------------------------------------------------------------------------------- /test/extends-in-media-query.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .woot { 4 | color: red; 5 | } 6 | 7 | .hello { 8 | font-size: 10px; 9 | } 10 | 11 | .yay { 12 | font-weight: bold; 13 | } 14 | 15 | @media (max-width: 480px) { 16 | .woot { 17 | color: blue; 18 | } 19 | } 20 | 21 | @media (max-width: 580px) { 22 | .woot { 23 | color: green; 24 | } 25 | } 26 | 27 | .underline { 28 | text-decoration: underline; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /test/extends-in-media-query.expected.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | .woot_2XWyPW { 4 | color: red; 5 | } 6 | 7 | .hello_2XWyPW { 8 | font-size: 10px; 9 | } 10 | 11 | .yay_2XWyPW { 12 | font-weight: bold; 13 | } 14 | 15 | @media (max-width: 480px) { 16 | .woot_2XWyPW { 17 | color: blue; 18 | } 19 | } 20 | 21 | @media (max-width: 580px) { 22 | .woot_2XWyPW { 23 | color: green; 24 | } 25 | } 26 | 27 | .underline_2XWyPW { 28 | text-decoration: underline; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /lib/regex.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var findClasses = /(\.)(?!\d)([^\s\.,{\[>+~#:)]*)(?![^{]*})/.source; 4 | var findKeyframes = /(@\S*keyframes\s*)([^{\s]*)/.source; 5 | var ignoreComments = /(?!(?:[^*/]|\*[^/]|\/[^*])*\*+\/)/.source; 6 | 7 | var classRegex = new RegExp(findClasses + ignoreComments, 'g'); 8 | var keyframesRegex = new RegExp(findKeyframes + ignoreComments, 'g'); 9 | 10 | module.exports = { 11 | classRegex: classRegex, 12 | keyframesRegex: keyframesRegex, 13 | ignoreComments: ignoreComments, 14 | }; 15 | -------------------------------------------------------------------------------- /lib/base62-encode.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * base62 encode implementation based on base62 module: 5 | * https://github.com/andrew/base62.js 6 | */ 7 | 8 | var CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 9 | 10 | module.exports = function encode(integer) { 11 | if (integer === 0) { 12 | return '0'; 13 | } 14 | var str = ''; 15 | while (integer > 0) { 16 | str = CHARS[integer % 62] + str; 17 | integer = Math.floor(integer / 62); 18 | } 19 | return str; 20 | }; 21 | -------------------------------------------------------------------------------- /lib/extract-exports.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var regex = require('./regex'); 4 | var classRegex = regex.classRegex; 5 | var keyframesRegex = regex.keyframesRegex; 6 | 7 | module.exports = extractExports; 8 | 9 | function extractExports(css) { 10 | return { 11 | css: css, 12 | keyframes: getExport(css, keyframesRegex), 13 | classes: getExport(css, classRegex) 14 | }; 15 | } 16 | 17 | function getExport(css, regex) { 18 | var prop = {}; 19 | var match; 20 | while((match = regex.exec(css)) !== null) { 21 | var name = match[2]; 22 | prop[name] = name; 23 | } 24 | return prop; 25 | } 26 | -------------------------------------------------------------------------------- /test/extends-in-media-query.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../'); 2 | 3 | const basic = require('./basic.source'); 4 | 5 | module.exports = csjs` 6 | 7 | .woot { 8 | color: red; 9 | } 10 | 11 | .hello { 12 | font-size: 10px; 13 | } 14 | 15 | .yay { 16 | font-weight: bold; 17 | } 18 | 19 | @media (max-width: 480px) { 20 | .woot extends .hello, .yay { 21 | color: blue; 22 | } 23 | } 24 | 25 | @media (max-width: 580px) { 26 | .woot extends ${basic.foo}, .underline { 27 | color: green; 28 | } 29 | } 30 | 31 | .underline { 32 | text-decoration: underline; 33 | } 34 | 35 | `; 36 | -------------------------------------------------------------------------------- /test/extends-in-media-query.noscope.source.js: -------------------------------------------------------------------------------- 1 | const csjs = require('../').noScope; 2 | 3 | const basic = require('./basic.noscope.source'); 4 | 5 | module.exports = csjs` 6 | 7 | .woot { 8 | color: red; 9 | } 10 | 11 | .hello { 12 | font-size: 10px; 13 | } 14 | 15 | .yay { 16 | font-weight: bold; 17 | } 18 | 19 | @media (max-width: 480px) { 20 | .woot extends .hello, .yay { 21 | color: blue; 22 | } 23 | } 24 | 25 | @media (max-width: 580px) { 26 | .woot extends ${basic.foo}, .underline { 27 | color: green; 28 | } 29 | } 30 | 31 | .underline { 32 | text-decoration: underline; 33 | } 34 | 35 | `; 36 | -------------------------------------------------------------------------------- /test/comments.noscope.expected.css: -------------------------------------------------------------------------------- 1 | 2 | /* Here's a comment */ 3 | /*squishedcomment*/ 4 | /********crazycomment********/ 5 | /* A comment with a period at the end. */ 6 | /* A comment with a period followed by numbers v1.2.3 */ 7 | /* A comment with a .className */ 8 | /* 9 | * Here's a comment 10 | * A comment with a period at the end. 11 | * A comment with a period followed by numbers v1.2.3 12 | * A comment with a .className 13 | */ 14 | /* .inlineCss { color: red; } */ 15 | /* 16 | .commentedOutCss { 17 | color: blue; 18 | } 19 | */ 20 | .foo { 21 | color: red; /* comment on a line */ 22 | animation-name: wow; 23 | } 24 | 25 | @keyframes wow {} 26 | 27 | /* 28 | @keyframes bam {} 29 | */ 30 | 31 | /* .woot { 32 | animation-name: bam; 33 | } */ 34 | 35 | /* .hmm { 36 | animation-name: wow; 37 | } */ 38 | 39 | -------------------------------------------------------------------------------- /test/comments.expected.css: -------------------------------------------------------------------------------- 1 | 2 | /* Here's a comment */ 3 | /*squishedcomment*/ 4 | /********crazycomment********/ 5 | /* A comment with a period at the end. */ 6 | /* A comment with a period followed by numbers v1.2.3 */ 7 | /* A comment with a .className */ 8 | /* 9 | * Here's a comment 10 | * A comment with a period at the end. 11 | * A comment with a period followed by numbers v1.2.3 12 | * A comment with a .className 13 | */ 14 | /* .inlineCss { color: red; } */ 15 | /* 16 | .commentedOutCss { 17 | color: blue; 18 | } 19 | */ 20 | .foo_1LMZVZ { 21 | color: red; /* comment on a line */ 22 | animation-name: wow_1LMZVZ; 23 | } 24 | 25 | @keyframes wow_1LMZVZ {} 26 | 27 | /* 28 | @keyframes bam {} 29 | */ 30 | 31 | /* .woot { 32 | animation-name: bam; 33 | } */ 34 | 35 | /* .hmm { 36 | animation-name: wow; 37 | } */ 38 | 39 | -------------------------------------------------------------------------------- /lib/replace-animations.js: -------------------------------------------------------------------------------- 1 | var ignoreComments = require('./regex').ignoreComments; 2 | 3 | module.exports = replaceAnimations; 4 | 5 | function replaceAnimations(result) { 6 | var animations = Object.keys(result.keyframes).reduce(function(acc, key) { 7 | acc[result.keyframes[key]] = key; 8 | return acc; 9 | }, {}); 10 | var unscoped = Object.keys(animations); 11 | 12 | if (unscoped.length) { 13 | var regexStr = '((?:animation|animation-name)\\s*:[^};]*)(' 14 | + unscoped.join('|') + ')([;\\s])' + ignoreComments; 15 | var regex = new RegExp(regexStr, 'g'); 16 | 17 | var replaced = result.css.replace(regex, function(match, preamble, name, ending) { 18 | return preamble + animations[name] + ending; 19 | }); 20 | 21 | return { 22 | css: replaced, 23 | keyframes: result.keyframes, 24 | classes: result.classes 25 | } 26 | } 27 | 28 | return result; 29 | } 30 | -------------------------------------------------------------------------------- /docs/comments.md: -------------------------------------------------------------------------------- 1 | # CSS Comments 2 | 3 | CSJS will ignore everything inside CSS `/* */` comments, but preserve it in the output CSS. Note that CSJS will break if there are unbalanced comments. 4 | 5 | ([Live editable codepen.io demo](http://codepen.io/rtsao/pen/MKNaPR?editors=0010)) 6 | 7 | ```javascript 8 | const csjs = require('csjs'); 9 | const {h1} = require('react').DOM; 10 | 11 | const styles = csjs` 12 | 13 | /* 14 | @keyframes fadeIn { 15 | from { opacity: 0; } 16 | to { opacity: 1; } 17 | } 18 | */ 19 | 20 | /* .foo { color: red; } */ 21 | 22 | .pulse { 23 | animation: 1s ease-in-out infinite fadeIn alternate; 24 | } 25 | 26 | `; 27 | 28 | const html = require('react-dom/server').renderToStaticMarkup( 29 | h1({className: styles.title}, 'Hello World!') 30 | ); 31 | /* 32 |