From bb69ed62e3897c89af49728f01ce1aaad300b38a Mon Sep 17 00:00:00 2001 From: Glen Maddern Date: Wed, 30 Dec 2015 12:31:14 +1100 Subject: [PATCH 1/2] change the test runner to reset exported names after each because thats a paaaain --- lib/index.js | 216 ++++++++++++++++++++++++++------------------------ src/index.js | 156 ++++++++++++++++++------------------ test/index.js | 40 +++++----- 3 files changed, 212 insertions(+), 200 deletions(-) diff --git a/lib/index.js b/lib/index.js index 4b2f3da..5f78c85 100644 --- a/lib/index.js +++ b/lib/index.js @@ -19,118 +19,124 @@ var _icssReplaceSymbols2 = _interopRequireDefault(_icssReplaceSymbols); var matchImports = /^(.+?)\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/; var matchValueDefinition = /(?:,\s+|^)([\w-]+):?\s+("[^"]*"|'[^']*'|\w+\([^\)]+\)|[^,]+)\s?/g; var matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/; -var options = {}; -var importIndex = 0; -var createImportedName = options && options.createImportedName || function (importName /*, path*/) { - return 'i__const_' + importName.replace(/\W/g, '_') + '_' + importIndex++; -}; -exports['default'] = function (css) { - var importAliases = []; - var definitions = {}; +var processor = _postcss2['default'].plugin('postcss-modules-values', function () { + var opts = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; + var _opts$importIndex = opts.importIndex; + var importIndex = _opts$importIndex === undefined ? 0 : _opts$importIndex; + var _opts$createImportedName = opts.createImportedName; + var createImportedName = _opts$createImportedName === undefined ? function (importName /*, path*/) { + return 'i__const_' + importName.replace(/\W/g, '_') + '_' + importIndex++; + } : _opts$createImportedName; + + return function (css) { + var importAliases = []; + var definitions = {}; + + var addDefinition = function addDefinition(atRule) { + var matches = undefined; + while (matches = matchValueDefinition.exec(atRule.params)) { + var _matches = matches; + + var _matches2 = _slicedToArray(_matches, 3); + + var /*match*/key = _matches2[1]; + var value = _matches2[2]; + + // Add to the definitions, knowing that values can refer to each other + definitions[key] = (0, _icssReplaceSymbols.replaceAll)(definitions, value); + atRule.remove(); + } + }; + + var addImport = function addImport(atRule) { + var matches = matchImports.exec(atRule.params); + if (matches) { + var _matches3 = _slicedToArray(matches, 3); + + var /*match*/aliases = _matches3[1]; + var path = _matches3[2]; + + // We can use constants for path names + if (definitions[path]) path = definitions[path]; + var imports = aliases.split(/\s*,\s*/).map(function (alias) { + var tokens = matchImport.exec(alias); + if (tokens) { + var _tokens = _slicedToArray(tokens, 3); + + var /*match*/theirName = _tokens[1]; + var _tokens$2 = _tokens[2]; + var myName = _tokens$2 === undefined ? theirName : _tokens$2; + + var importedName = createImportedName(myName); + definitions[myName] = importedName; + return { theirName: theirName, importedName: importedName }; + } else { + throw new Error('@import statement "' + alias + '" is invalid!'); + } + }); + importAliases.push({ path: path, imports: imports }); + atRule.remove(); + } + }; + + /* Look at all the @value statements and treat them as locals or as imports */ + css.walkAtRules('value', function (atRule) { + if (matchImports.exec(atRule.params)) { + addImport(atRule); + } else { + addDefinition(atRule); + } + }); - var addDefinition = function addDefinition(atRule) { - var matches = undefined; - while (matches = matchValueDefinition.exec(atRule.params)) { - var _matches = matches; + /* We want to export anything defined by now, but don't add it to the CSS yet or + it well get picked up by the replacement stuff */ + var exportDeclarations = Object.keys(definitions).map(function (key) { + return _postcss2['default'].decl({ + value: definitions[key], + prop: key, + raws: { before: "\n " }, + _autoprefixerDisabled: true + }); + }); - var _matches2 = _slicedToArray(_matches, 3); + /* If we have no definitions, don't continue */ + if (!Object.keys(definitions).length) return; - var /*match*/key = _matches2[1]; - var value = _matches2[2]; + /* Perform replacements */ + (0, _icssReplaceSymbols2['default'])(css, definitions); - // Add to the definitions, knowing that values can refer to each other - definitions[key] = (0, _icssReplaceSymbols.replaceAll)(definitions, value); - atRule.remove(); + /* Add export rules if any */ + if (exportDeclarations.length > 0) { + css.prepend(_postcss2['default'].rule({ + selector: ':export', + raws: { after: "\n" }, + nodes: exportDeclarations + })); } - }; - var addImport = function addImport(atRule) { - var matches = matchImports.exec(atRule.params); - if (matches) { - var _matches3 = _slicedToArray(matches, 3); - - var /*match*/aliases = _matches3[1]; - var path = _matches3[2]; - - // We can use constants for path names - if (definitions[path]) path = definitions[path]; - var imports = aliases.split(/\s*,\s*/).map(function (alias) { - var tokens = matchImport.exec(alias); - if (tokens) { - var _tokens = _slicedToArray(tokens, 3); - - var /*match*/theirName = _tokens[1]; - var _tokens$2 = _tokens[2]; - var myName = _tokens$2 === undefined ? theirName : _tokens$2; - - var importedName = createImportedName(myName); - definitions[myName] = importedName; - return { theirName: theirName, importedName: importedName }; - } else { - throw new Error('@import statement "' + alias + '" is invalid!'); - } - }); - importAliases.push({ path: path, imports: imports }); - atRule.remove(); - } - }; - - /* Look at all the @value statements and treat them as locals or as imports */ - css.walkAtRules('value', function (atRule) { - if (matchImports.exec(atRule.params)) { - addImport(atRule); - } else { - addDefinition(atRule); - } - }); - - /* We want to export anything defined by now, but don't add it to the CSS yet or - it well get picked up by the replacement stuff */ - var exportDeclarations = Object.keys(definitions).map(function (key) { - return _postcss2['default'].decl({ - value: definitions[key], - prop: key, - raws: { before: "\n " }, - _autoprefixerDisabled: true + /* Add import rules */ + importAliases.reverse().forEach(function (_ref) { + var path = _ref.path; + var imports = _ref.imports; + + css.prepend(_postcss2['default'].rule({ + selector: ':import(' + path + ')', + raws: { after: "\n" }, + nodes: imports.map(function (_ref2) { + var theirName = _ref2.theirName; + var importedName = _ref2.importedName; + return _postcss2['default'].decl({ + value: theirName, + prop: importedName, + raws: { before: "\n " }, + _autoprefixerDisabled: true + }); + }) + })); }); - }); - - /* If we have no definitions, don't continue */ - if (!Object.keys(definitions).length) return; - - /* Perform replacements */ - (0, _icssReplaceSymbols2['default'])(css, definitions); - - /* Add export rules if any */ - if (exportDeclarations.length > 0) { - css.prepend(_postcss2['default'].rule({ - selector: ':export', - raws: { after: "\n" }, - nodes: exportDeclarations - })); - } - - /* Add import rules */ - importAliases.reverse().forEach(function (_ref) { - var path = _ref.path; - var imports = _ref.imports; - - css.prepend(_postcss2['default'].rule({ - selector: ':import(' + path + ')', - raws: { after: "\n" }, - nodes: imports.map(function (_ref2) { - var theirName = _ref2.theirName; - var importedName = _ref2.importedName; - return _postcss2['default'].decl({ - value: theirName, - prop: importedName, - raws: { before: "\n " }, - _autoprefixerDisabled: true - }); - }) - })); - }); -}; + }; +}); +exports['default'] = processor; module.exports = exports['default']; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 5b39ab0..6bccfb0 100644 --- a/src/index.js +++ b/src/index.js @@ -4,90 +4,96 @@ import replaceSymbols, { replaceAll } from 'icss-replace-symbols' const matchImports = /^(.+?)\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/ const matchValueDefinition = /(?:,\s+|^)([\w-]+):?\s+("[^"]*"|'[^']*'|\w+\([^\)]+\)|[^,]+)\s?/g const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/ -let options = {} -let importIndex = 0 -let createImportedName = options && options.createImportedName || ((importName/*, path*/) => `i__const_${importName.replace(/\W/g, '_')}_${importIndex++}`) -export default css => { - let importAliases = [] - let definitions = {} +const processor = postcss.plugin('postcss-modules-values', function (opts = {}) { + let { + importIndex = 0, + createImportedName = (importName/*, path*/) => `i__const_${importName.replace(/\W/g, '_')}_${importIndex++}` + } = opts - const addDefinition = atRule => { - let matches - while (matches = matchValueDefinition.exec(atRule.params)) { - let [/*match*/, key, value] = matches - // Add to the definitions, knowing that values can refer to each other - definitions[key] = replaceAll(definitions, value) - atRule.remove() - } - } + return css => { + let importAliases = [] + let definitions = {} - const addImport = atRule => { - let matches = matchImports.exec(atRule.params) - if (matches) { - let [/*match*/, aliases, path] = matches - // We can use constants for path names - if (definitions[path]) path = definitions[path] - let imports = aliases.split(/\s*,\s*/).map(alias => { - let tokens = matchImport.exec(alias) - if (tokens) { - let [/*match*/, theirName, myName = theirName] = tokens - let importedName = createImportedName(myName) - definitions[myName] = importedName - return {theirName, importedName} - } else { - throw new Error(`@import statement "${alias}" is invalid!`) - } - }) - importAliases.push({path, imports}) - atRule.remove() + const addDefinition = atRule => { + let matches + while (matches = matchValueDefinition.exec(atRule.params)) { + let [/*match*/, key, value] = matches + // Add to the definitions, knowing that values can refer to each other + definitions[key] = replaceAll(definitions, value) + atRule.remove() + } } - } - /* Look at all the @value statements and treat them as locals or as imports */ - css.walkAtRules('value', atRule => { - if (matchImports.exec(atRule.params)) { - addImport(atRule) - } else { - addDefinition(atRule) + const addImport = atRule => { + let matches = matchImports.exec(atRule.params) + if (matches) { + let [/*match*/, aliases, path] = matches + // We can use constants for path names + if (definitions[path]) path = definitions[path] + let imports = aliases.split(/\s*,\s*/).map(alias => { + let tokens = matchImport.exec(alias) + if (tokens) { + let [/*match*/, theirName, myName = theirName] = tokens + let importedName = createImportedName(myName) + definitions[myName] = importedName + return {theirName, importedName} + } else { + throw new Error(`@import statement "${alias}" is invalid!`) + } + }) + importAliases.push({path, imports}) + atRule.remove() + } } - }) - /* We want to export anything defined by now, but don't add it to the CSS yet or - it well get picked up by the replacement stuff */ - let exportDeclarations = Object.keys(definitions).map(key => postcss.decl({ - value: definitions[key], - prop: key, - raws: { before: "\n " }, - _autoprefixerDisabled: true - })) + /* Look at all the @value statements and treat them as locals or as imports */ + css.walkAtRules('value', atRule => { + if (matchImports.exec(atRule.params)) { + addImport(atRule) + } else { + addDefinition(atRule) + } + }) - /* If we have no definitions, don't continue */ - if (!Object.keys(definitions).length) return + /* We want to export anything defined by now, but don't add it to the CSS yet or + it well get picked up by the replacement stuff */ + let exportDeclarations = Object.keys(definitions).map(key => postcss.decl({ + value: definitions[key], + prop: key, + raws: {before: "\n "}, + _autoprefixerDisabled: true + })) - /* Perform replacements */ - replaceSymbols(css, definitions) + /* If we have no definitions, don't continue */ + if (!Object.keys(definitions).length) return - /* Add export rules if any */ - if (exportDeclarations.length > 0) { - css.prepend(postcss.rule({ - selector: `:export`, - raws: { after: "\n" }, - nodes: exportDeclarations - })) - } + /* Perform replacements */ + replaceSymbols(css, definitions) - /* Add import rules */ - importAliases.reverse().forEach(({path, imports}) => { - css.prepend(postcss.rule({ - selector: `:import(${path})`, - raws: { after: "\n" }, - nodes: imports.map(({theirName, importedName}) => postcss.decl({ - value: theirName, - prop: importedName, - raws: { before: "\n " }, - _autoprefixerDisabled: true + /* Add export rules if any */ + if (exportDeclarations.length > 0) { + css.prepend(postcss.rule({ + selector: `:export`, + raws: {after: "\n"}, + nodes: exportDeclarations })) - })) - }) -} + } + + /* Add import rules */ + importAliases.reverse().forEach(({path, imports}) => { + css.prepend(postcss.rule({ + selector: `:import(${path})`, + raws: {after: "\n"}, + nodes: imports.map(({theirName, importedName}) => postcss.decl({ + value: theirName, + prop: importedName, + raws: {before: "\n "}, + _autoprefixerDisabled: true + })) + })) + }) + } +}) + +export default processor; diff --git a/test/index.js b/test/index.js index 22a0abf..f8bf3ec 100644 --- a/test/index.js +++ b/test/index.js @@ -3,10 +3,10 @@ import postcss from 'postcss' import assert from 'assert' -import constants from '../src' +import values from '../src' const test = (input, expected) => { - let processor = postcss([constants]) + let processor = postcss([values({importIndex: 0})]) assert.equal(processor.process(input).css, expected) } @@ -32,11 +32,11 @@ describe('constants', () => { }) it('should import a simple constant and replace usages', () => { - test('@value red from "./colors.css"; .foo { color: red; }', ':import("./colors.css") {\n i__const_red_1: red;\n}\n:export {\n red: i__const_red_1;\n}\n.foo { color: i__const_red_1; }') + test('@value red from "./colors.css"; .foo { color: red; }', ':import("./colors.css") {\n i__const_red_0: red;\n}\n:export {\n red: i__const_red_0;\n}\n.foo { color: i__const_red_0; }') }) it('should import and alias a constant and replace usages', () => { - test('@value blue as red from "./colors.css"; .foo { color: red; }', ':import("./colors.css") {\n i__const_red_2: blue;\n}\n:export {\n red: i__const_red_2;\n}\n.foo { color: i__const_red_2; }') + test('@value blue as red from "./colors.css"; .foo { color: red; }', ':import("./colors.css") {\n i__const_red_0: blue;\n}\n:export {\n red: i__const_red_0;\n}\n.foo { color: i__const_red_0; }') }) it('should import multiple from a single file', () => { @@ -45,30 +45,30 @@ describe('constants', () => { .foo { color: red; } .bar { color: blue }`, `:import("./colors.css") { - i__const_blue_3: blue; - i__const_red_4: red; + i__const_blue_0: blue; + i__const_red_1: red; } :export { - blue: i__const_blue_3; - red: i__const_red_4; + blue: i__const_blue_0; + red: i__const_red_1; } -.foo { color: i__const_red_4; } -.bar { color: i__const_blue_3 }`) +.foo { color: i__const_red_1; } +.bar { color: i__const_blue_0 }`) }) it('should import from a definition', () => { test( '@value colors: "./colors.css"; @value red from colors;', - ':import("./colors.css") {\n i__const_red_5: red\n}\n' + - ':export {\n colors: "./colors.css";\n red: i__const_red_5\n}' + ':import("./colors.css") {\n i__const_red_0: red\n}\n' + + ':export {\n colors: "./colors.css";\n red: i__const_red_0\n}' ) }) it('should only allow values for paths if defined in the right order', () => { test( '@value red from colors; @value colors: "./colors.css";', - ':import(colors) {\n i__const_red_6: red\n}\n' + - ':export {\n red: i__const_red_6;\n colors: "./colors.css"\n}' + ':import(colors) {\n i__const_red_0: red\n}\n' + + ':export {\n red: i__const_red_0;\n colors: "./colors.css"\n}' ) }) @@ -89,18 +89,18 @@ describe('constants', () => { it('should preserve import order', () => { test( '@value a from "./a.css"; @value b from "./b.css";', - ':import("./a.css") {\n i__const_a_7: a\n}\n' + - ':import("./b.css") {\n i__const_b_8: b\n}\n' + - ':export {\n a: i__const_a_7;\n b: i__const_b_8\n}' + ':import("./a.css") {\n i__const_a_0: a\n}\n' + + ':import("./b.css") {\n i__const_b_1: b\n}\n' + + ':export {\n a: i__const_a_0;\n b: i__const_b_1\n}' ) }) it('should allow custom-property-style names', () => { test( '@value --red from "./colors.css"; .foo { color: --red; }', - ':import("./colors.css") {\n i__const___red_9: --red;\n}\n' + - ':export {\n --red: i__const___red_9;\n}\n' + - '.foo { color: i__const___red_9; }') + ':import("./colors.css") {\n i__const___red_0: --red;\n}\n' + + ':export {\n --red: i__const___red_0;\n}\n' + + '.foo { color: i__const___red_0; }') }) it('should allow all colour types', () => { From 7a19ff354275fd5e974ee2b0bb0be29e5028b52f Mon Sep 17 00:00:00 2001 From: Glen Maddern Date: Thu, 14 Jan 2016 08:33:28 +1100 Subject: [PATCH 2/2] no more multiple definitions on one line --- lib/index.js | 20 +++++++++----------- src/index.js | 6 +++--- test/index.js | 34 +++++++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/lib/index.js b/lib/index.js index 5f78c85..94f5fb7 100644 --- a/lib/index.js +++ b/lib/index.js @@ -17,7 +17,7 @@ var _icssReplaceSymbols = require('icss-replace-symbols'); var _icssReplaceSymbols2 = _interopRequireDefault(_icssReplaceSymbols); var matchImports = /^(.+?)\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/; -var matchValueDefinition = /(?:,\s+|^)([\w-]+):?\s+("[^"]*"|'[^']*'|\w+\([^\)]+\)|[^,]+)\s?/g; +var matchValueDefinition = /^([\w-]+):?\s+(.*)\s*$/; var matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/; var processor = _postcss2['default'].plugin('postcss-modules-values', function () { @@ -34,14 +34,12 @@ var processor = _postcss2['default'].plugin('postcss-modules-values', function ( var definitions = {}; var addDefinition = function addDefinition(atRule) { - var matches = undefined; - while (matches = matchValueDefinition.exec(atRule.params)) { - var _matches = matches; - - var _matches2 = _slicedToArray(_matches, 3); + var matches = matchValueDefinition.exec(atRule.params); + if (matches) { + var _matches = _slicedToArray(matches, 3); - var /*match*/key = _matches2[1]; - var value = _matches2[2]; + var /*match*/key = _matches[1]; + var value = _matches[2]; // Add to the definitions, knowing that values can refer to each other definitions[key] = (0, _icssReplaceSymbols.replaceAll)(definitions, value); @@ -52,10 +50,10 @@ var processor = _postcss2['default'].plugin('postcss-modules-values', function ( var addImport = function addImport(atRule) { var matches = matchImports.exec(atRule.params); if (matches) { - var _matches3 = _slicedToArray(matches, 3); + var _matches2 = _slicedToArray(matches, 3); - var /*match*/aliases = _matches3[1]; - var path = _matches3[2]; + var /*match*/aliases = _matches2[1]; + var path = _matches2[2]; // We can use constants for path names if (definitions[path]) path = definitions[path]; diff --git a/src/index.js b/src/index.js index 6bccfb0..a5b95c3 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,7 @@ import postcss from 'postcss' import replaceSymbols, { replaceAll } from 'icss-replace-symbols' const matchImports = /^(.+?)\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/ -const matchValueDefinition = /(?:,\s+|^)([\w-]+):?\s+("[^"]*"|'[^']*'|\w+\([^\)]+\)|[^,]+)\s?/g +const matchValueDefinition = /^([\w-]+):?\s+(.*)\s*$/ const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/ const processor = postcss.plugin('postcss-modules-values', function (opts = {}) { @@ -16,8 +16,8 @@ const processor = postcss.plugin('postcss-modules-values', function (opts = {}) let definitions = {} const addDefinition = atRule => { - let matches - while (matches = matchValueDefinition.exec(atRule.params)) { + let matches = matchValueDefinition.exec(atRule.params) + if (matches) { let [/*match*/, key, value] = matches // Add to the definitions, knowing that values can refer to each other definitions[key] = replaceAll(definitions, value) diff --git a/test/index.js b/test/index.js index f8bf3ec..c7dbeec 100644 --- a/test/index.js +++ b/test/index.js @@ -41,19 +41,23 @@ describe('constants', () => { it('should import multiple from a single file', () => { test( - `@value blue, red from "./colors.css"; + `@value blue, red, green as three from "./colors.css"; .foo { color: red; } -.bar { color: blue }`, +.bar { color: blue; } +.baz { color: three; }`, `:import("./colors.css") { i__const_blue_0: blue; i__const_red_1: red; + i__const_three_2: green; } :export { blue: i__const_blue_0; red: i__const_red_1; + three: i__const_three_2; } .foo { color: i__const_red_1; } -.bar { color: i__const_blue_0 }`) +.bar { color: i__const_blue_0; } +.baz { color: i__const_three_2; }`) }) it('should import from a definition', () => { @@ -64,6 +68,13 @@ describe('constants', () => { ) }) + it('should compose from a definition', () => { + test( + '@value colors: "./colors.css"; .foo { composes: red from colors; }', + ':export {\n colors: "./colors.css";\n}\n.foo { composes: red from "./colors.css"; }' + ) + }) + it('should only allow values for paths if defined in the right order', () => { test( '@value red from colors; @value colors: "./colors.css";', @@ -110,4 +121,21 @@ describe('constants', () => { ':export {\n named: red;\n 3char: #0f0;\n 6char: #00ff00;\n rgba: rgba(34, 12, 64, 0.3);\n hsla: hsla(220, 13.0%, 18.0%, 1);\n}\n' + '.foo { color: red; background-color: #0f0; border-top-color: #00ff00; border-bottom-color: rgba(34, 12, 64, 0.3); outline-color: hsla(220, 13.0%, 18.0%, 1); }') }) + + it('should allow box shadow definitions with rgba', () => { + test( + '@value okShadow: 0 11px 15px -7px rgba(0,0,0,.2);\n' + + '.foo { box-shadow: okShadow; }', + ':export {\n okShadow: 0 11px 15px -7px rgba(0,0,0,.2);\n}\n' + + '.foo { box-shadow: 0 11px 15px -7px rgba(0,0,0,.2); }' + ) + }) + + it('should allow definitions with commas in them if quoted', () => { + test( + '@value coolShadow: 0 11px 15px -7px rgba(0,0,0,.2), 0 24px 38px 3px rgba(0,0,0,.14);\n' + + '.foo { box-shadow: coolShadow; }', + ':export {\n coolShadow: 0 11px 15px -7px rgba(0,0,0,.2), 0 24px 38px 3px rgba(0,0,0,.14);\n}\n' + + '.foo { box-shadow: 0 11px 15px -7px rgba(0,0,0,.2), 0 24px 38px 3px rgba(0,0,0,.14); }') + }) })