Skip to content
This repository was archived by the owner on Jan 6, 2024. It is now read-only.

Commit 8f82614

Browse files
committed
chore: problem description @ README
1 parent 11eafd9 commit 8f82614

File tree

1 file changed

+224
-1
lines changed

1 file changed

+224
-1
lines changed

README.md

Lines changed: 224 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,224 @@
1-
# postcss-nested-once
1+
# PostCSS Nested for rollup-plugin-styles
2+
3+
### Summary
4+
5+
This plugin allows using Sass-like nested rules in combination with [css-modules](https://github.com/css-modules/css-modules) by [rollup-plugin-styles](https://github.com/Anidetrix/rollup-plugin-styles).
6+
7+
Specifically, it solves the problem of the ampersand-combined selectors, i.e.:
8+
9+
```postcss
10+
// styles.css
11+
.list {
12+
color: red;
13+
14+
&_item {
15+
color: green;
16+
}
17+
}
18+
```
19+
20+
Results in:
21+
22+
```javascript
23+
// some-module.js
24+
import styles from "./styles.css";
25+
26+
// with any setup:
27+
console.log(styles.list); // => "styles_list__HASH"
28+
29+
// with postcss-nested plugin:
30+
console.log(styles.list_item); // => undefined ,
31+
32+
// with postcss-nested-once plugin:
33+
console.log(styles.list_item); // => "styles_list_item__HASH"
34+
```
35+
36+
### Usage
37+
38+
Install:
39+
40+
```shell
41+
yarn add postcss-nested-once -D
42+
```
43+
44+
It's intended to replace [postcss-nested](https://github.com/postcss/postcss-nested) for the following [rollup](https://github.com/rollup/rollup) configuration:
45+
46+
```javascript
47+
// rollup.config.js
48+
49+
// ...
50+
const stylesRollupPlugin = require("rollup-plugin-styles");
51+
const postcssNestedOncePlugin = require("postcss-nested-once");
52+
53+
module.exports = {
54+
// ...
55+
plugins: [
56+
// ...
57+
stylesRollupPlugin({
58+
// ...
59+
mode: "inject",
60+
modules: true,
61+
plugins: [
62+
// ...
63+
postcssNestedOnce(),
64+
],
65+
}),
66+
],
67+
};
68+
```
69+
70+
Assuming the following source:
71+
72+
```postcss
73+
// styles.css
74+
.parent {
75+
color: red;
76+
77+
& .child {
78+
color: green;
79+
}
80+
}
81+
82+
.list {
83+
color: red;
84+
85+
&_item {
86+
color: green;
87+
}
88+
}
89+
```
90+
91+
This will produce:
92+
93+
```javascript
94+
// styles.js
95+
// ...
96+
var css =
97+
".styles_parent__HASH {" +
98+
" color: red" +
99+
"}" +
100+
"" +
101+
" .styles_parent__HASH .styles_child__HASH {" +
102+
" color: green;" +
103+
" }" +
104+
"" +
105+
".styles_list__HASH {" +
106+
" color: red" +
107+
"}" +
108+
"" +
109+
".styles_list_item__HASH {" +
110+
" color: green;" +
111+
" }" +
112+
"";
113+
var modules = {
114+
parent: "styles_parent__HASH",
115+
child: "styles_child__HASH",
116+
list: "styles_list__HASH",
117+
list_item: "styles_list_item__HASH",
118+
};
119+
injectCss["default"](css, {});
120+
121+
exports.css = css;
122+
exports.default = modules;
123+
```
124+
125+
Which in turn allows to use all the four classes in js:
126+
127+
```javascript
128+
// some-module.js
129+
import styles from "./styles.css";
130+
131+
console.log(styles.parent); // => "styles_parent__HASH"
132+
console.log(styles.child); // => "styles_child__HASH"
133+
console.log(styles.list); // => "styles_list__HASH"
134+
console.log(styles.list_item); // => "styles_list_item__HASH"
135+
```
136+
137+
### Problem Details
138+
139+
The [rollup-plugin-styles](https://github.com/Anidetrix/rollup-plugin-styles) provides an ability to use css modules by simply specifying `modules: true | ModulesOptions` during configuration.
140+
141+
Under the hood it does not rely on the [postcss-modules](https://github.com/madyankin/postcss-modules) package directly, but introduces its own plugins pipeline instead:
142+
143+
```
144+
// built-in plugins
145+
styles-import - internal plugin, uses 'Once' hook, used only if the 'import' option is enabled;
146+
styles-url - internal plugin, uses 'Once' hook, used only if the 'url' option is enabled;
147+
148+
// bunch of plugins from options.plugins
149+
postcss-nested - could be listed here, if specified
150+
plugin-from-options #1
151+
plugin-from-options #2
152+
...
153+
154+
// bunch of plugins from postcss.config.js
155+
postcss-nested - or here, if specified
156+
plugin-from-postcss-config #1
157+
plugin-from-postcss-config #2
158+
...
159+
160+
// css-modules-related plugins
161+
postcss-modules-values - dependency plugin, uses 'Once' hook
162+
postcss-modules-local-by-default - dependency plugin, uses 'Once' hook
163+
postcss-modules-extract-imports - dependency plugin, uses 'Once' hook
164+
postcss-modules-scope - dependency plugin, uses 'Once' hook
165+
styles-icss - internal plugin involved in resulting exports generation, uses 'OnceExit' hook
166+
```
167+
168+
By that far it seems like everything should work as expected due to proper plugin's order.
169+
170+
So to make the next guess it's good to know the responsibility of every plugin. To cut the long story short:
171+
172+
- `postcss-modules-values` extracts `@value XX` and `@value YY from` into corresponding internal `:import {}` / `:export {}` selectors and gives local names;
173+
- `postcss-modules-local-by-default` wraps every suitable css selector in internal `:local` directive;
174+
- `postcss-modules-extract-imports` is responsible for the `compose` feature;
175+
- `postcss-modules-scope` among other actions generates `:export {}` directives for every `:local` selector;
176+
- `styles-icss` fills special object from the contents of every `:export {}` directive.
177+
178+
The object formed by `styles-icss` is used further down the pipeline to write exports from the generated `styles.js` file (which are consumed by `import styles from './styles.css''`).
179+
180+
As a result, for the above input we'll get the following output:
181+
182+
```javascript
183+
// styles.js (generated)
184+
var css =
185+
".styles_parent__HASH {" +
186+
" color: red" +
187+
"}" +
188+
"" +
189+
" .styles_parent__HASH .styles_child__HASH {" +
190+
" color: green;" +
191+
" }" +
192+
"" +
193+
".styles_list__HASH {" +
194+
" color: red" +
195+
"}" +
196+
"" +
197+
".styles_list__HASH_item {" +
198+
" color: green;" +
199+
" }" +
200+
"";
201+
var modules = {
202+
parent: "styles_parent__HASH",
203+
child: "styles_child__HASH",
204+
list: "styles_list__HASH",
205+
};
206+
injectCss["default"](css, {});
207+
208+
exports.css = css;
209+
exports.default = modules;
210+
```
211+
212+
So we have an actual rule `.styles_list__HASH_item` (which will be injected during the import), but do not have the corresponding export (making `styles.list_item === undefined` at runtime).
213+
214+
The key hint is that `_item` suffix is added after the `__HASH` part, which means that `postcss-nested` transformation runs after the `postcss-modules-scope` transformation. This happens because `postcss-nested` plugin uses `Rule` hook while other ones (mostly) use `Once` + `walk()` combination which comes first.
215+
216+
So the most simple solution is to move `postcss-nested`'s logic to the same `Once` hook, which resulted in `postcss-nested-once` plugin.
217+
218+
### Implementation
219+
220+
For the sake of simple maintenance this plugin lists `postcss-nested` as dependency and reuses it by calling `root.walkRules((rule) => { postcssNestedInstance.Rule(rule, postcssAPI); });` in `Once` hook.
221+
222+
It accepts (and passes down) the same options as `postcss-nested`.
223+
224+
Type definitions are copy-pasted from the original plugin.

0 commit comments

Comments
 (0)