postcss-nested 8.0.1 → 9.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 +3 -0
- package/index.d.ts +28 -32
- package/index.js +90 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -81,3 +81,6 @@ Alternatives:
|
|
|
81
81
|
[`postcss-nesting`]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting
|
|
82
82
|
[CSSWG draft]: https://drafts.csswg.org/css-nesting-1/
|
|
83
83
|
[PostCSS]: https://github.com/postcss/postcss
|
|
84
|
+
|
|
85
|
+
## Docs
|
|
86
|
+
Read full docs **[here](https://github.com/postcss/postcss-nested#readme)**.
|
package/index.d.ts
CHANGED
|
@@ -3,39 +3,35 @@
|
|
|
3
3
|
|
|
4
4
|
import type { PluginCreator } from 'postcss'
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
rootRuleName?: string
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
type Nested = PluginCreator<Options>
|
|
6
|
+
export interface Options {
|
|
7
|
+
/**
|
|
8
|
+
* By default, plugin will bubble only `@media`, `@supports` and `@layer`
|
|
9
|
+
* at-rules. Use this option to add your custom at-rules to this list.
|
|
10
|
+
*/
|
|
11
|
+
bubble?: string[]
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* By default, plugin will unwrap only `@font-face`, `@keyframes`,
|
|
15
|
+
* and `@document` at-rules. You can add your custom at-rules
|
|
16
|
+
* to this list by this option.
|
|
17
|
+
*/
|
|
18
|
+
unwrap?: string[]
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* By default, plugin will strip out any empty selector generated
|
|
22
|
+
* by intermediate nesting levels. You can set this option to `true`
|
|
23
|
+
* to preserve them.
|
|
24
|
+
*/
|
|
25
|
+
preserveEmpty?: boolean
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The plugin supports the SCSS custom at-rule `@at-root` which breaks
|
|
29
|
+
* rule blocks out of their nested position. If you want, you can choose
|
|
30
|
+
* a new custom name for this rule in your code.
|
|
31
|
+
*/
|
|
32
|
+
rootRuleName?: string
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
declare let nested:
|
|
35
|
+
declare let nested: PluginCreator<Options>
|
|
40
36
|
|
|
41
37
|
export default nested
|
package/index.js
CHANGED
|
@@ -20,6 +20,18 @@ function parse(rawSelector, rule) {
|
|
|
20
20
|
return nodes.at(0)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Replaces one "&" token with the parent selector
|
|
25
|
+
*/
|
|
26
|
+
function replaceAmp(node, parent) {
|
|
27
|
+
let clonedParent = parent.clone({})
|
|
28
|
+
if (node.value !== '&') {
|
|
29
|
+
node.replaceWith(parse(node.value.replace('&', clonedParent.toString())))
|
|
30
|
+
} else {
|
|
31
|
+
node.replaceWith(clonedParent)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
/**
|
|
24
36
|
* Replaces the "&" token in a node's selector with the parent selector
|
|
25
37
|
* similar to what SCSS does.
|
|
@@ -30,14 +42,7 @@ function interpolateAmpInSelector(nodes, parent) {
|
|
|
30
42
|
let replaced = false
|
|
31
43
|
nodes.each(node => {
|
|
32
44
|
if (node.type === 'nesting') {
|
|
33
|
-
|
|
34
|
-
if (node.value !== '&') {
|
|
35
|
-
node.replaceWith(
|
|
36
|
-
parse(node.value.replace('&', clonedParent.toString()))
|
|
37
|
-
)
|
|
38
|
-
} else {
|
|
39
|
-
node.replaceWith(clonedParent)
|
|
40
|
-
}
|
|
45
|
+
replaceAmp(node, parent)
|
|
41
46
|
replaced = true
|
|
42
47
|
} else if ('nodes' in node && node.nodes) {
|
|
43
48
|
if (interpolateAmpInSelector(node, parent)) {
|
|
@@ -48,20 +53,92 @@ function interpolateAmpInSelector(nodes, parent) {
|
|
|
48
53
|
return replaced
|
|
49
54
|
}
|
|
50
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Pseudo-classes which take a selector list
|
|
58
|
+
*/
|
|
59
|
+
const SELECTOR_LISTS = new Set([':has', ':is', ':not', ':where'])
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Replaces "&" inside :not() with the whole parent selector list, like Sass.
|
|
63
|
+
* A copy for each parent selector would turn "neither .a nor .b"
|
|
64
|
+
* into "not .a or not .b".
|
|
65
|
+
*
|
|
66
|
+
* Mutates the nodes list
|
|
67
|
+
*/
|
|
68
|
+
function replaceAmpsInNot(nodes, parents, negated = false) {
|
|
69
|
+
let replaced = false
|
|
70
|
+
nodes.each(node => {
|
|
71
|
+
if (node.type !== 'pseudo') {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
let name = node.value.toLowerCase()
|
|
75
|
+
let inside = negated || name === ':not'
|
|
76
|
+
node.each(selector => {
|
|
77
|
+
if (replaceAmpsInNot(selector, parents, inside)) {
|
|
78
|
+
replaced = true
|
|
79
|
+
}
|
|
80
|
+
let amps = selector.nodes.filter(i => i.type === 'nesting')
|
|
81
|
+
if (!inside || !SELECTOR_LISTS.has(name) || amps.length === 0) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
let combos = [[]]
|
|
85
|
+
for (let i = 0; i < amps.length; i++) {
|
|
86
|
+
combos = combos.flatMap(combo => parents.map(p => [...combo, p]))
|
|
87
|
+
}
|
|
88
|
+
let list = combos.map(combo => {
|
|
89
|
+
let copy = selector.clone()
|
|
90
|
+
copy.nodes
|
|
91
|
+
.filter(i => i.type === 'nesting')
|
|
92
|
+
.forEach((amp, i) => replaceAmp(amp, combo[i]))
|
|
93
|
+
return copy.toString().trim()
|
|
94
|
+
})
|
|
95
|
+
let space = node.index(selector) > 0 ? ' ' : ''
|
|
96
|
+
selector.replaceWith(...parser().astSync(space + list.join(', ')).nodes)
|
|
97
|
+
replaced = true
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
return replaced
|
|
101
|
+
}
|
|
102
|
+
|
|
51
103
|
/**
|
|
52
104
|
* Combines parent and child selectors, in a SCSS-like way
|
|
53
105
|
*/
|
|
54
106
|
function mergeSelectors(parent, child) {
|
|
55
107
|
let merged = []
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
108
|
+
let parents = parent.selectors.map(sel => parse(sel, parent))
|
|
109
|
+
for (let parentNode of parents) {
|
|
59
110
|
for (let selector of child.selectors) {
|
|
60
111
|
if (!selector) {
|
|
61
112
|
continue
|
|
62
113
|
}
|
|
63
114
|
let node = parse(selector, child)
|
|
64
|
-
let
|
|
115
|
+
let replacedInNot = replaceAmpsInNot(node, parents)
|
|
116
|
+
let ampsLeft = false
|
|
117
|
+
node.walkNesting(() => {
|
|
118
|
+
ampsLeft = true
|
|
119
|
+
})
|
|
120
|
+
// Without "&" outside :not(), every parent selector gives the same result
|
|
121
|
+
if (replacedInNot && !ampsLeft && parentNode !== parents[0]) {
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
let amps = node.nodes.filter(i => i.type === 'nesting')
|
|
125
|
+
if (amps.length > 1) {
|
|
126
|
+
// Like in Sass, every "&" after the first one takes each parent selector
|
|
127
|
+
let combos = [[parentNode]]
|
|
128
|
+
for (let i = 1; i < amps.length; i++) {
|
|
129
|
+
combos = combos.flatMap(combo => parents.map(p => [...combo, p]))
|
|
130
|
+
}
|
|
131
|
+
for (let combo of combos) {
|
|
132
|
+
let copy = node.clone()
|
|
133
|
+
copy.nodes
|
|
134
|
+
.filter(i => i.type === 'nesting')
|
|
135
|
+
.forEach((amp, i) => replaceAmp(amp, combo[i]))
|
|
136
|
+
interpolateAmpInSelector(copy, parentNode)
|
|
137
|
+
merged.push(copy.toString())
|
|
138
|
+
}
|
|
139
|
+
continue
|
|
140
|
+
}
|
|
141
|
+
let replaced = interpolateAmpInSelector(node, parentNode) || replacedInNot
|
|
65
142
|
if (!replaced) {
|
|
66
143
|
node.prepend(parser.combinator({ value: ' ' }))
|
|
67
144
|
node.prepend(parentNode.clone({}))
|
|
@@ -149,7 +226,7 @@ function pickAndClearDeclarations(
|
|
|
149
226
|
|
|
150
227
|
function atruleNames(defaults, custom = '') {
|
|
151
228
|
let names = defaults.concat(custom)
|
|
152
|
-
let list =
|
|
229
|
+
let list = Object.create(null)
|
|
153
230
|
for (let name of names) {
|
|
154
231
|
list[name.replace(/^@/, '')] = true
|
|
155
232
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postcss-nested",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "PostCSS plugin to unwrap nested rules like how Sass does it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"./package.json": "./package.json"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"postcss-selector-parser": "^7.1.
|
|
31
|
+
"postcss-selector-parser": "^7.1.6"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"postcss": "^8.2.14"
|