-
-
Notifications
You must be signed in to change notification settings - Fork 85
Expand file tree
/
Copy pathcomplex.ts
More file actions
122 lines (105 loc) · 3.18 KB
/
complex.ts
File metadata and controls
122 lines (105 loc) · 3.18 KB
1
2
3
4
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import parser from 'postcss-selector-parser';
import { sortCompoundSelectorsInsideComplexSelector } from './compound-selector-order';
import { childAdjacentChild } from './complex/child-adjacent-child';
import { isInCompoundWithOneOtherElement } from './complex/is-in-compound';
import type { Container } from 'postcss-selector-parser';
import { isPseudoInFirstCompound } from './complex/is-pseudo-in-first-compound';
import { samePrecedingElement } from './complex/same-preceding-element';
export default function complexSelectors(selectors: Array<string>, pluginOptions: { onComplexSelector?: 'warning' }, warnOnComplexSelector: () => void, warnOnPseudoElements: () => void): Array<string> {
return selectors.flatMap((selector) => {
if (selector.indexOf(':-csstools-matches') === -1 && selector.toLowerCase().indexOf(':is') === -1) {
return selector;
}
const selectorAST = parser().astSync(selector);
selectorAST.walkPseudos((pseudo) => {
// `:is()` -> `:not(*)`
if (
pseudo.value.toLowerCase() === ':is' &&
pseudo.nodes &&
pseudo.nodes.length &&
pseudo.nodes[0].type === 'selector' &&
pseudo.nodes[0].nodes.length === 0
) {
pseudo.value = ':not';
pseudo.nodes[0].append(parser.universal());
return;
}
if (pseudo.value !== ':-csstools-matches') {
return;
}
if (pseudo.nodes && !pseudo.nodes.length) {
pseudo.remove();
return;
}
pseudo.walkPseudos((pseudoElement) => {
if (parser.isPseudoElement(pseudoElement)) {
let value = pseudoElement.value;
if (value.startsWith('::-csstools-invalid-')) {
// already invalidated and reported
return;
}
while (value.startsWith(':')) {
value = value.slice(1);
}
pseudoElement.value = `::-csstools-invalid-${value}`;
warnOnPseudoElements();
}
});
if (
pseudo.nodes.length === 1 &&
pseudo.nodes[0].type === 'selector'
) {
if (pseudo.nodes[0].nodes.length === 1) {
pseudo.replaceWith(pseudo.nodes[0].nodes[0]);
return;
}
if (!pseudo.nodes[0].some((x) => {
return x.type === 'combinator';
})) {
pseudo.replaceWith(...pseudo.nodes[0].nodes);
return;
}
}
if (
selectorAST.nodes.length === 1 &&
selectorAST.nodes[0].type === 'selector' &&
selectorAST.nodes[0].nodes.length === 1 &&
selectorAST.nodes[0].nodes[0] === pseudo
) {
pseudo.replaceWith(...pseudo.nodes[0].nodes);
return;
}
if (
childAdjacentChild(pseudo.parent) ||
isInCompoundWithOneOtherElement(pseudo.parent) ||
isPseudoInFirstCompound(pseudo.parent) ||
samePrecedingElement(pseudo.parent)
) {
return;
}
if (pluginOptions.onComplexSelector === 'warning') {
warnOnComplexSelector();
}
pseudo.value = ':is';
});
selectorAST.walk((node) => {
if (node.type !== 'selector' || !('nodes' in node)) {
return;
}
if (
node.nodes.length === 1 &&
(node.nodes[0] as Container).type === 'selector'
) {
node.replaceWith(node.nodes[0]);
}
});
selectorAST.walk((node) => {
if ('nodes' in node) {
sortCompoundSelectorsInsideComplexSelector(node);
}
});
return selectorAST.toString();
}).filter((x) => {
return !!x;
});
}