webpack 5.108.4 → 5.109.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/lib/APIPlugin.js +48 -29
- package/lib/CacheFacade.js +8 -0
- package/lib/ChunkGroup.js +1 -0
- package/lib/CompatibilityPlugin.js +31 -29
- package/lib/Compilation.js +49 -4
- package/lib/ConstPlugin.js +11 -11
- package/lib/ContextModule.js +239 -6
- package/lib/ContextModuleFactory.js +139 -14
- package/lib/DefinePlugin.js +396 -101
- package/lib/Entrypoint.js +1 -1
- package/lib/ExportsInfoApiPlugin.js +2 -2
- package/lib/ExternalModule.js +107 -5
- package/lib/ExternalModuleFactoryPlugin.js +20 -1
- package/lib/FileSystemInfo.js +130 -74
- package/lib/HotModuleReplacementPlugin.js +18 -15
- package/lib/Module.js +3 -0
- package/lib/ModuleFilenameHelpers.js +2 -2
- package/lib/ModuleTypeConstants.js +8 -1
- package/lib/MultiCompiler.js +13 -2
- package/lib/MultiWatching.js +1 -0
- package/lib/NodeStuffPlugin.js +51 -33
- package/lib/NormalModule.js +169 -401
- package/lib/NormalModuleFactory.js +76 -2
- package/lib/ProgressPlugin.js +211 -119
- package/lib/ProvidePlugin.js +2 -2
- package/lib/RuntimeGlobals.js +47 -0
- package/lib/RuntimePlugin.js +33 -0
- package/lib/RuntimeTemplate.js +304 -16
- package/lib/UseStrictPlugin.js +1 -1
- package/lib/WebpackIsIncludedPlugin.js +1 -1
- package/lib/WebpackOptionsApply.js +24 -19
- package/lib/asset/AssetGenerator.js +110 -28
- package/lib/asset/AssetModulesPlugin.js +92 -2
- package/lib/asset/WebManifestGenerator.js +164 -0
- package/lib/asset/WebManifestParser.js +130 -0
- package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
- package/lib/async-modules/isGeneratorLowered.js +26 -0
- package/lib/cache/PackFileCacheStrategy.js +9 -3
- package/lib/config/browserslistTargetHandler.js +40 -0
- package/lib/config/defaults.js +219 -29
- package/lib/config/normalization.js +26 -0
- package/lib/config/target.js +9 -1
- package/lib/css/CssGenerator.js +40 -31
- package/lib/css/CssLoadingRuntimeModule.js +5 -1
- package/lib/css/CssModulesPlugin.js +13 -0
- package/lib/css/CssParser.js +594 -263
- package/lib/css/syntax.js +875 -462
- package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
- package/lib/dependencies/AMDPlugin.js +2 -2
- package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
- package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
- package/lib/dependencies/CommonJsExportsDependency.js +47 -2
- package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
- package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
- package/lib/dependencies/CommonJsPlugin.js +2 -2
- package/lib/dependencies/CommonJsRequireDependency.js +26 -1
- package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
- package/lib/dependencies/ContextDependencyHelpers.js +3 -3
- package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
- package/lib/dependencies/CssIcssExportDependency.js +162 -71
- package/lib/dependencies/CssUrlDependency.js +35 -4
- package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
- package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
- package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
- package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
- package/lib/dependencies/HarmonyImportDependency.js +12 -1
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +11 -11
- package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
- package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
- package/lib/dependencies/HtmlEntryDependency.js +797 -172
- package/lib/dependencies/HtmlSourceDependency.js +26 -0
- package/lib/dependencies/ImportDependency.js +43 -1
- package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
- package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
- package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
- package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
- package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
- package/lib/dependencies/ImportMetaPlugin.js +532 -180
- package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
- package/lib/dependencies/ImportParserPlugin.js +80 -17
- package/lib/dependencies/ImportPhase.js +10 -8
- package/lib/dependencies/LoaderPlugin.js +5 -1
- package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
- package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
- package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
- package/lib/dependencies/SystemPlugin.js +2 -4
- package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
- package/lib/dependencies/URLDependency.js +141 -21
- package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
- package/lib/dependencies/WorkerDependency.js +86 -18
- package/lib/dependencies/WorkletDependency.js +278 -0
- package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
- package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
- package/lib/errors/LoaderLoadingError.js +20 -0
- package/lib/errors/ModuleParseError.js +39 -22
- package/lib/errors/NodeStuffInWebError.js +1 -1
- package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
- package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
- package/lib/hmr/lazyCompilationBackend.js +32 -8
- package/lib/html/HtmlGenerator.js +887 -15
- package/lib/html/HtmlModule.js +2 -2
- package/lib/html/HtmlModulesPlugin.js +610 -166
- package/lib/html/HtmlParser.js +603 -247
- package/lib/html/favicon.svg +1 -0
- package/lib/html/syntax.js +1264 -661
- package/lib/ids/IdHelpers.js +1 -1
- package/lib/index.js +6 -0
- package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
- package/lib/javascript/BasicEvaluatedExpression.js +366 -48
- package/lib/javascript/JavascriptGenerator.js +197 -49
- package/lib/javascript/JavascriptModulesPlugin.js +178 -24
- package/lib/javascript/JavascriptParser.js +701 -355
- package/lib/javascript/JavascriptParserHelpers.js +3 -6
- package/lib/javascript/syntax.js +4380 -0
- package/lib/library/ModuleLibraryPlugin.js +38 -1
- package/lib/loaders/LoaderRunner.js +714 -0
- package/lib/loaders/loadLoader.js +100 -0
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +19 -0
- package/lib/node/ReadFileCompileWasmPlugin.js +14 -0
- package/lib/optimize/ConcatenatedModule.js +120 -16
- package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
- package/lib/optimize/InnerGraphPlugin.js +50 -30
- package/lib/optimize/ModuleConcatenationPlugin.js +69 -3
- package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
- package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
- package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
- package/lib/prefetch/ResourceHintPlugin.js +663 -0
- package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
- package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
- package/lib/prefetch/parseResourceHintOptions.js +103 -0
- package/lib/rules/RuleSetCompiler.js +114 -0
- package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
- package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
- package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
- package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
- package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
- package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
- package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
- package/lib/schemes/HttpUriPlugin.js +40 -1
- package/lib/schemes/VirtualUrlPlugin.js +1 -2
- package/lib/serialization/FileMiddleware.js +29 -3
- package/lib/stats/DefaultStatsFactoryPlugin.js +32 -3
- package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
- package/lib/stats/DefaultStatsPrinterPlugin.js +12 -2
- package/lib/stats/StatsFactory.js +15 -8
- package/lib/url/URLParserPlugin.js +66 -13
- package/lib/util/AsyncQueue.js +122 -72
- package/lib/util/SetHelpers.js +1 -1
- package/lib/util/StackedMap.js +122 -48
- package/lib/util/binarySearchBounds.js +2 -2
- package/lib/util/concatenate.js +40 -14
- package/lib/util/createHooksRegistry.js +13 -5
- package/lib/util/dataURL.js +2 -2
- package/lib/util/deterministicGrouping.js +7 -5
- package/lib/util/formatSize.js +6 -2
- package/lib/util/globUtils.js +710 -0
- package/lib/util/identifier.js +23 -6
- package/lib/util/implicitTypeLoaderFallback.js +62 -0
- package/lib/util/internalSerializables.js +88 -71
- package/lib/util/magicComment.js +121 -25
- package/lib/util/numberHash.js +2 -2
- package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
- package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
- package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
- package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
- package/lib/web/FetchCompileWasmPlugin.js +14 -0
- package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
- package/lib/webpack.js +65 -0
- package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
- package/module.d.ts +21 -0
- package/package.json +13 -12
- package/schemas/WebpackOptions.check.js +1 -1
- package/schemas/WebpackOptions.json +759 -36
- package/schemas/plugins/HtmlParserOptions.check.js +1 -1
- package/schemas/plugins/ProgressPlugin.check.js +1 -1
- package/schemas/plugins/ProgressPlugin.json +17 -1
- package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
- package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
- package/schemas/plugins/container/ExternalsType.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
- package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
- package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
- package/schemas/plugins/css/CssParserOptions.check.js +1 -1
- package/types.d.ts +1213 -96
package/lib/css/syntax.js
CHANGED
|
@@ -199,6 +199,7 @@ const CC_AT_SIGN = "@".charCodeAt(0);
|
|
|
199
199
|
|
|
200
200
|
const CC_LOW_LINE = "_".charCodeAt(0);
|
|
201
201
|
const CC_LOWER_A = "a".charCodeAt(0);
|
|
202
|
+
const CC_LOWER_D = "d".charCodeAt(0);
|
|
202
203
|
const CC_LOWER_F = "f".charCodeAt(0);
|
|
203
204
|
const CC_LOWER_E = "e".charCodeAt(0);
|
|
204
205
|
const CC_LOWER_U = "u".charCodeAt(0);
|
|
@@ -341,11 +342,15 @@ const _isIdentStartCodePointCC = (cc) =>
|
|
|
341
342
|
/**
|
|
342
343
|
* Spec: ident-code = ident-start / digit / hyphen-minus.
|
|
343
344
|
*/
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
|
|
345
|
+
// Full `charCodeAt` range (0..0xFFFF) so the per-code-point ident test is one
|
|
346
|
+
// table load with no `cc < 128` branch — `_consumeAnIdentSequence` runs this on
|
|
347
|
+
// every character of every ident / class / property name (the tokenizer's
|
|
348
|
+
// hottest loop). Every non-ASCII code unit (>= 0x80) is an ident code point per
|
|
349
|
+
// spec, so those default to 1; only the ASCII rows carry real classification.
|
|
350
|
+
// Callers must index with `cc | 0`: EOF (`charCodeAt` → NaN) becomes 0 (NUL,
|
|
351
|
+
// not an ident) — a raw NaN index is an out-of-range access that permanently
|
|
352
|
+
// degrades the load site's IC.
|
|
353
|
+
const _identCharTable = new Uint8Array(0x10000).fill(1);
|
|
349
354
|
for (let i = 0; i < 128; i++) {
|
|
350
355
|
_identCharTable[i] =
|
|
351
356
|
_isLetter(i) || i === CC_LOW_LINE || _isDigit(i) || i === CC_HYPHEN_MINUS
|
|
@@ -356,8 +361,7 @@ for (let i = 0; i < 128; i++) {
|
|
|
356
361
|
* @param {number} cc char code
|
|
357
362
|
* @returns {boolean} true, if cc is an ident-sequence code point
|
|
358
363
|
*/
|
|
359
|
-
const _isIdentCodePoint = (cc) =>
|
|
360
|
-
cc < 128 ? _identCharTable[cc] === 1 : _isIdentStartCodePointCC(cc);
|
|
364
|
+
const _isIdentCodePoint = (cc) => _identCharTable[cc | 0] === 1;
|
|
361
365
|
|
|
362
366
|
/**
|
|
363
367
|
* ASCII case-insensitive equality against a lowercase literal — avoids the
|
|
@@ -377,6 +381,58 @@ const equalsLowerCase = (s, lit) => {
|
|
|
377
381
|
return true;
|
|
378
382
|
};
|
|
379
383
|
|
|
384
|
+
/**
|
|
385
|
+
* Case-sensitive equality of a source range against a literal — no slice.
|
|
386
|
+
* @param {string} input source
|
|
387
|
+
* @param {number} start range start
|
|
388
|
+
* @param {number} end range end (exclusive)
|
|
389
|
+
* @param {string} lit literal to match
|
|
390
|
+
* @returns {boolean} true when the range equals `lit`
|
|
391
|
+
*/
|
|
392
|
+
const rangeEquals = (input, start, end, lit) =>
|
|
393
|
+
end - start === lit.length && input.startsWith(lit, start);
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* ASCII case-insensitive equality of a source range against a lowercase ASCII literal — no slice.
|
|
397
|
+
* @param {string} input source
|
|
398
|
+
* @param {number} start range start
|
|
399
|
+
* @param {number} end range end (exclusive)
|
|
400
|
+
* @param {string} lit lowercase ASCII literal to match
|
|
401
|
+
* @returns {boolean} true when the range equals `lit` ignoring ASCII case
|
|
402
|
+
*/
|
|
403
|
+
const rangeEqualsLowerCase = (input, start, end, lit) => {
|
|
404
|
+
if (end - start !== lit.length) return false;
|
|
405
|
+
for (let i = 0; i < lit.length; i++) {
|
|
406
|
+
let c = input.charCodeAt(start + i);
|
|
407
|
+
if (c >= CC_UPPER_A && c <= CC_UPPER_Z) c |= 0x20;
|
|
408
|
+
if (c !== lit.charCodeAt(i)) return false;
|
|
409
|
+
}
|
|
410
|
+
return true;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* `s.toLowerCase()` that returns `s` itself (no allocation) when it can't
|
|
415
|
+
* change — no ASCII uppercase and no non-ASCII (whose Unicode case mapping is
|
|
416
|
+
* left to the real `toLowerCase`).
|
|
417
|
+
* @param {string} s string
|
|
418
|
+
* @returns {string} lowercased string
|
|
419
|
+
*/
|
|
420
|
+
const toLowerCaseIfNeeded = (s) => {
|
|
421
|
+
for (let i = 0; i < s.length; i++) {
|
|
422
|
+
const c = s.charCodeAt(i);
|
|
423
|
+
if ((c >= CC_UPPER_A && c <= CC_UPPER_Z) || c > 127) return s.toLowerCase();
|
|
424
|
+
}
|
|
425
|
+
return s;
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* A custom property name (`<dashed-ident>`): a `--`-prefixed identifier other than bare `--`.
|
|
430
|
+
* @param {string} identifier identifier
|
|
431
|
+
* @returns {boolean} true when identifier is dashed, otherwise false
|
|
432
|
+
*/
|
|
433
|
+
const isDashedIdentifier = (identifier) =>
|
|
434
|
+
identifier.startsWith("--") && identifier.length >= 3;
|
|
435
|
+
|
|
380
436
|
/**
|
|
381
437
|
* Consume an escaped code point.
|
|
382
438
|
* @param {string} input input
|
|
@@ -394,7 +450,8 @@ const _consumeAnEscapedCodePoint = (input, pos) => {
|
|
|
394
450
|
if (pos === input.length) return pos;
|
|
395
451
|
if (_isHexDigit(cc)) {
|
|
396
452
|
for (let i = 0; i < 5; i++) {
|
|
397
|
-
if (_isHexDigit(input.charCodeAt(pos)))
|
|
453
|
+
if (!_isHexDigit(input.charCodeAt(pos))) break;
|
|
454
|
+
pos++;
|
|
398
455
|
}
|
|
399
456
|
const trail = input.charCodeAt(pos);
|
|
400
457
|
if (_isWhiteSpace(trail)) {
|
|
@@ -480,12 +537,13 @@ const _ifThreeCodePointsWouldStartANumber = (input, pos, f, s, t) => {
|
|
|
480
537
|
const _consumeAnIdentSequence = (input, pos) => {
|
|
481
538
|
// Hot loop (every ident, at-keyword, hash, function name, unit). Both checks
|
|
482
539
|
// are inlined from `_isIdentCodePoint` / `_ifTwoCodePointsAreValidEscape`: the
|
|
483
|
-
//
|
|
484
|
-
// following code point only when `cc` is a `\` (rare)
|
|
540
|
+
// ident test is a single full-range table load (no `cc < 128` branch), and the
|
|
541
|
+
// escape test reads the following code point only when `cc` is a `\` (rare)
|
|
542
|
+
// instead of eagerly.
|
|
485
543
|
for (;;) {
|
|
486
|
-
const cc = input.charCodeAt(pos);
|
|
544
|
+
const cc = input.charCodeAt(pos) | 0;
|
|
487
545
|
pos++;
|
|
488
|
-
if (
|
|
546
|
+
if (_identCharTable[cc] === 1) {
|
|
489
547
|
continue;
|
|
490
548
|
}
|
|
491
549
|
if (cc === CC_REVERSE_SOLIDUS && !_isNewline(input.charCodeAt(pos))) {
|
|
@@ -700,17 +758,33 @@ function consumeNumberSign(input, pos, out) {
|
|
|
700
758
|
* @returns {MutableToken | undefined} the resulting token, or undefined at EOF
|
|
701
759
|
*/
|
|
702
760
|
function consumeHyphenMinus(input, pos, out) {
|
|
703
|
-
|
|
761
|
+
// Read the two lookahead code points once; the lead is the known `-`.
|
|
762
|
+
const second = input.charCodeAt(pos);
|
|
763
|
+
const third = input.charCodeAt(pos + 1);
|
|
764
|
+
if (
|
|
765
|
+
_ifThreeCodePointsWouldStartANumber(
|
|
766
|
+
input,
|
|
767
|
+
pos,
|
|
768
|
+
CC_HYPHEN_MINUS,
|
|
769
|
+
second,
|
|
770
|
+
third
|
|
771
|
+
)
|
|
772
|
+
) {
|
|
704
773
|
pos--;
|
|
705
774
|
return consumeANumericToken(input, pos, out);
|
|
706
775
|
}
|
|
707
|
-
if (
|
|
708
|
-
input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
|
|
709
|
-
input.charCodeAt(pos + 1) === CC_GREATER_THAN_SIGN
|
|
710
|
-
) {
|
|
776
|
+
if (second === CC_HYPHEN_MINUS && third === CC_GREATER_THAN_SIGN) {
|
|
711
777
|
return fill(out, TT_CDC, pos - 1, pos + 2);
|
|
712
778
|
}
|
|
713
|
-
if (
|
|
779
|
+
if (
|
|
780
|
+
_ifThreeCodePointsWouldStartAnIdentSequence(
|
|
781
|
+
input,
|
|
782
|
+
pos,
|
|
783
|
+
CC_HYPHEN_MINUS,
|
|
784
|
+
second,
|
|
785
|
+
third
|
|
786
|
+
)
|
|
787
|
+
) {
|
|
714
788
|
pos--;
|
|
715
789
|
return consumeAnIdentLikeToken(input, pos, out);
|
|
716
790
|
}
|
|
@@ -801,7 +875,7 @@ function consumeAUrlToken(input, pos, fnStart, out) {
|
|
|
801
875
|
out.contentStart = contentStart;
|
|
802
876
|
for (;;) {
|
|
803
877
|
if (pos === input.length) {
|
|
804
|
-
out.contentEnd = pos
|
|
878
|
+
out.contentEnd = pos;
|
|
805
879
|
return fill(out, TT_URL, fnStart, pos);
|
|
806
880
|
}
|
|
807
881
|
const cc = input.charCodeAt(pos);
|
|
@@ -999,7 +1073,11 @@ const HC_AT_SIGN = 9;
|
|
|
999
1073
|
const HC_REVERSE_SOLIDUS = 10;
|
|
1000
1074
|
const HC_DIGIT = 11;
|
|
1001
1075
|
const HC_IDENT = 12;
|
|
1002
|
-
|
|
1076
|
+
// Full `charCodeAt` range so `consumeAToken` dispatches with one table load and
|
|
1077
|
+
// no `cc < 128` branch. Every non-ASCII code point (>= 0x80) is an ident-start
|
|
1078
|
+
// lead per §4, so those rows are seeded to `HC_IDENT`; the ASCII rows below
|
|
1079
|
+
// overwrite 0..127 with their real class.
|
|
1080
|
+
const _charClass = new Uint8Array(0x10000).fill(HC_IDENT, 128);
|
|
1003
1081
|
const _singleTT = new Uint8Array(128);
|
|
1004
1082
|
_singleTT[CC_LEFT_PARENTHESIS] = TT_LEFT_PARENTHESIS;
|
|
1005
1083
|
_singleTT[CC_RIGHT_PARENTHESIS] = TT_RIGHT_PARENTHESIS;
|
|
@@ -1054,7 +1132,7 @@ for (let i = 0; i < 128; i++) {
|
|
|
1054
1132
|
function consumeAToken(input, pos, cc, out) {
|
|
1055
1133
|
// `u` / `U` would start a unicode-range token in the spec; those are not
|
|
1056
1134
|
// produced, so they map to HC_IDENT and fall through to ident-like.
|
|
1057
|
-
switch (
|
|
1135
|
+
switch (_charClass[cc]) {
|
|
1058
1136
|
// Run of whitespace → one <whitespace-token>.
|
|
1059
1137
|
case HC_WHITESPACE:
|
|
1060
1138
|
return consumeSpace(input, pos, out);
|
|
@@ -1605,7 +1683,9 @@ _ttToNodeType[TT_CDC] = T_CDC;
|
|
|
1605
1683
|
// field access differs, so only those ops are swapped.
|
|
1606
1684
|
|
|
1607
1685
|
// Current loc converter for the active parse (object backend reads it).
|
|
1608
|
-
let
|
|
1686
|
+
let _objLocConverter = /** @type {LocConverter} */ (
|
|
1687
|
+
/** @type {unknown} */ (null)
|
|
1688
|
+
);
|
|
1609
1689
|
|
|
1610
1690
|
// Active skip state (from `CssProcessOptions.skip`), applied by the grammar.
|
|
1611
1691
|
// `_skipTypes` is indexed by `NodeType` (1 = skip): drop that component-value
|
|
@@ -1616,6 +1696,13 @@ let _lc = /** @type {LocConverter} */ (/** @type {unknown} */ (null));
|
|
|
1616
1696
|
// so it is never walked or read — the caller must only skip what nothing reads.
|
|
1617
1697
|
// `useObjectBackend` restores the defaults so `parseA*` build the full tree.
|
|
1618
1698
|
const _NO_SKIP_TYPES = new Uint8Array(32);
|
|
1699
|
+
// Shared frozen empty list for block bodies with no decls / no child rules (the
|
|
1700
|
+
// common case — most rules carry only declarations). Every consumer reads these
|
|
1701
|
+
// lists read-only and null-guards, so one immutable instance replaces ~one empty
|
|
1702
|
+
// array allocation per rule; frozen so any errant push fails loud.
|
|
1703
|
+
const _EMPTY_LIST = /** @type {Rule[]} */ (
|
|
1704
|
+
/** @type {unknown} */ (Object.freeze([]))
|
|
1705
|
+
);
|
|
1619
1706
|
/** @type {Uint8Array} */
|
|
1620
1707
|
let _skipTypes = _NO_SKIP_TYPES;
|
|
1621
1708
|
// Fast-path flag: true only when a real skip set is active, so the (dominant)
|
|
@@ -1625,14 +1712,17 @@ let _skipSelectorPrelude = false;
|
|
|
1625
1712
|
let _skipAtRulePrelude = false;
|
|
1626
1713
|
|
|
1627
1714
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1628
|
-
let
|
|
1715
|
+
let _makeLeaf;
|
|
1629
1716
|
/** @type {(start: number, end: number, contentStart: number, contentEnd: number) => Node} */
|
|
1630
|
-
let
|
|
1717
|
+
let _makeUrl;
|
|
1631
1718
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1632
|
-
let
|
|
1719
|
+
let _makeContainer;
|
|
1633
1720
|
/** @type {(start: number) => Node} */
|
|
1634
|
-
let
|
|
1635
|
-
|
|
1721
|
+
let _makeStylesheet;
|
|
1722
|
+
// Offsets only — the object backend derives the name string itself (an
|
|
1723
|
+
// at-rule's name skips its `@`), so the SoA backend never pays for a slice
|
|
1724
|
+
// it would immediately discard (it re-derives names from offsets on read).
|
|
1725
|
+
/** @type {(r: Node, nameStart: number, nameEnd: number) => void} */
|
|
1636
1726
|
let _setName;
|
|
1637
1727
|
/** @type {(r: Node, v: number) => void} */
|
|
1638
1728
|
let _setEnd;
|
|
@@ -1651,182 +1741,264 @@ let _setBody;
|
|
|
1651
1741
|
/** @type {(r: Node, list: Node[]) => void} */
|
|
1652
1742
|
let _setRules;
|
|
1653
1743
|
/** @type {(r: Node) => number} */
|
|
1654
|
-
let
|
|
1744
|
+
let _nodeTypeOf;
|
|
1655
1745
|
/** @type {(r: Node) => number} */
|
|
1656
|
-
let
|
|
1746
|
+
let _nodeStartOf;
|
|
1657
1747
|
/** @type {(r: Node) => string} */
|
|
1658
|
-
let
|
|
1748
|
+
let _nodeValueOf;
|
|
1659
1749
|
/** @type {(r: Node) => SimpleBlockToken} */
|
|
1660
|
-
let
|
|
1750
|
+
let _nodeTokenOf;
|
|
1661
1751
|
|
|
1662
1752
|
// Child lists are plain arrays in every backend; refs are pushed by value.
|
|
1663
|
-
|
|
1753
|
+
// Content-list allocation: the object backend uses a per-site `[]` literal
|
|
1754
|
+
// (each site keeps its own V8 allocation site — a shared helper collapses them
|
|
1755
|
+
// and defeats escape analysis / pretenuring), the SoA backend recycles scratch
|
|
1756
|
+
// arrays through a pool — a sealed list is copied into the flat value buffer
|
|
1757
|
+
// and its array returned to the pool by `_soaSetValue`, so the streaming parse
|
|
1758
|
+
// allocates no per-list array. An abandoned (never-sealed) list simply falls
|
|
1759
|
+
// out of the pool. `_soaActive` gates the pool at each site.
|
|
1760
|
+
let _soaActive = false;
|
|
1761
|
+
/** @type {Node[][]} */
|
|
1762
|
+
const _listPool = [];
|
|
1763
|
+
const _soaList = () =>
|
|
1764
|
+
_listPool.length > 0
|
|
1765
|
+
? /** @type {Node[]} */ (_listPool.pop())
|
|
1766
|
+
: /** @type {Node[]} */ ([]);
|
|
1664
1767
|
|
|
1665
1768
|
// -- object backend: builds the retainable class-instance tree --
|
|
1666
|
-
/** @type {typeof
|
|
1667
|
-
const _objLeaf = (type, start, end) =>
|
|
1668
|
-
|
|
1769
|
+
/** @type {typeof _makeLeaf} */
|
|
1770
|
+
const _objLeaf = (type, start, end) =>
|
|
1771
|
+
new Token(type, start, end, _objLocConverter);
|
|
1772
|
+
/** @type {typeof _makeUrl} */
|
|
1669
1773
|
const _objUrl = (start, end, cs, ce) => {
|
|
1670
|
-
const u = /** @type {UrlToken} */ (
|
|
1774
|
+
const u = /** @type {UrlToken} */ (
|
|
1775
|
+
new Token(T_URL, start, end, _objLocConverter)
|
|
1776
|
+
);
|
|
1671
1777
|
u.contentStart = cs;
|
|
1672
1778
|
u.contentEnd = ce;
|
|
1673
1779
|
return u;
|
|
1674
1780
|
};
|
|
1675
|
-
/** @type {typeof
|
|
1781
|
+
/** @type {typeof _makeContainer} */
|
|
1676
1782
|
const _objContainer = (type, start, end) =>
|
|
1677
|
-
new Container(type, start, end,
|
|
1678
|
-
/** @type {typeof
|
|
1783
|
+
new Container(type, start, end, _objLocConverter);
|
|
1784
|
+
/** @type {typeof _makeStylesheet} */
|
|
1679
1785
|
const _objStylesheet = (start) => {
|
|
1680
1786
|
const s = /** @type {Stylesheet} */ (
|
|
1681
|
-
new Node(T_STYLESHEET, start, start,
|
|
1787
|
+
new Node(T_STYLESHEET, start, start, _objLocConverter)
|
|
1682
1788
|
);
|
|
1683
1789
|
s.rules = [];
|
|
1684
1790
|
return s;
|
|
1685
1791
|
};
|
|
1792
|
+
// The backend function sets are module-level constants (not closures rebuilt
|
|
1793
|
+
// per parse): the dispatch slots keep one function identity forever, so the
|
|
1794
|
+
// per-node call sites in the consume algorithms stay monomorphic instead of
|
|
1795
|
+
// seeing a fresh closure per parse. They capture only module-level state.
|
|
1796
|
+
/** @type {typeof _setName} */
|
|
1797
|
+
const _objSetName = (r, ns, ne) => {
|
|
1798
|
+
const c = /** @type {Container} */ (r);
|
|
1799
|
+
// An at-rule's `nameStart` points at its `@`, which the name excludes.
|
|
1800
|
+
c.name = _objLocConverter._input.slice(
|
|
1801
|
+
r.type === T_AT_RULE ? ns + 1 : ns,
|
|
1802
|
+
ne
|
|
1803
|
+
);
|
|
1804
|
+
c.nameStart = ns;
|
|
1805
|
+
c.nameEnd = ne;
|
|
1806
|
+
};
|
|
1807
|
+
/** @type {typeof _setEnd} */
|
|
1808
|
+
const _objSetEnd = (r, v) => {
|
|
1809
|
+
r.end = v;
|
|
1810
|
+
};
|
|
1811
|
+
/** @type {typeof _setBlock} */
|
|
1812
|
+
const _objSetBlock = (r, bs, be) => {
|
|
1813
|
+
const c = /** @type {Container} */ (r);
|
|
1814
|
+
c.blockStart = bs;
|
|
1815
|
+
c.blockEnd = be;
|
|
1816
|
+
};
|
|
1817
|
+
/** @type {typeof _setImportant} */
|
|
1818
|
+
const _objSetImportant = (r) => {
|
|
1819
|
+
/** @type {Container} */ (r).important = true;
|
|
1820
|
+
};
|
|
1821
|
+
/** @type {typeof _setToken} */
|
|
1822
|
+
const _objSetToken = (r, ch) => {
|
|
1823
|
+
/** @type {Container} */ (r).token = ch;
|
|
1824
|
+
};
|
|
1825
|
+
/** @type {typeof _setValue} */
|
|
1826
|
+
const _objSetValue = (r, list) => {
|
|
1827
|
+
/** @type {Container} */ (r).value = /** @type {ComponentValue[]} */ (list);
|
|
1828
|
+
};
|
|
1829
|
+
/** @type {typeof _setPrelude} */
|
|
1830
|
+
const _objSetPrelude = (r, list) => {
|
|
1831
|
+
/** @type {Container} */ (r).prelude = /** @type {ComponentValue[]} */ (list);
|
|
1832
|
+
};
|
|
1833
|
+
/** @type {typeof _setBody} */
|
|
1834
|
+
const _objSetBody = (r, decls, childRules) => {
|
|
1835
|
+
const c = /** @type {Container} */ (r);
|
|
1836
|
+
c.declarations = /** @type {Declaration[]} */ (decls);
|
|
1837
|
+
c.childRules = /** @type {Rule[]} */ (childRules);
|
|
1838
|
+
};
|
|
1839
|
+
/** @type {typeof _setRules} */
|
|
1840
|
+
const _objSetRules = (r, list) => {
|
|
1841
|
+
/** @type {Stylesheet} */ (r).rules = /** @type {Rule[]} */ (list);
|
|
1842
|
+
};
|
|
1843
|
+
/** @type {typeof _nodeTypeOf} */
|
|
1844
|
+
const _objNodeTypeOf = (r) => r.type;
|
|
1845
|
+
/** @type {typeof _nodeStartOf} */
|
|
1846
|
+
const _objNodeStartOf = (r) => r.start;
|
|
1847
|
+
/** @type {typeof _nodeValueOf} */
|
|
1848
|
+
const _objNodeValueOf = (r) => /** @type {Token} */ (r).value;
|
|
1849
|
+
/** @type {typeof _nodeTokenOf} */
|
|
1850
|
+
const _objNodeTokenOf = (r) =>
|
|
1851
|
+
/** @type {SimpleBlockToken} */ (/** @type {Container} */ (r).token);
|
|
1686
1852
|
/** @param {LocConverter} lc loc converter for this parse */
|
|
1687
1853
|
const useObjectBackend = (lc) => {
|
|
1688
|
-
|
|
1854
|
+
_objLocConverter = lc;
|
|
1855
|
+
_soaActive = false;
|
|
1689
1856
|
// `parseA*` return the full tree, so nothing is skipped.
|
|
1690
1857
|
_skipTypes = _NO_SKIP_TYPES;
|
|
1691
1858
|
_skipActive = false;
|
|
1692
1859
|
_skipSelectorPrelude = false;
|
|
1693
1860
|
_skipAtRulePrelude = false;
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
_setName =
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
};
|
|
1712
|
-
_setImportant = (r) => {
|
|
1713
|
-
/** @type {Container} */ (r).important = true;
|
|
1714
|
-
};
|
|
1715
|
-
_setToken = (r, ch) => {
|
|
1716
|
-
/** @type {Container} */ (r).token = ch;
|
|
1717
|
-
};
|
|
1718
|
-
_setValue = (r, list) => {
|
|
1719
|
-
/** @type {Container} */ (r).value = /** @type {ComponentValue[]} */ (list);
|
|
1720
|
-
};
|
|
1721
|
-
_setPrelude = (r, list) => {
|
|
1722
|
-
/** @type {Container} */ (r).prelude = /** @type {ComponentValue[]} */ (
|
|
1723
|
-
list
|
|
1724
|
-
);
|
|
1725
|
-
};
|
|
1726
|
-
_setBody = (r, decls, childRules) => {
|
|
1727
|
-
const c = /** @type {Container} */ (r);
|
|
1728
|
-
c.declarations = /** @type {Declaration[]} */ (decls);
|
|
1729
|
-
c.childRules = /** @type {Rule[]} */ (childRules);
|
|
1730
|
-
};
|
|
1731
|
-
_setRules = (r, list) => {
|
|
1732
|
-
/** @type {Stylesheet} */ (r).rules = /** @type {Rule[]} */ (list);
|
|
1733
|
-
};
|
|
1734
|
-
_nType = (r) => r.type;
|
|
1735
|
-
_nStart = (r) => r.start;
|
|
1736
|
-
_nValue = (r) => /** @type {Token} */ (r).value;
|
|
1737
|
-
_nToken = (r) =>
|
|
1738
|
-
/** @type {SimpleBlockToken} */ (/** @type {Container} */ (r).token);
|
|
1861
|
+
_makeLeaf = _objLeaf;
|
|
1862
|
+
_makeUrl = _objUrl;
|
|
1863
|
+
_makeContainer = _objContainer;
|
|
1864
|
+
_makeStylesheet = _objStylesheet;
|
|
1865
|
+
_setName = _objSetName;
|
|
1866
|
+
_setEnd = _objSetEnd;
|
|
1867
|
+
_setBlock = _objSetBlock;
|
|
1868
|
+
_setImportant = _objSetImportant;
|
|
1869
|
+
_setToken = _objSetToken;
|
|
1870
|
+
_setValue = _objSetValue;
|
|
1871
|
+
_setPrelude = _objSetPrelude;
|
|
1872
|
+
_setBody = _objSetBody;
|
|
1873
|
+
_setRules = _objSetRules;
|
|
1874
|
+
_nodeTypeOf = _objNodeTypeOf;
|
|
1875
|
+
_nodeStartOf = _objNodeStartOf;
|
|
1876
|
+
_nodeValueOf = _objNodeValueOf;
|
|
1877
|
+
_nodeTokenOf = _objNodeTokenOf;
|
|
1739
1878
|
};
|
|
1740
1879
|
|
|
1741
1880
|
// -- struct-of-arrays backend: writes nodes into reused typed arrays --
|
|
1742
1881
|
// A node ref is its integer id; fields live in parallel arrays indexed by id.
|
|
1743
|
-
// Three reused int slots (`
|
|
1744
|
-
// extras; child lists hang off three object arrays.
|
|
1745
|
-
// url:
|
|
1746
|
-
// function:
|
|
1747
|
-
// declaration:
|
|
1748
|
-
// at-rule:
|
|
1749
|
-
// qualified:
|
|
1882
|
+
// Three reused int slots (`_soaAux0/1/2`) plus a flags byte carry the per-type
|
|
1883
|
+
// extras; child lists hang off three object arrays. Aux slot meaning by type:
|
|
1884
|
+
// url: aux0 contentStart, aux1 contentEnd
|
|
1885
|
+
// function: aux0 nameEnd
|
|
1886
|
+
// declaration: aux0 nameEnd, flags bit0 important
|
|
1887
|
+
// at-rule: aux0 nameEnd, aux1 blockStart, aux2 blockEnd
|
|
1888
|
+
// qualified: aux1 blockStart, aux2 blockEnd
|
|
1750
1889
|
// `name` / `nameStart` / a simple block's `token` are derived from the source
|
|
1751
|
-
// on read (see the SoA accessors), so they need no slot.
|
|
1752
|
-
// value | prelude | stylesheet rules
|
|
1753
|
-
// `grammar` resets `
|
|
1890
|
+
// on read (see the SoA accessors), so they need no slot. A node's main content
|
|
1891
|
+
// (value | prelude | stylesheet rules) is a `_soaFlat` span (see below).
|
|
1892
|
+
// `grammar` resets `_soaNodeCount` to 0 after each top-level rule's walk, so the
|
|
1754
1893
|
// buffers are reused across rules and the parse allocates almost nothing.
|
|
1755
|
-
let
|
|
1756
|
-
let
|
|
1757
|
-
let
|
|
1758
|
-
let
|
|
1759
|
-
let
|
|
1760
|
-
let
|
|
1761
|
-
let
|
|
1762
|
-
let
|
|
1763
|
-
let
|
|
1764
|
-
|
|
1765
|
-
|
|
1894
|
+
let _soaCapacity = 0;
|
|
1895
|
+
let _soaNodeCount = 0;
|
|
1896
|
+
let _soaTypes = new Uint8Array(0);
|
|
1897
|
+
let _soaStarts = new Int32Array(0);
|
|
1898
|
+
let _soaEnds = new Int32Array(0);
|
|
1899
|
+
let _soaAux0 = new Int32Array(0);
|
|
1900
|
+
let _soaAux1 = new Int32Array(0);
|
|
1901
|
+
let _soaAux2 = new Int32Array(0);
|
|
1902
|
+
let _soaFlags = new Uint8Array(0);
|
|
1903
|
+
// Content-list spans: a container's value / prelude is `_soaFlat[start, start+len)`
|
|
1904
|
+
// (node refs), recycled per top-level rule like the node columns.
|
|
1905
|
+
let _soaListStarts = new Int32Array(0);
|
|
1906
|
+
let _soaListLens = new Int32Array(0);
|
|
1907
|
+
let _soaFlat = new Int32Array(0);
|
|
1908
|
+
let _soaFlatTop = 0;
|
|
1909
|
+
// Peak usage of the current parse, and use-once regrow hints: after an
|
|
1910
|
+
// over-capacity shrink the next grow jumps straight back to the previous
|
|
1911
|
+
// parse's peak (one exact-fit allocation instead of re-doubling up).
|
|
1912
|
+
let _soaPeak = 0;
|
|
1913
|
+
let _soaFlatPeak = 0;
|
|
1914
|
+
let _soaGrowHint = 0;
|
|
1915
|
+
let _soaFlatGrowHint = 0;
|
|
1916
|
+
|
|
1917
|
+
/** @param {number} need minimum flat-buffer capacity */
|
|
1918
|
+
const _soaFlatGrow = (need) => {
|
|
1919
|
+
let cap = _soaFlat.length || 4096;
|
|
1920
|
+
if (_soaFlatGrowHint > cap) cap = _soaFlatGrowHint;
|
|
1921
|
+
_soaFlatGrowHint = 0;
|
|
1922
|
+
while (cap < need) cap *= 2;
|
|
1923
|
+
const next = new Int32Array(cap);
|
|
1924
|
+
next.set(_soaFlat);
|
|
1925
|
+
_soaFlat = next;
|
|
1926
|
+
};
|
|
1766
1927
|
/** @type {(Node[] | null)[]} */
|
|
1767
|
-
const
|
|
1928
|
+
const _soaDeclarationLists = [];
|
|
1768
1929
|
/** @type {(Node[] | null)[]} */
|
|
1769
|
-
const
|
|
1930
|
+
const _soaChildRuleLists = [];
|
|
1770
1931
|
let _soaInput = "";
|
|
1771
|
-
let
|
|
1932
|
+
let _soaLocConverter = /** @type {LocConverter} */ (
|
|
1933
|
+
/** @type {unknown} */ (null)
|
|
1934
|
+
);
|
|
1772
1935
|
|
|
1773
1936
|
// Node refs are integers here but typed `Node` across the parser; these are
|
|
1774
1937
|
// identity casts that just satisfy the type system at the boundary.
|
|
1775
1938
|
/** @type {(n: Node) => number} */
|
|
1776
|
-
const
|
|
1939
|
+
const _nodeIndex = (n) => /** @type {number} */ (/** @type {unknown} */ (n));
|
|
1777
1940
|
/** @type {(i: number) => Node} */
|
|
1778
|
-
const
|
|
1941
|
+
const _nodeRef = (i) => /** @type {Node} */ (/** @type {unknown} */ (i));
|
|
1779
1942
|
|
|
1780
1943
|
/** @param {number} need minimum capacity */
|
|
1781
1944
|
const _soaGrow = (need) => {
|
|
1782
|
-
let cap =
|
|
1945
|
+
let cap = _soaCapacity || 4096;
|
|
1946
|
+
if (_soaGrowHint > cap) cap = _soaGrowHint;
|
|
1947
|
+
_soaGrowHint = 0;
|
|
1783
1948
|
while (cap < need) cap *= 2;
|
|
1784
1949
|
const ty = new Uint8Array(cap);
|
|
1785
|
-
ty.set(
|
|
1786
|
-
|
|
1950
|
+
ty.set(_soaTypes);
|
|
1951
|
+
_soaTypes = ty;
|
|
1787
1952
|
const st = new Int32Array(cap);
|
|
1788
|
-
st.set(
|
|
1789
|
-
|
|
1953
|
+
st.set(_soaStarts);
|
|
1954
|
+
_soaStarts = st;
|
|
1790
1955
|
const en = new Int32Array(cap);
|
|
1791
|
-
en.set(
|
|
1792
|
-
|
|
1956
|
+
en.set(_soaEnds);
|
|
1957
|
+
_soaEnds = en;
|
|
1793
1958
|
const a0 = new Int32Array(cap);
|
|
1794
|
-
a0.set(
|
|
1795
|
-
|
|
1959
|
+
a0.set(_soaAux0);
|
|
1960
|
+
_soaAux0 = a0;
|
|
1796
1961
|
const a1 = new Int32Array(cap);
|
|
1797
|
-
a1.set(
|
|
1798
|
-
|
|
1962
|
+
a1.set(_soaAux1);
|
|
1963
|
+
_soaAux1 = a1;
|
|
1799
1964
|
const a2 = new Int32Array(cap);
|
|
1800
|
-
a2.set(
|
|
1801
|
-
|
|
1965
|
+
a2.set(_soaAux2);
|
|
1966
|
+
_soaAux2 = a2;
|
|
1802
1967
|
const fl = new Uint8Array(cap);
|
|
1803
|
-
fl.set(
|
|
1804
|
-
|
|
1805
|
-
|
|
1968
|
+
fl.set(_soaFlags);
|
|
1969
|
+
_soaFlags = fl;
|
|
1970
|
+
const ls = new Int32Array(cap);
|
|
1971
|
+
ls.set(_soaListStarts);
|
|
1972
|
+
_soaListStarts = ls;
|
|
1973
|
+
const ll = new Int32Array(cap);
|
|
1974
|
+
ll.set(_soaListLens);
|
|
1975
|
+
_soaListLens = ll;
|
|
1976
|
+
_soaCapacity = cap;
|
|
1806
1977
|
};
|
|
1807
1978
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1808
|
-
const
|
|
1979
|
+
const _soaAllocNode = (type, start, end) => {
|
|
1809
1980
|
// Ids are 1-based: a node ref is used in truthiness checks (`if (!parent)`),
|
|
1810
1981
|
// so 0 must stay reserved for "no node".
|
|
1811
1982
|
// Leaves never read the flag / list slots — `_soaAllocContainer` clears
|
|
1812
1983
|
// them instead, keeping the dominant leaf allocation at three writes.
|
|
1813
|
-
const i =
|
|
1814
|
-
if (i >=
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
return
|
|
1984
|
+
const i = _soaNodeCount + 1;
|
|
1985
|
+
if (i >= _soaCapacity) _soaGrow(i + 1);
|
|
1986
|
+
_soaTypes[i] = type;
|
|
1987
|
+
_soaStarts[i] = start;
|
|
1988
|
+
_soaEnds[i] = end;
|
|
1989
|
+
_soaNodeCount = i;
|
|
1990
|
+
return _nodeRef(i);
|
|
1820
1991
|
};
|
|
1821
1992
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1822
1993
|
const _soaAllocContainer = (type, start, end) => {
|
|
1823
|
-
const r =
|
|
1824
|
-
const i =
|
|
1825
|
-
|
|
1826
|
-
// Clear list slots so a reused id never exposes a previous node's children
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1994
|
+
const r = _soaAllocNode(type, start, end);
|
|
1995
|
+
const i = _nodeIndex(r);
|
|
1996
|
+
_soaFlags[i] = 0;
|
|
1997
|
+
// Clear list slots so a reused id never exposes a previous node's children
|
|
1998
|
+
// (content lists are flat spans, so zeroing the length suffices).
|
|
1999
|
+
_soaListLens[i] = 0;
|
|
2000
|
+
_soaDeclarationLists[i] = null;
|
|
2001
|
+
_soaChildRuleLists[i] = null;
|
|
1830
2002
|
return r;
|
|
1831
2003
|
};
|
|
1832
2004
|
// Raw token value (the lazy `Token.value` form): hash / at-keyword drop their
|
|
@@ -1837,65 +2009,107 @@ const _soaAllocContainer = (type, start, end) => {
|
|
|
1837
2009
|
* @returns {string} raw token value
|
|
1838
2010
|
*/
|
|
1839
2011
|
const _soaValueOf = (i) => {
|
|
1840
|
-
const ty =
|
|
2012
|
+
const ty = _soaTypes[i];
|
|
1841
2013
|
if (ty === T_HASH || ty === T_AT_KEYWORD) {
|
|
1842
|
-
return _soaInput.slice(
|
|
2014
|
+
return _soaInput.slice(_soaStarts[i] + 1, _soaEnds[i]);
|
|
1843
2015
|
}
|
|
1844
|
-
if (ty === T_URL) return _soaInput.slice(
|
|
1845
|
-
return _soaInput.slice(
|
|
2016
|
+
if (ty === T_URL) return _soaInput.slice(_soaAux0[i], _soaAux1[i]);
|
|
2017
|
+
return _soaInput.slice(_soaStarts[i], _soaEnds[i]);
|
|
2018
|
+
};
|
|
2019
|
+
// Module-level constants for the same reason as the `_objSet*` set above.
|
|
2020
|
+
/** @type {typeof _makeUrl} */
|
|
2021
|
+
const _soaMakeUrl = (start, end, cs, ce) => {
|
|
2022
|
+
const r = _soaAllocNode(T_URL, start, end);
|
|
2023
|
+
_soaAux0[_nodeIndex(r)] = cs;
|
|
2024
|
+
_soaAux1[_nodeIndex(r)] = ce;
|
|
2025
|
+
return r;
|
|
2026
|
+
};
|
|
2027
|
+
/** @type {typeof _makeStylesheet} */
|
|
2028
|
+
const _soaMakeStylesheet = (start) =>
|
|
2029
|
+
_soaAllocContainer(T_STYLESHEET, start, start);
|
|
2030
|
+
// name / nameStart are derived from start + nameEnd; only nameEnd is stored.
|
|
2031
|
+
/** @type {typeof _setName} */
|
|
2032
|
+
const _soaSetName = (r, ns, ne) => {
|
|
2033
|
+
_soaAux0[_nodeIndex(r)] = ne;
|
|
1846
2034
|
};
|
|
2035
|
+
/** @type {typeof _setEnd} */
|
|
2036
|
+
const _soaSetEnd = (r, v) => {
|
|
2037
|
+
_soaEnds[_nodeIndex(r)] = v;
|
|
2038
|
+
};
|
|
2039
|
+
/** @type {typeof _setBlock} */
|
|
2040
|
+
const _soaSetBlock = (r, bs, be) => {
|
|
2041
|
+
const i = _nodeIndex(r);
|
|
2042
|
+
_soaAux1[i] = bs;
|
|
2043
|
+
_soaAux2[i] = be;
|
|
2044
|
+
};
|
|
2045
|
+
/** @type {typeof _setImportant} */
|
|
2046
|
+
const _soaSetImportant = (r) => {
|
|
2047
|
+
_soaFlags[_nodeIndex(r)] |= 1;
|
|
2048
|
+
};
|
|
2049
|
+
// A simple block's token is derived from its opening char on read.
|
|
2050
|
+
/** @type {typeof _setToken} */
|
|
2051
|
+
const _soaSetToken = (r, ch) => {};
|
|
2052
|
+
/** @type {typeof _setValue} */
|
|
2053
|
+
const _soaSetValue = (r, list) => {
|
|
2054
|
+
// Seal the finished list: copy its refs into the flat buffer and hand the
|
|
2055
|
+
// scratch array back to the pool. The caller never touches `list` again.
|
|
2056
|
+
const i = _nodeIndex(r);
|
|
2057
|
+
const len = list.length;
|
|
2058
|
+
const start = _soaFlatTop;
|
|
2059
|
+
if (start + len > _soaFlat.length) _soaFlatGrow(start + len);
|
|
2060
|
+
for (let k = 0; k < len; k++) {
|
|
2061
|
+
_soaFlat[start + k] = _nodeIndex(list[k]);
|
|
2062
|
+
}
|
|
2063
|
+
_soaFlatTop = start + len;
|
|
2064
|
+
_soaListStarts[i] = start;
|
|
2065
|
+
_soaListLens[i] = len;
|
|
2066
|
+
list.length = 0;
|
|
2067
|
+
_listPool.push(list);
|
|
2068
|
+
};
|
|
2069
|
+
/** @type {typeof _setBody} */
|
|
2070
|
+
const _soaSetBody = (r, decls, childRules) => {
|
|
2071
|
+
const i = _nodeIndex(r);
|
|
2072
|
+
_soaDeclarationLists[i] = decls;
|
|
2073
|
+
_soaChildRuleLists[i] = childRules;
|
|
2074
|
+
};
|
|
2075
|
+
/** @type {typeof _nodeTypeOf} */
|
|
2076
|
+
const _soaNodeTypeOf = (r) => _soaTypes[_nodeIndex(r)];
|
|
2077
|
+
/** @type {typeof _nodeStartOf} */
|
|
2078
|
+
const _soaNodeStartOf = (r) => _soaStarts[_nodeIndex(r)];
|
|
2079
|
+
/** @type {typeof _nodeValueOf} */
|
|
2080
|
+
const _soaNodeValueOf = (r) => _soaValueOf(_nodeIndex(r));
|
|
2081
|
+
/** @type {typeof _nodeTokenOf} */
|
|
2082
|
+
const _soaNodeTokenOf = (r) =>
|
|
2083
|
+
/** @type {SimpleBlockToken} */ (_soaInput[_soaStarts[_nodeIndex(r)]]);
|
|
1847
2084
|
/**
|
|
1848
2085
|
* @param {string} input source
|
|
1849
2086
|
* @param {LocConverter} lc loc converter
|
|
1850
2087
|
*/
|
|
1851
2088
|
const useSoaBackend = (input, lc) => {
|
|
1852
2089
|
_soaInput = input;
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
_setImportant = (r) => {
|
|
1877
|
-
_soaFl[_ix(r)] |= 1;
|
|
1878
|
-
};
|
|
1879
|
-
// A simple block's token is derived from its opening char on read.
|
|
1880
|
-
_setToken = (r, ch) => {};
|
|
1881
|
-
_setValue = (r, list) => {
|
|
1882
|
-
_soaL0[_ix(r)] = list;
|
|
1883
|
-
};
|
|
1884
|
-
_setPrelude = (r, list) => {
|
|
1885
|
-
_soaL0[_ix(r)] = list;
|
|
1886
|
-
};
|
|
1887
|
-
_setBody = (r, decls, childRules) => {
|
|
1888
|
-
const i = _ix(r);
|
|
1889
|
-
_soaL1[i] = decls;
|
|
1890
|
-
_soaL2[i] = childRules;
|
|
1891
|
-
};
|
|
1892
|
-
_setRules = (r, list) => {
|
|
1893
|
-
_soaL0[_ix(r)] = list;
|
|
1894
|
-
};
|
|
1895
|
-
_nType = (r) => _soaTy[_ix(r)];
|
|
1896
|
-
_nStart = (r) => _soaSt[_ix(r)];
|
|
1897
|
-
_nValue = (r) => _soaValueOf(_ix(r));
|
|
1898
|
-
_nToken = (r) => /** @type {SimpleBlockToken} */ (_soaInput[_soaSt[_ix(r)]]);
|
|
2090
|
+
_soaLocConverter = lc;
|
|
2091
|
+
_soaNodeCount = 0;
|
|
2092
|
+
_soaFlatTop = 0;
|
|
2093
|
+
_soaActive = true;
|
|
2094
|
+
// The alloc primitives are the slot functions directly — no wrapper hop.
|
|
2095
|
+
_makeLeaf = _soaAllocNode;
|
|
2096
|
+
_makeUrl = _soaMakeUrl;
|
|
2097
|
+
_makeContainer = _soaAllocContainer;
|
|
2098
|
+
_makeStylesheet = _soaMakeStylesheet;
|
|
2099
|
+
_setName = _soaSetName;
|
|
2100
|
+
_setEnd = _soaSetEnd;
|
|
2101
|
+
_setBlock = _soaSetBlock;
|
|
2102
|
+
_setImportant = _soaSetImportant;
|
|
2103
|
+
_setToken = _soaSetToken;
|
|
2104
|
+
_setValue = _soaSetValue;
|
|
2105
|
+
// value / prelude / rules all land in the one content-list slot.
|
|
2106
|
+
_setPrelude = _soaSetValue;
|
|
2107
|
+
_setBody = _soaSetBody;
|
|
2108
|
+
_setRules = _soaSetValue;
|
|
2109
|
+
_nodeTypeOf = _soaNodeTypeOf;
|
|
2110
|
+
_nodeStartOf = _soaNodeStartOf;
|
|
2111
|
+
_nodeValueOf = _soaNodeValueOf;
|
|
2112
|
+
_nodeTokenOf = _soaNodeTokenOf;
|
|
1899
2113
|
};
|
|
1900
2114
|
|
|
1901
2115
|
/**
|
|
@@ -1909,9 +2123,9 @@ const tokenToNode = (t) => {
|
|
|
1909
2123
|
// plain leaf whose node type comes from the map.
|
|
1910
2124
|
if (tt === TT_URL) {
|
|
1911
2125
|
const ut = /** @type {CssUrlToken} */ (t);
|
|
1912
|
-
return
|
|
2126
|
+
return _makeUrl(t.start, t.end, ut.contentStart, ut.contentEnd);
|
|
1913
2127
|
}
|
|
1914
|
-
return
|
|
2128
|
+
return _makeLeaf(_ttToNodeType[tt], t.start, t.end);
|
|
1915
2129
|
};
|
|
1916
2130
|
|
|
1917
2131
|
/**
|
|
@@ -2088,7 +2302,7 @@ const normalizeIntoTokenStream = (input, pos, onComment) =>
|
|
|
2088
2302
|
// algorithms that drive the tokenizer.
|
|
2089
2303
|
|
|
2090
2304
|
/**
|
|
2091
|
-
* @typedef {object}
|
|
2305
|
+
* @typedef {object} ParseOptions
|
|
2092
2306
|
* @property {((input: string, start: number, end: number) => number)=} comment optional comment-token callback; the public `parse*` entry points use it to build the `TokenStream` so the outer parser's comment tracker still sees magic comments inside the consumed range
|
|
2093
2307
|
*/
|
|
2094
2308
|
|
|
@@ -2096,17 +2310,18 @@ const normalizeIntoTokenStream = (input, pos, onComment) =>
|
|
|
2096
2310
|
* Parse a stylesheet, CSS Syntax Level 3
|
|
2097
2311
|
* [§5.3.4](https://drafts.csswg.org/css-syntax/#parse-stylesheet).
|
|
2098
2312
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2099
|
-
* @param {
|
|
2313
|
+
* @param {number=} pos start position (string input only)
|
|
2314
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2100
2315
|
* @returns {Stylesheet} the parsed stylesheet
|
|
2101
2316
|
*/
|
|
2102
|
-
const parseAStylesheet = (input,
|
|
2317
|
+
const parseAStylesheet = (input, pos = 0, options = {}) => {
|
|
2103
2318
|
// 1. If input is a byte stream for a stylesheet, decode bytes from input, and set input to the result.
|
|
2104
2319
|
// 2. Normalize input, and set input to the result.
|
|
2105
|
-
const ts = normalizeIntoTokenStream(input,
|
|
2320
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2106
2321
|
useObjectBackend(ts.locConverter);
|
|
2107
2322
|
// 3. Create a new stylesheet, with its location set to location (or null, if location was not passed).
|
|
2108
2323
|
const start = ts.next().start;
|
|
2109
|
-
const stylesheet = /** @type {Stylesheet} */ (
|
|
2324
|
+
const stylesheet = /** @type {Stylesheet} */ (_makeStylesheet(start));
|
|
2110
2325
|
// 4. Consume a stylesheet's contents from input, and set the stylesheet's rules to the result.
|
|
2111
2326
|
_setRules(stylesheet, consumeAStylesheetsContents(ts));
|
|
2112
2327
|
_setEnd(stylesheet, ts.next().start);
|
|
@@ -2121,12 +2336,13 @@ const parseAStylesheet = (input, comment) => {
|
|
|
2121
2336
|
* declarations are parse errors (never produced) and top-level CDO (`<!--`) /
|
|
2122
2337
|
* CDC (`-->`) tokens are discarded.
|
|
2123
2338
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2124
|
-
* @param {
|
|
2339
|
+
* @param {number=} pos start position (string input only)
|
|
2340
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2125
2341
|
* @returns {Rule[]} top-level rules
|
|
2126
2342
|
*/
|
|
2127
|
-
const parseAStylesheetsContents = (input,
|
|
2343
|
+
const parseAStylesheetsContents = (input, pos = 0, options = {}) => {
|
|
2128
2344
|
// 1. Normalize input, and set input to the result.
|
|
2129
|
-
const ts = normalizeIntoTokenStream(input,
|
|
2345
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2130
2346
|
useObjectBackend(ts.locConverter);
|
|
2131
2347
|
// 2. Consume a stylesheet’s contents from input, and return the result.
|
|
2132
2348
|
return consumeAStylesheetsContents(ts);
|
|
@@ -2137,12 +2353,12 @@ const parseAStylesheetsContents = (input, comment) => {
|
|
|
2137
2353
|
* [§5.3.6](https://drafts.csswg.org/css-syntax/#parse-block-contents).
|
|
2138
2354
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2139
2355
|
* @param {number=} pos start position (string input only; just past the opening `{`, or 0)
|
|
2140
|
-
* @param {
|
|
2356
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2141
2357
|
* @returns {{ decls: Declaration[], rules: Rule[] }} block decls + rules
|
|
2142
2358
|
*/
|
|
2143
|
-
const parseABlocksContents = (input, pos,
|
|
2359
|
+
const parseABlocksContents = (input, pos = 0, options = {}) => {
|
|
2144
2360
|
// 1. Normalize input, and set input to the result.
|
|
2145
|
-
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2361
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2146
2362
|
useObjectBackend(ts.locConverter);
|
|
2147
2363
|
// 2. Consume a block’s contents from input, and return the result.
|
|
2148
2364
|
return consumeABlocksContents(ts);
|
|
@@ -2155,12 +2371,12 @@ const parseABlocksContents = (input, pos, comment) => {
|
|
|
2155
2371
|
* whitespace; `undefined` (syntax error) otherwise.
|
|
2156
2372
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2157
2373
|
* @param {number=} pos start position (string input only)
|
|
2158
|
-
* @param {
|
|
2374
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2159
2375
|
* @returns {Rule | undefined} the parsed rule
|
|
2160
2376
|
*/
|
|
2161
|
-
const parseARule = (input, pos,
|
|
2377
|
+
const parseARule = (input, pos = 0, options = {}) => {
|
|
2162
2378
|
// 1. Normalize input, and set input to the result.
|
|
2163
|
-
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2379
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2164
2380
|
useObjectBackend(ts.locConverter);
|
|
2165
2381
|
// 2. Discard whitespace from input.
|
|
2166
2382
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
@@ -2186,12 +2402,12 @@ const parseARule = (input, pos, comment) => {
|
|
|
2186
2402
|
* [§5.3.8](https://drafts.csswg.org/css-syntax/#parse-declaration).
|
|
2187
2403
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2188
2404
|
* @param {number=} pos start position (string input only)
|
|
2189
|
-
* @param {
|
|
2405
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2190
2406
|
* @returns {Declaration | undefined} the parsed declaration, or undefined
|
|
2191
2407
|
*/
|
|
2192
|
-
const parseADeclaration = (input, pos,
|
|
2408
|
+
const parseADeclaration = (input, pos = 0, options = {}) => {
|
|
2193
2409
|
// 1. Normalize input, and set input to the result.
|
|
2194
|
-
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2410
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2195
2411
|
useObjectBackend(ts.locConverter);
|
|
2196
2412
|
// 2. Discard whitespace from input.
|
|
2197
2413
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
@@ -2203,10 +2419,10 @@ const parseADeclaration = (input, pos, comment) => {
|
|
|
2203
2419
|
* Parse a component value, CSS Syntax Level 3 [§5.3.9](https://drafts.csswg.org/css-syntax/#parse-component-value) — strict entry point that consumes one value and returns `undefined` if non-whitespace input trails (use `consumeAComponentValue` for "one value, ignore the rest").
|
|
2204
2420
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2205
2421
|
* @param {number=} pos start position (string input only)
|
|
2206
|
-
* @param {
|
|
2422
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2207
2423
|
* @returns {ComponentValue | undefined} the parsed component value, or `undefined` on empty / trailing-garbage input
|
|
2208
2424
|
*/
|
|
2209
|
-
const parseAComponentValue = (input, pos, options = {}) => {
|
|
2425
|
+
const parseAComponentValue = (input, pos = 0, options = {}) => {
|
|
2210
2426
|
// 1. Normalize input, and set input to the result.
|
|
2211
2427
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2212
2428
|
useObjectBackend(ts.locConverter);
|
|
@@ -2228,27 +2444,28 @@ const parseAComponentValue = (input, pos, options = {}) => {
|
|
|
2228
2444
|
* [§5.3.10](https://drafts.csswg.org/css-syntax/#parse-list-of-components).
|
|
2229
2445
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2230
2446
|
* @param {number=} pos start position (string input only)
|
|
2231
|
-
* @param {
|
|
2447
|
+
* @param {ParseOptions=} options comment callback
|
|
2232
2448
|
* @returns {ComponentValue[]} component values
|
|
2233
2449
|
*/
|
|
2234
|
-
const parseAListOfComponentValues = (input, pos, options = {}) => {
|
|
2450
|
+
const parseAListOfComponentValues = (input, pos = 0, options = {}) => {
|
|
2235
2451
|
// 1. Normalize input, and set input to the result.
|
|
2236
2452
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2237
2453
|
useObjectBackend(ts.locConverter);
|
|
2238
2454
|
// 2. Consume a list of component values from input, and return the result.
|
|
2239
|
-
|
|
2455
|
+
// (`null` needs `bailOnCurly`, which is not passed here.)
|
|
2456
|
+
return /** @type {ComponentValue[]} */ (consumeAListOfComponentValues(ts));
|
|
2240
2457
|
};
|
|
2241
2458
|
|
|
2242
2459
|
/**
|
|
2243
2460
|
* Parse a comma-separated list of component values, CSS Syntax Level 3 [§5.3.11](https://drafts.csswg.org/css-syntax/#parse-comma-list) — consumes one `<comma-token>`-stopped group of component values per iteration until EOF.
|
|
2244
2461
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2245
2462
|
* @param {number=} pos start position (string input only)
|
|
2246
|
-
* @param {
|
|
2463
|
+
* @param {ParseOptions=} options comment callback
|
|
2247
2464
|
* @returns {ComponentValue[][]} comma-separated groups of component values
|
|
2248
2465
|
*/
|
|
2249
2466
|
const parseACommaSeparatedListOfComponentValues = (
|
|
2250
2467
|
input,
|
|
2251
|
-
pos,
|
|
2468
|
+
pos = 0,
|
|
2252
2469
|
options = {}
|
|
2253
2470
|
) => {
|
|
2254
2471
|
// 1. Normalize input, and set input to the result.
|
|
@@ -2260,7 +2477,11 @@ const parseACommaSeparatedListOfComponentValues = (
|
|
|
2260
2477
|
// 3. While input is not empty:
|
|
2261
2478
|
while (ts.next().type !== TT_EOF) {
|
|
2262
2479
|
// 3.1. Consume a list of component values from input, with <comma-token> as the stop token, and append the result to groups.
|
|
2263
|
-
groups.push(
|
|
2480
|
+
groups.push(
|
|
2481
|
+
/** @type {ComponentValue[]} */ (
|
|
2482
|
+
consumeAListOfComponentValues(ts, TT_COMMA)
|
|
2483
|
+
)
|
|
2484
|
+
);
|
|
2264
2485
|
// 3.2 Discard a token from input.
|
|
2265
2486
|
ts.discard();
|
|
2266
2487
|
}
|
|
@@ -2335,16 +2556,12 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2335
2556
|
// Consume a token from input, and let rule be a new at-rule with its name set to the returned token’s value, its prelude initially set to an empty list, and no declarations or child rules.
|
|
2336
2557
|
const head = ts.consume();
|
|
2337
2558
|
const rule = /** @type {AtRule} */ (
|
|
2338
|
-
|
|
2339
|
-
);
|
|
2340
|
-
_setName(
|
|
2341
|
-
rule,
|
|
2342
|
-
ts.input.slice(head.start + 1, head.end),
|
|
2343
|
-
head.start,
|
|
2344
|
-
head.end
|
|
2559
|
+
_makeContainer(T_AT_RULE, head.start, head.end)
|
|
2345
2560
|
);
|
|
2346
|
-
|
|
2347
|
-
_setPrelude
|
|
2561
|
+
_setName(rule, head.start, head.end);
|
|
2562
|
+
// Sealed (`_setPrelude`) at each return — the SoA backend consumes the
|
|
2563
|
+
// scratch array when sealing, so it must be complete by then.
|
|
2564
|
+
const prelude = _soaActive ? _soaList() : [];
|
|
2348
2565
|
// declarations / childRules / blockStart (-1) / blockEnd (-1) keep their
|
|
2349
2566
|
// defaults (no block: the `;` / EOF / nested-`}` at-rule forms).
|
|
2350
2567
|
|
|
@@ -2362,6 +2579,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2362
2579
|
// Discard a token from input. If rule is valid in the current context, return it; otherwise return nothing.
|
|
2363
2580
|
if (t.type === TT_SEMICOLON || t.type === TT_EOF) {
|
|
2364
2581
|
ts.discard();
|
|
2582
|
+
_setPrelude(rule, prelude);
|
|
2365
2583
|
_setEnd(rule, t.start);
|
|
2366
2584
|
return rule;
|
|
2367
2585
|
}
|
|
@@ -2370,6 +2588,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2370
2588
|
// Otherwise, consume a token and append the result to rule’s prelude.
|
|
2371
2589
|
else if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2372
2590
|
if (nested) {
|
|
2591
|
+
_setPrelude(rule, prelude);
|
|
2373
2592
|
_setEnd(rule, t.start);
|
|
2374
2593
|
return rule;
|
|
2375
2594
|
}
|
|
@@ -2380,6 +2599,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2380
2599
|
// <{-token>
|
|
2381
2600
|
// Consume a block from input, and assign the result to rule's declarations and child rules.
|
|
2382
2601
|
else if (t.type === TT_LEFT_CURLY_BRACKET) {
|
|
2602
|
+
_setPrelude(rule, prelude);
|
|
2383
2603
|
const block = consumeABlock(ts);
|
|
2384
2604
|
_setBody(rule, block.decls, block.rules);
|
|
2385
2605
|
_setBlock(rule, block.blockStart, block.blockEnd);
|
|
@@ -2392,7 +2612,10 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2392
2612
|
const node = consumeAComponentValue(ts, t);
|
|
2393
2613
|
if (!skip) {
|
|
2394
2614
|
prelude.push(node);
|
|
2395
|
-
} else if (
|
|
2615
|
+
} else if (
|
|
2616
|
+
_nodeTypeOf(node) === T_FUNCTION ||
|
|
2617
|
+
_nodeTypeOf(node) === T_URL
|
|
2618
|
+
) {
|
|
2396
2619
|
prelude.push(node);
|
|
2397
2620
|
}
|
|
2398
2621
|
}
|
|
@@ -2423,10 +2646,11 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2423
2646
|
const start = ts.next().start;
|
|
2424
2647
|
// Let rule be a new qualified rule with its prelude, declarations, and child rules all initially set to empty lists.
|
|
2425
2648
|
const rule = /** @type {QualifiedRule} */ (
|
|
2426
|
-
|
|
2649
|
+
_makeContainer(T_QUALIFIED_RULE, start, start)
|
|
2427
2650
|
);
|
|
2428
|
-
|
|
2429
|
-
|
|
2651
|
+
// Sealed (`_setPrelude`) at the `{` exit — the only path that returns the
|
|
2652
|
+
// rule; the parse-error exits abandon the scratch unsealed.
|
|
2653
|
+
const prelude = _soaActive ? _soaList() : [];
|
|
2430
2654
|
// declarations / childRules / blockStart (-1) / blockEnd (-1) keep their
|
|
2431
2655
|
// defaults (no block until a `{` is reached).
|
|
2432
2656
|
|
|
@@ -2471,14 +2695,14 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2471
2695
|
/* istanbul ignore next -- @preserve: leading whitespace is discarded before the rule, so the prelude never starts with it */
|
|
2472
2696
|
while (
|
|
2473
2697
|
firstIdx < prelude.length &&
|
|
2474
|
-
|
|
2698
|
+
_nodeTypeOf(prelude[firstIdx]) === T_WHITESPACE
|
|
2475
2699
|
) {
|
|
2476
2700
|
firstIdx++;
|
|
2477
2701
|
}
|
|
2478
2702
|
let secondIdx = firstIdx + 1;
|
|
2479
2703
|
while (
|
|
2480
2704
|
secondIdx < prelude.length &&
|
|
2481
|
-
|
|
2705
|
+
_nodeTypeOf(prelude[secondIdx]) === T_WHITESPACE
|
|
2482
2706
|
) {
|
|
2483
2707
|
secondIdx++;
|
|
2484
2708
|
}
|
|
@@ -2487,12 +2711,12 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2487
2711
|
}
|
|
2488
2712
|
if (
|
|
2489
2713
|
first &&
|
|
2490
|
-
|
|
2714
|
+
_nodeTypeOf(first) === T_IDENT &&
|
|
2491
2715
|
// Test the source bytes directly — avoids forcing the lazy `value`
|
|
2492
2716
|
// slice just to check the `--` custom-property prefix.
|
|
2493
|
-
ts.input.startsWith("--",
|
|
2717
|
+
ts.input.startsWith("--", _nodeStartOf(first)) &&
|
|
2494
2718
|
second &&
|
|
2495
|
-
|
|
2719
|
+
_nodeTypeOf(second) === T_COLON
|
|
2496
2720
|
) {
|
|
2497
2721
|
/* istanbul ignore if -- @preserve: when nested, `declarationStartLikely` routes every `--x:` to consumeADeclaration (which accepts custom properties), so this fallthrough is unreachable */
|
|
2498
2722
|
if (nested) {
|
|
@@ -2502,6 +2726,7 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2502
2726
|
}
|
|
2503
2727
|
return undefined;
|
|
2504
2728
|
}
|
|
2729
|
+
_setPrelude(rule, prelude);
|
|
2505
2730
|
const block = consumeABlock(ts);
|
|
2506
2731
|
_setBody(rule, block.decls, block.rules);
|
|
2507
2732
|
_setBlock(rule, block.blockStart, block.blockEnd);
|
|
@@ -2516,7 +2741,7 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2516
2741
|
// Keep only url-bearing nodes (url tokens, or functions that may hold a
|
|
2517
2742
|
// url like `:unknown(url(x))`) so the url visitor still rewrites them;
|
|
2518
2743
|
// other selector tokens have no non-modules consumer, so drop them.
|
|
2519
|
-
const ty =
|
|
2744
|
+
const ty = _nodeTypeOf(node);
|
|
2520
2745
|
if (ty === T_FUNCTION || ty === T_URL) prelude.push(node);
|
|
2521
2746
|
// Track the first two non-whitespace tokens for the disambiguation above.
|
|
2522
2747
|
if (t.type !== TT_WHITESPACE) {
|
|
@@ -2601,8 +2826,12 @@ const declarationStartLikely = (ts) => {
|
|
|
2601
2826
|
const consumeABlocksContents = (ts, onNode) => {
|
|
2602
2827
|
/** @type {Declaration[]} */
|
|
2603
2828
|
const decls = [];
|
|
2604
|
-
|
|
2605
|
-
|
|
2829
|
+
// Child rules are the common empty case (most rules carry only declarations),
|
|
2830
|
+
// so `rules` is allocated lazily and returned as the shared frozen
|
|
2831
|
+
// `_EMPTY_LIST` when nothing was appended — one fewer array per rule. `decls`
|
|
2832
|
+
// stays eager so the hot declaration append keeps a branch-free `push`.
|
|
2833
|
+
/** @type {Rule[] | null} */
|
|
2834
|
+
let rules = null;
|
|
2606
2835
|
|
|
2607
2836
|
// Process input:
|
|
2608
2837
|
for (;;) {
|
|
@@ -2616,7 +2845,7 @@ const consumeABlocksContents = (ts, onNode) => {
|
|
|
2616
2845
|
// <EOF-token> / <}-token>
|
|
2617
2846
|
// Return decls and rules.
|
|
2618
2847
|
else if (t.type === TT_EOF || t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2619
|
-
return { decls, rules };
|
|
2848
|
+
return { decls, rules: rules || _EMPTY_LIST };
|
|
2620
2849
|
}
|
|
2621
2850
|
// <at-keyword-token>
|
|
2622
2851
|
// Consume an at-rule from input, with nested set to true. If a rule was returned, append it to rules.
|
|
@@ -2624,7 +2853,7 @@ const consumeABlocksContents = (ts, onNode) => {
|
|
|
2624
2853
|
const atRule = consumeAnAtRule(ts, true);
|
|
2625
2854
|
if (atRule) {
|
|
2626
2855
|
if (onNode) onNode(atRule);
|
|
2627
|
-
else rules.push(atRule);
|
|
2856
|
+
else (rules || (rules = [])).push(atRule);
|
|
2628
2857
|
}
|
|
2629
2858
|
}
|
|
2630
2859
|
// anything else
|
|
@@ -2647,7 +2876,7 @@ const consumeABlocksContents = (ts, onNode) => {
|
|
|
2647
2876
|
const rule = consumeAQualifiedRule(ts, TT_SEMICOLON, true);
|
|
2648
2877
|
if (rule) {
|
|
2649
2878
|
if (onNode) onNode(rule);
|
|
2650
|
-
else rules.push(rule);
|
|
2879
|
+
else (rules || (rules = [])).push(rule);
|
|
2651
2880
|
}
|
|
2652
2881
|
}
|
|
2653
2882
|
}
|
|
@@ -2695,14 +2924,14 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2695
2924
|
// name "" / nameStart / nameEnd (= start) / important (false) keep their
|
|
2696
2925
|
// `Container` defaults; `value` is set unconditionally at step 5 below.
|
|
2697
2926
|
const decl = /** @type {Declaration} */ (
|
|
2698
|
-
|
|
2927
|
+
_makeContainer(T_DECLARATION, start, start)
|
|
2699
2928
|
);
|
|
2700
2929
|
|
|
2701
2930
|
// 1. If the next token is an <ident-token>, consume a token from input and set decl's name to the returned token's value.
|
|
2702
2931
|
// Otherwise, consume the remnants of a bad declaration from input, with nested, and return nothing.
|
|
2703
2932
|
if (ts.next().type === TT_IDENTIFIER) {
|
|
2704
2933
|
const head = ts.consume();
|
|
2705
|
-
_setName(decl,
|
|
2934
|
+
_setName(decl, head.start, head.end);
|
|
2706
2935
|
} else {
|
|
2707
2936
|
consumeTheRemnantsOfABadDeclaration(ts, nested);
|
|
2708
2937
|
return undefined;
|
|
@@ -2723,23 +2952,39 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2723
2952
|
// 4. Discard whitespace from input.
|
|
2724
2953
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
2725
2954
|
|
|
2955
|
+
// Step 8's custom-property test, computed early so the value parse can bail.
|
|
2956
|
+
const isCustomProperty = input.startsWith("--", start);
|
|
2957
|
+
|
|
2726
2958
|
// 5. Consume a list of component values from input, with nested, and with <semicolon-token> as the stop token, and set decl's value to the result.
|
|
2727
|
-
|
|
2728
|
-
|
|
2959
|
+
// A nested non-custom declaration bails on a top-level `{` — step 8 would
|
|
2960
|
+
// reject it and the caller restores its mark, so parsing the block (the
|
|
2961
|
+
// entire nested-rule body, re-parsed as a qualified rule after the
|
|
2962
|
+
// restore) would be pure waste.
|
|
2963
|
+
const value = consumeAListOfComponentValues(
|
|
2964
|
+
ts,
|
|
2965
|
+
TT_SEMICOLON,
|
|
2966
|
+
nested,
|
|
2967
|
+
nested && !isCustomProperty
|
|
2968
|
+
);
|
|
2969
|
+
if (value === null) return undefined;
|
|
2970
|
+
// `_setValue` waits until step 9: steps 6-8 still trim / scan the scratch,
|
|
2971
|
+
// and the SoA backend consumes it when sealing.
|
|
2729
2972
|
_setEnd(decl, ts.next().start);
|
|
2730
2973
|
|
|
2731
2974
|
// 6. If the last two non-<whitespace-token>s in decl's value are a <delim-token> with the value "!" followed by an <ident-token> with a value that is an ASCII case-insensitive match for "important", remove them from decl's value and set decl's important flag.
|
|
2732
2975
|
{
|
|
2733
2976
|
let last = value.length - 1;
|
|
2734
|
-
while (last >= 0 &&
|
|
2977
|
+
while (last >= 0 && _nodeTypeOf(value[last]) === T_WHITESPACE) last--;
|
|
2735
2978
|
let prev = last - 1;
|
|
2736
|
-
while (prev >= 0 &&
|
|
2979
|
+
while (prev >= 0 && _nodeTypeOf(value[prev]) === T_WHITESPACE) prev--;
|
|
2980
|
+
// `!` delim first: it's almost always absent, and `_nodeValueOf` allocates
|
|
2981
|
+
// a slice — this order pays it only for genuine `!important` candidates.
|
|
2737
2982
|
if (
|
|
2738
2983
|
prev >= 0 &&
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2984
|
+
_nodeTypeOf(value[prev]) === T_DELIM &&
|
|
2985
|
+
input.charCodeAt(_nodeStartOf(value[prev])) === CC_EXCLAMATION &&
|
|
2986
|
+
_nodeTypeOf(value[last]) === T_IDENT &&
|
|
2987
|
+
equalsLowerCase(_nodeValueOf(value[last]), "important")
|
|
2743
2988
|
) {
|
|
2744
2989
|
_setImportant(decl);
|
|
2745
2990
|
value.length = prev;
|
|
@@ -2747,7 +2992,10 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2747
2992
|
}
|
|
2748
2993
|
|
|
2749
2994
|
// 7. While the last item in decl's value is a <whitespace-token>, remove that token.
|
|
2750
|
-
while (
|
|
2995
|
+
while (
|
|
2996
|
+
value.length > 0 &&
|
|
2997
|
+
_nodeTypeOf(value[value.length - 1]) === T_WHITESPACE
|
|
2998
|
+
) {
|
|
2751
2999
|
value.pop();
|
|
2752
3000
|
}
|
|
2753
3001
|
|
|
@@ -2755,17 +3003,17 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2755
3003
|
// Otherwise, if decl's value contains a top-level simple block with an associated token of <{-token>, return nothing.
|
|
2756
3004
|
// (That is, a top-level {}-block is only allowed as the entire value of a non-custom property — for CSS Nesting, `consumeABlocksContents`'s `mark` / `restore a mark` will retry the input as a qualified rule.)
|
|
2757
3005
|
// Otherwise, accept the declaration. (The spec also checks "contains any non-whitespace-tokens at the top level" → return nothing; we keep empty-value declarations because callers — e.g. `@value name:;` — rely on them.)
|
|
2758
|
-
const isCustomProperty = input.startsWith("--", start);
|
|
2759
3006
|
if (!isCustomProperty) {
|
|
2760
3007
|
for (let i = 0; i < value.length; i++) {
|
|
2761
3008
|
const v = value[i];
|
|
2762
|
-
if (
|
|
3009
|
+
if (_nodeTypeOf(v) === T_SIMPLE_BLOCK && _nodeTokenOf(v) === "{") {
|
|
2763
3010
|
return undefined;
|
|
2764
3011
|
}
|
|
2765
3012
|
}
|
|
2766
3013
|
}
|
|
2767
3014
|
|
|
2768
3015
|
// 9. Return decl.
|
|
3016
|
+
_setValue(decl, value);
|
|
2769
3017
|
return decl;
|
|
2770
3018
|
};
|
|
2771
3019
|
|
|
@@ -2774,11 +3022,16 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2774
3022
|
* @param {TokenStream} ts token stream
|
|
2775
3023
|
* @param {number=} stopToken token type that terminates the list (left unconsumed)
|
|
2776
3024
|
* @param {boolean=} nested true inside a `{}` block — a top-level `}` ends the list (left unconsumed)
|
|
2777
|
-
* @
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
3025
|
+
* @param {boolean=} bailOnCurly abort with `null` on a top-level `{` (left unconsumed) — for callers that would reject the list anyway (consume-a-declaration step 8) and restore a mark
|
|
3026
|
+
* @returns {ComponentValue[] | null} consumed component values, or `null` when `bailOnCurly` hit
|
|
3027
|
+
*/
|
|
3028
|
+
const consumeAListOfComponentValues = (
|
|
3029
|
+
ts,
|
|
3030
|
+
stopToken,
|
|
3031
|
+
nested = false,
|
|
3032
|
+
bailOnCurly = false
|
|
3033
|
+
) => {
|
|
3034
|
+
const values = /** @type {ComponentValue[]} */ (_soaActive ? _soaList() : []);
|
|
2782
3035
|
// Process input
|
|
2783
3036
|
for (;;) {
|
|
2784
3037
|
const t = ts.next();
|
|
@@ -2797,9 +3050,14 @@ const consumeAListOfComponentValues = (ts, stopToken, nested = false) => {
|
|
|
2797
3050
|
const closer = consumeATokenAsNode(ts);
|
|
2798
3051
|
// Keep unless the type is explicitly marked skip (1); an out-of-range
|
|
2799
3052
|
// lookup on a short `skip.types` yields `undefined`, which must not drop.
|
|
2800
|
-
if (!_skipActive || _skipTypes[
|
|
3053
|
+
if (!_skipActive || _skipTypes[_nodeTypeOf(closer)] !== 1) {
|
|
3054
|
+
values.push(closer);
|
|
3055
|
+
}
|
|
2801
3056
|
continue;
|
|
2802
3057
|
}
|
|
3058
|
+
// A top-level `{` dooms the list for a bailing caller — stop before the
|
|
3059
|
+
// whole block is parsed only to be thrown away on the caller's restore.
|
|
3060
|
+
if (bailOnCurly && t.type === TT_LEFT_CURLY_BRACKET) return null;
|
|
2803
3061
|
// anything else
|
|
2804
3062
|
// Consume a component value from input, and append the result to values.
|
|
2805
3063
|
// Skipped leaf types short-circuit before materializing: no SoA slot is
|
|
@@ -2815,7 +3073,7 @@ const consumeAListOfComponentValues = (ts, stopToken, nested = false) => {
|
|
|
2815
3073
|
continue;
|
|
2816
3074
|
}
|
|
2817
3075
|
const node = consumeAComponentValue(ts, t);
|
|
2818
|
-
if (!_skipActive || _skipTypes[
|
|
3076
|
+
if (!_skipActive || _skipTypes[_nodeTypeOf(node)] !== 1) values.push(node);
|
|
2819
3077
|
}
|
|
2820
3078
|
};
|
|
2821
3079
|
|
|
@@ -2861,11 +3119,11 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2861
3119
|
|
|
2862
3120
|
// Let block be a new simple block with its associated token set to the next token and with its value initially set to an empty list.
|
|
2863
3121
|
const block = /** @type {SimpleBlock} */ (
|
|
2864
|
-
|
|
3122
|
+
_makeContainer(T_SIMPLE_BLOCK, open.start, open.end)
|
|
2865
3123
|
);
|
|
2866
3124
|
_setToken(block, token);
|
|
2867
|
-
|
|
2868
|
-
|
|
3125
|
+
// Sealed (`_setValue`) at the return, once complete.
|
|
3126
|
+
const val = _soaActive ? _soaList() : [];
|
|
2869
3127
|
|
|
2870
3128
|
// Discard a token from input.
|
|
2871
3129
|
ts.discard();
|
|
@@ -2879,6 +3137,7 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2879
3137
|
// Discard a token from input. Return block.
|
|
2880
3138
|
if (t.type === TT_EOF || t.type === ending) {
|
|
2881
3139
|
ts.discard();
|
|
3140
|
+
_setValue(block, val);
|
|
2882
3141
|
_setEnd(block, t.end);
|
|
2883
3142
|
return block;
|
|
2884
3143
|
}
|
|
@@ -2895,16 +3154,15 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2895
3154
|
* @returns {FunctionNode | undefined} the consumed function node
|
|
2896
3155
|
*/
|
|
2897
3156
|
const consumeAFunction = (ts) => {
|
|
2898
|
-
const { input } = ts;
|
|
2899
3157
|
// Assert (spec): the next token is a <function-token>.
|
|
2900
3158
|
// Consume a token from input, and let function be a new function with its name equal the returned token’s value, and a value set to an empty list.
|
|
2901
3159
|
const tFn = ts.consume();
|
|
2902
3160
|
const fn = /** @type {FunctionNode} */ (
|
|
2903
|
-
|
|
3161
|
+
_makeContainer(T_FUNCTION, tFn.start, tFn.end)
|
|
2904
3162
|
);
|
|
2905
|
-
_setName(fn,
|
|
2906
|
-
|
|
2907
|
-
|
|
3163
|
+
_setName(fn, tFn.start, tFn.end - 1);
|
|
3164
|
+
// Sealed (`_setValue`) at the return, once complete.
|
|
3165
|
+
const val = _soaActive ? _soaList() : [];
|
|
2908
3166
|
|
|
2909
3167
|
// Process input
|
|
2910
3168
|
for (;;) {
|
|
@@ -2915,6 +3173,7 @@ const consumeAFunction = (ts) => {
|
|
|
2915
3173
|
// <)-token>
|
|
2916
3174
|
// Discard a token from input. Return function.
|
|
2917
3175
|
ts.discard();
|
|
3176
|
+
_setValue(fn, val);
|
|
2918
3177
|
_setEnd(fn, t.end);
|
|
2919
3178
|
return fn;
|
|
2920
3179
|
}
|
|
@@ -2933,7 +3192,7 @@ const consumeAFunction = (ts) => {
|
|
|
2933
3192
|
continue;
|
|
2934
3193
|
}
|
|
2935
3194
|
const node = consumeAComponentValue(ts, t);
|
|
2936
|
-
if (!_skipActive || _skipTypes[
|
|
3195
|
+
if (!_skipActive || _skipTypes[_nodeTypeOf(node)] !== 1) val.push(node);
|
|
2937
3196
|
}
|
|
2938
3197
|
};
|
|
2939
3198
|
|
|
@@ -2991,13 +3250,18 @@ const _escapeIdentifier = (str) => {
|
|
|
2991
3250
|
}
|
|
2992
3251
|
output = lastFlush === 0 ? str : output + str.slice(lastFlush);
|
|
2993
3252
|
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
3253
|
+
// `-` and digits are class 0 (never escaped above), so testing `str`'s lead
|
|
3254
|
+
// char codes is equivalent to regexes over `output` — and keeps the common
|
|
3255
|
+
// nothing-to-do call regex-free.
|
|
3256
|
+
const first = str.charCodeAt(0);
|
|
3257
|
+
if (
|
|
3258
|
+
first === CC_HYPHEN_MINUS &&
|
|
3259
|
+
(str.charCodeAt(1) === CC_HYPHEN_MINUS || _isDigit(str.charCodeAt(1)))
|
|
3260
|
+
) {
|
|
2997
3261
|
output = `\\-${output.slice(1)}`;
|
|
2998
|
-
} else if (
|
|
3262
|
+
} else if (_isDigit(first)) {
|
|
2999
3263
|
// A leading digit becomes `\3<digit> `, another `\HEX ` run to clean up.
|
|
3000
|
-
output = `\\3${
|
|
3264
|
+
output = `\\3${str.charAt(0)} ${output.slice(1)}`;
|
|
3001
3265
|
needSpaceFix = true;
|
|
3002
3266
|
}
|
|
3003
3267
|
|
|
@@ -3031,7 +3295,6 @@ const _escapeIdentifier = (str) => {
|
|
|
3031
3295
|
*/
|
|
3032
3296
|
const gobbleHex = (str, start) => {
|
|
3033
3297
|
let hex = "";
|
|
3034
|
-
let spaceTerminated = false;
|
|
3035
3298
|
|
|
3036
3299
|
for (let i = 0; i < 6; i++) {
|
|
3037
3300
|
const code = str.charCodeAt(start + i);
|
|
@@ -3040,8 +3303,6 @@ const gobbleHex = (str, start) => {
|
|
|
3040
3303
|
(code >= 48 && code <= 57) ||
|
|
3041
3304
|
(code >= 65 && code <= 70) ||
|
|
3042
3305
|
(code >= 97 && code <= 102);
|
|
3043
|
-
// https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
|
|
3044
|
-
spaceTerminated = code === 32;
|
|
3045
3306
|
if (!valid) break;
|
|
3046
3307
|
// parseInt below is case-insensitive, so keep the original char.
|
|
3047
3308
|
hex += str[start + i];
|
|
@@ -3049,6 +3310,16 @@ const gobbleHex = (str, start) => {
|
|
|
3049
3310
|
|
|
3050
3311
|
if (hex.length === 0) return undefined;
|
|
3051
3312
|
|
|
3313
|
+
// One trailing whitespace terminates the escape, matching the tokenizer's
|
|
3314
|
+
// `_consumeAnEscapedCodePoint` — including after a full 6-digit escape, for
|
|
3315
|
+
// any CSS whitespace (not just space), plus the extra LF of a CRLF pair.
|
|
3316
|
+
// https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
|
|
3317
|
+
let consumed = hex.length;
|
|
3318
|
+
const trail = str.charCodeAt(start + hex.length);
|
|
3319
|
+
if (_isWhiteSpace(trail)) {
|
|
3320
|
+
consumed = consumeExtraNewline(trail, str, start + hex.length + 1) - start;
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3052
3323
|
const codePoint = Number.parseInt(hex, 16);
|
|
3053
3324
|
const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff;
|
|
3054
3325
|
|
|
@@ -3056,13 +3327,10 @@ const gobbleHex = (str, start) => {
|
|
|
3056
3327
|
// "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
|
|
3057
3328
|
// https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
|
|
3058
3329
|
if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) {
|
|
3059
|
-
return ["�",
|
|
3330
|
+
return ["�", consumed];
|
|
3060
3331
|
}
|
|
3061
3332
|
|
|
3062
|
-
return [
|
|
3063
|
-
String.fromCodePoint(codePoint),
|
|
3064
|
-
hex.length + (spaceTerminated ? 1 : 0)
|
|
3065
|
-
];
|
|
3333
|
+
return [String.fromCodePoint(codePoint), consumed];
|
|
3066
3334
|
};
|
|
3067
3335
|
|
|
3068
3336
|
/**
|
|
@@ -3108,6 +3376,67 @@ const _unescapeIdentifier = (str) => {
|
|
|
3108
3376
|
const escapeIdentifier = makeCacheable(_escapeIdentifier);
|
|
3109
3377
|
const unescapeIdentifier = makeCacheable(_unescapeIdentifier);
|
|
3110
3378
|
|
|
3379
|
+
// A url-token / url-string value's escaped newlines (`url("im\<newline>g.png")`).
|
|
3380
|
+
const STRING_MULTILINE = /\\[\n\r\f]/g;
|
|
3381
|
+
// Leading / trailing CSS whitespace inside a quoted url value.
|
|
3382
|
+
const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g;
|
|
3383
|
+
// One CSS escape: `\` + up to 6 hex digits (+ optional whitespace) or any char.
|
|
3384
|
+
const UNESCAPE = /\\([0-9a-f]{1,6}[ \t\n\r\f]?|[\s\S])/gi;
|
|
3385
|
+
|
|
3386
|
+
/**
|
|
3387
|
+
* Normalize a url value (a url-token's content or a url string's body) into
|
|
3388
|
+
* the form requests are resolved from: escaped newlines removed (string form),
|
|
3389
|
+
* edge whitespace trimmed, CSS escapes and percent-encoding decoded
|
|
3390
|
+
* (`data:` URIs excepted).
|
|
3391
|
+
* @param {string} str url string
|
|
3392
|
+
* @param {boolean} isString is url wrapped in quotes
|
|
3393
|
+
* @returns {string} normalized url
|
|
3394
|
+
*/
|
|
3395
|
+
const normalizeUrl = (str, isString) => {
|
|
3396
|
+
// Fast paths: skip the regex engine for the common URL with no escape and
|
|
3397
|
+
// no edge whitespace (e.g. `./img.png`). Each guard is equivalent to the
|
|
3398
|
+
// regex being a no-op.
|
|
3399
|
+
// Remove escaped newlines from a string-token url like `url("im\<newline>g.png")`.
|
|
3400
|
+
if (isString && str.includes("\\")) {
|
|
3401
|
+
str = str.replace(STRING_MULTILINE, "");
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
// Remove unnecessary spaces from `url(" img.png ")`
|
|
3405
|
+
if (
|
|
3406
|
+
str.length !== 0 &&
|
|
3407
|
+
(_isWhiteSpace(str.charCodeAt(0)) ||
|
|
3408
|
+
_isWhiteSpace(str.charCodeAt(str.length - 1)))
|
|
3409
|
+
) {
|
|
3410
|
+
str = str.replace(TRIM_WHITE_SPACES, "");
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
// Unescape
|
|
3414
|
+
if (str.includes("\\")) {
|
|
3415
|
+
str = str.replace(UNESCAPE, (match) => {
|
|
3416
|
+
if (match.length > 2) {
|
|
3417
|
+
return String.fromCharCode(Number.parseInt(match.slice(1).trim(), 16));
|
|
3418
|
+
}
|
|
3419
|
+
return match[1];
|
|
3420
|
+
});
|
|
3421
|
+
}
|
|
3422
|
+
|
|
3423
|
+
// Char-code gate so the dominant non-`data:` url skips the regex test.
|
|
3424
|
+
if ((str.charCodeAt(0) | 0x20) === CC_LOWER_D && /^data:/i.test(str)) {
|
|
3425
|
+
return str;
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
if (str.includes("%")) {
|
|
3429
|
+
// Convert `url('%2E/img.png')` -> `url('./img.png')`
|
|
3430
|
+
try {
|
|
3431
|
+
str = decodeURIComponent(str);
|
|
3432
|
+
} catch (_err) {
|
|
3433
|
+
// Ignore
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
return str;
|
|
3438
|
+
};
|
|
3439
|
+
|
|
3111
3440
|
// CSS-typed views over the generic visitor machinery (`util/SourceProcessor`),
|
|
3112
3441
|
// re-exported so consumers keep importing them from this module.
|
|
3113
3442
|
/**
|
|
@@ -3151,6 +3480,137 @@ const TOP_LEVEL_CONSUMERS = {
|
|
|
3151
3480
|
* @property {boolean=} atRulePrelude drop at-rule preludes — the at-rule and its block are still produced (default false)
|
|
3152
3481
|
*/
|
|
3153
3482
|
|
|
3483
|
+
// Per-parse walk state in module slots (same pattern as `_skip*`) so the walk
|
|
3484
|
+
// functions below are module-level constants: one function identity across
|
|
3485
|
+
// parses keeps the recursive per-node call sites monomorphic and drops the
|
|
3486
|
+
// per-parse closure allocations.
|
|
3487
|
+
/** @typedef {import("../util/SourceProcessor").CompiledVisitorBucket<CssPath>} CompiledVisitorBucket */
|
|
3488
|
+
/** @type {CompiledVisitorMap} */
|
|
3489
|
+
let _visitors = /** @type {CompiledVisitorMap} */ (/** @type {unknown} */ ([]));
|
|
3490
|
+
let _recurseBlocks = true;
|
|
3491
|
+
/** @type {CompiledVisitorBucket | undefined} */
|
|
3492
|
+
let _commentBucket;
|
|
3493
|
+
|
|
3494
|
+
// Comments reach the visitor map through `NodeType.Comment` instead of a
|
|
3495
|
+
// side callback. They fire during tokenization — in source order among
|
|
3496
|
+
// comments, not interleaved with the node walk — on a transient SoA node so
|
|
3497
|
+
// `A.start`/`end`/`loc`/`source` work. No comment visitor → no callback →
|
|
3498
|
+
// the tokenizer skips comments with zero overhead.
|
|
3499
|
+
/** @type {(input: string, start: number, end: number) => number} */
|
|
3500
|
+
const _grammarOnComment = (_input, start, end) => {
|
|
3501
|
+
const node = _soaAllocNode(T_COMMENT, start, end);
|
|
3502
|
+
_currentNode = node;
|
|
3503
|
+
_currentParent = null;
|
|
3504
|
+
const bucket = /** @type {CompiledVisitorBucket} */ (_commentBucket);
|
|
3505
|
+
const e = bucket.enter;
|
|
3506
|
+
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3507
|
+
const x = bucket.exit;
|
|
3508
|
+
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3509
|
+
return end;
|
|
3510
|
+
};
|
|
3511
|
+
|
|
3512
|
+
/**
|
|
3513
|
+
* Walk a component-value subtree; children are already materialized. Fetches
|
|
3514
|
+
* the node's visitor bucket once (reused for enter + exit) and uses index
|
|
3515
|
+
* loops — `for…of` would allocate an iterator per node on this hot path.
|
|
3516
|
+
* @param {Node} node component-value root
|
|
3517
|
+
* @param {Node | null} parent enclosing node
|
|
3518
|
+
*/
|
|
3519
|
+
const _walkValue = (node, parent) => {
|
|
3520
|
+
const ty = _soaTypes[_nodeIndex(node)];
|
|
3521
|
+
const b = _visitors[ty];
|
|
3522
|
+
let skip = false;
|
|
3523
|
+
if (b !== undefined && b.enter.length !== 0) {
|
|
3524
|
+
_walkSkip = false;
|
|
3525
|
+
_currentNode = node;
|
|
3526
|
+
_currentParent = parent;
|
|
3527
|
+
const e = b.enter;
|
|
3528
|
+
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3529
|
+
skip = _walkSkip;
|
|
3530
|
+
_walkSkip = false;
|
|
3531
|
+
}
|
|
3532
|
+
if (!skip && (ty === T_FUNCTION || ty === T_SIMPLE_BLOCK)) {
|
|
3533
|
+
const i0 = _nodeIndex(node);
|
|
3534
|
+
const vs = _soaListStarts[i0];
|
|
3535
|
+
const ve = vs + _soaListLens[i0];
|
|
3536
|
+
for (let i = vs; i < ve; i++) _walkValue(_nodeRef(_soaFlat[i]), node);
|
|
3537
|
+
}
|
|
3538
|
+
if (b !== undefined) {
|
|
3539
|
+
// Rebind: descending into children moved the path.
|
|
3540
|
+
_currentNode = node;
|
|
3541
|
+
_currentParent = parent;
|
|
3542
|
+
const x = b.exit;
|
|
3543
|
+
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3544
|
+
}
|
|
3545
|
+
};
|
|
3546
|
+
|
|
3547
|
+
/**
|
|
3548
|
+
* Walk a structural subtree; an at-rule / qualified-rule's block was parsed
|
|
3549
|
+
* eagerly (§5.4.4), so its `value` holds the nested rules / declarations.
|
|
3550
|
+
* @param {Node} node structural-tree root
|
|
3551
|
+
* @param {Node | null} parent enclosing node
|
|
3552
|
+
*/
|
|
3553
|
+
const _walkRule = (node, parent) => {
|
|
3554
|
+
const i0 = _nodeIndex(node);
|
|
3555
|
+
const ty = _soaTypes[i0];
|
|
3556
|
+
const b = _visitors[ty];
|
|
3557
|
+
let skip = false;
|
|
3558
|
+
if (b !== undefined && b.enter.length !== 0) {
|
|
3559
|
+
_walkSkip = false;
|
|
3560
|
+
_currentNode = node;
|
|
3561
|
+
_currentParent = parent;
|
|
3562
|
+
const e = b.enter;
|
|
3563
|
+
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3564
|
+
skip = _walkSkip;
|
|
3565
|
+
_walkSkip = false;
|
|
3566
|
+
}
|
|
3567
|
+
if (!skip) {
|
|
3568
|
+
if (ty === T_AT_RULE || ty === T_QUALIFIED_RULE) {
|
|
3569
|
+
const ps = _soaListStarts[i0];
|
|
3570
|
+
const pe = ps + _soaListLens[i0];
|
|
3571
|
+
for (let i = ps; i < pe; i++) _walkValue(_nodeRef(_soaFlat[i]), node);
|
|
3572
|
+
if (_recurseBlocks) {
|
|
3573
|
+
// Declarations then child rules — downstream consumers don't need them strictly interleaved in source order.
|
|
3574
|
+
const decls = _soaDeclarationLists[i0];
|
|
3575
|
+
if (decls) {
|
|
3576
|
+
for (let i = 0; i < decls.length; i++) _walkRule(decls[i], node);
|
|
3577
|
+
}
|
|
3578
|
+
const ch = _soaChildRuleLists[i0];
|
|
3579
|
+
if (ch) for (let i = 0; i < ch.length; i++) _walkRule(ch[i], node);
|
|
3580
|
+
}
|
|
3581
|
+
} else if (ty === T_DECLARATION) {
|
|
3582
|
+
const vs = _soaListStarts[i0];
|
|
3583
|
+
const ve = vs + _soaListLens[i0];
|
|
3584
|
+
for (let i = vs; i < ve; i++) _walkValue(_nodeRef(_soaFlat[i]), node);
|
|
3585
|
+
}
|
|
3586
|
+
}
|
|
3587
|
+
if (b !== undefined) {
|
|
3588
|
+
// Rebind: descending into children moved the path.
|
|
3589
|
+
_currentNode = node;
|
|
3590
|
+
_currentParent = parent;
|
|
3591
|
+
const x = b.exit;
|
|
3592
|
+
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3593
|
+
}
|
|
3594
|
+
};
|
|
3595
|
+
|
|
3596
|
+
/**
|
|
3597
|
+
* The `grammar` streaming sink: walk one top-level node, then recycle the SoA
|
|
3598
|
+
* buffers for the next.
|
|
3599
|
+
* @param {Rule | Declaration} node top-level node
|
|
3600
|
+
*/
|
|
3601
|
+
const _walkTopLevel = (node) => {
|
|
3602
|
+
_walkRule(node, null);
|
|
3603
|
+
if (_soaNodeCount > _soaPeak) _soaPeak = _soaNodeCount;
|
|
3604
|
+
if (_soaFlatTop > _soaFlatPeak) _soaFlatPeak = _soaFlatTop;
|
|
3605
|
+
_soaNodeCount = 0;
|
|
3606
|
+
_soaFlatTop = 0;
|
|
3607
|
+
};
|
|
3608
|
+
|
|
3609
|
+
// The SoA buffers grow to the largest single top-level rule ever parsed and
|
|
3610
|
+
// live at module level; above this capacity they are re-shrunk after a parse
|
|
3611
|
+
// so one pathological rule can't pin megabytes for the process lifetime.
|
|
3612
|
+
const _SOA_SHRINK_CAPACITY = 65536;
|
|
3613
|
+
|
|
3154
3614
|
/**
|
|
3155
3615
|
* The CSS `SourceProcessor` grammar: consume top-level rules one at a time
|
|
3156
3616
|
* (§5.4.1) and walk each immediately, firing `enter` / `exit` in source order
|
|
@@ -3169,130 +3629,58 @@ const grammar = (input, visitors, options) => {
|
|
|
3169
3629
|
_skipActive = _skipTypes !== _NO_SKIP_TYPES;
|
|
3170
3630
|
_skipSelectorPrelude = skip !== undefined && skip.selectorPrelude === true;
|
|
3171
3631
|
_skipAtRulePrelude = skip !== undefined && skip.atRulePrelude === true;
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
// side callback. They fire during tokenization — in source order among
|
|
3176
|
-
// comments, not interleaved with the node walk — on a transient SoA node so
|
|
3177
|
-
// `A.start`/`end`/`loc`/`source` work. No comment visitor → no callback →
|
|
3178
|
-
// the tokenizer skips comments with zero overhead.
|
|
3179
|
-
const commentBucket = visitors[T_COMMENT];
|
|
3180
|
-
const onComment =
|
|
3181
|
-
commentBucket === undefined
|
|
3182
|
-
? undefined
|
|
3183
|
-
: /** @type {(input: string, start: number, end: number) => number} */ (
|
|
3184
|
-
(_input, start, end) => {
|
|
3185
|
-
const node = _soaAlloc(T_COMMENT, start, end);
|
|
3186
|
-
_curNode = node;
|
|
3187
|
-
_curParent = null;
|
|
3188
|
-
const e = commentBucket.enter;
|
|
3189
|
-
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3190
|
-
const x = commentBucket.exit;
|
|
3191
|
-
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3192
|
-
return end;
|
|
3193
|
-
}
|
|
3194
|
-
);
|
|
3195
|
-
|
|
3196
|
-
/**
|
|
3197
|
-
* Walk a component-value subtree; children are already materialized. Fetches
|
|
3198
|
-
* the node's visitor bucket once (reused for enter + exit) and uses index
|
|
3199
|
-
* loops — `for…of` would allocate an iterator per node on this hot path.
|
|
3200
|
-
* @param {Node} node component-value root
|
|
3201
|
-
* @param {Node | null} parent enclosing node
|
|
3202
|
-
*/
|
|
3203
|
-
const walkValue = (node, parent) => {
|
|
3204
|
-
const ty = _soaTy[_ix(node)];
|
|
3205
|
-
const b = visitors[ty];
|
|
3206
|
-
let skip = false;
|
|
3207
|
-
if (b !== undefined && b.enter.length !== 0) {
|
|
3208
|
-
_walkSkip = false;
|
|
3209
|
-
_curNode = node;
|
|
3210
|
-
_curParent = parent;
|
|
3211
|
-
const e = b.enter;
|
|
3212
|
-
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3213
|
-
skip = _walkSkip;
|
|
3214
|
-
_walkSkip = false;
|
|
3215
|
-
}
|
|
3216
|
-
if (!skip && (ty === T_FUNCTION || ty === T_SIMPLE_BLOCK)) {
|
|
3217
|
-
const v = /** @type {Node[]} */ (_soaL0[_ix(node)]);
|
|
3218
|
-
for (let i = 0; i < v.length; i++) walkValue(v[i], node);
|
|
3219
|
-
}
|
|
3220
|
-
if (b !== undefined) {
|
|
3221
|
-
// Rebind: descending into children moved the path.
|
|
3222
|
-
_curNode = node;
|
|
3223
|
-
_curParent = parent;
|
|
3224
|
-
const x = b.exit;
|
|
3225
|
-
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3226
|
-
}
|
|
3227
|
-
};
|
|
3228
|
-
|
|
3229
|
-
/**
|
|
3230
|
-
* Walk a structural subtree; an at-rule / qualified-rule's block was parsed
|
|
3231
|
-
* eagerly (§5.4.4), so its `value` holds the nested rules / declarations.
|
|
3232
|
-
* @param {Node} node structural-tree root
|
|
3233
|
-
* @param {Node | null} parent enclosing node
|
|
3234
|
-
*/
|
|
3235
|
-
const walkRule = (node, parent) => {
|
|
3236
|
-
const i0 = _ix(node);
|
|
3237
|
-
const ty = _soaTy[i0];
|
|
3238
|
-
const b = visitors[ty];
|
|
3239
|
-
let skip = false;
|
|
3240
|
-
if (b !== undefined && b.enter.length !== 0) {
|
|
3241
|
-
_walkSkip = false;
|
|
3242
|
-
_curNode = node;
|
|
3243
|
-
_curParent = parent;
|
|
3244
|
-
const e = b.enter;
|
|
3245
|
-
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3246
|
-
skip = _walkSkip;
|
|
3247
|
-
_walkSkip = false;
|
|
3248
|
-
}
|
|
3249
|
-
if (!skip) {
|
|
3250
|
-
if (ty === T_AT_RULE || ty === T_QUALIFIED_RULE) {
|
|
3251
|
-
const p = /** @type {Node[]} */ (_soaL0[i0]);
|
|
3252
|
-
for (let i = 0; i < p.length; i++) walkValue(p[i], node);
|
|
3253
|
-
if (recurseBlocks) {
|
|
3254
|
-
// Declarations then child rules — downstream consumers don't need them strictly interleaved in source order.
|
|
3255
|
-
const decls = _soaL1[i0];
|
|
3256
|
-
if (decls) {
|
|
3257
|
-
for (let i = 0; i < decls.length; i++) walkRule(decls[i], node);
|
|
3258
|
-
}
|
|
3259
|
-
const ch = _soaL2[i0];
|
|
3260
|
-
if (ch) for (let i = 0; i < ch.length; i++) walkRule(ch[i], node);
|
|
3261
|
-
}
|
|
3262
|
-
} else if (ty === T_DECLARATION) {
|
|
3263
|
-
const v = /** @type {Node[]} */ (_soaL0[i0]);
|
|
3264
|
-
for (let i = 0; i < v.length; i++) walkValue(v[i], node);
|
|
3265
|
-
}
|
|
3266
|
-
}
|
|
3267
|
-
if (b !== undefined) {
|
|
3268
|
-
// Rebind: descending into children moved the path.
|
|
3269
|
-
_curNode = node;
|
|
3270
|
-
_curParent = parent;
|
|
3271
|
-
const x = b.exit;
|
|
3272
|
-
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3273
|
-
}
|
|
3274
|
-
};
|
|
3632
|
+
_recurseBlocks = options.recurseBlocks !== false;
|
|
3633
|
+
_visitors = visitors;
|
|
3634
|
+
_commentBucket = visitors[T_COMMENT];
|
|
3275
3635
|
|
|
3276
3636
|
// Stream each top-level node (selected by `as`) to the walker the moment it's
|
|
3277
3637
|
// consumed, rather than collecting them first — so the whole AST is never
|
|
3278
3638
|
// held at once; peak heap is ~one top-level node's subtree.
|
|
3279
|
-
const ts = new TokenStream(
|
|
3639
|
+
const ts = new TokenStream(
|
|
3640
|
+
input,
|
|
3641
|
+
0,
|
|
3642
|
+
locConverter,
|
|
3643
|
+
_commentBucket === undefined ? undefined : _grammarOnComment
|
|
3644
|
+
);
|
|
3280
3645
|
const consume =
|
|
3281
3646
|
TOP_LEVEL_CONSUMERS[options.as || "stylesheet"] ||
|
|
3282
3647
|
consumeAStylesheetsContents;
|
|
3283
3648
|
try {
|
|
3284
|
-
consume(ts,
|
|
3285
|
-
walkRule(node, null);
|
|
3286
|
-
_soaN = 0;
|
|
3287
|
-
});
|
|
3649
|
+
consume(ts, _walkTopLevel);
|
|
3288
3650
|
} finally {
|
|
3289
3651
|
// Drop the module-level SoA references so the last parsed source (and
|
|
3290
|
-
// its LocConverter / child lists) don't stay alive between
|
|
3652
|
+
// its LocConverter / child lists / visitors) don't stay alive between
|
|
3653
|
+
// parses.
|
|
3291
3654
|
_soaInput = "";
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3655
|
+
_soaLocConverter = /** @type {LocConverter} */ (
|
|
3656
|
+
/** @type {unknown} */ (null)
|
|
3657
|
+
);
|
|
3658
|
+
_soaDeclarationLists.length = 0;
|
|
3659
|
+
_soaChildRuleLists.length = 0;
|
|
3660
|
+
_soaFlatTop = 0;
|
|
3661
|
+
_listPool.length = 0;
|
|
3662
|
+
if (_soaFlat.length > _SOA_SHRINK_CAPACITY) {
|
|
3663
|
+
_soaFlatGrowHint = _soaFlatPeak;
|
|
3664
|
+
_soaFlat = new Int32Array(0);
|
|
3665
|
+
}
|
|
3666
|
+
_visitors = /** @type {CompiledVisitorMap} */ (/** @type {unknown} */ ([]));
|
|
3667
|
+
_commentBucket = undefined;
|
|
3668
|
+
if (_soaCapacity > _SOA_SHRINK_CAPACITY) {
|
|
3669
|
+
// +1: node ids are 1-based and grow fires at `id >= capacity`.
|
|
3670
|
+
_soaGrowHint = _soaPeak + 1;
|
|
3671
|
+
_soaCapacity = 0;
|
|
3672
|
+
_soaTypes = new Uint8Array(0);
|
|
3673
|
+
_soaStarts = new Int32Array(0);
|
|
3674
|
+
_soaEnds = new Int32Array(0);
|
|
3675
|
+
_soaAux0 = new Int32Array(0);
|
|
3676
|
+
_soaAux1 = new Int32Array(0);
|
|
3677
|
+
_soaAux2 = new Int32Array(0);
|
|
3678
|
+
_soaFlags = new Uint8Array(0);
|
|
3679
|
+
_soaListStarts = new Int32Array(0);
|
|
3680
|
+
_soaListLens = new Int32Array(0);
|
|
3681
|
+
}
|
|
3682
|
+
_soaPeak = 0;
|
|
3683
|
+
_soaFlatPeak = 0;
|
|
3296
3684
|
}
|
|
3297
3685
|
};
|
|
3298
3686
|
|
|
@@ -3340,15 +3728,28 @@ const buildSkipSet = (nodeTypes) => {
|
|
|
3340
3728
|
*/
|
|
3341
3729
|
/* eslint-enable jsdoc/require-template */
|
|
3342
3730
|
|
|
3731
|
+
// A fresh (safely retainable) array view of a node's flat content span —
|
|
3732
|
+
// visitors that read `A.children` / `A.prelude` may keep the result.
|
|
3733
|
+
/** @type {(n: Node) => Node[]} */
|
|
3734
|
+
const _materializeList = (n) => {
|
|
3735
|
+
const i = _nodeIndex(n);
|
|
3736
|
+
const start = _soaListStarts[i];
|
|
3737
|
+
const len = _soaListLens[i];
|
|
3738
|
+
/** @type {Node[]} */
|
|
3739
|
+
const out = [];
|
|
3740
|
+
for (let k = 0; k < len; k++) out.push(_nodeRef(_soaFlat[start + k]));
|
|
3741
|
+
return out;
|
|
3742
|
+
};
|
|
3743
|
+
|
|
3343
3744
|
// Babel's `path.skip()`, children-only: set by `A.skipChildren()` during an
|
|
3344
3745
|
// `enter` dispatch, consumed by the walk.
|
|
3345
3746
|
let _walkSkip = false;
|
|
3346
3747
|
// The walk's current position (`A.node` / `A.parent` read these; module-level
|
|
3347
3748
|
// so the accessor methods' defaults avoid self-referential `this` typing).
|
|
3348
3749
|
/** @type {Node} */
|
|
3349
|
-
let
|
|
3750
|
+
let _currentNode = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
3350
3751
|
/** @type {Node | null} */
|
|
3351
|
-
let
|
|
3752
|
+
let _currentParent = null;
|
|
3352
3753
|
|
|
3353
3754
|
// AST field-access seam. Every AST-node field read by `CssParser` goes through
|
|
3354
3755
|
// one of these accessors so the node representation can change underneath the
|
|
@@ -3363,13 +3764,13 @@ const A = {
|
|
|
3363
3764
|
* @returns {Node} current node — only valid during a visitor callback
|
|
3364
3765
|
*/
|
|
3365
3766
|
get node() {
|
|
3366
|
-
return
|
|
3767
|
+
return _currentNode;
|
|
3367
3768
|
},
|
|
3368
3769
|
/**
|
|
3369
3770
|
* @returns {Node | null} enclosing node (null = a top-level node)
|
|
3370
3771
|
*/
|
|
3371
3772
|
get parent() {
|
|
3372
|
-
return
|
|
3773
|
+
return _currentParent;
|
|
3373
3774
|
},
|
|
3374
3775
|
/** Stop the walk descending into the current node (enter only). */
|
|
3375
3776
|
skipChildren() {
|
|
@@ -3380,42 +3781,42 @@ const A = {
|
|
|
3380
3781
|
* @param {Node=} n node
|
|
3381
3782
|
* @returns {number} node type
|
|
3382
3783
|
*/
|
|
3383
|
-
type(n =
|
|
3384
|
-
return
|
|
3784
|
+
type(n = _currentNode) {
|
|
3785
|
+
return _soaTypes[_nodeIndex(n)];
|
|
3385
3786
|
},
|
|
3386
3787
|
/**
|
|
3387
3788
|
* @param {Node=} n node
|
|
3388
3789
|
* @returns {number} start offset
|
|
3389
3790
|
*/
|
|
3390
|
-
start(n =
|
|
3391
|
-
return
|
|
3791
|
+
start(n = _currentNode) {
|
|
3792
|
+
return _soaStarts[_nodeIndex(n)];
|
|
3392
3793
|
},
|
|
3393
3794
|
/**
|
|
3394
3795
|
* @param {Node=} n node
|
|
3395
3796
|
* @returns {number} end offset
|
|
3396
3797
|
*/
|
|
3397
|
-
end(n =
|
|
3398
|
-
return
|
|
3798
|
+
end(n = _currentNode) {
|
|
3799
|
+
return _soaEnds[_nodeIndex(n)];
|
|
3399
3800
|
},
|
|
3400
3801
|
/**
|
|
3401
3802
|
* @param {Node=} n node
|
|
3402
3803
|
* @returns {[number, number]} start / end offsets
|
|
3403
3804
|
*/
|
|
3404
|
-
range(n =
|
|
3405
|
-
const i =
|
|
3406
|
-
return [
|
|
3805
|
+
range(n = _currentNode) {
|
|
3806
|
+
const i = _nodeIndex(n);
|
|
3807
|
+
return [_soaStarts[i], _soaEnds[i]];
|
|
3407
3808
|
},
|
|
3408
3809
|
/**
|
|
3409
3810
|
* @param {Node=} n node
|
|
3410
3811
|
* @returns {{ start: { line: number, column: number }, end: { line: number, column: number } }} source location
|
|
3411
3812
|
*/
|
|
3412
|
-
loc(n =
|
|
3413
|
-
const i =
|
|
3414
|
-
const lc =
|
|
3415
|
-
const s = lc.get(
|
|
3813
|
+
loc(n = _currentNode) {
|
|
3814
|
+
const i = _nodeIndex(n);
|
|
3815
|
+
const lc = _soaLocConverter;
|
|
3816
|
+
const s = lc.get(_soaStarts[i]);
|
|
3416
3817
|
const sl = s.line;
|
|
3417
3818
|
const sc = s.column;
|
|
3418
|
-
const e = lc.get(
|
|
3819
|
+
const e = lc.get(_soaEnds[i]);
|
|
3419
3820
|
return {
|
|
3420
3821
|
start: { line: sl, column: sc },
|
|
3421
3822
|
end: { line: e.line, column: e.column }
|
|
@@ -3425,25 +3826,25 @@ const A = {
|
|
|
3425
3826
|
* @param {Node=} n node
|
|
3426
3827
|
* @returns {string} raw source slice
|
|
3427
3828
|
*/
|
|
3428
|
-
source(n =
|
|
3429
|
-
const i =
|
|
3430
|
-
return _soaInput.slice(
|
|
3829
|
+
source(n = _currentNode) {
|
|
3830
|
+
const i = _nodeIndex(n);
|
|
3831
|
+
return _soaInput.slice(_soaStarts[i], _soaEnds[i]);
|
|
3431
3832
|
},
|
|
3432
3833
|
/**
|
|
3433
3834
|
* @param {Node=} n node
|
|
3434
3835
|
* @returns {string} raw token value
|
|
3435
3836
|
*/
|
|
3436
|
-
value(n =
|
|
3437
|
-
return _soaValueOf(
|
|
3837
|
+
value(n = _currentNode) {
|
|
3838
|
+
return _soaValueOf(_nodeIndex(n));
|
|
3438
3839
|
},
|
|
3439
3840
|
/**
|
|
3440
3841
|
* @param {Node=} n node
|
|
3441
3842
|
* @returns {string} unescaped token value
|
|
3442
3843
|
*/
|
|
3443
|
-
unescaped(n =
|
|
3444
|
-
const i =
|
|
3844
|
+
unescaped(n = _currentNode) {
|
|
3845
|
+
const i = _nodeIndex(n);
|
|
3445
3846
|
const v = _soaValueOf(i);
|
|
3446
|
-
return
|
|
3847
|
+
return _soaTypes[i] === T_STRING
|
|
3447
3848
|
? unescapeIdentifier(v.slice(1, -1))
|
|
3448
3849
|
: unescapeIdentifier(v);
|
|
3449
3850
|
},
|
|
@@ -3451,11 +3852,11 @@ const A = {
|
|
|
3451
3852
|
* @param {Node=} n node
|
|
3452
3853
|
* @returns {string} hash / numeric type flag
|
|
3453
3854
|
*/
|
|
3454
|
-
typeFlag(n =
|
|
3455
|
-
const i =
|
|
3456
|
-
if (
|
|
3855
|
+
typeFlag(n = _currentNode) {
|
|
3856
|
+
const i = _nodeIndex(n);
|
|
3857
|
+
if (_soaTypes[i] === T_HASH) {
|
|
3457
3858
|
const input = _soaInput;
|
|
3458
|
-
const p =
|
|
3859
|
+
const p = _soaStarts[i] + 1;
|
|
3459
3860
|
return _ifThreeCodePointsWouldStartAnIdentSequence(
|
|
3460
3861
|
input,
|
|
3461
3862
|
p,
|
|
@@ -3468,109 +3869,113 @@ const A = {
|
|
|
3468
3869
|
}
|
|
3469
3870
|
const v = _soaValueOf(i);
|
|
3470
3871
|
return _typeFlagOf(
|
|
3471
|
-
|
|
3872
|
+
_soaTypes[i] === T_DIMENSION ? v.slice(0, _consumeANumber(v, 0)) : v
|
|
3472
3873
|
);
|
|
3473
3874
|
},
|
|
3474
3875
|
/**
|
|
3475
3876
|
* @param {Node=} n node
|
|
3476
3877
|
* @returns {number} url content start offset
|
|
3477
3878
|
*/
|
|
3478
|
-
contentStart(n =
|
|
3479
|
-
return
|
|
3879
|
+
contentStart(n = _currentNode) {
|
|
3880
|
+
return _soaAux0[_nodeIndex(n)];
|
|
3480
3881
|
},
|
|
3481
3882
|
/**
|
|
3482
3883
|
* @param {Node=} n node
|
|
3483
3884
|
* @returns {number} url content end offset
|
|
3484
3885
|
*/
|
|
3485
|
-
contentEnd(n =
|
|
3486
|
-
return
|
|
3886
|
+
contentEnd(n = _currentNode) {
|
|
3887
|
+
return _soaAux1[_nodeIndex(n)];
|
|
3487
3888
|
},
|
|
3488
3889
|
/**
|
|
3489
3890
|
* @param {Node=} n node
|
|
3490
3891
|
* @returns {string} rule / declaration / function name
|
|
3491
3892
|
*/
|
|
3492
|
-
name(n =
|
|
3493
|
-
const i =
|
|
3494
|
-
return
|
|
3495
|
-
? _soaInput.slice(
|
|
3496
|
-
: _soaInput.slice(
|
|
3893
|
+
name(n = _currentNode) {
|
|
3894
|
+
const i = _nodeIndex(n);
|
|
3895
|
+
return _soaTypes[i] === T_AT_RULE
|
|
3896
|
+
? _soaInput.slice(_soaStarts[i] + 1, _soaAux0[i])
|
|
3897
|
+
: _soaInput.slice(_soaStarts[i], _soaAux0[i]);
|
|
3497
3898
|
},
|
|
3498
3899
|
/**
|
|
3499
3900
|
* @param {Node=} n node
|
|
3500
3901
|
* @returns {number} name start offset
|
|
3501
3902
|
*/
|
|
3502
|
-
nameStart(n =
|
|
3503
|
-
return
|
|
3903
|
+
nameStart(n = _currentNode) {
|
|
3904
|
+
return _soaStarts[_nodeIndex(n)];
|
|
3504
3905
|
},
|
|
3505
3906
|
/**
|
|
3506
3907
|
* @param {Node=} n node
|
|
3507
3908
|
* @returns {number} name end offset
|
|
3508
3909
|
*/
|
|
3509
|
-
nameEnd(n =
|
|
3510
|
-
return
|
|
3910
|
+
nameEnd(n = _currentNode) {
|
|
3911
|
+
return _soaAux0[_nodeIndex(n)];
|
|
3511
3912
|
},
|
|
3512
3913
|
/**
|
|
3513
3914
|
* @param {Node=} n node
|
|
3514
3915
|
* @returns {string} unescaped name
|
|
3515
3916
|
*/
|
|
3516
|
-
unescapedName(n =
|
|
3917
|
+
unescapedName(n = _currentNode) {
|
|
3517
3918
|
return unescapeIdentifier(A.name(n));
|
|
3518
3919
|
},
|
|
3519
3920
|
/**
|
|
3520
3921
|
* @param {Node=} n node
|
|
3521
3922
|
* @returns {ComponentValue[]} function / block children
|
|
3522
3923
|
*/
|
|
3523
|
-
children(n =
|
|
3524
|
-
return /** @type {ComponentValue[]} */ (
|
|
3924
|
+
children(n = _currentNode) {
|
|
3925
|
+
return /** @type {ComponentValue[]} */ (_materializeList(n));
|
|
3525
3926
|
},
|
|
3526
3927
|
/**
|
|
3527
3928
|
* @param {Node=} n node
|
|
3528
3929
|
* @returns {ComponentValue[]} rule prelude
|
|
3529
3930
|
*/
|
|
3530
|
-
prelude(n =
|
|
3531
|
-
return /** @type {ComponentValue[]} */ (
|
|
3931
|
+
prelude(n = _currentNode) {
|
|
3932
|
+
return /** @type {ComponentValue[]} */ (_materializeList(n));
|
|
3532
3933
|
},
|
|
3533
3934
|
/**
|
|
3534
3935
|
* @param {Node=} n node
|
|
3535
3936
|
* @returns {Declaration[] | null} block declarations
|
|
3536
3937
|
*/
|
|
3537
|
-
declarations(n =
|
|
3538
|
-
return /** @type {Declaration[] | null} */ (
|
|
3938
|
+
declarations(n = _currentNode) {
|
|
3939
|
+
return /** @type {Declaration[] | null} */ (
|
|
3940
|
+
_soaDeclarationLists[_nodeIndex(n)]
|
|
3941
|
+
);
|
|
3539
3942
|
},
|
|
3540
3943
|
/**
|
|
3541
3944
|
* @param {Node=} n node
|
|
3542
3945
|
* @returns {Rule[] | null} block child rules
|
|
3543
3946
|
*/
|
|
3544
|
-
childRules(n =
|
|
3545
|
-
return /** @type {Rule[] | null} */ (
|
|
3947
|
+
childRules(n = _currentNode) {
|
|
3948
|
+
return /** @type {Rule[] | null} */ (_soaChildRuleLists[_nodeIndex(n)]);
|
|
3546
3949
|
},
|
|
3547
3950
|
/**
|
|
3548
3951
|
* @param {Node=} n node
|
|
3549
3952
|
* @returns {number} block start offset
|
|
3550
3953
|
*/
|
|
3551
|
-
blockStart(n =
|
|
3552
|
-
return
|
|
3954
|
+
blockStart(n = _currentNode) {
|
|
3955
|
+
return _soaAux1[_nodeIndex(n)];
|
|
3553
3956
|
},
|
|
3554
3957
|
/**
|
|
3555
3958
|
* @param {Node=} n node
|
|
3556
3959
|
* @returns {number} block end offset
|
|
3557
3960
|
*/
|
|
3558
|
-
blockEnd(n =
|
|
3559
|
-
return
|
|
3961
|
+
blockEnd(n = _currentNode) {
|
|
3962
|
+
return _soaAux2[_nodeIndex(n)];
|
|
3560
3963
|
},
|
|
3561
3964
|
/**
|
|
3562
3965
|
* @param {Node=} n node
|
|
3563
3966
|
* @returns {boolean} `!important` flag
|
|
3564
3967
|
*/
|
|
3565
|
-
important(n =
|
|
3566
|
-
return (
|
|
3968
|
+
important(n = _currentNode) {
|
|
3969
|
+
return (_soaFlags[_nodeIndex(n)] & 1) !== 0;
|
|
3567
3970
|
},
|
|
3568
3971
|
/**
|
|
3569
3972
|
* @param {Node=} n node
|
|
3570
3973
|
* @returns {SimpleBlockToken} block opening token
|
|
3571
3974
|
*/
|
|
3572
|
-
blockToken(n =
|
|
3573
|
-
return /** @type {SimpleBlockToken} */ (
|
|
3975
|
+
blockToken(n = _currentNode) {
|
|
3976
|
+
return /** @type {SimpleBlockToken} */ (
|
|
3977
|
+
_soaInput[_soaStarts[_nodeIndex(n)]]
|
|
3978
|
+
);
|
|
3574
3979
|
},
|
|
3575
3980
|
// Writers — `CssParser` rewrites a rule's end / block-end when it folds an
|
|
3576
3981
|
// inline ICSS `:import` / `:export` body into a single dependency. The
|
|
@@ -3580,14 +3985,14 @@ const A = {
|
|
|
3580
3985
|
* @param {number} v new end offset
|
|
3581
3986
|
*/
|
|
3582
3987
|
setEnd(n, v) {
|
|
3583
|
-
|
|
3988
|
+
_soaEnds[_nodeIndex(n)] = v;
|
|
3584
3989
|
},
|
|
3585
3990
|
/**
|
|
3586
3991
|
* @param {Node} n node
|
|
3587
3992
|
* @param {number} v new block end offset
|
|
3588
3993
|
*/
|
|
3589
3994
|
setBlockEnd(n, v) {
|
|
3590
|
-
|
|
3995
|
+
_soaAux2[_nodeIndex(n)] = v;
|
|
3591
3996
|
}
|
|
3592
3997
|
};
|
|
3593
3998
|
|
|
@@ -3632,6 +4037,11 @@ module.exports.TokenStream = TokenStream;
|
|
|
3632
4037
|
module.exports.buildSkipSet = buildSkipSet;
|
|
3633
4038
|
module.exports.equalsLowerCase = equalsLowerCase;
|
|
3634
4039
|
module.exports.escapeIdentifier = escapeIdentifier;
|
|
4040
|
+
module.exports.isDashedIdentifier = isDashedIdentifier;
|
|
4041
|
+
// CSS Syntax §4.2 "whitespace" (space / tab / newline / CR / FF) — the
|
|
4042
|
+
// tokenizer's whitespace class, exported under the spec's name.
|
|
4043
|
+
module.exports.isWhitespace = _isWhiteSpace;
|
|
4044
|
+
module.exports.normalizeUrl = normalizeUrl;
|
|
3635
4045
|
module.exports.parseABlocksContents = parseABlocksContents;
|
|
3636
4046
|
module.exports.parseACommaSeparatedListOfComponentValues =
|
|
3637
4047
|
parseACommaSeparatedListOfComponentValues;
|
|
@@ -3641,5 +4051,8 @@ module.exports.parseAListOfComponentValues = parseAListOfComponentValues;
|
|
|
3641
4051
|
module.exports.parseARule = parseARule;
|
|
3642
4052
|
module.exports.parseAStylesheet = parseAStylesheet;
|
|
3643
4053
|
module.exports.parseAStylesheetsContents = parseAStylesheetsContents;
|
|
4054
|
+
module.exports.rangeEquals = rangeEquals;
|
|
4055
|
+
module.exports.rangeEqualsLowerCase = rangeEqualsLowerCase;
|
|
3644
4056
|
module.exports.readToken = readToken;
|
|
4057
|
+
module.exports.toLowerCaseIfNeeded = toLowerCaseIfNeeded;
|
|
3645
4058
|
module.exports.unescapeIdentifier = unescapeIdentifier;
|