postcss-nested 6.0.1 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -27
- package/index.js +107 -79
- package/package.json +13 -7
package/README.md
CHANGED
|
@@ -8,25 +8,25 @@
|
|
|
8
8
|
|
|
9
9
|
```css
|
|
10
10
|
.phone {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
body.is_dark & {
|
|
17
|
-
color: white;
|
|
18
|
-
}
|
|
11
|
+
&_title {
|
|
12
|
+
width: 500px;
|
|
13
|
+
@media (max-width: 500px) {
|
|
14
|
+
width: auto;
|
|
19
15
|
}
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
body.is_dark & {
|
|
17
|
+
color: white;
|
|
22
18
|
}
|
|
19
|
+
}
|
|
20
|
+
img {
|
|
21
|
+
display: block;
|
|
22
|
+
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.title {
|
|
26
26
|
font-size: var(--font);
|
|
27
27
|
|
|
28
28
|
@at-root html {
|
|
29
|
-
|
|
29
|
+
--font: 16px;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
```
|
|
@@ -35,39 +35,39 @@ will be processed to:
|
|
|
35
35
|
|
|
36
36
|
```css
|
|
37
37
|
.phone_title {
|
|
38
|
-
|
|
38
|
+
width: 500px;
|
|
39
39
|
}
|
|
40
40
|
@media (max-width: 500px) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
.phone_title {
|
|
42
|
+
width: auto;
|
|
43
|
+
}
|
|
44
44
|
}
|
|
45
45
|
body.is_dark .phone_title {
|
|
46
|
-
|
|
46
|
+
color: white;
|
|
47
47
|
}
|
|
48
48
|
.phone img {
|
|
49
|
-
|
|
49
|
+
display: block;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
.title {
|
|
53
53
|
font-size: var(--font);
|
|
54
54
|
}
|
|
55
55
|
html {
|
|
56
|
-
--font: 16px
|
|
56
|
+
--font: 16px;
|
|
57
57
|
}
|
|
58
58
|
```
|
|
59
59
|
|
|
60
60
|
Related plugins:
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
- Use [`postcss-current-selector`] **after** this plugin if you want
|
|
63
63
|
to use current selector in properties or variables values.
|
|
64
|
-
|
|
64
|
+
- Use [`postcss-nested-ancestors`] **before** this plugin if you want
|
|
65
65
|
to reference any ancestor element directly in your selectors with `^&`.
|
|
66
66
|
|
|
67
67
|
Alternatives:
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
- See also [`postcss-nesting`], which implements [CSSWG draft].
|
|
70
|
+
- [`postcss-nested-props`] for nested properties like `font-size`.
|
|
71
71
|
|
|
72
72
|
<a href="https://evilmartians.com/?utm_source=postcss-nested">
|
|
73
73
|
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg"
|
|
@@ -76,11 +76,10 @@ Alternatives:
|
|
|
76
76
|
|
|
77
77
|
[`postcss-current-selector`]: https://github.com/komlev/postcss-current-selector
|
|
78
78
|
[`postcss-nested-ancestors`]: https://github.com/toomuchdesign/postcss-nested-ancestors
|
|
79
|
-
[`postcss-nested-props`]:
|
|
80
|
-
[`postcss-nesting`]:
|
|
81
|
-
[CSSWG draft]:
|
|
82
|
-
[PostCSS]:
|
|
83
|
-
|
|
79
|
+
[`postcss-nested-props`]: https://github.com/jedmao/postcss-nested-props
|
|
80
|
+
[`postcss-nesting`]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting
|
|
81
|
+
[CSSWG draft]: https://drafts.csswg.org/css-nesting-1/
|
|
82
|
+
[PostCSS]: https://github.com/postcss/postcss
|
|
84
83
|
|
|
85
84
|
## Docs
|
|
86
85
|
Read full docs **[here](https://github.com/postcss/postcss-nested#readme)**.
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { AtRule, Rule } = require('postcss')
|
|
2
2
|
let parser = require('postcss-selector-parser')
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -53,12 +53,12 @@ function interpolateAmpInSelector(nodes, parent) {
|
|
|
53
53
|
*/
|
|
54
54
|
function mergeSelectors(parent, child) {
|
|
55
55
|
let merged = []
|
|
56
|
-
parent.selectors
|
|
56
|
+
for (let sel of parent.selectors) {
|
|
57
57
|
let parentNode = parse(sel, parent)
|
|
58
58
|
|
|
59
|
-
child.selectors
|
|
59
|
+
for (let selector of child.selectors) {
|
|
60
60
|
if (!selector) {
|
|
61
|
-
|
|
61
|
+
continue
|
|
62
62
|
}
|
|
63
63
|
let node = parse(selector, child)
|
|
64
64
|
let replaced = interpolateAmpInSelector(node, parentNode)
|
|
@@ -67,22 +67,43 @@ function mergeSelectors(parent, child) {
|
|
|
67
67
|
node.prepend(parentNode.clone({}))
|
|
68
68
|
}
|
|
69
69
|
merged.push(node.toString())
|
|
70
|
-
}
|
|
71
|
-
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
72
|
return merged
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* Move a child and its
|
|
76
|
+
* Move a child and its preceding comment(s) to after "after"
|
|
77
|
+
* ! It is necessary to clarify the comment
|
|
77
78
|
*/
|
|
78
79
|
function breakOut(child, after) {
|
|
79
|
-
let
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
let changeParent = true
|
|
81
|
+
|
|
82
|
+
for (let node of after.nodes) {
|
|
83
|
+
if (!node.nodes) continue
|
|
84
|
+
|
|
85
|
+
let prevNode = node.prev()
|
|
86
|
+
if (prevNode?.type !== 'comment') continue
|
|
87
|
+
|
|
88
|
+
let parentRule = after.toString()
|
|
89
|
+
|
|
90
|
+
/* Checking that the comment "describes" the rule following. Like this:
|
|
91
|
+
/* comment about the rule below /*
|
|
92
|
+
.rule {}
|
|
93
|
+
*/
|
|
94
|
+
let regexp = new RegExp(`${prevNode.toString()} *\n *${node.toString()}`)
|
|
95
|
+
|
|
96
|
+
if (parentRule.match(regexp)) {
|
|
97
|
+
changeParent = false
|
|
98
|
+
after.after(node).after(prevNode)
|
|
99
|
+
}
|
|
85
100
|
}
|
|
101
|
+
|
|
102
|
+
// It is necessary if the above child has never been moved
|
|
103
|
+
if (changeParent) {
|
|
104
|
+
after.after(child)
|
|
105
|
+
}
|
|
106
|
+
|
|
86
107
|
return child
|
|
87
108
|
}
|
|
88
109
|
|
|
@@ -104,37 +125,42 @@ function createFnAtruleChilds(bubble) {
|
|
|
104
125
|
children.push(child)
|
|
105
126
|
}
|
|
106
127
|
})
|
|
107
|
-
if (bubbling) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
clone.append(child)
|
|
112
|
-
}
|
|
113
|
-
atrule.prepend(clone)
|
|
128
|
+
if (bubbling && children.length) {
|
|
129
|
+
let clone = rule.clone({ nodes: [] })
|
|
130
|
+
for (let child of children) {
|
|
131
|
+
clone.append(child)
|
|
114
132
|
}
|
|
133
|
+
atrule.prepend(clone)
|
|
115
134
|
}
|
|
116
135
|
}
|
|
117
136
|
}
|
|
118
137
|
|
|
119
138
|
function pickDeclarations(selector, declarations, after) {
|
|
120
139
|
let parent = new Rule({
|
|
121
|
-
|
|
122
|
-
|
|
140
|
+
nodes: [],
|
|
141
|
+
selector
|
|
123
142
|
})
|
|
124
143
|
parent.append(declarations)
|
|
125
144
|
after.after(parent)
|
|
126
145
|
return parent
|
|
127
146
|
}
|
|
147
|
+
function pickAndClearDeclarations(ruleSelector, declarations, after, clear = true) {
|
|
148
|
+
if (!declarations.length) return [after, declarations]
|
|
128
149
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
150
|
+
after = pickDeclarations(ruleSelector, declarations, after)
|
|
151
|
+
|
|
152
|
+
if (clear) {
|
|
153
|
+
declarations = []
|
|
133
154
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
155
|
+
|
|
156
|
+
return [after, declarations]
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function atruleNames(defaults, custom = '') {
|
|
160
|
+
let names = defaults.concat(custom)
|
|
161
|
+
let list = {}
|
|
162
|
+
for (let name of names) {
|
|
163
|
+
list[name.replace(/^@/, '')] = true
|
|
138
164
|
}
|
|
139
165
|
return list
|
|
140
166
|
}
|
|
@@ -143,7 +169,7 @@ function parseRootRuleParams(params) {
|
|
|
143
169
|
params = params.trim()
|
|
144
170
|
let braceBlock = params.match(/^\((.*)\)$/)
|
|
145
171
|
if (!braceBlock) {
|
|
146
|
-
return { type: 'basic'
|
|
172
|
+
return { selector: params, type: 'basic' }
|
|
147
173
|
}
|
|
148
174
|
let bits = braceBlock[1].match(/^(with(?:out)?):(.+)$/)
|
|
149
175
|
if (bits) {
|
|
@@ -165,8 +191,8 @@ function parseRootRuleParams(params) {
|
|
|
165
191
|
}
|
|
166
192
|
|
|
167
193
|
return {
|
|
168
|
-
|
|
169
|
-
|
|
194
|
+
escapes,
|
|
195
|
+
type: 'withrules'
|
|
170
196
|
}
|
|
171
197
|
}
|
|
172
198
|
// Unrecognized brace block
|
|
@@ -250,14 +276,14 @@ const rootRuleEscapes = Symbol('rootRuleEscapes')
|
|
|
250
276
|
|
|
251
277
|
function normalizeRootRule(rule) {
|
|
252
278
|
let { params } = rule
|
|
253
|
-
let {
|
|
279
|
+
let { escapes, selector, type } = parseRootRuleParams(params)
|
|
254
280
|
if (type === 'unknown') {
|
|
255
281
|
throw rule.error(
|
|
256
282
|
`Unknown @${rule.name} parameter ${JSON.stringify(params)}`
|
|
257
283
|
)
|
|
258
284
|
}
|
|
259
285
|
if (type === 'basic' && selector) {
|
|
260
|
-
let selectorBlock = new Rule({
|
|
286
|
+
let selectorBlock = new Rule({ nodes: rule.nodes, selector })
|
|
261
287
|
rule.removeAll()
|
|
262
288
|
rule.append(selectorBlock)
|
|
263
289
|
}
|
|
@@ -269,7 +295,7 @@ const hasRootRule = Symbol('hasRootRule')
|
|
|
269
295
|
|
|
270
296
|
module.exports = (opts = {}) => {
|
|
271
297
|
let bubble = atruleNames(
|
|
272
|
-
['media', 'supports', 'layer', 'container'],
|
|
298
|
+
['media', 'supports', 'layer', 'container', 'starting-style'],
|
|
273
299
|
opts.bubble
|
|
274
300
|
)
|
|
275
301
|
let atruleChilds = createFnAtruleChilds(bubble)
|
|
@@ -287,8 +313,6 @@ module.exports = (opts = {}) => {
|
|
|
287
313
|
let preserveEmpty = opts.preserveEmpty
|
|
288
314
|
|
|
289
315
|
return {
|
|
290
|
-
postcssPlugin: 'postcss-nested',
|
|
291
|
-
|
|
292
316
|
Once(root) {
|
|
293
317
|
root.walkAtRules(rootRuleName, node => {
|
|
294
318
|
normalizeRootRule(node)
|
|
@@ -296,6 +320,15 @@ module.exports = (opts = {}) => {
|
|
|
296
320
|
})
|
|
297
321
|
},
|
|
298
322
|
|
|
323
|
+
postcssPlugin: 'postcss-nested',
|
|
324
|
+
|
|
325
|
+
RootExit(root) {
|
|
326
|
+
if (!root[hasRootRule]) return
|
|
327
|
+
|
|
328
|
+
root.walkAtRules(rootRuleName, unwrapRootRule)
|
|
329
|
+
root[hasRootRule] = false
|
|
330
|
+
},
|
|
331
|
+
|
|
299
332
|
Rule(rule) {
|
|
300
333
|
let unwrapped = false
|
|
301
334
|
let after = rule
|
|
@@ -303,58 +336,53 @@ module.exports = (opts = {}) => {
|
|
|
303
336
|
let declarations = []
|
|
304
337
|
|
|
305
338
|
rule.each(child => {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
after =
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
339
|
+
switch (child.type) {
|
|
340
|
+
case 'atrule':
|
|
341
|
+
[after, declarations] = pickAndClearDeclarations(rule.selector, declarations, after)
|
|
342
|
+
|
|
343
|
+
if (child.name === rootRuleName) {
|
|
344
|
+
unwrapped = true
|
|
345
|
+
atruleChilds(rule, child, true, child[rootRuleMergeSel])
|
|
346
|
+
after = breakOut(child, after)
|
|
347
|
+
} else if (bubble[child.name]) {
|
|
348
|
+
copyDeclarations = true
|
|
349
|
+
unwrapped = true
|
|
350
|
+
atruleChilds(rule, child, true)
|
|
351
|
+
after = breakOut(child, after)
|
|
352
|
+
} else if (unwrap[child.name]) {
|
|
353
|
+
copyDeclarations = true
|
|
354
|
+
unwrapped = true
|
|
355
|
+
atruleChilds(rule, child, false)
|
|
356
|
+
after = breakOut(child, after)
|
|
357
|
+
} else if (copyDeclarations) {
|
|
358
|
+
declarations.push(child)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
break
|
|
362
|
+
case 'decl':
|
|
363
|
+
if (copyDeclarations) {
|
|
364
|
+
declarations.push(child)
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
break
|
|
368
|
+
case 'rule':
|
|
369
|
+
[after, declarations] = pickAndClearDeclarations(rule.selector, declarations, after)
|
|
370
|
+
|
|
331
371
|
copyDeclarations = true
|
|
332
372
|
unwrapped = true
|
|
333
|
-
|
|
373
|
+
child.selectors = mergeSelectors(rule, child)
|
|
334
374
|
after = breakOut(child, after)
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
} else if (child.type === 'decl' && copyDeclarations) {
|
|
339
|
-
declarations.push(child)
|
|
375
|
+
|
|
376
|
+
break
|
|
340
377
|
}
|
|
341
378
|
})
|
|
342
379
|
|
|
343
|
-
|
|
344
|
-
after = pickDeclarations(rule.selector, declarations, after)
|
|
345
|
-
}
|
|
380
|
+
pickAndClearDeclarations(rule.selector, declarations, after, false)
|
|
346
381
|
|
|
347
382
|
if (unwrapped && preserveEmpty !== true) {
|
|
348
383
|
rule.raws.semicolon = true
|
|
349
384
|
if (rule.nodes.length === 0) rule.remove()
|
|
350
385
|
}
|
|
351
|
-
},
|
|
352
|
-
|
|
353
|
-
RootExit(root) {
|
|
354
|
-
if (root[hasRootRule]) {
|
|
355
|
-
root.walkAtRules(rootRuleName, unwrapRootRule)
|
|
356
|
-
root[hasRootRule] = false
|
|
357
|
-
}
|
|
358
386
|
}
|
|
359
387
|
}
|
|
360
388
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-nested",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0",
|
|
4
4
|
"description": "PostCSS plugin to unwrap nested rules like how Sass does it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"postcss",
|
|
@@ -13,16 +13,22 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": "postcss/postcss-nested",
|
|
15
15
|
"engines": {
|
|
16
|
-
"node": ">=
|
|
17
|
-
},
|
|
18
|
-
"funding": {
|
|
19
|
-
"type": "opencollective",
|
|
20
|
-
"url": "https://opencollective.com/postcss/"
|
|
16
|
+
"node": ">=18.0"
|
|
21
17
|
},
|
|
18
|
+
"funding": [
|
|
19
|
+
{
|
|
20
|
+
"type": "opencollective",
|
|
21
|
+
"url": "https://opencollective.com/postcss/"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"type": "github",
|
|
25
|
+
"url": "https://github.com/sponsors/ai"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
22
28
|
"peerDependencies": {
|
|
23
29
|
"postcss": "^8.2.14"
|
|
24
30
|
},
|
|
25
31
|
"dependencies": {
|
|
26
|
-
"postcss-selector-parser": "^
|
|
32
|
+
"postcss-selector-parser": "^7.0.0"
|
|
27
33
|
}
|
|
28
34
|
}
|