Skip to content

Commit 05a42e2

Browse files
feat: support @value at-rule in selectors (#941)
BREAKING CHANGE: if you have name of `@value` in selector it will be replaced, you need to rename your name of `@value` or rename your selector
1 parent fd8d2e6 commit 05a42e2

File tree

9 files changed

+476
-87
lines changed

9 files changed

+476
-87
lines changed

README.md

+27
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,33 @@ To import from multiple modules use multiple `composes:` rules.
434434
}
435435
```
436436

437+
##### `Values`
438+
439+
You can use `@value` to specific values to be reused throughout a document.
440+
441+
We recommend use prefix `v-` for values, `s-` for selectors and `m-` for media at-rules.
442+
443+
```css
444+
@value v-primary: #BF4040;
445+
@value s-black: black-selector;
446+
@value m-large: (min-width: 960px);
447+
448+
.header {
449+
color: v-primary;
450+
padding: 0 10px;
451+
}
452+
453+
.s-black {
454+
color: black;
455+
}
456+
457+
@media m-large {
458+
.header {
459+
padding: 0 20px;
460+
}
461+
}
462+
```
463+
437464
#### `Boolean`
438465

439466
Enable **CSS Modules** features.

package-lock.json

+8-36
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@
4848
"normalize-path": "^3.0.0",
4949
"postcss": "^7.0.14",
5050
"postcss-modules-extract-imports": "^2.0.0",
51-
"postcss-modules-local-by-default": "^2.0.6",
51+
"postcss-modules-local-by-default": "^3.0.1",
5252
"postcss-modules-scope": "^2.1.0",
53-
"postcss-modules-values": "^2.0.0",
53+
"postcss-modules-values": "^3.0.0",
5454
"postcss-value-parser": "^3.3.0",
5555
"schema-utils": "^1.0.0"
5656
},

src/plugins/postcss-icss-parser.js

+29-26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import postcss from 'postcss';
22
import valueParser from 'postcss-value-parser';
3-
import { extractICSS } from 'icss-utils';
3+
import { extractICSS, replaceValueSymbols } from 'icss-utils';
44
import loaderUtils from 'loader-utils';
55

66
const pluginName = 'postcss-icss-parser';
@@ -9,22 +9,23 @@ export default postcss.plugin(
99
pluginName,
1010
() =>
1111
function process(css, result) {
12-
const imports = {};
13-
const icss = extractICSS(css);
14-
const exports = icss.icssExports;
12+
const importReplacements = Object.create(null);
13+
const { icssImports, icssExports } = extractICSS(css);
1514

16-
Object.keys(icss.icssImports).forEach((key) => {
15+
let index = 0;
16+
17+
Object.keys(icssImports).forEach((key) => {
1718
const url = loaderUtils.parseString(key);
1819

19-
Object.keys(icss.icssImports[key]).forEach((prop) => {
20-
const index = Object.keys(imports).length;
20+
Object.keys(icssImports[key]).forEach((prop) => {
21+
index += 1;
2122

22-
imports[`$${prop}`] = index;
23+
importReplacements[prop] = `___CSS_LOADER_IMPORT___${index}___`;
2324

2425
result.messages.push({
2526
pluginName,
2627
type: 'icss-import',
27-
item: { url, export: icss.icssImports[key][prop], index },
28+
item: { url, export: icssImports[key][prop], index },
2829
});
2930

3031
const alreadyIncluded = result.messages.find(
@@ -56,41 +57,43 @@ export default postcss.plugin(
5657
}
5758

5859
const token = node.value;
59-
const importIndex = imports[`$${token}`];
60+
const replacement = importReplacements[token];
6061

61-
if (typeof importIndex === 'number') {
62+
if (replacement) {
6263
// eslint-disable-next-line no-param-reassign
63-
node.value = `___CSS_LOADER_IMPORT___${importIndex}___`;
64+
node.value = replacement;
6465
}
6566
});
6667

6768
return tokens.toString();
6869
}
6970

70-
// Replace tokens in declarations
71-
css.walkDecls((decl) => {
72-
// eslint-disable-next-line no-param-reassign
73-
decl.value = replaceImportsInString(decl.value.toString());
74-
});
75-
76-
// Replace tokens in at-rules
77-
css.walkAtRules((atrule) => {
78-
// Due reusing `ast` from `postcss-loader` some plugins may lack
79-
// `params` property, we need to account for this possibility
80-
if (atrule.params) {
71+
// Replace tokens
72+
css.walk((node) => {
73+
// Due reusing `ast` from `postcss-loader` some plugins may remove `value`, `selector` or `params` properties
74+
if (node.type === 'decl' && node.value) {
75+
// eslint-disable-next-line no-param-reassign
76+
node.value = replaceImportsInString(node.value.toString());
77+
} else if (node.type === 'rule' && node.selector) {
78+
// eslint-disable-next-line no-param-reassign
79+
node.selector = replaceValueSymbols(
80+
node.selector.toString(),
81+
importReplacements
82+
);
83+
} else if (node.type === 'atrule' && node.params) {
8184
// eslint-disable-next-line no-param-reassign
82-
atrule.params = replaceImportsInString(atrule.params.toString());
85+
node.params = replaceImportsInString(node.params.toString());
8386
}
8487
});
8588

8689
// Replace tokens in export
87-
Object.keys(exports).forEach((exportName) => {
90+
Object.keys(icssExports).forEach((exportName) => {
8891
result.messages.push({
8992
pluginName,
9093
type: 'export',
9194
item: {
9295
key: exportName,
93-
value: replaceImportsInString(exports[exportName]),
96+
value: replaceImportsInString(icssExports[exportName]),
9497
},
9598
});
9699
});

0 commit comments

Comments
 (0)