single-file-core 1.6.9 → 1.6.11
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/core/index.js +88 -7
- package/core/lib/processor-helper-common.js +1 -1
- package/core/lib/processor-helper-inline.js +36 -21
- package/core/lib/processor-helper.js +64 -15
- package/modules/css-identifier.js +55 -0
- package/modules/css-rules-minifier.js +663 -262
- package/modules/css-selector-sanitizer.js +28 -24
- package/modules/css-specificity.js +18 -73
- package/modules/template-formatter.js +1 -0
- package/package.json +1 -1
- package/vendor/css-tree.js +16 -12
|
@@ -23,9 +23,11 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import * as cssTree from "./../vendor/css-tree.js";
|
|
26
|
+
import { decodeName } from "./css-identifier.js";
|
|
26
27
|
|
|
27
28
|
const TREE_STRUCTURAL_PSEUDO_CLASSES = [
|
|
28
29
|
"root",
|
|
30
|
+
"scope",
|
|
29
31
|
"empty",
|
|
30
32
|
"first-child",
|
|
31
33
|
"last-child",
|
|
@@ -47,6 +49,10 @@ const FUNCTIONAL_PSEUDO_CLASSES = [
|
|
|
47
49
|
"where",
|
|
48
50
|
"has"
|
|
49
51
|
];
|
|
52
|
+
const MATCHING_FUNCTIONAL_PSEUDO_CLASSES = [
|
|
53
|
+
"is",
|
|
54
|
+
"where"
|
|
55
|
+
];
|
|
50
56
|
const STATE_ATTRIBUTE_NAMES = [
|
|
51
57
|
"open"
|
|
52
58
|
];
|
|
@@ -56,15 +62,15 @@ export {
|
|
|
56
62
|
sanitizeSelector,
|
|
57
63
|
};
|
|
58
64
|
|
|
59
|
-
function sanitizeSelector(selector,
|
|
65
|
+
function sanitizeSelector(selector, docContext) {
|
|
60
66
|
if (!docContext.normalizedSelectorText) {
|
|
61
67
|
docContext.normalizedSelectorText = new WeakMap();
|
|
62
68
|
}
|
|
63
69
|
if (docContext.normalizedSelectorText.has(selector)) {
|
|
64
70
|
return docContext.normalizedSelectorText.get(selector);
|
|
65
71
|
}
|
|
66
|
-
const ast =
|
|
67
|
-
normalizeSelectorNode(ast
|
|
72
|
+
const ast = copySelectorNode(selector.data);
|
|
73
|
+
normalizeSelectorNode(ast);
|
|
68
74
|
let normalized = cssTree.generate(ast);
|
|
69
75
|
if (!normalized || !normalized.trim()) {
|
|
70
76
|
normalized = "*";
|
|
@@ -73,26 +79,19 @@ function sanitizeSelector(selector, ancestors, docContext) {
|
|
|
73
79
|
return normalized;
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
function
|
|
82
|
+
function copySelectorNode(node) {
|
|
83
|
+
// A node with no children is a Raw one, which clone would hand back raw where the round-trip
|
|
84
|
+
// parses it into the selector list the rest of this module walks.
|
|
85
|
+
return node.children ? cssTree.clone(node) : cssTree.parse(cssTree.generate(node), { context: "selectorList" });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function normalizeSelectorNode(selector) {
|
|
77
89
|
let current = selector.children.head;
|
|
78
90
|
while (current) {
|
|
79
91
|
const next = current.next;
|
|
80
92
|
const childNode = current.data;
|
|
81
93
|
if (childNode.type === "NestingSelector") {
|
|
82
|
-
|
|
83
|
-
const lastAncestor = ancestors[ancestors.length - 1];
|
|
84
|
-
let ancestorAst = lastAncestor && lastAncestor.data ? lastAncestor.data : lastAncestor;
|
|
85
|
-
if (ancestorAst && ancestorAst.type === "SelectorList" && ancestorAst.children && ancestorAst.children.tail) {
|
|
86
|
-
ancestorAst = ancestorAst.children.tail.data;
|
|
87
|
-
}
|
|
88
|
-
if (ancestorAst && ancestorAst.children) {
|
|
89
|
-
for (let a = ancestorAst.children.head; a; a = a.next) {
|
|
90
|
-
const cloned = cssTree.clone(a.data);
|
|
91
|
-
selector.children.insertData(cloned, current);
|
|
92
|
-
}
|
|
93
|
-
selector.children.remove(current);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
94
|
+
selector.children.replace(current, cssTree.parse(":scope", { context: "selector" }).children.head);
|
|
96
95
|
} else if (childNode.type === "TypeSelector" && typeof childNode.name === "string" && childNode.name.includes("|")) {
|
|
97
96
|
childNode.name = childNode.name.substring(childNode.name.lastIndexOf("|") + 1);
|
|
98
97
|
} else if (childNode.type === "PseudoElementSelector") {
|
|
@@ -100,13 +99,15 @@ function normalizeSelectorNode(selector, ancestors) {
|
|
|
100
99
|
} else if (childNode.type === "PseudoClassSelector") {
|
|
101
100
|
if (matchUnqueryablePseudoClass(childNode)) {
|
|
102
101
|
removeNode(selector.children, current);
|
|
102
|
+
} else if (childNode.children && MATCHING_FUNCTIONAL_PSEUDO_CLASSES.includes(childNode.name.toLowerCase())) {
|
|
103
|
+
normalizeSelectorNode(childNode.children.head.data);
|
|
103
104
|
}
|
|
104
105
|
} else if (childNode.type === "AttributeSelector") {
|
|
105
106
|
if (matchUnqueryableAttributeSelector(childNode)) {
|
|
106
107
|
removeNode(selector.children, current);
|
|
107
108
|
}
|
|
108
109
|
} else if (childNode.type === "Selector") {
|
|
109
|
-
normalizeSelectorNode(childNode
|
|
110
|
+
normalizeSelectorNode(childNode);
|
|
110
111
|
}
|
|
111
112
|
current = next;
|
|
112
113
|
}
|
|
@@ -121,13 +122,16 @@ function removeNode(list, item) {
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
function matchUnqueryableAttributeSelector(attributeSelector) {
|
|
124
|
-
return Boolean(attributeSelector.name) && STATE_ATTRIBUTE_NAMES.includes(attributeSelector.name.name
|
|
125
|
+
return Boolean(attributeSelector.name) && STATE_ATTRIBUTE_NAMES.includes(decodeName(attributeSelector.name.name));
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
function matchUnqueryablePseudoClass(pseudoClass) {
|
|
128
|
-
|
|
129
|
+
// A functional pseudo-class is compared by spelling on purpose: css-tree recognises the known
|
|
130
|
+
// names by spelling too, so it parses the argument of an escaped one as Raw rather than as a
|
|
131
|
+
// selector list, and reading `:i\73 (…)` as `:is(…)` would hand the rest of the pass an argument
|
|
132
|
+
// it cannot walk. Leaving it unrecognised keeps it unqueryable, which is the safe answer.
|
|
129
133
|
return pseudoClass.children ? (
|
|
130
|
-
!TREE_STRUCTURAL_FUNCTIONAL_PSEUDO_CLASSES.includes(name) &&
|
|
131
|
-
!FUNCTIONAL_PSEUDO_CLASSES.includes(name)
|
|
132
|
-
) : !TREE_STRUCTURAL_PSEUDO_CLASSES.includes(name);
|
|
134
|
+
!TREE_STRUCTURAL_FUNCTIONAL_PSEUDO_CLASSES.includes(pseudoClass.name.toLowerCase()) &&
|
|
135
|
+
!FUNCTIONAL_PSEUDO_CLASSES.includes(pseudoClass.name.toLowerCase())
|
|
136
|
+
) : !TREE_STRUCTURAL_PSEUDO_CLASSES.includes(decodeName(pseudoClass.name));
|
|
133
137
|
}
|
|
@@ -91,12 +91,7 @@ function computeSpecificity(selector, specificity = { a: 0, b: 0, c: 0 }) {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// Regular pseudo-classes contribute to 'b'
|
|
94
|
-
|
|
95
|
-
if (pseudoName === "scope") {
|
|
96
|
-
specificity.c++;
|
|
97
|
-
} else {
|
|
98
|
-
specificity.b++;
|
|
99
|
-
}
|
|
94
|
+
specificity.b++;
|
|
100
95
|
break;
|
|
101
96
|
}
|
|
102
97
|
|
|
@@ -139,75 +134,25 @@ function getMaxSpecificityFromList(selectorList) {
|
|
|
139
134
|
return maxSpec;
|
|
140
135
|
}
|
|
141
136
|
|
|
142
|
-
function computeMaxSpecificity(selector
|
|
143
|
-
// If no ancestors provided, keep existing behavior
|
|
144
|
-
if (!ancestorsSelectors || !ancestorsSelectors.length) {
|
|
145
|
-
let maxSpecificity = { a: 0, b: 0, c: 0 };
|
|
146
|
-
const stack = [];
|
|
147
|
-
cssTree.walk(selector, {
|
|
148
|
-
enter(node) {
|
|
149
|
-
stack.push(node);
|
|
150
|
-
if (node.type === "Selector") {
|
|
151
|
-
const insideWhere = stack.some(n => n.type === "PseudoClassSelector" && n.name === "where");
|
|
152
|
-
if (insideWhere) return;
|
|
153
|
-
const specificity = computeSpecificity(node);
|
|
154
|
-
if (specificity.a > maxSpecificity.a ||
|
|
155
|
-
(specificity.a === maxSpecificity.a && specificity.b > maxSpecificity.b) ||
|
|
156
|
-
(specificity.a === maxSpecificity.a && specificity.b === maxSpecificity.b && specificity.c > maxSpecificity.c)) {
|
|
157
|
-
maxSpecificity = specificity;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
leave() {
|
|
162
|
-
stack.pop();
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
if (scopeStack && scopeStack.length) {
|
|
166
|
-
maxSpecificity.b++;
|
|
167
|
-
}
|
|
168
|
-
return maxSpecificity;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// When ancestors are provided, compute specificity for every expanded selector
|
|
172
|
-
// Build context strings for ancestors (similar to combineWithAncestors behavior)
|
|
173
|
-
const childText = cssTree.generate(selector);
|
|
174
|
-
let contexts = [""];
|
|
175
|
-
ancestorsSelectors.forEach(selectorList => {
|
|
176
|
-
if (!selectorList || !selectorList.children || !selectorList.children.size) return;
|
|
177
|
-
const parentSelectors = selectorList.children.toArray();
|
|
178
|
-
const nextContexts = [];
|
|
179
|
-
contexts.forEach(context => parentSelectors.forEach(parentSelector => {
|
|
180
|
-
const parentText = cssTree.generate(parentSelector);
|
|
181
|
-
const combined = context ? context + " " + parentText : parentText;
|
|
182
|
-
if (!nextContexts.includes(combined)) nextContexts.push(combined);
|
|
183
|
-
}));
|
|
184
|
-
if (nextContexts.length) contexts = nextContexts;
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
function combineStrings(context, child) {
|
|
188
|
-
if (!context) return child;
|
|
189
|
-
if (!child) return context;
|
|
190
|
-
if (child.indexOf("&") !== -1) return child.split("&").join(context);
|
|
191
|
-
return context + " " + child;
|
|
192
|
-
}
|
|
193
|
-
|
|
137
|
+
function computeMaxSpecificity(selector) {
|
|
194
138
|
let maxSpecificity = { a: 0, b: 0, c: 0 };
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
139
|
+
const stack = [];
|
|
140
|
+
cssTree.walk(selector, {
|
|
141
|
+
enter(node) {
|
|
142
|
+
stack.push(node);
|
|
143
|
+
if (node.type === "Selector") {
|
|
144
|
+
const insideWhere = stack.some(n => n.type === "PseudoClassSelector" && n.name.toLowerCase() === "where");
|
|
145
|
+
if (insideWhere) return;
|
|
146
|
+
const specificity = computeSpecificity(node);
|
|
147
|
+
if (specificity.a > maxSpecificity.a ||
|
|
148
|
+
(specificity.a === maxSpecificity.a && specificity.b > maxSpecificity.b) ||
|
|
149
|
+
(specificity.a === maxSpecificity.a && specificity.b === maxSpecificity.b && specificity.c > maxSpecificity.c)) {
|
|
150
|
+
maxSpecificity = specificity;
|
|
151
|
+
}
|
|
208
152
|
}
|
|
209
|
-
}
|
|
210
|
-
|
|
153
|
+
},
|
|
154
|
+
leave() {
|
|
155
|
+
stack.pop();
|
|
211
156
|
}
|
|
212
157
|
});
|
|
213
158
|
return maxSpecificity;
|
|
@@ -17336,6 +17336,7 @@ function getFilteredOptions(options) {
|
|
|
17336
17336
|
delete filteredOptions.stylesheets;
|
|
17337
17337
|
delete filteredOptions.images;
|
|
17338
17338
|
delete filteredOptions.posters;
|
|
17339
|
+
delete filteredOptions.generatedDataURIs;
|
|
17339
17340
|
delete filteredOptions.videos;
|
|
17340
17341
|
delete filteredOptions.shadowRoots;
|
|
17341
17342
|
delete filteredOptions.referrer;
|