From 32e2c484b539b19dc3c39c780ef9edd77160bb87 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 23 May 2017 17:20:10 +0300 Subject: [PATCH 1/8] Purify icss utils --- src/icss.js | 40 ++++++++++++++++++++++++++++++++++++++++ src/index.js | 43 +++---------------------------------------- 2 files changed, 43 insertions(+), 40 deletions(-) create mode 100644 src/icss.js diff --git a/src/icss.js b/src/icss.js new file mode 100644 index 0000000..982ff3b --- /dev/null +++ b/src/icss.js @@ -0,0 +1,40 @@ +const postcss = require('postcss') + +const genICSSImportsRules = imports => { + return imports.map(({ path, aliases }) => { + const declarations = Object.keys(aliases).map(key => + postcss.decl({ + prop: key, + value: aliases[key], + raws: { before: '\n ' } + }) + ) + return postcss + .rule({ + selector: `:import(${path})`, + raws: { after: '\n' } + }) + .append(declarations) + }) +} + +const genICSSExportsRule = exports => { + const declarations = Object.keys(exports).map(key => + postcss.decl({ + prop: key, + value: exports[key], + raws: { before: '\n ' } + }) + ) + return postcss + .rule({ + selector: `:export`, + raws: { after: '\n' } + }) + .append(declarations) +} + +export const genICSSRules = (imports, exports) => [ + ...genICSSImportsRules(imports), + genICSSExportsRule(exports) +] diff --git a/src/index.js b/src/index.js index 2ebd59c..af0dae3 100644 --- a/src/index.js +++ b/src/index.js @@ -1,46 +1,11 @@ const postcss = require('postcss') const { default: replaceSymbols, replaceAll } = require('icss-replace-symbols') +const { genICSSRules } = require('./icss.js') const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/ const matchValueDefinition = /(?:\s+|^)([\w-]+):?\s+(.+?)\s*$/g const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/ -const addImportsRules = (css, imports) => { - const rules = imports.map(({ path, aliases }) => { - const declarations = Object.keys(aliases).map(key => - postcss.decl({ - prop: key, - value: aliases[key], - raws: { before: '\n ' } - }) - ) - return postcss - .rule({ - selector: `:import(${path})`, - raws: { after: '\n' } - }) - .append(declarations) - }) - css.prepend(rules) -} - -const addExportsRule = (css, exports) => { - const declarations = Object.keys(exports).map(key => - postcss.decl({ - prop: key, - value: exports[key], - raws: { before: '\n ' } - }) - ) - const rule = postcss - .rule({ - selector: `:export`, - raws: { after: '\n' } - }) - .append(declarations) - css.prepend(rule) -} - let importIndex = 0 const createImportedName = importName => `i__const_${importName.replace(/\W/g, '_')}_${importIndex++}` @@ -74,7 +39,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( .map(alias => { let tokens = matchImport.exec(alias) if (tokens) { - let [, /*match*/ theirName, myName = theirName] = tokens + let [, theirName, myName = theirName] = tokens let importedName = createImportedName(myName) definitions[myName] = importedName return { theirName, importedName } @@ -110,7 +75,5 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( /* Perform replacements */ replaceSymbols(css, definitions) - addExportsRule(css, definitions) - - addImportsRules(css, importAliases) + css.prepend(genICSSRules(importAliases, definitions)) }) From ce736f6be3c3320f93e24822b69bae46fac4e693 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 23 May 2017 18:03:39 +0300 Subject: [PATCH 2/8] Enforce double-underscored aliases according icss spec --- src/index.js | 6 +++-- test/test.js | 62 ++++++++++++++++++++++++++-------------------------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/index.js b/src/index.js index af0dae3..5cd21bb 100644 --- a/src/index.js +++ b/src/index.js @@ -6,9 +6,11 @@ const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/ const matchValueDefinition = /(?:\s+|^)([\w-]+):?\s+(.+?)\s*$/g const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/ +const getAliasName = (name, index) => + `__value_${name.replace(/\W/g, '_')}_${index}` + let importIndex = 0 -const createImportedName = importName => - `i__const_${importName.replace(/\W/g, '_')}_${importIndex++}` +const createImportedName = importName => getAliasName(importName, importIndex++) module.exports = postcss.plugin('postcss-modules-values', () => ( css, diff --git a/test/test.js b/test/test.js index 4bb7507..637ea99 100644 --- a/test/test.js +++ b/test/test.js @@ -76,10 +76,10 @@ test('should import and re-export a simple constant', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__const_red_0: red + __value_red_0: red } :export { - red: i__const_red_0 + red: __value_red_0 } `) ) @@ -94,12 +94,12 @@ test('should import a simple constant and replace usages', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__const_red_1: red; + __value_red_1: red; } :export { - red: i__const_red_1; + red: __value_red_1; } - .foo { color: i__const_red_1; } + .foo { color: __value_red_1; } `) ) }) @@ -113,12 +113,12 @@ test('should import and alias a constant and replace usages', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__const_red_2: blue; + __value_red_2: blue; } :export { - red: i__const_red_2; + red: __value_red_2; } - .foo { color: i__const_red_2; } + .foo { color: __value_red_2; } `) ) }) @@ -133,15 +133,15 @@ test('should import multiple from a single file', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__const_blue_3: blue; - i__const_red_4: red; + __value_blue_3: blue; + __value_red_4: red; } :export { - blue: i__const_blue_3; - red: i__const_red_4; + blue: __value_blue_3; + red: __value_red_4; } - .foo { color: i__const_red_4; } - .bar { color: i__const_blue_3 } + .foo { color: __value_red_4; } + .bar { color: __value_blue_3 } `) ) }) @@ -154,11 +154,11 @@ test('should import from a definition', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__const_red_5: red + __value_red_5: red } :export { colors: "./colors.css"; - red: i__const_red_5 + red: __value_red_5 } `) ) @@ -172,10 +172,10 @@ test('should only allow values for paths if defined in the right order', () => { ).resolves.toEqual( strip(` :import(colors) { - i__const_red_6: red + __value_red_6: red } :export { - red: i__const_red_6; + red: __value_red_6; colors: "./colors.css" } `) @@ -226,14 +226,14 @@ test('should preserve import order', () => { ).resolves.toEqual( strip(` :import("./a.css") { - i__const_a_7: a + __value_a_7: a } :import("./b.css") { - i__const_b_8: b + __value_b_8: b } :export { - a: i__const_a_7; - b: i__const_b_8 + a: __value_a_7; + b: __value_b_8 } `) ) @@ -247,12 +247,12 @@ test('should allow custom-property-style names', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__const___red_9: --red; + __value___red_9: --red; } :export { - --red: i__const___red_9; + --red: __value___red_9; } - .foo { color: i__const___red_9; } + .foo { color: __value___red_9; } `) ) }) @@ -306,15 +306,15 @@ test('should import multiple from a single file on multiple lines', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__const_blue_10: blue; - i__const_red_11: red; + __value_blue_10: blue; + __value_red_11: red; } :export { - blue: i__const_blue_10; - red: i__const_red_11; + blue: __value_blue_10; + red: __value_red_11; } - .foo { color: i__const_red_11; } - .bar { color: i__const_blue_10 } + .foo { color: __value_red_11; } + .bar { color: __value_blue_10 } `) ) }) From b88d76b3603ce9a75ece2925c35b96c3e68dc963 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Tue, 23 May 2017 23:59:42 +0300 Subject: [PATCH 3/8] Ensure :imports and :exports --- src/icss.js | 37 +++++++++++++++++++++- src/index.js | 27 +++++++++------- test/test.js | 87 +++++++++++++++++++++++++++++++++------------------- 3 files changed, 108 insertions(+), 43 deletions(-) diff --git a/src/icss.js b/src/icss.js index 982ff3b..5cd422f 100644 --- a/src/icss.js +++ b/src/icss.js @@ -1,7 +1,42 @@ const postcss = require('postcss') +const importPattern = /^:import\(("[^"]*"|'[^']*'|[\w-]+)\)$/ + +const exportPattern = /^:export$/ + +const getDeclsObject = rule => { + const object = {} + rule.walkDecls(decl => { + object[decl.prop] = decl.value + }) + return object +} + +export const extractICSSImports = css => { + const imports = {} + css.walkRules(rule => { + const matches = importPattern.exec(rule.selector) + if (matches) { + const path = matches[1] + imports[path] = Object.assign({}, imports[path], getDeclsObject(rule)) + rule.remove() + } + }) + return imports +} + +export const extractICSSExports = css => { + const exports = {} + css.walkRules(exportPattern, rule => { + Object.assign(exports, getDeclsObject(rule)) + rule.remove() + }) + return exports +} + const genICSSImportsRules = imports => { - return imports.map(({ path, aliases }) => { + return Object.keys(imports).map(path => { + const aliases = imports[path] const declarations = Object.keys(aliases).map(key => postcss.decl({ prop: key, diff --git a/src/index.js b/src/index.js index 5cd21bb..f2b7a11 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,18 @@ const postcss = require('postcss') const { default: replaceSymbols, replaceAll } = require('icss-replace-symbols') -const { genICSSRules } = require('./icss.js') +const { + extractICSSImports, + extractICSSExports, + genICSSRules +} = require('./icss.js') const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/ const matchValueDefinition = /(?:\s+|^)([\w-]+):?\s+(.+?)\s*$/g const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/ +// 'i' prefix to prevent postcss parsing "_" as css hook const getAliasName = (name, index) => - `__value_${name.replace(/\W/g, '_')}_${index}` + `i__value_${name.replace(/\W/g, '_')}_${index}` let importIndex = 0 const createImportedName = importName => getAliasName(importName, importIndex++) @@ -16,15 +21,15 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( css, result ) => { - let importAliases = [] - let definitions = {} + const imports = extractICSSImports(css) + const exports = extractICSSExports(css) const addDefinition = atRule => { let matches while ((matches = matchValueDefinition.exec(atRule.params))) { let [, key, value] = matches // Add to the definitions, knowing that values can refer to each other - definitions[key] = replaceAll(definitions, value) + exports[key] = replaceAll(exports, value) atRule.remove() } } @@ -34,7 +39,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( if (matches) { let [, aliasesString, path] = matches // We can use constants for path names - if (definitions[path]) path = definitions[path] + if (exports[path]) path = exports[path] let aliases = aliasesString .replace(/^\(\s*([\s\S]+)\s*\)$/, '$1') .split(/\s*,\s*/) @@ -43,7 +48,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( if (tokens) { let [, theirName, myName = theirName] = tokens let importedName = createImportedName(myName) - definitions[myName] = importedName + exports[myName] = importedName return { theirName, importedName } } else { throw new Error(`@import statement "${alias}" is invalid!`) @@ -53,7 +58,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( acc[importedName] = theirName return acc }, {}) - importAliases.push({ path, aliases }) + imports[path] = Object.assign({}, imports[path], aliases) atRule.remove() } } @@ -72,10 +77,10 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( }) /* If we have no definitions, don't continue */ - if (Object.keys(definitions).length === 0) return + if (Object.keys(exports).length === 0) return /* Perform replacements */ - replaceSymbols(css, definitions) + replaceSymbols(css, exports) - css.prepend(genICSSRules(importAliases, definitions)) + css.prepend(genICSSRules(imports, exports)) }) diff --git a/test/test.js b/test/test.js index 637ea99..035e0cc 100644 --- a/test/test.js +++ b/test/test.js @@ -76,10 +76,10 @@ test('should import and re-export a simple constant', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - __value_red_0: red + i__value_red_0: red } :export { - red: __value_red_0 + red: i__value_red_0 } `) ) @@ -94,12 +94,12 @@ test('should import a simple constant and replace usages', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - __value_red_1: red; + i__value_red_1: red; } :export { - red: __value_red_1; + red: i__value_red_1; } - .foo { color: __value_red_1; } + .foo { color: i__value_red_1; } `) ) }) @@ -113,12 +113,12 @@ test('should import and alias a constant and replace usages', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - __value_red_2: blue; + i__value_red_2: blue; } :export { - red: __value_red_2; + red: i__value_red_2; } - .foo { color: __value_red_2; } + .foo { color: i__value_red_2; } `) ) }) @@ -133,15 +133,15 @@ test('should import multiple from a single file', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - __value_blue_3: blue; - __value_red_4: red; + i__value_blue_3: blue; + i__value_red_4: red; } :export { - blue: __value_blue_3; - red: __value_red_4; + blue: i__value_blue_3; + red: i__value_red_4; } - .foo { color: __value_red_4; } - .bar { color: __value_blue_3 } + .foo { color: i__value_red_4; } + .bar { color: i__value_blue_3 } `) ) }) @@ -154,11 +154,11 @@ test('should import from a definition', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - __value_red_5: red + i__value_red_5: red } :export { colors: "./colors.css"; - red: __value_red_5 + red: i__value_red_5 } `) ) @@ -172,10 +172,10 @@ test('should only allow values for paths if defined in the right order', () => { ).resolves.toEqual( strip(` :import(colors) { - __value_red_6: red + i__value_red_6: red } :export { - red: __value_red_6; + red: i__value_red_6; colors: "./colors.css" } `) @@ -226,14 +226,14 @@ test('should preserve import order', () => { ).resolves.toEqual( strip(` :import("./a.css") { - __value_a_7: a + i__value_a_7: a } :import("./b.css") { - __value_b_8: b + i__value_b_8: b } :export { - a: __value_a_7; - b: __value_b_8 + a: i__value_a_7; + b: i__value_b_8 } `) ) @@ -247,12 +247,12 @@ test('should allow custom-property-style names', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - __value___red_9: --red; + i__value___red_9: --red; } :export { - --red: __value___red_9; + --red: i__value___red_9; } - .foo { color: __value___red_9; } + .foo { color: i__value___red_9; } `) ) }) @@ -306,15 +306,15 @@ test('should import multiple from a single file on multiple lines', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - __value_blue_10: blue; - __value_red_11: red; + i__value_blue_10: blue; + i__value_red_11: red; } :export { - blue: __value_blue_10; - red: __value_red_11; + blue: i__value_blue_10; + red: i__value_red_11; } - .foo { color: __value_red_11; } - .bar { color: __value_blue_10 } + .foo { color: i__value_red_11; } + .bar { color: i__value_blue_10 } `) ) }) @@ -348,3 +348,28 @@ test('should allow values with nested parantheses', () => { `) ) }) + +test('reuse existing :import with same name and :export', () => { + return expect( + runCSS(` + :import('./colors.css') { + i__some_import: blue; + } + :export { + b: i__c; + } + @value a from './colors.css'; + `) + ).resolves.toEqual( + strip(` + :import('./colors.css') { + i__some_import: blue; + i__value_a_12: a + } + :export { + b: i__c; + a: i__value_a_12 + } + `) + ) +}) From b5d8dbbe7421c32c22b5ed5929a927e26fbf2794 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 24 May 2017 00:25:45 +0300 Subject: [PATCH 4/8] Prevent imported names collision --- src/index.js | 12 ++++++++++-- test/test.js | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index f2b7a11..364d572 100644 --- a/src/index.js +++ b/src/index.js @@ -15,7 +15,6 @@ const getAliasName = (name, index) => `i__value_${name.replace(/\W/g, '_')}_${index}` let importIndex = 0 -const createImportedName = importName => getAliasName(importName, importIndex++) module.exports = postcss.plugin('postcss-modules-values', () => ( css, @@ -23,6 +22,15 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( ) => { const imports = extractICSSImports(css) const exports = extractICSSExports(css) + const createImportedName = (path, name) => { + const importedName = getAliasName(name, importIndex) + if (imports[path] && imports[path][importedName]) { + importIndex += 1 + return createImportedName(path, name) + } + importIndex += 1 + return importedName + } const addDefinition = atRule => { let matches @@ -47,7 +55,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( let tokens = matchImport.exec(alias) if (tokens) { let [, theirName, myName = theirName] = tokens - let importedName = createImportedName(myName) + let importedName = createImportedName(path, myName) exports[myName] = importedName return { theirName, importedName } } else { diff --git a/test/test.js b/test/test.js index 035e0cc..eed18ba 100644 --- a/test/test.js +++ b/test/test.js @@ -373,3 +373,24 @@ test('reuse existing :import with same name and :export', () => { `) ) }) + +test('prevent imported names collision', () => { + return expect( + runCSS(` + :import(colors) { + i__value_a_13: a; + } + @value a from colors; + `) + ).resolves.toEqual( + strip(` + :import(colors) { + i__value_a_13: a; + i__value_a_14: a + } + :export { + a: i__value_a_14 + } + `) + ) +}) From 8aa385348b04f794f991be181bca31bb406f7394 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 24 May 2017 00:30:12 +0300 Subject: [PATCH 5/8] Init imported name index locally --- src/index.js | 3 +-- test/test.js | 70 ++++++++++++++++++++++++++-------------------------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/index.js b/src/index.js index 364d572..183550b 100644 --- a/src/index.js +++ b/src/index.js @@ -14,14 +14,13 @@ const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/ const getAliasName = (name, index) => `i__value_${name.replace(/\W/g, '_')}_${index}` -let importIndex = 0 - module.exports = postcss.plugin('postcss-modules-values', () => ( css, result ) => { const imports = extractICSSImports(css) const exports = extractICSSExports(css) + let importIndex = 0 const createImportedName = (path, name) => { const importedName = getAliasName(name, importIndex) if (imports[path] && imports[path][importedName]) { diff --git a/test/test.js b/test/test.js index eed18ba..aa5654a 100644 --- a/test/test.js +++ b/test/test.js @@ -94,12 +94,12 @@ test('should import a simple constant and replace usages', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__value_red_1: red; + i__value_red_0: red; } :export { - red: i__value_red_1; + red: i__value_red_0; } - .foo { color: i__value_red_1; } + .foo { color: i__value_red_0; } `) ) }) @@ -113,12 +113,12 @@ test('should import and alias a constant and replace usages', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__value_red_2: blue; + i__value_red_0: blue; } :export { - red: i__value_red_2; + red: i__value_red_0; } - .foo { color: i__value_red_2; } + .foo { color: i__value_red_0; } `) ) }) @@ -133,15 +133,15 @@ test('should import multiple from a single file', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__value_blue_3: blue; - i__value_red_4: red; + i__value_blue_0: blue; + i__value_red_1: red; } :export { - blue: i__value_blue_3; - red: i__value_red_4; + blue: i__value_blue_0; + red: i__value_red_1; } - .foo { color: i__value_red_4; } - .bar { color: i__value_blue_3 } + .foo { color: i__value_red_1; } + .bar { color: i__value_blue_0 } `) ) }) @@ -154,11 +154,11 @@ test('should import from a definition', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__value_red_5: red + i__value_red_0: red } :export { colors: "./colors.css"; - red: i__value_red_5 + red: i__value_red_0 } `) ) @@ -172,10 +172,10 @@ test('should only allow values for paths if defined in the right order', () => { ).resolves.toEqual( strip(` :import(colors) { - i__value_red_6: red + i__value_red_0: red } :export { - red: i__value_red_6; + red: i__value_red_0; colors: "./colors.css" } `) @@ -226,14 +226,14 @@ test('should preserve import order', () => { ).resolves.toEqual( strip(` :import("./a.css") { - i__value_a_7: a + i__value_a_0: a } :import("./b.css") { - i__value_b_8: b + i__value_b_1: b } :export { - a: i__value_a_7; - b: i__value_b_8 + a: i__value_a_0; + b: i__value_b_1 } `) ) @@ -247,12 +247,12 @@ test('should allow custom-property-style names', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__value___red_9: --red; + i__value___red_0: --red; } :export { - --red: i__value___red_9; + --red: i__value___red_0; } - .foo { color: i__value___red_9; } + .foo { color: i__value___red_0; } `) ) }) @@ -306,15 +306,15 @@ test('should import multiple from a single file on multiple lines', () => { ).resolves.toEqual( strip(` :import("./colors.css") { - i__value_blue_10: blue; - i__value_red_11: red; + i__value_blue_0: blue; + i__value_red_1: red; } :export { - blue: i__value_blue_10; - red: i__value_red_11; + blue: i__value_blue_0; + red: i__value_red_1; } - .foo { color: i__value_red_11; } - .bar { color: i__value_blue_10 } + .foo { color: i__value_red_1; } + .bar { color: i__value_blue_0 } `) ) }) @@ -364,11 +364,11 @@ test('reuse existing :import with same name and :export', () => { strip(` :import('./colors.css') { i__some_import: blue; - i__value_a_12: a + i__value_a_0: a } :export { b: i__c; - a: i__value_a_12 + a: i__value_a_0 } `) ) @@ -378,18 +378,18 @@ test('prevent imported names collision', () => { return expect( runCSS(` :import(colors) { - i__value_a_13: a; + i__value_a_0: a; } @value a from colors; `) ).resolves.toEqual( strip(` :import(colors) { - i__value_a_13: a; - i__value_a_14: a + i__value_a_0: a; + i__value_a_1: a } :export { - a: i__value_a_14 + a: i__value_a_1 } `) ) From bb501ee08dfff864744b5ee08abb67234709935d Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 24 May 2017 00:51:48 +0300 Subject: [PATCH 6/8] Combine extracters and prevent deep iteration --- src/icss.js | 43 +++++++++++++++++++++---------------------- src/index.js | 11 +++-------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/icss.js b/src/icss.js index 5cd422f..d63f0b9 100644 --- a/src/icss.js +++ b/src/icss.js @@ -12,29 +12,28 @@ const getDeclsObject = rule => { return object } -export const extractICSSImports = css => { +export const extractICSS = css => { const imports = {} - css.walkRules(rule => { - const matches = importPattern.exec(rule.selector) - if (matches) { - const path = matches[1] - imports[path] = Object.assign({}, imports[path], getDeclsObject(rule)) - rule.remove() - } - }) - return imports -} - -export const extractICSSExports = css => { const exports = {} - css.walkRules(exportPattern, rule => { - Object.assign(exports, getDeclsObject(rule)) - rule.remove() + css.each(node => { + if (node.type === 'rule') { + const matches = importPattern.exec(node.selector) + if (matches) { + const path = matches[1] + const aliases = Object.assign({}, imports[path], getDeclsObject(node)) + imports[path] = aliases + node.remove() + } + if (exportPattern.test(node.selector)) { + Object.assign(exports, getDeclsObject(node)) + node.remove() + } + } }) - return exports + return { imports, exports } } -const genICSSImportsRules = imports => { +const createICSSImportsRules = imports => { return Object.keys(imports).map(path => { const aliases = imports[path] const declarations = Object.keys(aliases).map(key => @@ -53,7 +52,7 @@ const genICSSImportsRules = imports => { }) } -const genICSSExportsRule = exports => { +const createICSSExportsRule = exports => { const declarations = Object.keys(exports).map(key => postcss.decl({ prop: key, @@ -69,7 +68,7 @@ const genICSSExportsRule = exports => { .append(declarations) } -export const genICSSRules = (imports, exports) => [ - ...genICSSImportsRules(imports), - genICSSExportsRule(exports) +export const createICSSRules = (imports, exports) => [ + ...createICSSImportsRules(imports), + createICSSExportsRule(exports) ] diff --git a/src/index.js b/src/index.js index 183550b..19b5911 100644 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,6 @@ const postcss = require('postcss') const { default: replaceSymbols, replaceAll } = require('icss-replace-symbols') -const { - extractICSSImports, - extractICSSExports, - genICSSRules -} = require('./icss.js') +const { extractICSS, createICSSRules } = require('./icss.js') const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/ const matchValueDefinition = /(?:\s+|^)([\w-]+):?\s+(.+?)\s*$/g @@ -18,8 +14,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( css, result ) => { - const imports = extractICSSImports(css) - const exports = extractICSSExports(css) + const { imports, exports } = extractICSS(css) let importIndex = 0 const createImportedName = (path, name) => { const importedName = getAliasName(name, importIndex) @@ -89,5 +84,5 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( /* Perform replacements */ replaceSymbols(css, exports) - css.prepend(genICSSRules(imports, exports)) + css.prepend(createICSSRules(imports, exports)) }) From e901c0d1c3b00ab8e8b914b77992fa6d4042870a Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 24 May 2017 21:27:25 +0300 Subject: [PATCH 7/8] Add icss-utils --- package.json | 2 +- src/icss.js | 74 ---------------------------------------------------- src/index.js | 28 +++++++++++--------- yarn.lock | 8 +++--- 4 files changed, 22 insertions(+), 90 deletions(-) delete mode 100644 src/icss.js diff --git a/package.json b/package.json index ac4ef21..c68b6f2 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "strip-indent": "^2.0.0" }, "dependencies": { - "icss-replace-symbols": "^1.1.0", + "icss-utils": "^2.0.0", "postcss": "^6.0.1" } } diff --git a/src/icss.js b/src/icss.js deleted file mode 100644 index d63f0b9..0000000 --- a/src/icss.js +++ /dev/null @@ -1,74 +0,0 @@ -const postcss = require('postcss') - -const importPattern = /^:import\(("[^"]*"|'[^']*'|[\w-]+)\)$/ - -const exportPattern = /^:export$/ - -const getDeclsObject = rule => { - const object = {} - rule.walkDecls(decl => { - object[decl.prop] = decl.value - }) - return object -} - -export const extractICSS = css => { - const imports = {} - const exports = {} - css.each(node => { - if (node.type === 'rule') { - const matches = importPattern.exec(node.selector) - if (matches) { - const path = matches[1] - const aliases = Object.assign({}, imports[path], getDeclsObject(node)) - imports[path] = aliases - node.remove() - } - if (exportPattern.test(node.selector)) { - Object.assign(exports, getDeclsObject(node)) - node.remove() - } - } - }) - return { imports, exports } -} - -const createICSSImportsRules = imports => { - return Object.keys(imports).map(path => { - const aliases = imports[path] - const declarations = Object.keys(aliases).map(key => - postcss.decl({ - prop: key, - value: aliases[key], - raws: { before: '\n ' } - }) - ) - return postcss - .rule({ - selector: `:import(${path})`, - raws: { after: '\n' } - }) - .append(declarations) - }) -} - -const createICSSExportsRule = exports => { - const declarations = Object.keys(exports).map(key => - postcss.decl({ - prop: key, - value: exports[key], - raws: { before: '\n ' } - }) - ) - return postcss - .rule({ - selector: `:export`, - raws: { after: '\n' } - }) - .append(declarations) -} - -export const createICSSRules = (imports, exports) => [ - ...createICSSImportsRules(imports), - createICSSExportsRule(exports) -] diff --git a/src/index.js b/src/index.js index 19b5911..fbfe063 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,10 @@ -const postcss = require('postcss') -const { default: replaceSymbols, replaceAll } = require('icss-replace-symbols') -const { extractICSS, createICSSRules } = require('./icss.js') +import postcss from 'postcss' +import { + replaceSymbols, + replaceValueSymbols, + extractICSS, + createICSSRules +} from 'icss-utils' const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/ const matchValueDefinition = /(?:\s+|^)([\w-]+):?\s+(.+?)\s*$/g @@ -14,11 +18,11 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( css, result ) => { - const { imports, exports } = extractICSS(css) + const { icssImports, icssExports } = extractICSS(css) let importIndex = 0 const createImportedName = (path, name) => { const importedName = getAliasName(name, importIndex) - if (imports[path] && imports[path][importedName]) { + if (icssImports[path] && icssImports[path][importedName]) { importIndex += 1 return createImportedName(path, name) } @@ -31,7 +35,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( while ((matches = matchValueDefinition.exec(atRule.params))) { let [, key, value] = matches // Add to the definitions, knowing that values can refer to each other - exports[key] = replaceAll(exports, value) + icssExports[key] = replaceValueSymbols(value, icssExports) atRule.remove() } } @@ -41,7 +45,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( if (matches) { let [, aliasesString, path] = matches // We can use constants for path names - if (exports[path]) path = exports[path] + if (icssExports[path]) path = icssExports[path] let aliases = aliasesString .replace(/^\(\s*([\s\S]+)\s*\)$/, '$1') .split(/\s*,\s*/) @@ -50,7 +54,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( if (tokens) { let [, theirName, myName = theirName] = tokens let importedName = createImportedName(path, myName) - exports[myName] = importedName + icssExports[myName] = importedName return { theirName, importedName } } else { throw new Error(`@import statement "${alias}" is invalid!`) @@ -60,7 +64,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( acc[importedName] = theirName return acc }, {}) - imports[path] = Object.assign({}, imports[path], aliases) + icssImports[path] = Object.assign({}, icssImports[path], aliases) atRule.remove() } } @@ -79,10 +83,10 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( }) /* If we have no definitions, don't continue */ - if (Object.keys(exports).length === 0) return + if (Object.keys(icssExports).length === 0) return /* Perform replacements */ - replaceSymbols(css, exports) + replaceSymbols(css, icssExports) - css.prepend(createICSSRules(imports, exports)) + css.prepend(createICSSRules(icssImports, icssExports)) }) diff --git a/yarn.lock b/yarn.lock index bb92687..929966c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1370,9 +1370,11 @@ iconv-lite@0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" -icss-replace-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" +icss-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.0.0.tgz#9eb8432af871adc003e4ac7a574d24169398317d" + dependencies: + postcss "^6.0.1" indent-string@^2.1.0: version "2.1.0" From b760f05bc989c136f4e3d90b6f9b3b94c769ef61 Mon Sep 17 00:00:00 2001 From: Bogdan Chadkin Date: Wed, 24 May 2017 21:39:06 +0300 Subject: [PATCH 8/8] Remove noisy comments --- src/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/index.js b/src/index.js index fbfe063..d839301 100644 --- a/src/index.js +++ b/src/index.js @@ -82,10 +82,8 @@ module.exports = postcss.plugin('postcss-modules-values', () => ( } }) - /* If we have no definitions, don't continue */ if (Object.keys(icssExports).length === 0) return - /* Perform replacements */ replaceSymbols(css, icssExports) css.prepend(createICSSRules(icssImports, icssExports))