Skip to content

Commit b88d76b

Browse files
committed
Ensure :imports and :exports
1 parent ce736f6 commit b88d76b

File tree

3 files changed

+108
-43
lines changed

3 files changed

+108
-43
lines changed

src/icss.js

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,42 @@
11
const postcss = require('postcss')
22

3+
const importPattern = /^:import\(("[^"]*"|'[^']*'|[\w-]+)\)$/
4+
5+
const exportPattern = /^:export$/
6+
7+
const getDeclsObject = rule => {
8+
const object = {}
9+
rule.walkDecls(decl => {
10+
object[decl.prop] = decl.value
11+
})
12+
return object
13+
}
14+
15+
export const extractICSSImports = css => {
16+
const imports = {}
17+
css.walkRules(rule => {
18+
const matches = importPattern.exec(rule.selector)
19+
if (matches) {
20+
const path = matches[1]
21+
imports[path] = Object.assign({}, imports[path], getDeclsObject(rule))
22+
rule.remove()
23+
}
24+
})
25+
return imports
26+
}
27+
28+
export const extractICSSExports = css => {
29+
const exports = {}
30+
css.walkRules(exportPattern, rule => {
31+
Object.assign(exports, getDeclsObject(rule))
32+
rule.remove()
33+
})
34+
return exports
35+
}
36+
337
const genICSSImportsRules = imports => {
4-
return imports.map(({ path, aliases }) => {
38+
return Object.keys(imports).map(path => {
39+
const aliases = imports[path]
540
const declarations = Object.keys(aliases).map(key =>
641
postcss.decl({
742
prop: key,

src/index.js

+16-11
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
const postcss = require('postcss')
22
const { default: replaceSymbols, replaceAll } = require('icss-replace-symbols')
3-
const { genICSSRules } = require('./icss.js')
3+
const {
4+
extractICSSImports,
5+
extractICSSExports,
6+
genICSSRules
7+
} = require('./icss.js')
48

59
const matchImports = /^(.+?|\([\s\S]+?\))\s+from\s+("[^"]*"|'[^']*'|[\w-]+)$/
610
const matchValueDefinition = /(?:\s+|^)([\w-]+):?\s+(.+?)\s*$/g
711
const matchImport = /^([\w-]+)(?:\s+as\s+([\w-]+))?/
812

13+
// 'i' prefix to prevent postcss parsing "_" as css hook
914
const getAliasName = (name, index) =>
10-
`__value_${name.replace(/\W/g, '_')}_${index}`
15+
`i__value_${name.replace(/\W/g, '_')}_${index}`
1116

1217
let importIndex = 0
1318
const createImportedName = importName => getAliasName(importName, importIndex++)
@@ -16,15 +21,15 @@ module.exports = postcss.plugin('postcss-modules-values', () => (
1621
css,
1722
result
1823
) => {
19-
let importAliases = []
20-
let definitions = {}
24+
const imports = extractICSSImports(css)
25+
const exports = extractICSSExports(css)
2126

2227
const addDefinition = atRule => {
2328
let matches
2429
while ((matches = matchValueDefinition.exec(atRule.params))) {
2530
let [, key, value] = matches
2631
// Add to the definitions, knowing that values can refer to each other
27-
definitions[key] = replaceAll(definitions, value)
32+
exports[key] = replaceAll(exports, value)
2833
atRule.remove()
2934
}
3035
}
@@ -34,7 +39,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => (
3439
if (matches) {
3540
let [, aliasesString, path] = matches
3641
// We can use constants for path names
37-
if (definitions[path]) path = definitions[path]
42+
if (exports[path]) path = exports[path]
3843
let aliases = aliasesString
3944
.replace(/^\(\s*([\s\S]+)\s*\)$/, '$1')
4045
.split(/\s*,\s*/)
@@ -43,7 +48,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => (
4348
if (tokens) {
4449
let [, theirName, myName = theirName] = tokens
4550
let importedName = createImportedName(myName)
46-
definitions[myName] = importedName
51+
exports[myName] = importedName
4752
return { theirName, importedName }
4853
} else {
4954
throw new Error(`@import statement "${alias}" is invalid!`)
@@ -53,7 +58,7 @@ module.exports = postcss.plugin('postcss-modules-values', () => (
5358
acc[importedName] = theirName
5459
return acc
5560
}, {})
56-
importAliases.push({ path, aliases })
61+
imports[path] = Object.assign({}, imports[path], aliases)
5762
atRule.remove()
5863
}
5964
}
@@ -72,10 +77,10 @@ module.exports = postcss.plugin('postcss-modules-values', () => (
7277
})
7378

7479
/* If we have no definitions, don't continue */
75-
if (Object.keys(definitions).length === 0) return
80+
if (Object.keys(exports).length === 0) return
7681

7782
/* Perform replacements */
78-
replaceSymbols(css, definitions)
83+
replaceSymbols(css, exports)
7984

80-
css.prepend(genICSSRules(importAliases, definitions))
85+
css.prepend(genICSSRules(imports, exports))
8186
})

test/test.js

+56-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-
__value_red_0: red
79+
i__value_red_0: red
8080
}
8181
:export {
82-
red: __value_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-
__value_red_1: red;
97+
i__value_red_1: red;
9898
}
9999
:export {
100-
red: __value_red_1;
100+
red: i__value_red_1;
101101
}
102-
.foo { color: __value_red_1; }
102+
.foo { color: i__value_red_1; }
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-
__value_red_2: blue;
116+
i__value_red_2: blue;
117117
}
118118
:export {
119-
red: __value_red_2;
119+
red: i__value_red_2;
120120
}
121-
.foo { color: __value_red_2; }
121+
.foo { color: i__value_red_2; }
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-
__value_blue_3: blue;
137-
__value_red_4: red;
136+
i__value_blue_3: blue;
137+
i__value_red_4: red;
138138
}
139139
:export {
140-
blue: __value_blue_3;
141-
red: __value_red_4;
140+
blue: i__value_blue_3;
141+
red: i__value_red_4;
142142
}
143-
.foo { color: __value_red_4; }
144-
.bar { color: __value_blue_3 }
143+
.foo { color: i__value_red_4; }
144+
.bar { color: i__value_blue_3 }
145145
`)
146146
)
147147
})
@@ -154,11 +154,11 @@ test('should import from a definition', () => {
154154
).resolves.toEqual(
155155
strip(`
156156
:import("./colors.css") {
157-
__value_red_5: red
157+
i__value_red_5: red
158158
}
159159
:export {
160160
colors: "./colors.css";
161-
red: __value_red_5
161+
red: i__value_red_5
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-
__value_red_6: red
175+
i__value_red_6: red
176176
}
177177
:export {
178-
red: __value_red_6;
178+
red: i__value_red_6;
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-
__value_a_7: a
229+
i__value_a_7: a
230230
}
231231
:import("./b.css") {
232-
__value_b_8: b
232+
i__value_b_8: b
233233
}
234234
:export {
235-
a: __value_a_7;
236-
b: __value_b_8
235+
a: i__value_a_7;
236+
b: i__value_b_8
237237
}
238238
`)
239239
)
@@ -247,12 +247,12 @@ test('should allow custom-property-style names', () => {
247247
).resolves.toEqual(
248248
strip(`
249249
:import("./colors.css") {
250-
__value___red_9: --red;
250+
i__value___red_9: --red;
251251
}
252252
:export {
253-
--red: __value___red_9;
253+
--red: i__value___red_9;
254254
}
255-
.foo { color: __value___red_9; }
255+
.foo { color: i__value___red_9; }
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-
__value_blue_10: blue;
310-
__value_red_11: red;
309+
i__value_blue_10: blue;
310+
i__value_red_11: red;
311311
}
312312
:export {
313-
blue: __value_blue_10;
314-
red: __value_red_11;
313+
blue: i__value_blue_10;
314+
red: i__value_red_11;
315315
}
316-
.foo { color: __value_red_11; }
317-
.bar { color: __value_blue_10 }
316+
.foo { color: i__value_red_11; }
317+
.bar { color: i__value_blue_10 }
318318
`)
319319
)
320320
})
@@ -348,3 +348,28 @@ 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_12: a
368+
}
369+
:export {
370+
b: i__c;
371+
a: i__value_a_12
372+
}
373+
`)
374+
)
375+
})

0 commit comments

Comments
 (0)