Skip to content

Commit e84686a

Browse files
authored
Merge pull request #105 from css-modules/icss-utils
Icss utils
2 parents f8f6f79 + b760f05 commit e84686a

File tree

4 files changed

+113
-91
lines changed

4 files changed

+113
-91
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"strip-indent": "^2.0.0"
5757
},
5858
"dependencies": {
59-
"icss-replace-symbols": "^1.1.0",
59+
"icss-utils": "^2.0.0",
6060
"postcss": "^6.0.1"
6161
}
6262
}

src/index.js

+30-56
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,41 @@
1-
const postcss = require('postcss')
2-
const { default: replaceSymbols, replaceAll } = require('icss-replace-symbols')
1+
import postcss from 'postcss'
2+
import {
3+
replaceSymbols,
4+
replaceValueSymbols,
5+
extractICSS,
6+
createICSSRules
7+
} from 'icss-utils'
38

49
const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/
510
const matchValueDefinition = /(?:\s+|^)([\w-]+):?\s+(.+?)\s*$/g
611
const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/
712

8-
const addImportsRules = (css, imports) => {
9-
const rules = imports.map(({ path, aliases }) => {
10-
const declarations = Object.keys(aliases).map(key =>
11-
postcss.decl({
12-
prop: key,
13-
value: aliases[key],
14-
raws: { before: '\n ' }
15-
})
16-
)
17-
return postcss
18-
.rule({
19-
selector: `:import(${path})`,
20-
raws: { after: '\n' }
21-
})
22-
.append(declarations)
23-
})
24-
css.prepend(rules)
25-
}
26-
27-
const addExportsRule = (css, exports) => {
28-
const declarations = Object.keys(exports).map(key =>
29-
postcss.decl({
30-
prop: key,
31-
value: exports[key],
32-
raws: { before: '\n ' }
33-
})
34-
)
35-
const rule = postcss
36-
.rule({
37-
selector: `:export`,
38-
raws: { after: '\n' }
39-
})
40-
.append(declarations)
41-
css.prepend(rule)
42-
}
43-
44-
let importIndex = 0
45-
const createImportedName = importName =>
46-
`i__const_${importName.replace(/\W/g, '_')}_${importIndex++}`
13+
// 'i' prefix to prevent postcss parsing "_" as css hook
14+
const getAliasName = (name, index) =>
15+
`i__value_${name.replace(/\W/g, '_')}_${index}`
4716

4817
module.exports = postcss.plugin('postcss-modules-values', () => (
4918
css,
5019
result
5120
) => {
52-
let importAliases = []
53-
let definitions = {}
21+
const { icssImports, icssExports } = extractICSS(css)
22+
let importIndex = 0
23+
const createImportedName = (path, name) => {
24+
const importedName = getAliasName(name, importIndex)
25+
if (icssImports[path] && icssImports[path][importedName]) {
26+
importIndex += 1
27+
return createImportedName(path, name)
28+
}
29+
importIndex += 1
30+
return importedName
31+
}
5432

5533
const addDefinition = atRule => {
5634
let matches
5735
while ((matches = matchValueDefinition.exec(atRule.params))) {
5836
let [, key, value] = matches
5937
// Add to the definitions, knowing that values can refer to each other
60-
definitions[key] = replaceAll(definitions, value)
38+
icssExports[key] = replaceValueSymbols(value, icssExports)
6139
atRule.remove()
6240
}
6341
}
@@ -67,16 +45,16 @@ module.exports = postcss.plugin('postcss-modules-values', () => (
6745
if (matches) {
6846
let [, aliasesString, path] = matches
6947
// We can use constants for path names
70-
if (definitions[path]) path = definitions[path]
48+
if (icssExports[path]) path = icssExports[path]
7149
let aliases = aliasesString
7250
.replace(/^\(\s*([\s\S]+)\s*\)$/, '$1')
7351
.split(/\s*,\s*/)
7452
.map(alias => {
7553
let tokens = matchImport.exec(alias)
7654
if (tokens) {
77-
let [, /*match*/ theirName, myName = theirName] = tokens
78-
let importedName = createImportedName(myName)
79-
definitions[myName] = importedName
55+
let [, theirName, myName = theirName] = tokens
56+
let importedName = createImportedName(path, myName)
57+
icssExports[myName] = importedName
8058
return { theirName, importedName }
8159
} else {
8260
throw new Error(`@import statement "${alias}" is invalid!`)
@@ -86,7 +64,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => (
8664
acc[importedName] = theirName
8765
return acc
8866
}, {})
89-
importAliases.push({ path, aliases })
67+
icssImports[path] = Object.assign({}, icssImports[path], aliases)
9068
atRule.remove()
9169
}
9270
}
@@ -104,13 +82,9 @@ module.exports = postcss.plugin('postcss-modules-values', () => (
10482
}
10583
})
10684

107-
/* If we have no definitions, don't continue */
108-
if (Object.keys(definitions).length === 0) return
109-
110-
/* Perform replacements */
111-
replaceSymbols(css, definitions)
85+
if (Object.keys(icssExports).length === 0) return
11286

113-
addExportsRule(css, definitions)
87+
replaceSymbols(css, icssExports)
11488

115-
addImportsRules(css, importAliases)
89+
css.prepend(createICSSRules(icssImports, icssExports))
11690
})

test/test.js

+77-31
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ test('should import and re-export a simple constant', () => {
7676
).resolves.toEqual(
7777
strip(`
7878
:import("./colors.css") {
79-
i__const_red_0: red
79+
i__value_red_0: red
8080
}
8181
:export {
82-
red: i__const_red_0
82+
red: i__value_red_0
8383
}
8484
`)
8585
)
@@ -94,12 +94,12 @@ test('should import a simple constant and replace usages', () => {
9494
).resolves.toEqual(
9595
strip(`
9696
:import("./colors.css") {
97-
i__const_red_1: red;
97+
i__value_red_0: red;
9898
}
9999
:export {
100-
red: i__const_red_1;
100+
red: i__value_red_0;
101101
}
102-
.foo { color: i__const_red_1; }
102+
.foo { color: i__value_red_0; }
103103
`)
104104
)
105105
})
@@ -113,12 +113,12 @@ test('should import and alias a constant and replace usages', () => {
113113
).resolves.toEqual(
114114
strip(`
115115
:import("./colors.css") {
116-
i__const_red_2: blue;
116+
i__value_red_0: blue;
117117
}
118118
:export {
119-
red: i__const_red_2;
119+
red: i__value_red_0;
120120
}
121-
.foo { color: i__const_red_2; }
121+
.foo { color: i__value_red_0; }
122122
`)
123123
)
124124
})
@@ -133,15 +133,15 @@ test('should import multiple from a single file', () => {
133133
).resolves.toEqual(
134134
strip(`
135135
:import("./colors.css") {
136-
i__const_blue_3: blue;
137-
i__const_red_4: red;
136+
i__value_blue_0: blue;
137+
i__value_red_1: red;
138138
}
139139
:export {
140-
blue: i__const_blue_3;
141-
red: i__const_red_4;
140+
blue: i__value_blue_0;
141+
red: i__value_red_1;
142142
}
143-
.foo { color: i__const_red_4; }
144-
.bar { color: i__const_blue_3 }
143+
.foo { color: i__value_red_1; }
144+
.bar { color: i__value_blue_0 }
145145
`)
146146
)
147147
})
@@ -154,11 +154,11 @@ test('should import from a definition', () => {
154154
).resolves.toEqual(
155155
strip(`
156156
:import("./colors.css") {
157-
i__const_red_5: red
157+
i__value_red_0: red
158158
}
159159
:export {
160160
colors: "./colors.css";
161-
red: i__const_red_5
161+
red: i__value_red_0
162162
}
163163
`)
164164
)
@@ -172,10 +172,10 @@ test('should only allow values for paths if defined in the right order', () => {
172172
).resolves.toEqual(
173173
strip(`
174174
:import(colors) {
175-
i__const_red_6: red
175+
i__value_red_0: red
176176
}
177177
:export {
178-
red: i__const_red_6;
178+
red: i__value_red_0;
179179
colors: "./colors.css"
180180
}
181181
`)
@@ -226,14 +226,14 @@ test('should preserve import order', () => {
226226
).resolves.toEqual(
227227
strip(`
228228
:import("./a.css") {
229-
i__const_a_7: a
229+
i__value_a_0: a
230230
}
231231
:import("./b.css") {
232-
i__const_b_8: b
232+
i__value_b_1: b
233233
}
234234
:export {
235-
a: i__const_a_7;
236-
b: i__const_b_8
235+
a: i__value_a_0;
236+
b: i__value_b_1
237237
}
238238
`)
239239
)
@@ -247,12 +247,12 @@ test('should allow custom-property-style names', () => {
247247
).resolves.toEqual(
248248
strip(`
249249
:import("./colors.css") {
250-
i__const___red_9: --red;
250+
i__value___red_0: --red;
251251
}
252252
:export {
253-
--red: i__const___red_9;
253+
--red: i__value___red_0;
254254
}
255-
.foo { color: i__const___red_9; }
255+
.foo { color: i__value___red_0; }
256256
`)
257257
)
258258
})
@@ -306,15 +306,15 @@ test('should import multiple from a single file on multiple lines', () => {
306306
).resolves.toEqual(
307307
strip(`
308308
:import("./colors.css") {
309-
i__const_blue_10: blue;
310-
i__const_red_11: red;
309+
i__value_blue_0: blue;
310+
i__value_red_1: red;
311311
}
312312
:export {
313-
blue: i__const_blue_10;
314-
red: i__const_red_11;
313+
blue: i__value_blue_0;
314+
red: i__value_red_1;
315315
}
316-
.foo { color: i__const_red_11; }
317-
.bar { color: i__const_blue_10 }
316+
.foo { color: i__value_red_1; }
317+
.bar { color: i__value_blue_0 }
318318
`)
319319
)
320320
})
@@ -348,3 +348,49 @@ test('should allow values with nested parantheses', () => {
348348
`)
349349
)
350350
})
351+
352+
test('reuse existing :import with same name and :export', () => {
353+
return expect(
354+
runCSS(`
355+
:import('./colors.css') {
356+
i__some_import: blue;
357+
}
358+
:export {
359+
b: i__c;
360+
}
361+
@value a from './colors.css';
362+
`)
363+
).resolves.toEqual(
364+
strip(`
365+
:import('./colors.css') {
366+
i__some_import: blue;
367+
i__value_a_0: a
368+
}
369+
:export {
370+
b: i__c;
371+
a: i__value_a_0
372+
}
373+
`)
374+
)
375+
})
376+
377+
test('prevent imported names collision', () => {
378+
return expect(
379+
runCSS(`
380+
:import(colors) {
381+
i__value_a_0: a;
382+
}
383+
@value a from colors;
384+
`)
385+
).resolves.toEqual(
386+
strip(`
387+
:import(colors) {
388+
i__value_a_0: a;
389+
i__value_a_1: a
390+
}
391+
:export {
392+
a: i__value_a_1
393+
}
394+
`)
395+
)
396+
})

yarn.lock

+5-3
Original file line numberDiff line numberDiff line change
@@ -1370,9 +1370,11 @@ iconv-lite@0.4.13:
13701370
version "0.4.13"
13711371
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2"
13721372

1373-
icss-replace-symbols@^1.1.0:
1374-
version "1.1.0"
1375-
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
1373+
icss-utils@^2.0.0:
1374+
version "2.0.0"
1375+
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.0.0.tgz#9eb8432af871adc003e4ac7a574d24169398317d"
1376+
dependencies:
1377+
postcss "^6.0.1"
13761378

13771379
indent-string@^2.1.0:
13781380
version "2.1.0"

0 commit comments

Comments
 (0)