webpack 5.108.3 → 5.108.4
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/lib/Compilation.js +1 -4
- package/lib/FlagDependencyExportsPlugin.js +55 -8
- package/lib/LazyBarrel.js +30 -3
- package/lib/NormalModule.js +8 -12
- package/lib/config/defaults.js +3 -3
- package/lib/css/CssGenerator.js +33 -42
- package/lib/css/CssModulesPlugin.js +79 -53
- package/lib/css/CssParser.js +306 -96
- package/lib/css/syntax.js +451 -128
- package/lib/dependencies/CssIcssExportDependency.js +28 -16
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +23 -96
- package/lib/dependencies/HarmonyImportGuard.js +192 -20
- package/lib/dependencies/ImportParserPlugin.js +7 -9
- package/lib/html/HtmlGenerator.js +53 -76
- package/lib/html/HtmlModule.js +1 -0
- package/lib/html/HtmlModulesPlugin.js +24 -13
- package/lib/html/HtmlParser.js +240 -124
- package/lib/html/syntax.js +1362 -683
- package/lib/javascript/JavascriptParser.js +10 -0
- package/lib/url/URLParserPlugin.js +33 -7
- package/lib/util/LocConverter.js +9 -8
- package/lib/util/SourceProcessor.js +41 -28
- package/package.json +3 -3
- package/types.d.ts +79 -221
package/lib/css/CssParser.js
CHANGED
|
@@ -32,6 +32,7 @@ const {
|
|
|
32
32
|
A,
|
|
33
33
|
NodeType,
|
|
34
34
|
SourceProcessor,
|
|
35
|
+
buildSkipSet,
|
|
35
36
|
equalsLowerCase,
|
|
36
37
|
unescapeIdentifier
|
|
37
38
|
} = require("./syntax");
|
|
@@ -93,8 +94,32 @@ const IMAGE_SET_FUNCTION = /^(?:-\w+-)?image-set$/i;
|
|
|
93
94
|
const MAGIC_COMMENT_FAST_PATH =
|
|
94
95
|
/^\s*(webpack[A-Z][A-Za-z]+)\s*:\s*(true|false|null|-?\d+(?:\.\d+)?)\s*$/;
|
|
95
96
|
const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(?:-\w+-)?keyframes$/;
|
|
97
|
+
const VENDOR_PREFIX = /^-\w+-/;
|
|
96
98
|
const COMPOSES_PROPERTY = /^(?:composes|compose-with)$/i;
|
|
97
99
|
const IS_MODULES = /\.modules?\.[^.]+$/i;
|
|
100
|
+
|
|
101
|
+
// Skip options for a non-CSS-Modules parse: drop the selector prelude (never
|
|
102
|
+
// walked without modules) plus value / function-arg leaves nothing reads (the
|
|
103
|
+
// `Ident` visitor no-ops, the `Declaration` visitor returns early, no ICSS).
|
|
104
|
+
// `url` / functions / strings / blocks / commas are kept — they carry url()
|
|
105
|
+
// rewrites and image-set fences. At-rule preludes are kept (`@media` / `@import`
|
|
106
|
+
// are read). CSS-Modules parses skip nothing: selectors are walked and ICSS
|
|
107
|
+
// `:export { k: v }` captures each value's byte range from its first / last node.
|
|
108
|
+
const SKIP_NON_MODULES = {
|
|
109
|
+
types: buildSkipSet([
|
|
110
|
+
NodeType.Number,
|
|
111
|
+
NodeType.Dimension,
|
|
112
|
+
NodeType.Percentage,
|
|
113
|
+
NodeType.Ident,
|
|
114
|
+
NodeType.Hash,
|
|
115
|
+
NodeType.Colon,
|
|
116
|
+
NodeType.Delim,
|
|
117
|
+
// Nothing reads value/arg whitespace either — consumers use
|
|
118
|
+
// `nextNonWhitespace` / type checks that tolerate its absence.
|
|
119
|
+
NodeType.Whitespace
|
|
120
|
+
]),
|
|
121
|
+
selectorPrelude: true
|
|
122
|
+
};
|
|
98
123
|
const CSS_COMMENT = /\/\*((?!\*\/)[\s\S]*?)\*\//g;
|
|
99
124
|
// `@value` recognizers (postcss-modules-values shape): the import form `<names> from <source>`, and the `<importName> as <localName>` alias inside it.
|
|
100
125
|
const VALUE_IMPORT_FORM = /from(\/\*|\s)(?:[\s\S]+)$/i;
|
|
@@ -184,6 +209,109 @@ const normalizeUrl = (str, isString) => {
|
|
|
184
209
|
const isDashedIdentifier = (identifier) =>
|
|
185
210
|
identifier.startsWith("--") && identifier.length >= 3;
|
|
186
211
|
|
|
212
|
+
/**
|
|
213
|
+
* `s.toLowerCase()` that returns `s` itself (no allocation) when it can't
|
|
214
|
+
* change — no ASCII uppercase and no non-ASCII (whose Unicode case mapping is
|
|
215
|
+
* left to the real `toLowerCase`).
|
|
216
|
+
* @param {string} s string
|
|
217
|
+
* @returns {string} lowercased string
|
|
218
|
+
*/
|
|
219
|
+
const toLowerCaseIfNeeded = (s) => {
|
|
220
|
+
for (let i = 0; i < s.length; i++) {
|
|
221
|
+
const c = s.charCodeAt(i);
|
|
222
|
+
if ((c >= 65 && c <= 90) || c > 127) return s.toLowerCase();
|
|
223
|
+
}
|
|
224
|
+
return s;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Case-sensitive equality of a source range against a literal — no slice.
|
|
229
|
+
* @param {string} input source
|
|
230
|
+
* @param {number} start range start
|
|
231
|
+
* @param {number} end range end (exclusive)
|
|
232
|
+
* @param {string} lit literal to match
|
|
233
|
+
* @returns {boolean} true when the range equals `lit`
|
|
234
|
+
*/
|
|
235
|
+
const rangeEquals = (input, start, end, lit) =>
|
|
236
|
+
end - start === lit.length && input.startsWith(lit, start);
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* ASCII case-insensitive equality of a source range against a lowercase ASCII literal — no slice.
|
|
240
|
+
* @param {string} input source
|
|
241
|
+
* @param {number} start range start
|
|
242
|
+
* @param {number} end range end (exclusive)
|
|
243
|
+
* @param {string} lit lowercase ASCII literal to match
|
|
244
|
+
* @returns {boolean} true when the range equals `lit` ignoring ASCII case
|
|
245
|
+
*/
|
|
246
|
+
const rangeEqualsLowerCase = (input, start, end, lit) => {
|
|
247
|
+
if (end - start !== lit.length) return false;
|
|
248
|
+
for (let i = 0; i < lit.length; i++) {
|
|
249
|
+
let c = input.charCodeAt(start + i);
|
|
250
|
+
if (c >= 65 && c <= 90) c |= 0x20;
|
|
251
|
+
if (c !== lit.charCodeAt(i)) return false;
|
|
252
|
+
}
|
|
253
|
+
return true;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Range-keyed index over a known-properties table: ASCII-case-folded 31-hash
|
|
258
|
+
* of the name bytes → canonical key(s). Lets the Declaration visitor answer
|
|
259
|
+
* "is this a known property" (and get the canonical lowercase name) without
|
|
260
|
+
* slicing the property name out of the source per declaration.
|
|
261
|
+
* @type {WeakMap<Map<string, Record<string, number>>, Map<number, string | string[]>>}
|
|
262
|
+
*/
|
|
263
|
+
const KNOWN_PROPERTY_INDEX_CACHE = new WeakMap();
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Gets (or builds) the hash index for a known-properties table.
|
|
267
|
+
* @param {Map<string, Record<string, number>>} knownProperties known properties table
|
|
268
|
+
* @returns {Map<number, string | string[]>} hash → canonical name(s)
|
|
269
|
+
*/
|
|
270
|
+
const getKnownPropertyIndex = (knownProperties) => {
|
|
271
|
+
let index = KNOWN_PROPERTY_INDEX_CACHE.get(knownProperties);
|
|
272
|
+
if (index === undefined) {
|
|
273
|
+
index = new Map();
|
|
274
|
+
for (const name of knownProperties.keys()) {
|
|
275
|
+
let h = name.length;
|
|
276
|
+
for (let i = 0; i < name.length; i++) {
|
|
277
|
+
h = ((h << 5) - h + name.charCodeAt(i)) | 0;
|
|
278
|
+
}
|
|
279
|
+
const hit = index.get(h);
|
|
280
|
+
if (hit === undefined) index.set(h, name);
|
|
281
|
+
else if (typeof hit === "string") index.set(h, [hit, name]);
|
|
282
|
+
else hit.push(name);
|
|
283
|
+
}
|
|
284
|
+
KNOWN_PROPERTY_INDEX_CACHE.set(knownProperties, index);
|
|
285
|
+
}
|
|
286
|
+
return index;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Canonical known-property name for a source range (ASCII case-insensitive), without slicing.
|
|
291
|
+
* @param {Map<number, string | string[]>} index hash index from `getKnownPropertyIndex`
|
|
292
|
+
* @param {string} input source
|
|
293
|
+
* @param {number} start name start
|
|
294
|
+
* @param {number} end name end (exclusive)
|
|
295
|
+
* @returns {string | undefined} canonical lowercase name, or undefined when unknown
|
|
296
|
+
*/
|
|
297
|
+
const knownPropertyForRange = (index, input, start, end) => {
|
|
298
|
+
let h = end - start;
|
|
299
|
+
for (let i = start; i < end; i++) {
|
|
300
|
+
let c = input.charCodeAt(i);
|
|
301
|
+
if (c >= 65 && c <= 90) c |= 0x20;
|
|
302
|
+
h = ((h << 5) - h + c) | 0;
|
|
303
|
+
}
|
|
304
|
+
const hit = index.get(h);
|
|
305
|
+
if (hit === undefined) return undefined;
|
|
306
|
+
if (typeof hit === "string") {
|
|
307
|
+
return rangeEqualsLowerCase(input, start, end, hit) ? hit : undefined;
|
|
308
|
+
}
|
|
309
|
+
for (let i = 0; i < hit.length; i++) {
|
|
310
|
+
if (rangeEqualsLowerCase(input, start, end, hit[i])) return hit[i];
|
|
311
|
+
}
|
|
312
|
+
return undefined;
|
|
313
|
+
};
|
|
314
|
+
|
|
187
315
|
/**
|
|
188
316
|
* `binarySearchBounds` comparator for `getComments` — hoisted so the lookup
|
|
189
317
|
* doesn't allocate a fresh closure per call.
|
|
@@ -598,7 +726,7 @@ const scanRuleBody = (declarations, childRules) => {
|
|
|
598
726
|
hasNestedBlock = true;
|
|
599
727
|
if (
|
|
600
728
|
!hasDirectDecl &&
|
|
601
|
-
!isPureBodyAtRule(`@${A.name(child)
|
|
729
|
+
!isPureBodyAtRule(`@${toLowerCaseIfNeeded(A.name(child))}`) &&
|
|
602
730
|
scanRuleBody(atDecls, atChildRules).hasDirectDecl
|
|
603
731
|
) {
|
|
604
732
|
hasDirectDecl = true;
|
|
@@ -916,6 +1044,8 @@ class CssParser extends Parser {
|
|
|
916
1044
|
exportMode = CssIcssExportDependency.EXPORT_MODE.REPLACE,
|
|
917
1045
|
exportType = CssIcssExportDependency.EXPORT_TYPE.NORMAL
|
|
918
1046
|
) => {
|
|
1047
|
+
// Flat location numbers — the nested loc objects were the parser's
|
|
1048
|
+
// hottest allocation (3 objects per exported name).
|
|
919
1049
|
cssExportEntries.push({
|
|
920
1050
|
name,
|
|
921
1051
|
value,
|
|
@@ -923,7 +1053,10 @@ class CssParser extends Parser {
|
|
|
923
1053
|
interpolate,
|
|
924
1054
|
exportMode,
|
|
925
1055
|
exportType,
|
|
926
|
-
|
|
1056
|
+
locStartLine: sl,
|
|
1057
|
+
locStartColumn: sc,
|
|
1058
|
+
locEndLine: el,
|
|
1059
|
+
locEndColumn: ec
|
|
927
1060
|
});
|
|
928
1061
|
};
|
|
929
1062
|
|
|
@@ -940,12 +1073,29 @@ class CssParser extends Parser {
|
|
|
940
1073
|
|
|
941
1074
|
const isModules = mode === "global" || mode === "local";
|
|
942
1075
|
|
|
1076
|
+
/** @type {Map<string, boolean>} */
|
|
1077
|
+
const selfReferenceCache = new Map();
|
|
1078
|
+
|
|
943
1079
|
/**
|
|
944
1080
|
* Whether a relative `from "<request>"` resolves back to the current module (matching query/fragment too).
|
|
1081
|
+
* Memoized per parse — `composes … from "./x.css"` repeats the same request many times per file.
|
|
945
1082
|
* @param {string} request request string from `from "<request>"`
|
|
946
1083
|
* @returns {boolean} true if request resolves to the current module
|
|
947
1084
|
*/
|
|
948
1085
|
const isSelfReferenceRequest = (request) => {
|
|
1086
|
+
const cached = selfReferenceCache.get(request);
|
|
1087
|
+
if (cached !== undefined) return cached;
|
|
1088
|
+
const result = isSelfReferenceRequestUncached(request);
|
|
1089
|
+
selfReferenceCache.set(request, result);
|
|
1090
|
+
return result;
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* Uncached `isSelfReferenceRequest`.
|
|
1095
|
+
* @param {string} request request string from `from "<request>"`
|
|
1096
|
+
* @returns {boolean} true if request resolves to the current module
|
|
1097
|
+
*/
|
|
1098
|
+
const isSelfReferenceRequestUncached = (request) => {
|
|
949
1099
|
if (!RELATIVE_REQUEST.test(request)) return false;
|
|
950
1100
|
if (!module.context) return false;
|
|
951
1101
|
const parsedRequest = parseResource(request);
|
|
@@ -969,6 +1119,7 @@ class CssParser extends Parser {
|
|
|
969
1119
|
customIdents: this.options.customIdents,
|
|
970
1120
|
grid: this.options.grid
|
|
971
1121
|
});
|
|
1122
|
+
const knownPropertyIndex = getKnownPropertyIndex(knownProperties);
|
|
972
1123
|
|
|
973
1124
|
/** @type {CssModuleBuildMeta} */
|
|
974
1125
|
(module.buildMeta).isCssModule = isModules;
|
|
@@ -1242,18 +1393,17 @@ class CssParser extends Parser {
|
|
|
1242
1393
|
modeData ? modeData === "local" : mode === "local";
|
|
1243
1394
|
|
|
1244
1395
|
/**
|
|
1245
|
-
* Comment
|
|
1246
|
-
* @param {
|
|
1247
|
-
* @param {number} start start
|
|
1248
|
-
* @param {number} end end
|
|
1249
|
-
* @returns {number} end
|
|
1396
|
+
* Comment visitor (`NodeType.Comment`): push every comment (in source order) onto the local `comments`, read back by `advanceCommentCursor` (pure-mode flags) and `parseCommentOptions` (magic comments).
|
|
1397
|
+
* @param {import("./syntax").CssPath} path walk path at the comment node
|
|
1250
1398
|
*/
|
|
1251
|
-
const
|
|
1399
|
+
const commentVisitor = (path) => {
|
|
1400
|
+
const node = path.node;
|
|
1401
|
+
const start = A.start(node);
|
|
1402
|
+
const end = A.end(node);
|
|
1252
1403
|
comments.push({
|
|
1253
|
-
value:
|
|
1404
|
+
value: source.slice(start + 2, end - 2),
|
|
1254
1405
|
range: [start, end]
|
|
1255
1406
|
});
|
|
1256
|
-
return end;
|
|
1257
1407
|
};
|
|
1258
1408
|
|
|
1259
1409
|
/**
|
|
@@ -1516,10 +1666,9 @@ class CssParser extends Parser {
|
|
|
1516
1666
|
for (const cv of A.children(fn)) {
|
|
1517
1667
|
if (A.type(cv) !== NodeType.Ident) continue;
|
|
1518
1668
|
let identifier = A.unescaped(cv);
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
} = A.loc(cv);
|
|
1669
|
+
// Cursor reads instead of `A.loc` — no location objects allocated.
|
|
1670
|
+
const { line: sl, column: sc } = locConverter.get(A.start(cv));
|
|
1671
|
+
const { line: el, column: ec } = locConverter.get(A.end(cv));
|
|
1523
1672
|
const isDashedIdent = isDashedIdentifier(identifier);
|
|
1524
1673
|
if (isDashedIdent) identifier = identifier.slice(2);
|
|
1525
1674
|
addCssExport(
|
|
@@ -1560,10 +1709,8 @@ class CssParser extends Parser {
|
|
|
1560
1709
|
if (cvType === NodeType.String) {
|
|
1561
1710
|
if (!found && options.string) {
|
|
1562
1711
|
const value = A.unescaped(cv);
|
|
1563
|
-
const {
|
|
1564
|
-
|
|
1565
|
-
end: { line: el, column: ec }
|
|
1566
|
-
} = A.loc(cv);
|
|
1712
|
+
const { line: sl, column: sc } = locConverter.get(A.start(cv));
|
|
1713
|
+
const { line: el, column: ec } = locConverter.get(A.end(cv));
|
|
1567
1714
|
addCssExport(
|
|
1568
1715
|
sl,
|
|
1569
1716
|
sc,
|
|
@@ -1590,10 +1737,8 @@ class CssParser extends Parser {
|
|
|
1590
1737
|
) {
|
|
1591
1738
|
continue;
|
|
1592
1739
|
}
|
|
1593
|
-
const {
|
|
1594
|
-
|
|
1595
|
-
end: { line: el, column: ec }
|
|
1596
|
-
} = A.loc(cv);
|
|
1740
|
+
const { line: sl, column: sc } = locConverter.get(A.start(cv));
|
|
1741
|
+
const { line: el, column: ec } = locConverter.get(A.end(cv));
|
|
1597
1742
|
addCssExport(
|
|
1598
1743
|
sl,
|
|
1599
1744
|
sc,
|
|
@@ -1614,9 +1759,12 @@ class CssParser extends Parser {
|
|
|
1614
1759
|
|
|
1615
1760
|
if (cvType === NodeType.Function) {
|
|
1616
1761
|
const fn = /** @type {FunctionNode} */ (cv);
|
|
1617
|
-
const fname = A.unescapedName(fn)
|
|
1618
|
-
const type =
|
|
1619
|
-
|
|
1762
|
+
const fname = A.unescapedName(fn);
|
|
1763
|
+
const type = equalsLowerCase(fname, "local")
|
|
1764
|
+
? 1
|
|
1765
|
+
: equalsLowerCase(fname, "global")
|
|
1766
|
+
? 2
|
|
1767
|
+
: undefined;
|
|
1620
1768
|
if (!found && type) {
|
|
1621
1769
|
found = true;
|
|
1622
1770
|
if (type === 1) pure.markLocal();
|
|
@@ -1626,7 +1774,7 @@ class CssParser extends Parser {
|
|
|
1626
1774
|
if (
|
|
1627
1775
|
this.options.dashedIdents &&
|
|
1628
1776
|
isLocalMode() &&
|
|
1629
|
-
(fname
|
|
1777
|
+
(equalsLowerCase(fname, "var") || equalsLowerCase(fname, "style"))
|
|
1630
1778
|
) {
|
|
1631
1779
|
processDashedIdentInVarFunction(fn);
|
|
1632
1780
|
}
|
|
@@ -1758,7 +1906,7 @@ class CssParser extends Parser {
|
|
|
1758
1906
|
if (
|
|
1759
1907
|
j >= fv.length ||
|
|
1760
1908
|
A.type(fv[j]) !== NodeType.Ident ||
|
|
1761
|
-
!
|
|
1909
|
+
!rangeEqualsLowerCase(input, A.start(fv[j]), A.end(fv[j]), "from")
|
|
1762
1910
|
) {
|
|
1763
1911
|
emitDashedIdentExport(identStart, identEnd);
|
|
1764
1912
|
return;
|
|
@@ -1772,7 +1920,10 @@ class CssParser extends Parser {
|
|
|
1772
1920
|
if (j >= fv.length) return;
|
|
1773
1921
|
|
|
1774
1922
|
const src = fv[j];
|
|
1775
|
-
if (
|
|
1923
|
+
if (
|
|
1924
|
+
A.type(src) === NodeType.Ident &&
|
|
1925
|
+
rangeEquals(input, A.start(src), A.end(src), "global")
|
|
1926
|
+
) {
|
|
1776
1927
|
emitDashedIdentFromGlobal(identEnd, A.end(src));
|
|
1777
1928
|
return;
|
|
1778
1929
|
}
|
|
@@ -2081,10 +2232,8 @@ class CssParser extends Parser {
|
|
|
2081
2232
|
// ID selectors emit the ICSS export but aren't a `composes:` anchor.
|
|
2082
2233
|
const idValueStart = A.start(v) + 1;
|
|
2083
2234
|
const idName = A.unescaped(v);
|
|
2084
|
-
const {
|
|
2085
|
-
|
|
2086
|
-
end: { line: idEl, column: idEc }
|
|
2087
|
-
} = A.loc(v);
|
|
2235
|
+
const { line: idSl, column: idSc } = locConverter.get(A.start(v));
|
|
2236
|
+
const { line: idEl, column: idEc } = locConverter.get(A.end(v));
|
|
2088
2237
|
addCssExport(
|
|
2089
2238
|
idSl,
|
|
2090
2239
|
idSc,
|
|
@@ -2124,10 +2273,9 @@ class CssParser extends Parser {
|
|
|
2124
2273
|
if (segmentMode === "local") {
|
|
2125
2274
|
// `.<ident>` in local mode is a class selector (dep covers the ident bytes only).
|
|
2126
2275
|
const name = A.unescaped(next);
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
} = A.loc(next);
|
|
2276
|
+
// Cursor reads instead of `A.loc` — this is the hottest export site.
|
|
2277
|
+
const { line: sl, column: sc } = locConverter.get(A.start(next));
|
|
2278
|
+
const { line: el, column: ec } = locConverter.get(A.end(next));
|
|
2131
2279
|
addCssExport(
|
|
2132
2280
|
sl,
|
|
2133
2281
|
sc,
|
|
@@ -2142,7 +2290,7 @@ class CssParser extends Parser {
|
|
|
2142
2290
|
currentRule.hasLocalAnchor = true;
|
|
2143
2291
|
currentRule.localIdentifiers.push(name);
|
|
2144
2292
|
pure.markLocal();
|
|
2145
|
-
} else {
|
|
2293
|
+
} else if (icssDefinitions.size !== 0) {
|
|
2146
2294
|
// `.<ident>` in global mode: not localized, but the ident may be `@value`-defined and need ICSS rewrite.
|
|
2147
2295
|
const ident = A.value(next);
|
|
2148
2296
|
if (!isDashedIdentifier(ident) && icssDefinitions.has(ident)) {
|
|
@@ -2154,7 +2302,8 @@ class CssParser extends Parser {
|
|
|
2154
2302
|
break;
|
|
2155
2303
|
}
|
|
2156
2304
|
case NodeType.Ident: {
|
|
2157
|
-
// ICSS rewrite for a bare `@value`-defined ident used as a type-style selector.
|
|
2305
|
+
// ICSS rewrite for a bare `@value`-defined ident used as a type-style selector; only worth the slice when definitions exist.
|
|
2306
|
+
if (icssDefinitions.size === 0) break;
|
|
2158
2307
|
const ident = A.value(v);
|
|
2159
2308
|
if (!isDashedIdentifier(ident) && icssDefinitions.has(ident)) {
|
|
2160
2309
|
emitICSSSymbol(ident, A.start(v), A.end(v));
|
|
@@ -2181,10 +2330,8 @@ class CssParser extends Parser {
|
|
|
2181
2330
|
const urlActive = () => {
|
|
2182
2331
|
if (!this.options.url || !currentStructural) return false;
|
|
2183
2332
|
if (A.type(currentStructural) === NodeType.AtRule) {
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
currentUrlRecovery
|
|
2187
|
-
);
|
|
2333
|
+
// `currentAtRuleName` is cached on at-rule enter — no per-value slice.
|
|
2334
|
+
return currentAtRuleName !== "@import" || currentUrlRecovery;
|
|
2188
2335
|
}
|
|
2189
2336
|
return true;
|
|
2190
2337
|
};
|
|
@@ -2534,10 +2681,10 @@ class CssParser extends Parser {
|
|
|
2534
2681
|
/**
|
|
2535
2682
|
* Emit url() deps for a `url(...)` / `src(...)` / `image-set(...)` value function.
|
|
2536
2683
|
* @param {FunctionNode} fn the function node
|
|
2537
|
-
* @param {string} fname the function name
|
|
2684
|
+
* @param {string} fname the unescaped function name (any case)
|
|
2538
2685
|
*/
|
|
2539
2686
|
const emitUrlFunction = (fn, fname) => {
|
|
2540
|
-
if (fname
|
|
2687
|
+
if (equalsLowerCase(fname, "url") || equalsLowerCase(fname, "src")) {
|
|
2541
2688
|
// Quoted `url("…")` / `src("…")`: first non-whitespace value must be the string token.
|
|
2542
2689
|
const first = A.children(fn)[nextNonWhitespace(A.children(fn), 0)];
|
|
2543
2690
|
if (!first || A.type(first) !== NodeType.String) return;
|
|
@@ -2994,7 +3141,8 @@ class CssParser extends Parser {
|
|
|
2994
3141
|
break;
|
|
2995
3142
|
}
|
|
2996
3143
|
const identifier = A.value(cv);
|
|
2997
|
-
|
|
3144
|
+
// Values are almost always lowercase already — avoid the copy.
|
|
3145
|
+
const keyword = toLowerCaseIfNeeded(identifier);
|
|
2998
3146
|
parsedKeywords[keyword] =
|
|
2999
3147
|
typeof parsedKeywords[keyword] !== "undefined"
|
|
3000
3148
|
? parsedKeywords[keyword] + 1
|
|
@@ -3101,11 +3249,13 @@ class CssParser extends Parser {
|
|
|
3101
3249
|
// Drive the walk through SourceProcessor: structural enter / exit map to the `walkAst…Enter` / `…Exit` halves; value visitors handle url / ICSS / local-global.
|
|
3102
3250
|
/** @type {VisitorMap} */
|
|
3103
3251
|
const visitors = {
|
|
3252
|
+
[NodeType.Comment]: commentVisitor,
|
|
3104
3253
|
[NodeType.AtRule]: {
|
|
3105
3254
|
// At-rule enter: scope save, name dispatch, prelude value context, pure-block push.
|
|
3106
|
-
enter: (
|
|
3255
|
+
enter: (path) => {
|
|
3256
|
+
const node = path.node;
|
|
3107
3257
|
const at = /** @type {AtRule} */ (node);
|
|
3108
|
-
const topLevel = parent === null;
|
|
3258
|
+
const topLevel = path.parent === null;
|
|
3109
3259
|
currentUrlRecovery = false;
|
|
3110
3260
|
advanceCommentCursor(A.start(at));
|
|
3111
3261
|
const savedAnchor = currentRule.hasLocalAnchor;
|
|
@@ -3115,7 +3265,7 @@ class CssParser extends Parser {
|
|
|
3115
3265
|
currentRule.localIdentifiers = isModules
|
|
3116
3266
|
? [...savedLocalIdentifiers]
|
|
3117
3267
|
: savedLocalIdentifiers;
|
|
3118
|
-
const name = `@${A.name(at)
|
|
3268
|
+
const name = `@${toLowerCaseIfNeeded(A.name(at))}`;
|
|
3119
3269
|
switch (name) {
|
|
3120
3270
|
case "@namespace": {
|
|
3121
3271
|
this._emitWarning(
|
|
@@ -3264,7 +3414,8 @@ class CssParser extends Parser {
|
|
|
3264
3414
|
});
|
|
3265
3415
|
},
|
|
3266
3416
|
// At-rule exit: pure-frame finalization, `suppressNextRulePrelude`, scope restore, top-level reset.
|
|
3267
|
-
exit: (
|
|
3417
|
+
exit: (path) => {
|
|
3418
|
+
const node = path.node;
|
|
3268
3419
|
const state = atRuleStateStack.pop();
|
|
3269
3420
|
if (!state) return;
|
|
3270
3421
|
if (state.hasBlock) {
|
|
@@ -3279,14 +3430,15 @@ class CssParser extends Parser {
|
|
|
3279
3430
|
}
|
|
3280
3431
|
currentRule.hasLocalAnchor = state.savedAnchor;
|
|
3281
3432
|
currentRule.localIdentifiers = state.savedLocalIdentifiers;
|
|
3282
|
-
if (parent === null) finishTopLevelRule(node, true);
|
|
3433
|
+
if (path.parent === null) finishTopLevelRule(node, true);
|
|
3283
3434
|
}
|
|
3284
3435
|
},
|
|
3285
3436
|
[NodeType.QualifiedRule]: {
|
|
3286
|
-
// Qualified-rule enter: scope setup, selector + prelude context, pure-block push; `:import` / `:export` bail via `
|
|
3287
|
-
enter: (
|
|
3437
|
+
// Qualified-rule enter: scope setup, selector + prelude context, pure-block push; `:import` / `:export` bail via `path.skipChildren()`.
|
|
3438
|
+
enter: (path) => {
|
|
3439
|
+
const node = path.node;
|
|
3288
3440
|
const rule = /** @type {QualifiedRule} */ (node);
|
|
3289
|
-
const topLevel = parent === null;
|
|
3441
|
+
const topLevel = path.parent === null;
|
|
3290
3442
|
advanceCommentCursor(A.start(rule));
|
|
3291
3443
|
// `:import(…) { … }` / `:export { … }` ICSS pseudo-rules are processed inline at top level; nested ones bail out.
|
|
3292
3444
|
if (isModules) {
|
|
@@ -3325,7 +3477,7 @@ class CssParser extends Parser {
|
|
|
3325
3477
|
A.setEnd(rule, A.blockEnd(rule));
|
|
3326
3478
|
}
|
|
3327
3479
|
// Don't recurse into the body — handled inline above.
|
|
3328
|
-
|
|
3480
|
+
path.skipChildren();
|
|
3329
3481
|
qualifiedRuleStateStack.push({ bailed: true });
|
|
3330
3482
|
return;
|
|
3331
3483
|
}
|
|
@@ -3400,7 +3552,8 @@ class CssParser extends Parser {
|
|
|
3400
3552
|
});
|
|
3401
3553
|
},
|
|
3402
3554
|
// Qualified-rule exit: pure-frame finalization, scope restore, top-level reset; no-op for bailed ICSS.
|
|
3403
|
-
exit: (
|
|
3555
|
+
exit: (path) => {
|
|
3556
|
+
const node = path.node;
|
|
3404
3557
|
const state = qualifiedRuleStateStack.pop();
|
|
3405
3558
|
if (!state || state.bailed) return;
|
|
3406
3559
|
pure.exitBlock();
|
|
@@ -3408,11 +3561,12 @@ class CssParser extends Parser {
|
|
|
3408
3561
|
currentRule.localIdentifiers = state.savedLocalIdentifiers;
|
|
3409
3562
|
currentRule.composesPrevFile = state.savedPrevComposesFile;
|
|
3410
3563
|
currentRule.composesFiles = state.savedComposesFiles;
|
|
3411
|
-
if (parent === null) finishTopLevelRule(node, false);
|
|
3564
|
+
if (path.parent === null) finishTopLevelRule(node, false);
|
|
3412
3565
|
}
|
|
3413
3566
|
},
|
|
3414
3567
|
// Top-level declarations are parse errors (dropped by `parseAStylesheet`), so a declaration's parent is always a block.
|
|
3415
|
-
[NodeType.Declaration]: (
|
|
3568
|
+
[NodeType.Declaration]: (path) => {
|
|
3569
|
+
const node = path.node;
|
|
3416
3570
|
const decl = /** @type {Declaration} */ (node);
|
|
3417
3571
|
// Reset value-visitor context, read by the value visitors below.
|
|
3418
3572
|
currentStructural = decl;
|
|
@@ -3432,20 +3586,47 @@ class CssParser extends Parser {
|
|
|
3432
3586
|
// Property-name analysis and value exports are CSS-Modules-only; a plain
|
|
3433
3587
|
// stylesheet's declarations need no per-decl name slice / known-property lookup.
|
|
3434
3588
|
if (!isModules) return;
|
|
3435
|
-
const
|
|
3436
|
-
const
|
|
3437
|
-
//
|
|
3438
|
-
|
|
3589
|
+
const nameStart = A.nameStart(decl);
|
|
3590
|
+
const nameEnd = A.nameEnd(decl);
|
|
3591
|
+
// Range-based name analysis — the common declaration never
|
|
3592
|
+
// slices its name out of the source.
|
|
3593
|
+
/** @type {string | undefined} */
|
|
3594
|
+
let declName;
|
|
3595
|
+
/** @type {string | undefined} */
|
|
3596
|
+
let declPropertyName;
|
|
3597
|
+
if (source.charCodeAt(nameStart) === CC_HYPHEN_MINUS) {
|
|
3598
|
+
// Vendor-prefixed or custom property — rare, take the string path.
|
|
3599
|
+
declName = A.name(decl);
|
|
3600
|
+
declPropertyName = toLowerCaseIfNeeded(
|
|
3601
|
+
declName.replace(VENDOR_PREFIX, "")
|
|
3602
|
+
);
|
|
3603
|
+
currentDeclIsKnownProperty = knownProperties.has(declPropertyName);
|
|
3604
|
+
} else {
|
|
3605
|
+
declPropertyName = knownPropertyForRange(
|
|
3606
|
+
knownPropertyIndex,
|
|
3607
|
+
source,
|
|
3608
|
+
nameStart,
|
|
3609
|
+
nameEnd
|
|
3610
|
+
);
|
|
3611
|
+
// Cache the known-property flag so the per-token value visitors don't recompute it.
|
|
3612
|
+
currentDeclIsKnownProperty = declPropertyName !== undefined;
|
|
3613
|
+
}
|
|
3439
3614
|
const effectiveLocalMode = isEffectivelyLocal();
|
|
3440
3615
|
// `composes:` with a local anchor: its strip-dep covers the whole declaration, so suppress the value's local/global/dashed/ICSS rewrites.
|
|
3441
3616
|
currentDeclComposesSkip =
|
|
3442
3617
|
currentRule.hasLocalAnchor &&
|
|
3443
|
-
|
|
3618
|
+
(declPropertyName !== undefined
|
|
3619
|
+
? COMPOSES_PROPERTY.test(declPropertyName)
|
|
3620
|
+
: rangeEqualsLowerCase(source, nameStart, nameEnd, "composes") ||
|
|
3621
|
+
rangeEqualsLowerCase(source, nameStart, nameEnd, "compose-with"));
|
|
3444
3622
|
if (currentDeclComposesSkip) emitComposesWithAnchor(decl);
|
|
3445
3623
|
const skipForComposes = currentDeclComposesSkip;
|
|
3446
3624
|
// Known-property value localization (`animation-name: foo` exports `foo`).
|
|
3447
3625
|
if (effectiveLocalMode && currentDeclIsKnownProperty) {
|
|
3448
|
-
emitKnownPropertyExports(
|
|
3626
|
+
emitKnownPropertyExports(
|
|
3627
|
+
decl,
|
|
3628
|
+
/** @type {string} */ (declPropertyName)
|
|
3629
|
+
);
|
|
3449
3630
|
}
|
|
3450
3631
|
// Dashed-ident (custom-property) export of the property name; the value's dashed idents are scoped by the Ident / Function visitors (top-level only for unknown properties).
|
|
3451
3632
|
if (
|
|
@@ -3453,24 +3634,33 @@ class CssParser extends Parser {
|
|
|
3453
3634
|
effectiveLocalMode &&
|
|
3454
3635
|
!skipForComposes
|
|
3455
3636
|
) {
|
|
3456
|
-
|
|
3457
|
-
|
|
3637
|
+
// Only the `-`-prefixed path can carry a dashed ident.
|
|
3638
|
+
if (declName !== undefined && isDashedIdentifier(declName)) {
|
|
3639
|
+
emitDashedIdentExport(nameStart, nameEnd);
|
|
3458
3640
|
}
|
|
3459
3641
|
dashed.active = true;
|
|
3460
3642
|
dashed.emit = !currentDeclIsKnownProperty;
|
|
3461
3643
|
}
|
|
3462
|
-
// ICSS-symbol rewrite (`color: foo` when `foo` is `@value`-defined), skipping known properties, the composes anchor, and dashed idents (handled above).
|
|
3644
|
+
// ICSS-symbol rewrite (`color: foo` when `foo` is `@value`-defined), skipping known properties, the composes anchor, and dashed idents (handled above). Nothing to rewrite without definitions — don't slice the name.
|
|
3463
3645
|
if (
|
|
3464
3646
|
!skipForComposes &&
|
|
3465
3647
|
!currentDeclIsKnownProperty &&
|
|
3466
|
-
|
|
3467
|
-
icssDefinitions.has(declName)
|
|
3648
|
+
icssDefinitions.size !== 0
|
|
3468
3649
|
) {
|
|
3469
|
-
|
|
3650
|
+
if (declName === undefined) {
|
|
3651
|
+
declName = source.slice(nameStart, nameEnd);
|
|
3652
|
+
}
|
|
3653
|
+
if (
|
|
3654
|
+
!(dashed.active && isDashedIdentifier(declName)) &&
|
|
3655
|
+
icssDefinitions.has(declName)
|
|
3656
|
+
) {
|
|
3657
|
+
emitICSSSymbol(declName, nameStart, nameEnd);
|
|
3658
|
+
}
|
|
3470
3659
|
}
|
|
3471
3660
|
},
|
|
3472
3661
|
// Value-level visitors decide handling from the enclosing node via `urlActive()` / `localGlobalActive()` / `icssActive()`.
|
|
3473
|
-
[NodeType.Url]: (
|
|
3662
|
+
[NodeType.Url]: (path) => {
|
|
3663
|
+
const node = path.node;
|
|
3474
3664
|
const url = /** @type {UrlToken} */ (node);
|
|
3475
3665
|
if (!urlActive()) return;
|
|
3476
3666
|
// Skip bare url-tokens for a known property in CSS-Modules local mode.
|
|
@@ -3530,25 +3720,27 @@ class CssParser extends Parser {
|
|
|
3530
3720
|
module.addDependency(dep);
|
|
3531
3721
|
module.addCodeGenerationDependency(dep);
|
|
3532
3722
|
},
|
|
3533
|
-
[NodeType.Comma](
|
|
3723
|
+
[NodeType.Comma](path) {
|
|
3724
|
+
const node = path.node;
|
|
3534
3725
|
if (urlActive()) lastTokenEndForComments = A.start(node);
|
|
3535
3726
|
},
|
|
3536
3727
|
[NodeType.Function]: {
|
|
3537
|
-
enter: (
|
|
3728
|
+
enter: (path) => {
|
|
3729
|
+
const node = path.node;
|
|
3538
3730
|
const fn = /** @type {FunctionNode} */ (node);
|
|
3539
3731
|
const fnameRaw = A.unescapedName(fn);
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
) {
|
|
3546
|
-
processLocalOrGlobalFunction(fn,
|
|
3732
|
+
// `equalsLowerCase` keeps this visitor free of a per-function
|
|
3733
|
+
// lowercased copy — functions are the densest value nodes.
|
|
3734
|
+
const isLocalFn = equalsLowerCase(fnameRaw, "local");
|
|
3735
|
+
const isGlobalFn = !isLocalFn && equalsLowerCase(fnameRaw, "global");
|
|
3736
|
+
if (urlActive()) emitUrlFunction(fn, fnameRaw);
|
|
3737
|
+
if (localGlobalActive() && (isLocalFn || isGlobalFn)) {
|
|
3738
|
+
processLocalOrGlobalFunction(fn, isLocalFn ? 1 : 2);
|
|
3547
3739
|
}
|
|
3548
3740
|
if (
|
|
3549
3741
|
icssActive() &&
|
|
3550
|
-
|
|
3551
|
-
|
|
3742
|
+
!isLocalFn &&
|
|
3743
|
+
!isGlobalFn &&
|
|
3552
3744
|
!(dashed.active && isDashedIdentifier(fnameRaw)) &&
|
|
3553
3745
|
icssDefinitions.has(fnameRaw)
|
|
3554
3746
|
) {
|
|
@@ -3557,10 +3749,13 @@ class CssParser extends Parser {
|
|
|
3557
3749
|
// Dashed-ident scoping: handle this function, then set the child nesting level's state for the walk.
|
|
3558
3750
|
dashed.push();
|
|
3559
3751
|
if (dashed.active) {
|
|
3560
|
-
if (
|
|
3752
|
+
if (isLocalFn || isGlobalFn) {
|
|
3561
3753
|
// `local()` / `global()` dashed args go through the ICSS path above, not here.
|
|
3562
3754
|
dashed.active = false;
|
|
3563
|
-
} else if (
|
|
3755
|
+
} else if (
|
|
3756
|
+
equalsLowerCase(fnameRaw, "var") ||
|
|
3757
|
+
equalsLowerCase(fnameRaw, "style")
|
|
3758
|
+
) {
|
|
3564
3759
|
// `var(--foo, …)` / `style(--foo, …)`: emit the first ident; the fallback doesn't self-emit.
|
|
3565
3760
|
processDashedIdentInVarFunction(fn);
|
|
3566
3761
|
dashed.emit = false;
|
|
@@ -3574,34 +3769,46 @@ class CssParser extends Parser {
|
|
|
3574
3769
|
dashed.pop();
|
|
3575
3770
|
}
|
|
3576
3771
|
},
|
|
3577
|
-
[NodeType.Ident](
|
|
3772
|
+
[NodeType.Ident](path) {
|
|
3773
|
+
const node = path.node;
|
|
3578
3774
|
// Fast exit before slicing the ident value: outside dashed-ident scoping
|
|
3579
3775
|
// and ICSS context (any non-CSS-Modules stylesheet) a bare ident carries
|
|
3580
3776
|
// no work, and idents are the most common node, so skipping the per-ident
|
|
3581
|
-
// `value` slice matters.
|
|
3777
|
+
// `value` slice matters. With no `@value`/`:import` definitions the ICSS
|
|
3778
|
+
// probe can never hit, so it doesn't warrant the slice either.
|
|
3582
3779
|
const dashedActive = dashed.active;
|
|
3583
|
-
const icss = icssActive();
|
|
3780
|
+
const icss = icssActive() && icssDefinitions.size !== 0;
|
|
3584
3781
|
if (!dashedActive && !icss) return;
|
|
3585
3782
|
const identValue = A.value(node);
|
|
3586
3783
|
if (dashedActive && isDashedIdentifier(identValue)) {
|
|
3587
3784
|
// Dashed idents are scoped here, never `@value` ICSS-rewritten.
|
|
3588
3785
|
if (!dashed.emit) return;
|
|
3589
3786
|
// Resolve the `--foo from "./x.css"` / `--foo from global` import suffix via sibling lookahead.
|
|
3590
|
-
const siblings = childrenOf(parent);
|
|
3787
|
+
const siblings = childrenOf(path.parent);
|
|
3591
3788
|
const i = siblings ? siblings.indexOf(node) : -1;
|
|
3592
3789
|
if (siblings && i !== -1) {
|
|
3593
3790
|
const j = nextNonWhitespace(siblings, i + 1);
|
|
3594
3791
|
if (
|
|
3595
3792
|
j < siblings.length &&
|
|
3596
3793
|
A.type(siblings[j]) === NodeType.Ident &&
|
|
3597
|
-
|
|
3794
|
+
rangeEqualsLowerCase(
|
|
3795
|
+
input,
|
|
3796
|
+
A.start(siblings[j]),
|
|
3797
|
+
A.end(siblings[j]),
|
|
3798
|
+
"from"
|
|
3799
|
+
)
|
|
3598
3800
|
) {
|
|
3599
3801
|
const fromIdent = siblings[j];
|
|
3600
3802
|
const sourceNode = siblings[nextNonWhitespace(siblings, j + 1)];
|
|
3601
3803
|
if (
|
|
3602
3804
|
sourceNode &&
|
|
3603
3805
|
A.type(sourceNode) === NodeType.Ident &&
|
|
3604
|
-
|
|
3806
|
+
rangeEquals(
|
|
3807
|
+
input,
|
|
3808
|
+
A.start(sourceNode),
|
|
3809
|
+
A.end(sourceNode),
|
|
3810
|
+
"global"
|
|
3811
|
+
)
|
|
3605
3812
|
) {
|
|
3606
3813
|
emitDashedIdentFromGlobal(A.end(node), A.end(sourceNode));
|
|
3607
3814
|
return;
|
|
@@ -3628,14 +3835,17 @@ class CssParser extends Parser {
|
|
|
3628
3835
|
}
|
|
3629
3836
|
};
|
|
3630
3837
|
// `as` selects the top-level production (§5.3): a `style` attribute is a
|
|
3631
|
-
// block's contents, everything else a full stylesheet (the default).
|
|
3632
|
-
|
|
3838
|
+
// block's contents, everything else a full stylesheet (the default). `as`
|
|
3839
|
+
// and `skip` are per-parse config on the processor; only `locConverter`
|
|
3840
|
+
// (per-source) is passed to `process`. One skip set: selector prelude +
|
|
3841
|
+
// unread value leaves (non-modules only; CSS Modules needs its selectors
|
|
3842
|
+
// and ICSS-captured values).
|
|
3843
|
+
new SourceProcessor({
|
|
3844
|
+
as: /** @type {"stylesheet" | "block-contents"} */ (this.options.as),
|
|
3845
|
+
skip: isModules ? undefined : SKIP_NON_MODULES
|
|
3846
|
+
})
|
|
3633
3847
|
.use(/** @type {VisitorMap} */ (visitors))
|
|
3634
|
-
.process(source, {
|
|
3635
|
-
locConverter,
|
|
3636
|
-
comment,
|
|
3637
|
-
as: /** @type {"stylesheet" | "block-contents"} */ (this.options.as)
|
|
3638
|
-
});
|
|
3848
|
+
.process(source, { locConverter });
|
|
3639
3849
|
|
|
3640
3850
|
/** @type {BuildInfo} */
|
|
3641
3851
|
(module.buildInfo).strict = true;
|