diff options
Diffstat (limited to 'node_modules/postcss-modules-scope/lib')
-rw-r--r-- | node_modules/postcss-modules-scope/lib/index.js | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/node_modules/postcss-modules-scope/lib/index.js b/node_modules/postcss-modules-scope/lib/index.js new file mode 100644 index 0000000..e060712 --- /dev/null +++ b/node_modules/postcss-modules-scope/lib/index.js @@ -0,0 +1,183 @@ +'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'];
\ No newline at end of file |