postcss-modules-scope/0000755000175000017500000000000013624163411014710 5ustar xavierxavierpostcss-modules-scope/lib/0000755000175000017500000000000013624163411015456 5ustar xavierxavierpostcss-modules-scope/lib/index.js0000644000175000017500000001535313624163411017132 0ustar xavierxavier'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _postcss = require('postcss'); var _postcss2 = _interopRequireDefault(_postcss); var _cssSelectorTokenizer = require('css-selector-tokenizer'); var _cssSelectorTokenizer2 = _interopRequireDefault(_cssSelectorTokenizer); var hasOwnProperty = Object.prototype.hasOwnProperty; function getSingleLocalNamesForComposes(selectors) { return selectors.nodes.map(function (node) { if (node.type !== 'selector' || node.nodes.length !== 1) { throw new Error('composition is only allowed when selector is single :local class name not in "' + _cssSelectorTokenizer2['default'].stringify(selectors) + '"'); } node = node.nodes[0]; if (node.type !== 'nested-pseudo-class' || node.name !== 'local' || node.nodes.length !== 1) { throw new Error('composition is only allowed when selector is single :local class name not in "' + _cssSelectorTokenizer2['default'].stringify(selectors) + '", "' + _cssSelectorTokenizer2['default'].stringify(node) + '" is weird'); } node = node.nodes[0]; if (node.type !== 'selector' || node.nodes.length !== 1) { throw new Error('composition is only allowed when selector is single :local class name not in "' + _cssSelectorTokenizer2['default'].stringify(selectors) + '", "' + _cssSelectorTokenizer2['default'].stringify(node) + '" is weird'); } node = node.nodes[0]; if (node.type !== 'class') { // 'id' is not possible, because you can't compose ids throw new Error('composition is only allowed when selector is single :local class name not in "' + _cssSelectorTokenizer2['default'].stringify(selectors) + '", "' + _cssSelectorTokenizer2['default'].stringify(node) + '" is weird'); } return node.name; }); } var processor = _postcss2['default'].plugin('postcss-modules-scope', function (options) { return function (css) { var generateScopedName = options && options.generateScopedName || processor.generateScopedName; var exports = {}; function exportScopedName(name) { var scopedName = generateScopedName(name, css.source.input.from, css.source.input.css); exports[name] = exports[name] || []; if (exports[name].indexOf(scopedName) < 0) { exports[name].push(scopedName); } return scopedName; } function localizeNode(node) { var newNode = Object.create(node); switch (node.type) { case 'selector': newNode.nodes = node.nodes.map(localizeNode); return newNode; case 'class': case 'id': var scopedName = exportScopedName(node.name); newNode.name = scopedName; return newNode; } throw new Error(node.type + ' ("' + _cssSelectorTokenizer2['default'].stringify(node) + '") is not allowed in a :local block'); } function traverseNode(node) { switch (node.type) { case 'nested-pseudo-class': if (node.name === 'local') { if (node.nodes.length !== 1) { throw new Error('Unexpected comma (",") in :local block'); } return localizeNode(node.nodes[0]); } /* falls through */ case 'selectors': case 'selector': var newNode = Object.create(node); newNode.nodes = node.nodes.map(traverseNode); return newNode; } return node; } // Find any :import and remember imported names var importedNames = {}; css.walkRules(function (rule) { if (/^:import\(.+\)$/.test(rule.selector)) { rule.walkDecls(function (decl) { importedNames[decl.prop] = true; }); } }); // Find any :local classes css.walkRules(function (rule) { var selector = _cssSelectorTokenizer2['default'].parse(rule.selector); var newSelector = traverseNode(selector); rule.selector = _cssSelectorTokenizer2['default'].stringify(newSelector); rule.walkDecls(/composes|compose-with/, function (decl) { var localNames = getSingleLocalNamesForComposes(selector); var classes = decl.value.split(/\s+/); classes.forEach(function (className) { var global = /^global\(([^\)]+)\)$/.exec(className); if (global) { localNames.forEach(function (exportedName) { exports[exportedName].push(global[1]); }); } else if (hasOwnProperty.call(importedNames, className)) { localNames.forEach(function (exportedName) { exports[exportedName].push(className); }); } else if (hasOwnProperty.call(exports, className)) { localNames.forEach(function (exportedName) { exports[className].forEach(function (item) { exports[exportedName].push(item); }); }); } else { throw decl.error('referenced class name "' + className + '" in ' + decl.prop + ' not found'); } }); decl.remove(); }); rule.walkDecls(function (decl) { var tokens = decl.value.split(/(,|'[^']*'|"[^"]*")/); tokens = tokens.map(function (token, idx) { if (idx === 0 || tokens[idx - 1] === ',') { var localMatch = /^(\s*):local\s*\((.+?)\)/.exec(token); if (localMatch) { return localMatch[1] + exportScopedName(localMatch[2]) + token.substr(localMatch[0].length); } else { return token; } } else { return token; } }); decl.value = tokens.join(''); }); }); // Find any :local keyframes css.walkAtRules(function (atrule) { if (/keyframes$/.test(atrule.name)) { var localMatch = /^\s*:local\s*\((.+?)\)\s*$/.exec(atrule.params); if (localMatch) { atrule.params = exportScopedName(localMatch[1]); } } }); // If we found any :locals, insert an :export rule var exportedNames = Object.keys(exports); if (exportedNames.length > 0) { (function () { var exportRule = _postcss2['default'].rule({ selector: ':export' }); exportedNames.forEach(function (exportedName) { return exportRule.append({ prop: exportedName, value: exports[exportedName].join(' '), raws: { before: '\n ' } }); }); css.append(exportRule); })(); } }; }); processor.generateScopedName = function (exportedName, path) { var sanitisedPath = path.replace(/\.[^\.\/\\]+$/, '').replace(/[\W_]+/g, '_').replace(/^_|_$/g, ''); return '_' + sanitisedPath + '__' + exportedName; }; exports['default'] = processor; module.exports = exports['default'];postcss-modules-scope/package.json0000644000175000017500000000270713624163411017204 0ustar xavierxavier{ "name": "postcss-modules-scope", "version": "1.1.0", "description": "A CSS Modules transform to extract export statements from local-scope classes", "main": "lib/index.js", "scripts": { "lint": "eslint src", "build": "babel --out-dir lib src", "watch": "chokidar src -c 'npm run build'", "test": "mocha --compilers js:babel/register", "posttest": "npm run lint && npm run build", "autotest": "chokidar src test -c 'npm test'", "precover": "npm run lint && npm run build", "cover": "babel-istanbul cover node_modules/.bin/_mocha", "travis": "npm run cover -- --report lcovonly", "prepublish": "npm run build" }, "repository": { "type": "git", "url": "https://github.com/css-modules/postcss-modules-scope.git" }, "keywords": [ "css-modules", "postcss", "plugin" ], "files": [ "lib" ], "author": "Glen Maddern", "license": "ISC", "bugs": { "url": "https://github.com/css-modules/postcss-modules-scope/issues" }, "homepage": "https://github.com/css-modules/postcss-modules-scope", "dependencies": { "css-selector-tokenizer": "^0.7.0", "postcss": "^6.0.1" }, "devDependencies": { "babel": "^5.4.7", "babel-eslint": "^6.1.2", "babel-istanbul": "^0.4.0", "babelify": "^7.1.0", "chokidar-cli": "^1.0.1", "codecov.io": "^0.1.2", "coveralls": "^2.11.2", "css-selector-parser": "^1.0.4", "eslint": "^1.5.0", "mocha": "^3.0.1" } } postcss-modules-scope/README.md0000644000175000017500000000440113624163411016166 0ustar xavierxavier# CSS Modules: Scope Locals & Extend [![Build Status](https://travis-ci.org/css-modules/postcss-modules-scope.svg?branch=master)](https://travis-ci.org/css-modules/postcss-modules-scope) Transforms: ```css :local(.continueButton) { color: green; } ``` into: ```css :export { continueButton: __buttons_continueButton_djd347adcxz9; } .__buttons_continueButton_djd347adcxz9 { color: green; } ``` so it doesn't pollute CSS global scope and can be simply used in JS like so: ```js import styles from './buttons.css' elem.innerHTML = `` ``` ## Composition Since we're exporting class names, there's no reason to export only one. This can give us some really useful reuse of styles: ```css .globalButtonStyle { background: white; border: 1px solid; border-radius: 0.25rem; } .globalButtonStyle:hover { box-shadow: 0 0 4px -2px; } :local(.continueButton) { compose-with: globalButtonStyle; color: green; } ``` becomes: ``` .globalButtonStyle { background: white; border: 1px solid; border-radius: 0.25rem; } .globalButtonStyle:hover { box-shadow: 0 0 4px -2px; } :local(.continueButton) { compose-with: globalButtonStyle; color: green; } ``` **Note:** you can also use `composes` as a shorthand for `compose-with` ## Local-by-default & reuse across files You're looking for [CSS Modules](https://github.com/css-modules/css-modules). It uses this plugin as well as a few others, and it's amazing. ## Building ``` npm install npm test ``` [![Build Status](https://travis-ci.org/css-modules/postcss-modules-scope.svg?branch=master)](https://travis-ci.org/css-modules/postcss-modules-scope) * Lines: [![Coverage Status](https://coveralls.io/repos/css-modules/postcss-modules-scope/badge.svg?branch=master)](https://coveralls.io/r/css-modules/postcss-modules-scope?branch=master) * Statements: [![codecov.io](http://codecov.io/github/css-modules/postcss-modules-scope/coverage.svg?branch=master)](http://codecov.io/github/css-modules/postcss-modules-scope?branch=master) ## Development - `npm autotest` will watch `src` and `test` for changes and run the tests, and transpile the ES6 to ES5 on success ## License ISC ## With thanks - Mark Dalgleish - Tobias Koppers - Guy Bedford --- Glen Maddern, 2015.