single-file-core 1.6.22 → 1.6.23
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 +27 -4
- package/core/lib/processor-helper-common.js +49 -10
- package/core/lib/processor-helper-inline.js +2 -2
- package/core/lib/processor-helper.js +3 -3
- package/modules/css-fonts-minifier.js +65 -47
- package/modules/css-media-query-utils.js +44 -44
- package/modules/css-medias-alt-minifier.js +3 -0
- package/modules/css-nested-rules.js +44 -0
- package/modules/css-rules-minifier.js +1 -2
- package/modules/css-scope-prelude-parser.js +61 -61
- package/modules/css-selector-sanitizer.js +79 -79
- package/modules/css-specificity.js +138 -110
- package/modules/html-minifier.js +1 -1
- package/package.json +1 -1
- package/processors/compression/compression.js +5 -0
- package/processors/hooks/content/content-hooks-frames-web.js +25 -12
- package/vendor/zip/zip.js +31 -15
- package/vendor/zip/zip.min.js +1 -1
|
@@ -25,79 +25,79 @@ import * as cssTree from "../vendor/css-tree.js";
|
|
|
25
25
|
const CANONICAL_PSEUDO_ELEMENT_NAMES = new Set(["after", "before", "first-letter", "first-line", "placeholder", "selection", "part", "marker"]);
|
|
26
26
|
|
|
27
27
|
export {
|
|
28
|
-
|
|
28
|
+
parsePrelude
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
function parsePrelude(prelude) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
if (!prelude) {
|
|
33
|
+
return { include: [], exclude: [] };
|
|
34
|
+
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
const scopeNode = findScopeNode(prelude);
|
|
37
|
+
if (!scopeNode) {
|
|
38
|
+
return { include: [], exclude: [] };
|
|
39
|
+
}
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const include = extractSelectorList(scopeNode.root);
|
|
42
|
+
const exclude = extractSelectorList(scopeNode.limit);
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
44
|
+
// Validate: pseudo-elements are not allowed in scope start/end selectors
|
|
45
|
+
function containsPseudoElement(selectorAst) {
|
|
46
|
+
let found = false;
|
|
47
|
+
cssTree.walk(selectorAst, {
|
|
48
|
+
visit: "PseudoElementSelector",
|
|
49
|
+
enter() { found = true; }
|
|
50
|
+
});
|
|
51
|
+
if (!found) {
|
|
52
|
+
// also check for pseudo-class names that are treated as pseudo-elements by some authors
|
|
53
|
+
cssTree.walk(selectorAst, {
|
|
54
|
+
visit: "PseudoClassSelector",
|
|
55
|
+
enter(node) {
|
|
56
|
+
const name = (node.name || "").toLowerCase();
|
|
57
|
+
// keep this conservative: disallow known pseudo-element names if used as pseudo-class
|
|
58
|
+
if (CANONICAL_PSEUDO_ELEMENT_NAMES.has(name)) {
|
|
59
|
+
found = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return found;
|
|
65
|
+
}
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
for (const s of include) {
|
|
68
|
+
if (containsPseudoElement(s.data)) {
|
|
69
|
+
throw new Error("Pseudo-elements are not allowed in @scope prelude (scope-start)");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const s of exclude) {
|
|
73
|
+
if (containsPseudoElement(s.data)) {
|
|
74
|
+
throw new Error("Pseudo-elements are not allowed in @scope prelude (scope-end)");
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
return { include, exclude };
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
function findScopeNode(prelude) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
if (!prelude || !prelude.children) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
for (let node = prelude.children.head; node; node = node.next) {
|
|
86
|
+
if (node.data && node.data.type === "Scope") {
|
|
87
|
+
return node.data;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
function extractSelectorList(selectorList) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
if (!selectorList || !selectorList.children) {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
const selectors = [];
|
|
98
|
+
for (let node = selectorList.children.head; node; node = node.next) {
|
|
99
|
+
const selector = node.data;
|
|
100
|
+
selectors.push({ data: selector, text: cssTree.generate(selector) });
|
|
101
|
+
}
|
|
102
|
+
return selectors;
|
|
103
103
|
}
|
|
@@ -26,112 +26,112 @@ import * as cssTree from "./../vendor/css-tree.js";
|
|
|
26
26
|
import { decodeName } from "./css-identifier.js";
|
|
27
27
|
|
|
28
28
|
const TREE_STRUCTURAL_PSEUDO_CLASSES = [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
"root",
|
|
30
|
+
"scope",
|
|
31
|
+
"empty",
|
|
32
|
+
"first-child",
|
|
33
|
+
"last-child",
|
|
34
|
+
"only-child",
|
|
35
|
+
"first-of-type",
|
|
36
|
+
"last-of-type",
|
|
37
|
+
"only-of-type"
|
|
38
38
|
];
|
|
39
39
|
const TREE_STRUCTURAL_FUNCTIONAL_PSEUDO_CLASSES = [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
"nth-child",
|
|
41
|
+
"nth-last-child",
|
|
42
|
+
"heading",
|
|
43
|
+
"nth-of-type",
|
|
44
|
+
"nth-last-of-type"
|
|
45
45
|
];
|
|
46
46
|
const FUNCTIONAL_PSEUDO_CLASSES = [
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
"not",
|
|
48
|
+
"is",
|
|
49
|
+
"where",
|
|
50
|
+
"has"
|
|
51
51
|
];
|
|
52
52
|
const MATCHING_FUNCTIONAL_PSEUDO_CLASSES = [
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
"is",
|
|
54
|
+
"where"
|
|
55
55
|
];
|
|
56
56
|
const STATE_ATTRIBUTE_NAMES = [
|
|
57
|
-
|
|
57
|
+
"open"
|
|
58
58
|
];
|
|
59
59
|
export {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
matchUnqueryableAttributeSelector,
|
|
61
|
+
matchUnqueryablePseudoClass,
|
|
62
|
+
sanitizeSelector,
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
function sanitizeSelector(selector, docContext) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
66
|
+
if (!docContext.normalizedSelectorText) {
|
|
67
|
+
docContext.normalizedSelectorText = new WeakMap();
|
|
68
|
+
}
|
|
69
|
+
if (docContext.normalizedSelectorText.has(selector)) {
|
|
70
|
+
return docContext.normalizedSelectorText.get(selector);
|
|
71
|
+
}
|
|
72
|
+
const ast = copySelectorNode(selector.data);
|
|
73
|
+
normalizeSelectorNode(ast);
|
|
74
|
+
let normalized = cssTree.generate(ast);
|
|
75
|
+
if (!normalized || !normalized.trim()) {
|
|
76
|
+
normalized = "*";
|
|
77
|
+
}
|
|
78
|
+
docContext.normalizedSelectorText.set(selector, normalized);
|
|
79
|
+
return normalized;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
function copySelectorNode(node) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
86
|
}
|
|
87
87
|
|
|
88
88
|
function normalizeSelectorNode(selector) {
|
|
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
|
-
|
|
89
|
+
let current = selector.children.head;
|
|
90
|
+
while (current) {
|
|
91
|
+
const next = current.next;
|
|
92
|
+
const childNode = current.data;
|
|
93
|
+
if (childNode.type === "NestingSelector") {
|
|
94
|
+
selector.children.replace(current, cssTree.parse(":scope", { context: "selector" }).children.head);
|
|
95
|
+
} else if (childNode.type === "TypeSelector" && typeof childNode.name === "string" && childNode.name.includes("|")) {
|
|
96
|
+
childNode.name = childNode.name.substring(childNode.name.lastIndexOf("|") + 1);
|
|
97
|
+
} else if (childNode.type === "PseudoElementSelector") {
|
|
98
|
+
selector.children.remove(current);
|
|
99
|
+
} else if (childNode.type === "PseudoClassSelector") {
|
|
100
|
+
if (matchUnqueryablePseudoClass(childNode)) {
|
|
101
|
+
removeNode(selector.children, current);
|
|
102
|
+
} else if (childNode.children && MATCHING_FUNCTIONAL_PSEUDO_CLASSES.includes(childNode.name.toLowerCase())) {
|
|
103
|
+
normalizeSelectorNode(childNode.children.head.data);
|
|
104
|
+
}
|
|
105
|
+
} else if (childNode.type === "AttributeSelector") {
|
|
106
|
+
if (matchUnqueryableAttributeSelector(childNode)) {
|
|
107
|
+
removeNode(selector.children, current);
|
|
108
|
+
}
|
|
109
|
+
} else if (childNode.type === "Selector") {
|
|
110
|
+
normalizeSelectorNode(childNode);
|
|
111
|
+
}
|
|
112
|
+
current = next;
|
|
113
|
+
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
function removeNode(list, item) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
if (item.prev == null || item.prev.data.type == "Combinator" || item.prev.data.type == "WhiteSpace") {
|
|
118
|
+
list.replace(item, cssTree.parse("*", { context: "selector" }).children.head);
|
|
119
|
+
} else {
|
|
120
|
+
list.remove(item);
|
|
121
|
+
}
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
function matchUnqueryableAttributeSelector(attributeSelector) {
|
|
125
|
-
|
|
125
|
+
return Boolean(attributeSelector.name) && STATE_ATTRIBUTE_NAMES.includes(decodeName(attributeSelector.name.name));
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
function matchUnqueryablePseudoClass(pseudoClass) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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.
|
|
133
|
+
return pseudoClass.children ? (
|
|
134
|
+
!TREE_STRUCTURAL_FUNCTIONAL_PSEUDO_CLASSES.includes(pseudoClass.name.toLowerCase()) &&
|
|
135
135
|
!FUNCTIONAL_PSEUDO_CLASSES.includes(pseudoClass.name.toLowerCase())
|
|
136
|
-
|
|
136
|
+
) : !TREE_STRUCTURAL_PSEUDO_CLASSES.includes(decodeName(pseudoClass.name));
|
|
137
137
|
}
|
|
@@ -22,138 +22,166 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import * as cssTree from "./../vendor/css-tree.js";
|
|
25
|
+
import { decodeName } from "./css-identifier.js";
|
|
26
|
+
|
|
27
|
+
const PSEUDO_ELEMENT_SYNONYMS = new Set(["after", "before", "first-letter", "first-line"]);
|
|
28
|
+
const SELECTOR_ARGUMENT_PSEUDO_CLASSES = new Set(["host", "host-context"]);
|
|
29
|
+
const SELECTOR_ARGUMENT_PSEUDO_ELEMENTS = new Set(["slotted"]);
|
|
25
30
|
|
|
26
31
|
export {
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
computeSpecificity,
|
|
33
|
+
computeMaxSpecificity,
|
|
34
|
+
PSEUDO_ELEMENT_SYNONYMS
|
|
29
35
|
};
|
|
30
36
|
|
|
31
37
|
function computeSpecificity(selector, specificity = { a: 0, b: 0, c: 0 }) {
|
|
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
|
-
|
|
38
|
+
if (!selector || !selector.type) {
|
|
39
|
+
return specificity;
|
|
40
|
+
}
|
|
41
|
+
switch (selector.type) {
|
|
42
|
+
case "Selector":
|
|
43
|
+
traverseChildren(selector.children, (child) => computeSpecificity(child, specificity));
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case "IdSelector":
|
|
47
|
+
specificity.a++;
|
|
48
|
+
break;
|
|
49
|
+
|
|
50
|
+
case "ClassSelector":
|
|
51
|
+
specificity.b++;
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
case "AttributeSelector":
|
|
55
|
+
specificity.b++;
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case "TypeSelector":
|
|
59
|
+
if (selector.name !== "*") {
|
|
60
|
+
specificity.c++;
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case "PseudoElementSelector":
|
|
65
|
+
specificity.c++;
|
|
66
|
+
if (SELECTOR_ARGUMENT_PSEUDO_ELEMENTS.has(decodeName(selector.name))) {
|
|
67
|
+
addArgumentSpecificity(specificity, selector);
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
|
|
71
|
+
case "PseudoClassSelector": {
|
|
72
|
+
const pseudoName = decodeName(selector.name);
|
|
73
|
+
|
|
74
|
+
if (pseudoName === "where") {
|
|
75
|
+
// :where() has zero specificity - do nothing
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (PSEUDO_ELEMENT_SYNONYMS.has(pseudoName)) {
|
|
80
|
+
specificity.c++;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (SELECTOR_ARGUMENT_PSEUDO_CLASSES.has(pseudoName)) {
|
|
85
|
+
specificity.b++;
|
|
86
|
+
addArgumentSpecificity(specificity, selector);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (pseudoName === "is" || pseudoName === "not" || pseudoName === "has") {
|
|
91
|
+
// :is(), :not(), :has() - use the max specificity from their selector list
|
|
92
|
+
traverseChildren(selector.children, (child) => {
|
|
93
|
+
if (child.type === "SelectorList") {
|
|
94
|
+
addMaxSpecificity(specificity, getMaxSpecificityFromList(child));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (pseudoName === "nth-child" || pseudoName === "nth-last-child") {
|
|
101
|
+
// :nth-child() and :nth-last-child() count as one pseudo-class
|
|
102
|
+
specificity.b++;
|
|
103
|
+
|
|
104
|
+
// Plus the max specificity from their selector list (if any)
|
|
105
|
+
traverseChildren(selector.children, (child) => {
|
|
106
|
+
if (child.type === "Nth" && child.selector) {
|
|
107
|
+
addMaxSpecificity(specificity, getMaxSpecificityFromList(child.selector));
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Regular pseudo-classes contribute to 'b'
|
|
114
|
+
specificity.b++;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
case "Combinator":
|
|
119
|
+
case "Raw":
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return specificity;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function addArgumentSpecificity(specificity, selector) {
|
|
127
|
+
traverseChildren(selector.children, (child) => {
|
|
128
|
+
if (child.type === "Selector") {
|
|
129
|
+
addMaxSpecificity(specificity, computeSpecificity(child, { a: 0, b: 0, c: 0 }));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
104
132
|
}
|
|
105
133
|
|
|
106
134
|
function addMaxSpecificity(specificity, maxSpec) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
135
|
+
specificity.a += maxSpec.a;
|
|
136
|
+
specificity.b += maxSpec.b;
|
|
137
|
+
specificity.c += maxSpec.c;
|
|
110
138
|
}
|
|
111
139
|
|
|
112
140
|
function traverseChildren(children, callback) {
|
|
113
|
-
|
|
141
|
+
if (!children) return;
|
|
114
142
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
let current = children.head;
|
|
144
|
+
while (current) {
|
|
145
|
+
callback(current.data);
|
|
146
|
+
current = current.next;
|
|
147
|
+
}
|
|
120
148
|
}
|
|
121
149
|
|
|
122
150
|
function getMaxSpecificityFromList(selectorList) {
|
|
123
|
-
|
|
151
|
+
let maxSpec = { a: 0, b: 0, c: 0 };
|
|
124
152
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
153
|
+
traverseChildren(selectorList.children, (selector) => {
|
|
154
|
+
const spec = computeSpecificity(selector, { a: 0, b: 0, c: 0 });
|
|
155
|
+
if (spec.a > maxSpec.a ||
|
|
128
156
|
(spec.a === maxSpec.a && spec.b > maxSpec.b) ||
|
|
129
157
|
(spec.a === maxSpec.a && spec.b === maxSpec.b && spec.c > maxSpec.c)) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
158
|
+
maxSpec = spec;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
133
161
|
|
|
134
|
-
|
|
162
|
+
return maxSpec;
|
|
135
163
|
}
|
|
136
164
|
|
|
137
165
|
function computeMaxSpecificity(selector) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
let maxSpecificity = { a: 0, b: 0, c: 0 };
|
|
167
|
+
const stack = [];
|
|
168
|
+
cssTree.walk(selector, {
|
|
169
|
+
enter(node) {
|
|
170
|
+
stack.push(node);
|
|
171
|
+
if (node.type === "Selector") {
|
|
172
|
+
const insideWhere = stack.some(n => n.type === "PseudoClassSelector" && n.name.toLowerCase() === "where");
|
|
173
|
+
if (insideWhere) return;
|
|
174
|
+
const specificity = computeSpecificity(node);
|
|
175
|
+
if (specificity.a > maxSpecificity.a ||
|
|
148
176
|
(specificity.a === maxSpecificity.a && specificity.b > maxSpecificity.b) ||
|
|
149
177
|
(specificity.a === maxSpecificity.a && specificity.b === maxSpecificity.b && specificity.c > maxSpecificity.c)) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
178
|
+
maxSpecificity = specificity;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
leave() {
|
|
183
|
+
stack.pop();
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
return maxSpecificity;
|
|
159
187
|
}
|
package/modules/html-minifier.js
CHANGED
|
@@ -250,7 +250,7 @@ function compressJSONLD(node) {
|
|
|
250
250
|
|
|
251
251
|
function removeEmptyInlineElements(doc) {
|
|
252
252
|
doc.querySelectorAll("style, script:not([src])").forEach(element => {
|
|
253
|
-
if (!element.textContent.trim()) {
|
|
253
|
+
if (!element.textContent.trim() && element.parentElement == doc.head) {
|
|
254
254
|
element.remove();
|
|
255
255
|
}
|
|
256
256
|
});
|
package/package.json
CHANGED
|
@@ -988,9 +988,14 @@ async function getContent() {
|
|
|
988
988
|
}
|
|
989
989
|
let storedDirectoryOffset = view.getUint32(endOfDirectoryOffset + 16, true);
|
|
990
990
|
let directoryOffset = endOfDirectoryOffset - view.getUint32(endOfDirectoryOffset + 12, true);
|
|
991
|
+
let signature = 0x02014b50;
|
|
991
992
|
if (locatorOffset >= 0 && view.getUint32(locatorOffset, true) == 0x07064b50) {
|
|
992
993
|
storedDirectoryOffset = view.getUint32(locatorOffset + 8, true) + view.getUint32(locatorOffset + 12, true) * 0x100000000;
|
|
993
994
|
directoryOffset = locatorOffset - 56;
|
|
995
|
+
signature = 0x06064b50;
|
|
996
|
+
}
|
|
997
|
+
if (directoryOffset < 0 || view.getUint32(directoryOffset, true) != signature) {
|
|
998
|
+
return 0;
|
|
994
999
|
}
|
|
995
1000
|
return Math.max(0, storedDirectoryOffset - directoryOffset);
|
|
996
1001
|
}
|