webpack 5.108.3 → 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 +50 -8
- 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/FlagDependencyExportsPlugin.js +55 -8
- package/lib/HotModuleReplacementPlugin.js +18 -15
- package/lib/LazyBarrel.js +30 -3
- 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 -405
- 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 +222 -32
- package/lib/config/normalization.js +26 -0
- package/lib/config/target.js +9 -1
- package/lib/css/CssGenerator.js +73 -73
- package/lib/css/CssLoadingRuntimeModule.js +5 -1
- package/lib/css/CssModulesPlugin.js +92 -53
- package/lib/css/CssParser.js +843 -302
- package/lib/css/syntax.js +1219 -483
- 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 +190 -87
- 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 +34 -107
- package/lib/dependencies/HarmonyImportGuard.js +192 -20
- 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 +87 -26
- 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 +940 -91
- package/lib/html/HtmlModule.js +3 -2
- package/lib/html/HtmlModulesPlugin.js +625 -170
- package/lib/html/HtmlParser.js +812 -340
- package/lib/html/favicon.svg +1 -0
- package/lib/html/syntax.js +2116 -834
- 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 +711 -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 +98 -19
- package/lib/util/AsyncQueue.js +122 -72
- package/lib/util/LocConverter.js +9 -8
- package/lib/util/SetHelpers.js +1 -1
- package/lib/util/SourceProcessor.js +41 -28
- 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 +14 -13
- 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 +1300 -325
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);
|
|
@@ -297,6 +298,15 @@ const _isSpace = (cc) => cc === CC_SPACE || cc === CC_TAB;
|
|
|
297
298
|
// rarer tab / newline tests.
|
|
298
299
|
const _isWhiteSpace = (cc) => _isSpace(cc) || _isNewline(cc);
|
|
299
300
|
|
|
301
|
+
// Whitespace membership table for the run-consumption loop — one load instead
|
|
302
|
+
// of up to five compares per char. EOF (NaN) / non-ASCII index to undefined.
|
|
303
|
+
const _wsTable = new Uint8Array(128);
|
|
304
|
+
_wsTable[CC_SPACE] = 1;
|
|
305
|
+
_wsTable[CC_TAB] = 1;
|
|
306
|
+
_wsTable[CC_LINE_FEED] = 1;
|
|
307
|
+
_wsTable[CC_CARRIAGE_RETURN] = 1;
|
|
308
|
+
_wsTable[CC_FORM_FEED] = 1;
|
|
309
|
+
|
|
300
310
|
/**
|
|
301
311
|
* @param {number} cc char code
|
|
302
312
|
* @returns {boolean} true, if cc is a digit
|
|
@@ -332,11 +342,15 @@ const _isIdentStartCodePointCC = (cc) =>
|
|
|
332
342
|
/**
|
|
333
343
|
* Spec: ident-code = ident-start / digit / hyphen-minus.
|
|
334
344
|
*/
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
//
|
|
339
|
-
|
|
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);
|
|
340
354
|
for (let i = 0; i < 128; i++) {
|
|
341
355
|
_identCharTable[i] =
|
|
342
356
|
_isLetter(i) || i === CC_LOW_LINE || _isDigit(i) || i === CC_HYPHEN_MINUS
|
|
@@ -347,8 +361,7 @@ for (let i = 0; i < 128; i++) {
|
|
|
347
361
|
* @param {number} cc char code
|
|
348
362
|
* @returns {boolean} true, if cc is an ident-sequence code point
|
|
349
363
|
*/
|
|
350
|
-
const _isIdentCodePoint = (cc) =>
|
|
351
|
-
cc < 128 ? _identCharTable[cc] === 1 : _isIdentStartCodePointCC(cc);
|
|
364
|
+
const _isIdentCodePoint = (cc) => _identCharTable[cc | 0] === 1;
|
|
352
365
|
|
|
353
366
|
/**
|
|
354
367
|
* ASCII case-insensitive equality against a lowercase literal — avoids the
|
|
@@ -368,6 +381,58 @@ const equalsLowerCase = (s, lit) => {
|
|
|
368
381
|
return true;
|
|
369
382
|
};
|
|
370
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
|
+
|
|
371
436
|
/**
|
|
372
437
|
* Consume an escaped code point.
|
|
373
438
|
* @param {string} input input
|
|
@@ -385,7 +450,8 @@ const _consumeAnEscapedCodePoint = (input, pos) => {
|
|
|
385
450
|
if (pos === input.length) return pos;
|
|
386
451
|
if (_isHexDigit(cc)) {
|
|
387
452
|
for (let i = 0; i < 5; i++) {
|
|
388
|
-
if (_isHexDigit(input.charCodeAt(pos)))
|
|
453
|
+
if (!_isHexDigit(input.charCodeAt(pos))) break;
|
|
454
|
+
pos++;
|
|
389
455
|
}
|
|
390
456
|
const trail = input.charCodeAt(pos);
|
|
391
457
|
if (_isWhiteSpace(trail)) {
|
|
@@ -471,12 +537,13 @@ const _ifThreeCodePointsWouldStartANumber = (input, pos, f, s, t) => {
|
|
|
471
537
|
const _consumeAnIdentSequence = (input, pos) => {
|
|
472
538
|
// Hot loop (every ident, at-keyword, hash, function name, unit). Both checks
|
|
473
539
|
// are inlined from `_isIdentCodePoint` / `_ifTwoCodePointsAreValidEscape`: the
|
|
474
|
-
//
|
|
475
|
-
// 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.
|
|
476
543
|
for (;;) {
|
|
477
|
-
const cc = input.charCodeAt(pos);
|
|
544
|
+
const cc = input.charCodeAt(pos) | 0;
|
|
478
545
|
pos++;
|
|
479
|
-
if (
|
|
546
|
+
if (_identCharTable[cc] === 1) {
|
|
480
547
|
continue;
|
|
481
548
|
}
|
|
482
549
|
if (cc === CC_REVERSE_SOLIDUS && !_isNewline(input.charCodeAt(pos))) {
|
|
@@ -612,7 +679,7 @@ const fill = (out, type, start, end) => {
|
|
|
612
679
|
*/
|
|
613
680
|
function consumeSpace(input, pos, out) {
|
|
614
681
|
const start = pos - 1;
|
|
615
|
-
while (
|
|
682
|
+
while (_wsTable[input.charCodeAt(pos)] === 1) pos++;
|
|
616
683
|
return fill(out, TT_WHITESPACE, start, pos);
|
|
617
684
|
}
|
|
618
685
|
|
|
@@ -691,17 +758,33 @@ function consumeNumberSign(input, pos, out) {
|
|
|
691
758
|
* @returns {MutableToken | undefined} the resulting token, or undefined at EOF
|
|
692
759
|
*/
|
|
693
760
|
function consumeHyphenMinus(input, pos, out) {
|
|
694
|
-
|
|
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
|
+
) {
|
|
695
773
|
pos--;
|
|
696
774
|
return consumeANumericToken(input, pos, out);
|
|
697
775
|
}
|
|
698
|
-
if (
|
|
699
|
-
input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
|
|
700
|
-
input.charCodeAt(pos + 1) === CC_GREATER_THAN_SIGN
|
|
701
|
-
) {
|
|
776
|
+
if (second === CC_HYPHEN_MINUS && third === CC_GREATER_THAN_SIGN) {
|
|
702
777
|
return fill(out, TT_CDC, pos - 1, pos + 2);
|
|
703
778
|
}
|
|
704
|
-
if (
|
|
779
|
+
if (
|
|
780
|
+
_ifThreeCodePointsWouldStartAnIdentSequence(
|
|
781
|
+
input,
|
|
782
|
+
pos,
|
|
783
|
+
CC_HYPHEN_MINUS,
|
|
784
|
+
second,
|
|
785
|
+
third
|
|
786
|
+
)
|
|
787
|
+
) {
|
|
705
788
|
pos--;
|
|
706
789
|
return consumeAnIdentLikeToken(input, pos, out);
|
|
707
790
|
}
|
|
@@ -792,7 +875,7 @@ function consumeAUrlToken(input, pos, fnStart, out) {
|
|
|
792
875
|
out.contentStart = contentStart;
|
|
793
876
|
for (;;) {
|
|
794
877
|
if (pos === input.length) {
|
|
795
|
-
out.contentEnd = pos
|
|
878
|
+
out.contentEnd = pos;
|
|
796
879
|
return fill(out, TT_URL, fnStart, pos);
|
|
797
880
|
}
|
|
798
881
|
const cc = input.charCodeAt(pos);
|
|
@@ -990,7 +1073,11 @@ const HC_AT_SIGN = 9;
|
|
|
990
1073
|
const HC_REVERSE_SOLIDUS = 10;
|
|
991
1074
|
const HC_DIGIT = 11;
|
|
992
1075
|
const HC_IDENT = 12;
|
|
993
|
-
|
|
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);
|
|
994
1081
|
const _singleTT = new Uint8Array(128);
|
|
995
1082
|
_singleTT[CC_LEFT_PARENTHESIS] = TT_LEFT_PARENTHESIS;
|
|
996
1083
|
_singleTT[CC_RIGHT_PARENTHESIS] = TT_RIGHT_PARENTHESIS;
|
|
@@ -1045,7 +1132,7 @@ for (let i = 0; i < 128; i++) {
|
|
|
1045
1132
|
function consumeAToken(input, pos, cc, out) {
|
|
1046
1133
|
// `u` / `U` would start a unicode-range token in the spec; those are not
|
|
1047
1134
|
// produced, so they map to HC_IDENT and fall through to ident-like.
|
|
1048
|
-
switch (
|
|
1135
|
+
switch (_charClass[cc]) {
|
|
1049
1136
|
// Run of whitespace → one <whitespace-token>.
|
|
1050
1137
|
case HC_WHITESPACE:
|
|
1051
1138
|
return consumeSpace(input, pos, out);
|
|
@@ -1166,7 +1253,10 @@ const NodeType = {
|
|
|
1166
1253
|
Declaration: 23,
|
|
1167
1254
|
AtRule: 24,
|
|
1168
1255
|
QualifiedRule: 25,
|
|
1169
|
-
Stylesheet: 26
|
|
1256
|
+
Stylesheet: 26,
|
|
1257
|
+
// Comments are never tree nodes; this type exists only so a `NodeType.Comment`
|
|
1258
|
+
// visitor can be registered (fired during tokenization — see `grammar`).
|
|
1259
|
+
Comment: 27
|
|
1170
1260
|
};
|
|
1171
1261
|
const {
|
|
1172
1262
|
Ident: T_IDENT,
|
|
@@ -1194,7 +1284,8 @@ const {
|
|
|
1194
1284
|
Declaration: T_DECLARATION,
|
|
1195
1285
|
AtRule: T_AT_RULE,
|
|
1196
1286
|
QualifiedRule: T_QUALIFIED_RULE,
|
|
1197
|
-
Stylesheet: T_STYLESHEET
|
|
1287
|
+
Stylesheet: T_STYLESHEET,
|
|
1288
|
+
Comment: T_COMMENT
|
|
1198
1289
|
} = NodeType;
|
|
1199
1290
|
|
|
1200
1291
|
/**
|
|
@@ -1592,17 +1683,46 @@ _ttToNodeType[TT_CDC] = T_CDC;
|
|
|
1592
1683
|
// field access differs, so only those ops are swapped.
|
|
1593
1684
|
|
|
1594
1685
|
// Current loc converter for the active parse (object backend reads it).
|
|
1595
|
-
let
|
|
1686
|
+
let _objLocConverter = /** @type {LocConverter} */ (
|
|
1687
|
+
/** @type {unknown} */ (null)
|
|
1688
|
+
);
|
|
1689
|
+
|
|
1690
|
+
// Active skip state (from `CssProcessOptions.skip`), applied by the grammar.
|
|
1691
|
+
// `_skipTypes` is indexed by `NodeType` (1 = skip): drop that component-value
|
|
1692
|
+
// leaf / container from declaration value and function-arg lists. The two
|
|
1693
|
+
// prelude flags scan a rule's prelude without materializing its tree (url tokens
|
|
1694
|
+
// / functions kept, so `url()` in a selector or `@import url(…)` still resolves).
|
|
1695
|
+
// A skipped node is still tokenized (positions stay correct) but never pushed,
|
|
1696
|
+
// so it is never walked or read — the caller must only skip what nothing reads.
|
|
1697
|
+
// `useObjectBackend` restores the defaults so `parseA*` build the full tree.
|
|
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
|
+
);
|
|
1706
|
+
/** @type {Uint8Array} */
|
|
1707
|
+
let _skipTypes = _NO_SKIP_TYPES;
|
|
1708
|
+
// Fast-path flag: true only when a real skip set is active, so the (dominant)
|
|
1709
|
+
// no-skip parses pay one boolean test instead of a node-type lookup per value.
|
|
1710
|
+
let _skipActive = false;
|
|
1711
|
+
let _skipSelectorPrelude = false;
|
|
1712
|
+
let _skipAtRulePrelude = false;
|
|
1596
1713
|
|
|
1597
1714
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1598
|
-
let
|
|
1715
|
+
let _makeLeaf;
|
|
1599
1716
|
/** @type {(start: number, end: number, contentStart: number, contentEnd: number) => Node} */
|
|
1600
|
-
let
|
|
1717
|
+
let _makeUrl;
|
|
1601
1718
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1602
|
-
let
|
|
1719
|
+
let _makeContainer;
|
|
1603
1720
|
/** @type {(start: number) => Node} */
|
|
1604
|
-
let
|
|
1605
|
-
|
|
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} */
|
|
1606
1726
|
let _setName;
|
|
1607
1727
|
/** @type {(r: Node, v: number) => void} */
|
|
1608
1728
|
let _setEnd;
|
|
@@ -1621,170 +1741,265 @@ let _setBody;
|
|
|
1621
1741
|
/** @type {(r: Node, list: Node[]) => void} */
|
|
1622
1742
|
let _setRules;
|
|
1623
1743
|
/** @type {(r: Node) => number} */
|
|
1624
|
-
let
|
|
1744
|
+
let _nodeTypeOf;
|
|
1625
1745
|
/** @type {(r: Node) => number} */
|
|
1626
|
-
let
|
|
1746
|
+
let _nodeStartOf;
|
|
1627
1747
|
/** @type {(r: Node) => string} */
|
|
1628
|
-
let
|
|
1748
|
+
let _nodeValueOf;
|
|
1629
1749
|
/** @type {(r: Node) => SimpleBlockToken} */
|
|
1630
|
-
let
|
|
1750
|
+
let _nodeTokenOf;
|
|
1631
1751
|
|
|
1632
1752
|
// Child lists are plain arrays in every backend; refs are pushed by value.
|
|
1633
|
-
|
|
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[]} */ ([]);
|
|
1634
1767
|
|
|
1635
1768
|
// -- object backend: builds the retainable class-instance tree --
|
|
1636
|
-
/** @type {typeof
|
|
1637
|
-
const _objLeaf = (type, start, end) =>
|
|
1638
|
-
|
|
1769
|
+
/** @type {typeof _makeLeaf} */
|
|
1770
|
+
const _objLeaf = (type, start, end) =>
|
|
1771
|
+
new Token(type, start, end, _objLocConverter);
|
|
1772
|
+
/** @type {typeof _makeUrl} */
|
|
1639
1773
|
const _objUrl = (start, end, cs, ce) => {
|
|
1640
|
-
const u = /** @type {UrlToken} */ (
|
|
1774
|
+
const u = /** @type {UrlToken} */ (
|
|
1775
|
+
new Token(T_URL, start, end, _objLocConverter)
|
|
1776
|
+
);
|
|
1641
1777
|
u.contentStart = cs;
|
|
1642
1778
|
u.contentEnd = ce;
|
|
1643
1779
|
return u;
|
|
1644
1780
|
};
|
|
1645
|
-
/** @type {typeof
|
|
1781
|
+
/** @type {typeof _makeContainer} */
|
|
1646
1782
|
const _objContainer = (type, start, end) =>
|
|
1647
|
-
new Container(type, start, end,
|
|
1648
|
-
/** @type {typeof
|
|
1783
|
+
new Container(type, start, end, _objLocConverter);
|
|
1784
|
+
/** @type {typeof _makeStylesheet} */
|
|
1649
1785
|
const _objStylesheet = (start) => {
|
|
1650
1786
|
const s = /** @type {Stylesheet} */ (
|
|
1651
|
-
new Node(T_STYLESHEET, start, start,
|
|
1787
|
+
new Node(T_STYLESHEET, start, start, _objLocConverter)
|
|
1652
1788
|
);
|
|
1653
1789
|
s.rules = [];
|
|
1654
1790
|
return s;
|
|
1655
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);
|
|
1656
1852
|
/** @param {LocConverter} lc loc converter for this parse */
|
|
1657
1853
|
const useObjectBackend = (lc) => {
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
};
|
|
1683
|
-
_setValue = (r, list) => {
|
|
1684
|
-
/** @type {Container} */ (r).value = /** @type {ComponentValue[]} */ (list);
|
|
1685
|
-
};
|
|
1686
|
-
_setPrelude = (r, list) => {
|
|
1687
|
-
/** @type {Container} */ (r).prelude = /** @type {ComponentValue[]} */ (
|
|
1688
|
-
list
|
|
1689
|
-
);
|
|
1690
|
-
};
|
|
1691
|
-
_setBody = (r, decls, childRules) => {
|
|
1692
|
-
const c = /** @type {Container} */ (r);
|
|
1693
|
-
c.declarations = /** @type {Declaration[]} */ (decls);
|
|
1694
|
-
c.childRules = /** @type {Rule[]} */ (childRules);
|
|
1695
|
-
};
|
|
1696
|
-
_setRules = (r, list) => {
|
|
1697
|
-
/** @type {Stylesheet} */ (r).rules = /** @type {Rule[]} */ (list);
|
|
1698
|
-
};
|
|
1699
|
-
_nType = (r) => r.type;
|
|
1700
|
-
_nStart = (r) => r.start;
|
|
1701
|
-
_nValue = (r) => /** @type {Token} */ (r).value;
|
|
1702
|
-
_nToken = (r) =>
|
|
1703
|
-
/** @type {SimpleBlockToken} */ (/** @type {Container} */ (r).token);
|
|
1854
|
+
_objLocConverter = lc;
|
|
1855
|
+
_soaActive = false;
|
|
1856
|
+
// `parseA*` return the full tree, so nothing is skipped.
|
|
1857
|
+
_skipTypes = _NO_SKIP_TYPES;
|
|
1858
|
+
_skipActive = false;
|
|
1859
|
+
_skipSelectorPrelude = false;
|
|
1860
|
+
_skipAtRulePrelude = false;
|
|
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;
|
|
1704
1878
|
};
|
|
1705
1879
|
|
|
1706
1880
|
// -- struct-of-arrays backend: writes nodes into reused typed arrays --
|
|
1707
1881
|
// A node ref is its integer id; fields live in parallel arrays indexed by id.
|
|
1708
|
-
// Three reused int slots (`
|
|
1709
|
-
// extras; child lists hang off three object arrays.
|
|
1710
|
-
// url:
|
|
1711
|
-
// function:
|
|
1712
|
-
// declaration:
|
|
1713
|
-
// at-rule:
|
|
1714
|
-
// 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
|
|
1715
1889
|
// `name` / `nameStart` / a simple block's `token` are derived from the source
|
|
1716
|
-
// on read (see the SoA accessors), so they need no slot.
|
|
1717
|
-
// value | prelude | stylesheet rules
|
|
1718
|
-
// `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
|
|
1719
1893
|
// buffers are reused across rules and the parse allocates almost nothing.
|
|
1720
|
-
let
|
|
1721
|
-
let
|
|
1722
|
-
let
|
|
1723
|
-
let
|
|
1724
|
-
let
|
|
1725
|
-
let
|
|
1726
|
-
let
|
|
1727
|
-
let
|
|
1728
|
-
let
|
|
1729
|
-
|
|
1730
|
-
|
|
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
|
+
};
|
|
1731
1927
|
/** @type {(Node[] | null)[]} */
|
|
1732
|
-
const
|
|
1928
|
+
const _soaDeclarationLists = [];
|
|
1733
1929
|
/** @type {(Node[] | null)[]} */
|
|
1734
|
-
const
|
|
1930
|
+
const _soaChildRuleLists = [];
|
|
1735
1931
|
let _soaInput = "";
|
|
1736
|
-
let
|
|
1932
|
+
let _soaLocConverter = /** @type {LocConverter} */ (
|
|
1933
|
+
/** @type {unknown} */ (null)
|
|
1934
|
+
);
|
|
1737
1935
|
|
|
1738
1936
|
// Node refs are integers here but typed `Node` across the parser; these are
|
|
1739
1937
|
// identity casts that just satisfy the type system at the boundary.
|
|
1740
1938
|
/** @type {(n: Node) => number} */
|
|
1741
|
-
const
|
|
1939
|
+
const _nodeIndex = (n) => /** @type {number} */ (/** @type {unknown} */ (n));
|
|
1742
1940
|
/** @type {(i: number) => Node} */
|
|
1743
|
-
const
|
|
1941
|
+
const _nodeRef = (i) => /** @type {Node} */ (/** @type {unknown} */ (i));
|
|
1744
1942
|
|
|
1745
1943
|
/** @param {number} need minimum capacity */
|
|
1746
1944
|
const _soaGrow = (need) => {
|
|
1747
|
-
let cap =
|
|
1945
|
+
let cap = _soaCapacity || 4096;
|
|
1946
|
+
if (_soaGrowHint > cap) cap = _soaGrowHint;
|
|
1947
|
+
_soaGrowHint = 0;
|
|
1748
1948
|
while (cap < need) cap *= 2;
|
|
1749
1949
|
const ty = new Uint8Array(cap);
|
|
1750
|
-
ty.set(
|
|
1751
|
-
|
|
1950
|
+
ty.set(_soaTypes);
|
|
1951
|
+
_soaTypes = ty;
|
|
1752
1952
|
const st = new Int32Array(cap);
|
|
1753
|
-
st.set(
|
|
1754
|
-
|
|
1953
|
+
st.set(_soaStarts);
|
|
1954
|
+
_soaStarts = st;
|
|
1755
1955
|
const en = new Int32Array(cap);
|
|
1756
|
-
en.set(
|
|
1757
|
-
|
|
1956
|
+
en.set(_soaEnds);
|
|
1957
|
+
_soaEnds = en;
|
|
1758
1958
|
const a0 = new Int32Array(cap);
|
|
1759
|
-
a0.set(
|
|
1760
|
-
|
|
1959
|
+
a0.set(_soaAux0);
|
|
1960
|
+
_soaAux0 = a0;
|
|
1761
1961
|
const a1 = new Int32Array(cap);
|
|
1762
|
-
a1.set(
|
|
1763
|
-
|
|
1962
|
+
a1.set(_soaAux1);
|
|
1963
|
+
_soaAux1 = a1;
|
|
1764
1964
|
const a2 = new Int32Array(cap);
|
|
1765
|
-
a2.set(
|
|
1766
|
-
|
|
1965
|
+
a2.set(_soaAux2);
|
|
1966
|
+
_soaAux2 = a2;
|
|
1767
1967
|
const fl = new Uint8Array(cap);
|
|
1768
|
-
fl.set(
|
|
1769
|
-
|
|
1770
|
-
|
|
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;
|
|
1771
1977
|
};
|
|
1772
1978
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1773
|
-
const
|
|
1979
|
+
const _soaAllocNode = (type, start, end) => {
|
|
1774
1980
|
// Ids are 1-based: a node ref is used in truthiness checks (`if (!parent)`),
|
|
1775
1981
|
// so 0 must stay reserved for "no node".
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1982
|
+
// Leaves never read the flag / list slots — `_soaAllocContainer` clears
|
|
1983
|
+
// them instead, keeping the dominant leaf allocation at three writes.
|
|
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);
|
|
1991
|
+
};
|
|
1992
|
+
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1993
|
+
const _soaAllocContainer = (type, start, end) => {
|
|
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;
|
|
2002
|
+
return r;
|
|
1788
2003
|
};
|
|
1789
2004
|
// Raw token value (the lazy `Token.value` form): hash / at-keyword drop their
|
|
1790
2005
|
// one-char prefix, url uses its content range. Shared by the parser's
|
|
@@ -1794,65 +2009,107 @@ const _soaAlloc = (type, start, end) => {
|
|
|
1794
2009
|
* @returns {string} raw token value
|
|
1795
2010
|
*/
|
|
1796
2011
|
const _soaValueOf = (i) => {
|
|
1797
|
-
const ty =
|
|
2012
|
+
const ty = _soaTypes[i];
|
|
1798
2013
|
if (ty === T_HASH || ty === T_AT_KEYWORD) {
|
|
1799
|
-
return _soaInput.slice(
|
|
2014
|
+
return _soaInput.slice(_soaStarts[i] + 1, _soaEnds[i]);
|
|
1800
2015
|
}
|
|
1801
|
-
if (ty === T_URL) return _soaInput.slice(
|
|
1802
|
-
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;
|
|
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;
|
|
1803
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)]]);
|
|
1804
2084
|
/**
|
|
1805
2085
|
* @param {string} input source
|
|
1806
2086
|
* @param {LocConverter} lc loc converter
|
|
1807
2087
|
*/
|
|
1808
2088
|
const useSoaBackend = (input, lc) => {
|
|
1809
2089
|
_soaInput = input;
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
_setImportant = (r) => {
|
|
1834
|
-
_soaFl[_ix(r)] |= 1;
|
|
1835
|
-
};
|
|
1836
|
-
// A simple block's token is derived from its opening char on read.
|
|
1837
|
-
_setToken = (r, ch) => {};
|
|
1838
|
-
_setValue = (r, list) => {
|
|
1839
|
-
_soaL0[_ix(r)] = list;
|
|
1840
|
-
};
|
|
1841
|
-
_setPrelude = (r, list) => {
|
|
1842
|
-
_soaL0[_ix(r)] = list;
|
|
1843
|
-
};
|
|
1844
|
-
_setBody = (r, decls, childRules) => {
|
|
1845
|
-
const i = _ix(r);
|
|
1846
|
-
_soaL1[i] = decls;
|
|
1847
|
-
_soaL2[i] = childRules;
|
|
1848
|
-
};
|
|
1849
|
-
_setRules = (r, list) => {
|
|
1850
|
-
_soaL0[_ix(r)] = list;
|
|
1851
|
-
};
|
|
1852
|
-
_nType = (r) => _soaTy[_ix(r)];
|
|
1853
|
-
_nStart = (r) => _soaSt[_ix(r)];
|
|
1854
|
-
_nValue = (r) => _soaValueOf(_ix(r));
|
|
1855
|
-
_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;
|
|
1856
2113
|
};
|
|
1857
2114
|
|
|
1858
2115
|
/**
|
|
@@ -1866,9 +2123,9 @@ const tokenToNode = (t) => {
|
|
|
1866
2123
|
// plain leaf whose node type comes from the map.
|
|
1867
2124
|
if (tt === TT_URL) {
|
|
1868
2125
|
const ut = /** @type {CssUrlToken} */ (t);
|
|
1869
|
-
return
|
|
2126
|
+
return _makeUrl(t.start, t.end, ut.contentStart, ut.contentEnd);
|
|
1870
2127
|
}
|
|
1871
|
-
return
|
|
2128
|
+
return _makeLeaf(_ttToNodeType[tt], t.start, t.end);
|
|
1872
2129
|
};
|
|
1873
2130
|
|
|
1874
2131
|
/**
|
|
@@ -1918,12 +2175,12 @@ class TokenStream {
|
|
|
1918
2175
|
/** @type {number} */
|
|
1919
2176
|
this._commentHigh = pos;
|
|
1920
2177
|
// Single reused token the lexer writes into on the `next` path — see
|
|
1921
|
-
// `MutableToken`. `
|
|
1922
|
-
//
|
|
2178
|
+
// `MutableToken`. `_hasNext` marks it cached — a boolean instead of an
|
|
2179
|
+
// object slot, so caching a token never pays a GC write barrier.
|
|
1923
2180
|
/** @type {MutableToken} */
|
|
1924
2181
|
this._tok = createToken();
|
|
1925
|
-
/** @type {
|
|
1926
|
-
this.
|
|
2182
|
+
/** @type {boolean} whether `_tok` holds the (lazily tokenized) next token */
|
|
2183
|
+
this._hasNext = false;
|
|
1927
2184
|
/** @type {number[]} byte offsets to rewind to */
|
|
1928
2185
|
this._marks = [];
|
|
1929
2186
|
}
|
|
@@ -1937,14 +2194,14 @@ class TokenStream {
|
|
|
1937
2194
|
* @returns {MutableToken} the next token
|
|
1938
2195
|
*/
|
|
1939
2196
|
next() {
|
|
1940
|
-
if (this.
|
|
2197
|
+
if (!this._hasNext) {
|
|
1941
2198
|
const input = this.input;
|
|
1942
2199
|
const tok = this._tok;
|
|
1943
2200
|
let pos = this._pos;
|
|
1944
2201
|
for (;;) {
|
|
1945
2202
|
const t = readToken(input, pos, tok);
|
|
1946
2203
|
if (t === undefined) {
|
|
1947
|
-
|
|
2204
|
+
fill(tok, TT_EOF, input.length, input.length);
|
|
1948
2205
|
break;
|
|
1949
2206
|
}
|
|
1950
2207
|
if (t.type === TT_COMMENT) {
|
|
@@ -1955,11 +2212,11 @@ class TokenStream {
|
|
|
1955
2212
|
pos = t.end;
|
|
1956
2213
|
continue;
|
|
1957
2214
|
}
|
|
1958
|
-
this._next = t;
|
|
1959
2215
|
break;
|
|
1960
2216
|
}
|
|
2217
|
+
this._hasNext = true;
|
|
1961
2218
|
}
|
|
1962
|
-
return
|
|
2219
|
+
return this._tok;
|
|
1963
2220
|
}
|
|
1964
2221
|
|
|
1965
2222
|
/**
|
|
@@ -1972,7 +2229,7 @@ class TokenStream {
|
|
|
1972
2229
|
const t = this.next();
|
|
1973
2230
|
if (t.type !== TT_EOF) {
|
|
1974
2231
|
this._pos = t.end;
|
|
1975
|
-
this.
|
|
2232
|
+
this._hasNext = false;
|
|
1976
2233
|
}
|
|
1977
2234
|
return t;
|
|
1978
2235
|
}
|
|
@@ -1986,7 +2243,7 @@ class TokenStream {
|
|
|
1986
2243
|
const t = this.next();
|
|
1987
2244
|
if (t.type !== TT_EOF) {
|
|
1988
2245
|
this._pos = t.end;
|
|
1989
|
-
this.
|
|
2246
|
+
this._hasNext = false;
|
|
1990
2247
|
}
|
|
1991
2248
|
}
|
|
1992
2249
|
|
|
@@ -2006,7 +2263,7 @@ class TokenStream {
|
|
|
2006
2263
|
*/
|
|
2007
2264
|
restoreMark() {
|
|
2008
2265
|
this._pos = /** @type {number} */ (this._marks.pop());
|
|
2009
|
-
this.
|
|
2266
|
+
this._hasNext = false;
|
|
2010
2267
|
}
|
|
2011
2268
|
|
|
2012
2269
|
/**
|
|
@@ -2045,7 +2302,7 @@ const normalizeIntoTokenStream = (input, pos, onComment) =>
|
|
|
2045
2302
|
// algorithms that drive the tokenizer.
|
|
2046
2303
|
|
|
2047
2304
|
/**
|
|
2048
|
-
* @typedef {object}
|
|
2305
|
+
* @typedef {object} ParseOptions
|
|
2049
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
|
|
2050
2307
|
*/
|
|
2051
2308
|
|
|
@@ -2053,17 +2310,18 @@ const normalizeIntoTokenStream = (input, pos, onComment) =>
|
|
|
2053
2310
|
* Parse a stylesheet, CSS Syntax Level 3
|
|
2054
2311
|
* [§5.3.4](https://drafts.csswg.org/css-syntax/#parse-stylesheet).
|
|
2055
2312
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2056
|
-
* @param {
|
|
2313
|
+
* @param {number=} pos start position (string input only)
|
|
2314
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2057
2315
|
* @returns {Stylesheet} the parsed stylesheet
|
|
2058
2316
|
*/
|
|
2059
|
-
const parseAStylesheet = (input,
|
|
2317
|
+
const parseAStylesheet = (input, pos = 0, options = {}) => {
|
|
2060
2318
|
// 1. If input is a byte stream for a stylesheet, decode bytes from input, and set input to the result.
|
|
2061
2319
|
// 2. Normalize input, and set input to the result.
|
|
2062
|
-
const ts = normalizeIntoTokenStream(input,
|
|
2320
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2063
2321
|
useObjectBackend(ts.locConverter);
|
|
2064
2322
|
// 3. Create a new stylesheet, with its location set to location (or null, if location was not passed).
|
|
2065
2323
|
const start = ts.next().start;
|
|
2066
|
-
const stylesheet = /** @type {Stylesheet} */ (
|
|
2324
|
+
const stylesheet = /** @type {Stylesheet} */ (_makeStylesheet(start));
|
|
2067
2325
|
// 4. Consume a stylesheet's contents from input, and set the stylesheet's rules to the result.
|
|
2068
2326
|
_setRules(stylesheet, consumeAStylesheetsContents(ts));
|
|
2069
2327
|
_setEnd(stylesheet, ts.next().start);
|
|
@@ -2078,12 +2336,13 @@ const parseAStylesheet = (input, comment) => {
|
|
|
2078
2336
|
* declarations are parse errors (never produced) and top-level CDO (`<!--`) /
|
|
2079
2337
|
* CDC (`-->`) tokens are discarded.
|
|
2080
2338
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2081
|
-
* @param {
|
|
2339
|
+
* @param {number=} pos start position (string input only)
|
|
2340
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2082
2341
|
* @returns {Rule[]} top-level rules
|
|
2083
2342
|
*/
|
|
2084
|
-
const parseAStylesheetsContents = (input,
|
|
2343
|
+
const parseAStylesheetsContents = (input, pos = 0, options = {}) => {
|
|
2085
2344
|
// 1. Normalize input, and set input to the result.
|
|
2086
|
-
const ts = normalizeIntoTokenStream(input,
|
|
2345
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2087
2346
|
useObjectBackend(ts.locConverter);
|
|
2088
2347
|
// 2. Consume a stylesheet’s contents from input, and return the result.
|
|
2089
2348
|
return consumeAStylesheetsContents(ts);
|
|
@@ -2094,12 +2353,12 @@ const parseAStylesheetsContents = (input, comment) => {
|
|
|
2094
2353
|
* [§5.3.6](https://drafts.csswg.org/css-syntax/#parse-block-contents).
|
|
2095
2354
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2096
2355
|
* @param {number=} pos start position (string input only; just past the opening `{`, or 0)
|
|
2097
|
-
* @param {
|
|
2356
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2098
2357
|
* @returns {{ decls: Declaration[], rules: Rule[] }} block decls + rules
|
|
2099
2358
|
*/
|
|
2100
|
-
const parseABlocksContents = (input, pos,
|
|
2359
|
+
const parseABlocksContents = (input, pos = 0, options = {}) => {
|
|
2101
2360
|
// 1. Normalize input, and set input to the result.
|
|
2102
|
-
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2361
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2103
2362
|
useObjectBackend(ts.locConverter);
|
|
2104
2363
|
// 2. Consume a block’s contents from input, and return the result.
|
|
2105
2364
|
return consumeABlocksContents(ts);
|
|
@@ -2112,12 +2371,12 @@ const parseABlocksContents = (input, pos, comment) => {
|
|
|
2112
2371
|
* whitespace; `undefined` (syntax error) otherwise.
|
|
2113
2372
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2114
2373
|
* @param {number=} pos start position (string input only)
|
|
2115
|
-
* @param {
|
|
2374
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2116
2375
|
* @returns {Rule | undefined} the parsed rule
|
|
2117
2376
|
*/
|
|
2118
|
-
const parseARule = (input, pos,
|
|
2377
|
+
const parseARule = (input, pos = 0, options = {}) => {
|
|
2119
2378
|
// 1. Normalize input, and set input to the result.
|
|
2120
|
-
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2379
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2121
2380
|
useObjectBackend(ts.locConverter);
|
|
2122
2381
|
// 2. Discard whitespace from input.
|
|
2123
2382
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
@@ -2143,12 +2402,12 @@ const parseARule = (input, pos, comment) => {
|
|
|
2143
2402
|
* [§5.3.8](https://drafts.csswg.org/css-syntax/#parse-declaration).
|
|
2144
2403
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2145
2404
|
* @param {number=} pos start position (string input only)
|
|
2146
|
-
* @param {
|
|
2405
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2147
2406
|
* @returns {Declaration | undefined} the parsed declaration, or undefined
|
|
2148
2407
|
*/
|
|
2149
|
-
const parseADeclaration = (input, pos,
|
|
2408
|
+
const parseADeclaration = (input, pos = 0, options = {}) => {
|
|
2150
2409
|
// 1. Normalize input, and set input to the result.
|
|
2151
|
-
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2410
|
+
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2152
2411
|
useObjectBackend(ts.locConverter);
|
|
2153
2412
|
// 2. Discard whitespace from input.
|
|
2154
2413
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
@@ -2160,10 +2419,10 @@ const parseADeclaration = (input, pos, comment) => {
|
|
|
2160
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").
|
|
2161
2420
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2162
2421
|
* @param {number=} pos start position (string input only)
|
|
2163
|
-
* @param {
|
|
2422
|
+
* @param {ParseOptions=} options optional comment-token callback (string input only)
|
|
2164
2423
|
* @returns {ComponentValue | undefined} the parsed component value, or `undefined` on empty / trailing-garbage input
|
|
2165
2424
|
*/
|
|
2166
|
-
const parseAComponentValue = (input, pos, options = {}) => {
|
|
2425
|
+
const parseAComponentValue = (input, pos = 0, options = {}) => {
|
|
2167
2426
|
// 1. Normalize input, and set input to the result.
|
|
2168
2427
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2169
2428
|
useObjectBackend(ts.locConverter);
|
|
@@ -2185,27 +2444,28 @@ const parseAComponentValue = (input, pos, options = {}) => {
|
|
|
2185
2444
|
* [§5.3.10](https://drafts.csswg.org/css-syntax/#parse-list-of-components).
|
|
2186
2445
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2187
2446
|
* @param {number=} pos start position (string input only)
|
|
2188
|
-
* @param {
|
|
2447
|
+
* @param {ParseOptions=} options comment callback
|
|
2189
2448
|
* @returns {ComponentValue[]} component values
|
|
2190
2449
|
*/
|
|
2191
|
-
const parseAListOfComponentValues = (input, pos, options = {}) => {
|
|
2450
|
+
const parseAListOfComponentValues = (input, pos = 0, options = {}) => {
|
|
2192
2451
|
// 1. Normalize input, and set input to the result.
|
|
2193
2452
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2194
2453
|
useObjectBackend(ts.locConverter);
|
|
2195
2454
|
// 2. Consume a list of component values from input, and return the result.
|
|
2196
|
-
|
|
2455
|
+
// (`null` needs `bailOnCurly`, which is not passed here.)
|
|
2456
|
+
return /** @type {ComponentValue[]} */ (consumeAListOfComponentValues(ts));
|
|
2197
2457
|
};
|
|
2198
2458
|
|
|
2199
2459
|
/**
|
|
2200
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.
|
|
2201
2461
|
* @param {string | TokenStream} input source string or an existing token stream
|
|
2202
2462
|
* @param {number=} pos start position (string input only)
|
|
2203
|
-
* @param {
|
|
2463
|
+
* @param {ParseOptions=} options comment callback
|
|
2204
2464
|
* @returns {ComponentValue[][]} comma-separated groups of component values
|
|
2205
2465
|
*/
|
|
2206
2466
|
const parseACommaSeparatedListOfComponentValues = (
|
|
2207
2467
|
input,
|
|
2208
|
-
pos,
|
|
2468
|
+
pos = 0,
|
|
2209
2469
|
options = {}
|
|
2210
2470
|
) => {
|
|
2211
2471
|
// 1. Normalize input, and set input to the result.
|
|
@@ -2217,7 +2477,11 @@ const parseACommaSeparatedListOfComponentValues = (
|
|
|
2217
2477
|
// 3. While input is not empty:
|
|
2218
2478
|
while (ts.next().type !== TT_EOF) {
|
|
2219
2479
|
// 3.1. Consume a list of component values from input, with <comma-token> as the stop token, and append the result to groups.
|
|
2220
|
-
groups.push(
|
|
2480
|
+
groups.push(
|
|
2481
|
+
/** @type {ComponentValue[]} */ (
|
|
2482
|
+
consumeAListOfComponentValues(ts, TT_COMMA)
|
|
2483
|
+
)
|
|
2484
|
+
);
|
|
2221
2485
|
// 3.2 Discard a token from input.
|
|
2222
2486
|
ts.discard();
|
|
2223
2487
|
}
|
|
@@ -2292,19 +2556,20 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2292
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.
|
|
2293
2557
|
const head = ts.consume();
|
|
2294
2558
|
const rule = /** @type {AtRule} */ (
|
|
2295
|
-
|
|
2296
|
-
);
|
|
2297
|
-
_setName(
|
|
2298
|
-
rule,
|
|
2299
|
-
ts.input.slice(head.start + 1, head.end),
|
|
2300
|
-
head.start,
|
|
2301
|
-
head.end
|
|
2559
|
+
_makeContainer(T_AT_RULE, head.start, head.end)
|
|
2302
2560
|
);
|
|
2303
|
-
|
|
2304
|
-
_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() : [];
|
|
2305
2565
|
// declarations / childRules / blockStart (-1) / blockEnd (-1) keep their
|
|
2306
2566
|
// defaults (no block: the `;` / EOF / nested-`}` at-rule forms).
|
|
2307
2567
|
|
|
2568
|
+
// Like `consumeAQualifiedRule`: skip mode scans the prelude without
|
|
2569
|
+
// materializing it (url tokens / functions kept so `@import url(…)` still
|
|
2570
|
+
// resolves); the block boundary is found by scanning, not the prelude nodes.
|
|
2571
|
+
const skip = _skipAtRulePrelude;
|
|
2572
|
+
|
|
2308
2573
|
// Process input
|
|
2309
2574
|
for (;;) {
|
|
2310
2575
|
const t = ts.next();
|
|
@@ -2314,6 +2579,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2314
2579
|
// Discard a token from input. If rule is valid in the current context, return it; otherwise return nothing.
|
|
2315
2580
|
if (t.type === TT_SEMICOLON || t.type === TT_EOF) {
|
|
2316
2581
|
ts.discard();
|
|
2582
|
+
_setPrelude(rule, prelude);
|
|
2317
2583
|
_setEnd(rule, t.start);
|
|
2318
2584
|
return rule;
|
|
2319
2585
|
}
|
|
@@ -2322,15 +2588,18 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2322
2588
|
// Otherwise, consume a token and append the result to rule’s prelude.
|
|
2323
2589
|
else if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2324
2590
|
if (nested) {
|
|
2591
|
+
_setPrelude(rule, prelude);
|
|
2325
2592
|
_setEnd(rule, t.start);
|
|
2326
2593
|
return rule;
|
|
2327
2594
|
}
|
|
2328
|
-
|
|
2595
|
+
const node = consumeATokenAsNode(ts);
|
|
2596
|
+
if (!skip) prelude.push(node);
|
|
2329
2597
|
continue;
|
|
2330
2598
|
}
|
|
2331
2599
|
// <{-token>
|
|
2332
2600
|
// Consume a block from input, and assign the result to rule's declarations and child rules.
|
|
2333
2601
|
else if (t.type === TT_LEFT_CURLY_BRACKET) {
|
|
2602
|
+
_setPrelude(rule, prelude);
|
|
2334
2603
|
const block = consumeABlock(ts);
|
|
2335
2604
|
_setBody(rule, block.decls, block.rules);
|
|
2336
2605
|
_setBlock(rule, block.blockStart, block.blockEnd);
|
|
@@ -2340,7 +2609,15 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2340
2609
|
|
|
2341
2610
|
// anything else
|
|
2342
2611
|
// Consume a component value from input and append the returned value to rule’s prelude.
|
|
2343
|
-
|
|
2612
|
+
const node = consumeAComponentValue(ts, t);
|
|
2613
|
+
if (!skip) {
|
|
2614
|
+
prelude.push(node);
|
|
2615
|
+
} else if (
|
|
2616
|
+
_nodeTypeOf(node) === T_FUNCTION ||
|
|
2617
|
+
_nodeTypeOf(node) === T_URL
|
|
2618
|
+
) {
|
|
2619
|
+
prelude.push(node);
|
|
2620
|
+
}
|
|
2344
2621
|
}
|
|
2345
2622
|
};
|
|
2346
2623
|
|
|
@@ -2369,13 +2646,21 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2369
2646
|
const start = ts.next().start;
|
|
2370
2647
|
// Let rule be a new qualified rule with its prelude, declarations, and child rules all initially set to empty lists.
|
|
2371
2648
|
const rule = /** @type {QualifiedRule} */ (
|
|
2372
|
-
|
|
2649
|
+
_makeContainer(T_QUALIFIED_RULE, start, start)
|
|
2373
2650
|
);
|
|
2374
|
-
|
|
2375
|
-
|
|
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() : [];
|
|
2376
2654
|
// declarations / childRules / blockStart (-1) / blockEnd (-1) keep their
|
|
2377
2655
|
// defaults (no block until a `{` is reached).
|
|
2378
2656
|
|
|
2657
|
+
// Skip mode leaves `prelude` empty (selector text is recovered from the
|
|
2658
|
+
// rule's byte range, not its nodes); `first`/`second` still track the first
|
|
2659
|
+
// two non-whitespace tokens the `--foo: {` disambiguation below needs.
|
|
2660
|
+
const skip = _skipSelectorPrelude;
|
|
2661
|
+
let first = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
2662
|
+
let second = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
2663
|
+
|
|
2379
2664
|
// Process input
|
|
2380
2665
|
for (;;) {
|
|
2381
2666
|
const t = ts.next();
|
|
@@ -2389,7 +2674,13 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2389
2674
|
// This is a parse error. If nested is true, return nothing. Otherwise, consume a token and append the result to rule’s prelude.
|
|
2390
2675
|
else if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2391
2676
|
if (nested) return undefined;
|
|
2392
|
-
|
|
2677
|
+
const node = consumeATokenAsNode(ts);
|
|
2678
|
+
if (skip) {
|
|
2679
|
+
if (!first) first = node;
|
|
2680
|
+
else if (!second) second = node;
|
|
2681
|
+
} else {
|
|
2682
|
+
prelude.push(node);
|
|
2683
|
+
}
|
|
2393
2684
|
continue;
|
|
2394
2685
|
}
|
|
2395
2686
|
// <{-token>
|
|
@@ -2399,31 +2690,33 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2399
2690
|
// (This disambiguates custom-property declarations from nested qualified rules — `--foo: { … }` at top level of a block is a declaration, not a rule.)
|
|
2400
2691
|
// Otherwise, consume a block from input, and let child rules be the result.
|
|
2401
2692
|
else if (t.type === TT_LEFT_CURLY_BRACKET) {
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2693
|
+
if (!skip) {
|
|
2694
|
+
let firstIdx = 0;
|
|
2695
|
+
/* istanbul ignore next -- @preserve: leading whitespace is discarded before the rule, so the prelude never starts with it */
|
|
2696
|
+
while (
|
|
2697
|
+
firstIdx < prelude.length &&
|
|
2698
|
+
_nodeTypeOf(prelude[firstIdx]) === T_WHITESPACE
|
|
2699
|
+
) {
|
|
2700
|
+
firstIdx++;
|
|
2701
|
+
}
|
|
2702
|
+
let secondIdx = firstIdx + 1;
|
|
2703
|
+
while (
|
|
2704
|
+
secondIdx < prelude.length &&
|
|
2705
|
+
_nodeTypeOf(prelude[secondIdx]) === T_WHITESPACE
|
|
2706
|
+
) {
|
|
2707
|
+
secondIdx++;
|
|
2708
|
+
}
|
|
2709
|
+
first = prelude[firstIdx];
|
|
2710
|
+
second = prelude[secondIdx];
|
|
2416
2711
|
}
|
|
2417
|
-
const first = prelude[firstIdx];
|
|
2418
|
-
const second = prelude[secondIdx];
|
|
2419
2712
|
if (
|
|
2420
2713
|
first &&
|
|
2421
|
-
|
|
2714
|
+
_nodeTypeOf(first) === T_IDENT &&
|
|
2422
2715
|
// Test the source bytes directly — avoids forcing the lazy `value`
|
|
2423
2716
|
// slice just to check the `--` custom-property prefix.
|
|
2424
|
-
ts.input.startsWith("--",
|
|
2717
|
+
ts.input.startsWith("--", _nodeStartOf(first)) &&
|
|
2425
2718
|
second &&
|
|
2426
|
-
|
|
2719
|
+
_nodeTypeOf(second) === T_COLON
|
|
2427
2720
|
) {
|
|
2428
2721
|
/* istanbul ignore if -- @preserve: when nested, `declarationStartLikely` routes every `--x:` to consumeADeclaration (which accepts custom properties), so this fallthrough is unreachable */
|
|
2429
2722
|
if (nested) {
|
|
@@ -2433,6 +2726,7 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2433
2726
|
}
|
|
2434
2727
|
return undefined;
|
|
2435
2728
|
}
|
|
2729
|
+
_setPrelude(rule, prelude);
|
|
2436
2730
|
const block = consumeABlock(ts);
|
|
2437
2731
|
_setBody(rule, block.decls, block.rules);
|
|
2438
2732
|
_setBlock(rule, block.blockStart, block.blockEnd);
|
|
@@ -2442,7 +2736,21 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2442
2736
|
|
|
2443
2737
|
// anything else
|
|
2444
2738
|
// Consume a component value from input and append the result to rule’s prelude.
|
|
2445
|
-
|
|
2739
|
+
const node = consumeAComponentValue(ts, t);
|
|
2740
|
+
if (skip) {
|
|
2741
|
+
// Keep only url-bearing nodes (url tokens, or functions that may hold a
|
|
2742
|
+
// url like `:unknown(url(x))`) so the url visitor still rewrites them;
|
|
2743
|
+
// other selector tokens have no non-modules consumer, so drop them.
|
|
2744
|
+
const ty = _nodeTypeOf(node);
|
|
2745
|
+
if (ty === T_FUNCTION || ty === T_URL) prelude.push(node);
|
|
2746
|
+
// Track the first two non-whitespace tokens for the disambiguation above.
|
|
2747
|
+
if (t.type !== TT_WHITESPACE) {
|
|
2748
|
+
if (!first) first = node;
|
|
2749
|
+
else if (!second) second = node;
|
|
2750
|
+
}
|
|
2751
|
+
} else {
|
|
2752
|
+
prelude.push(node);
|
|
2753
|
+
}
|
|
2446
2754
|
}
|
|
2447
2755
|
};
|
|
2448
2756
|
|
|
@@ -2466,7 +2774,8 @@ const consumeABlock = (ts) => {
|
|
|
2466
2774
|
};
|
|
2467
2775
|
|
|
2468
2776
|
/**
|
|
2469
|
-
* 2-token lookahead: is the next non-whitespace pair `<ident> <colon
|
|
2777
|
+
* 2-token lookahead: is the next non-whitespace pair `<ident> <colon>`?
|
|
2778
|
+
* Peeks raw code points without advancing; comments still fire `onComment` later.
|
|
2470
2779
|
* @param {TokenStream} ts token stream
|
|
2471
2780
|
* @returns {boolean} true if consume-a-declaration's step 1 + step 3 would both succeed on the current input
|
|
2472
2781
|
*/
|
|
@@ -2517,8 +2826,12 @@ const declarationStartLikely = (ts) => {
|
|
|
2517
2826
|
const consumeABlocksContents = (ts, onNode) => {
|
|
2518
2827
|
/** @type {Declaration[]} */
|
|
2519
2828
|
const decls = [];
|
|
2520
|
-
|
|
2521
|
-
|
|
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;
|
|
2522
2835
|
|
|
2523
2836
|
// Process input:
|
|
2524
2837
|
for (;;) {
|
|
@@ -2532,7 +2845,7 @@ const consumeABlocksContents = (ts, onNode) => {
|
|
|
2532
2845
|
// <EOF-token> / <}-token>
|
|
2533
2846
|
// Return decls and rules.
|
|
2534
2847
|
else if (t.type === TT_EOF || t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2535
|
-
return { decls, rules };
|
|
2848
|
+
return { decls, rules: rules || _EMPTY_LIST };
|
|
2536
2849
|
}
|
|
2537
2850
|
// <at-keyword-token>
|
|
2538
2851
|
// Consume an at-rule from input, with nested set to true. If a rule was returned, append it to rules.
|
|
@@ -2540,7 +2853,7 @@ const consumeABlocksContents = (ts, onNode) => {
|
|
|
2540
2853
|
const atRule = consumeAnAtRule(ts, true);
|
|
2541
2854
|
if (atRule) {
|
|
2542
2855
|
if (onNode) onNode(atRule);
|
|
2543
|
-
else rules.push(atRule);
|
|
2856
|
+
else (rules || (rules = [])).push(atRule);
|
|
2544
2857
|
}
|
|
2545
2858
|
}
|
|
2546
2859
|
// anything else
|
|
@@ -2563,7 +2876,7 @@ const consumeABlocksContents = (ts, onNode) => {
|
|
|
2563
2876
|
const rule = consumeAQualifiedRule(ts, TT_SEMICOLON, true);
|
|
2564
2877
|
if (rule) {
|
|
2565
2878
|
if (onNode) onNode(rule);
|
|
2566
|
-
else rules.push(rule);
|
|
2879
|
+
else (rules || (rules = [])).push(rule);
|
|
2567
2880
|
}
|
|
2568
2881
|
}
|
|
2569
2882
|
}
|
|
@@ -2611,14 +2924,14 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2611
2924
|
// name "" / nameStart / nameEnd (= start) / important (false) keep their
|
|
2612
2925
|
// `Container` defaults; `value` is set unconditionally at step 5 below.
|
|
2613
2926
|
const decl = /** @type {Declaration} */ (
|
|
2614
|
-
|
|
2927
|
+
_makeContainer(T_DECLARATION, start, start)
|
|
2615
2928
|
);
|
|
2616
2929
|
|
|
2617
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.
|
|
2618
2931
|
// Otherwise, consume the remnants of a bad declaration from input, with nested, and return nothing.
|
|
2619
2932
|
if (ts.next().type === TT_IDENTIFIER) {
|
|
2620
2933
|
const head = ts.consume();
|
|
2621
|
-
_setName(decl,
|
|
2934
|
+
_setName(decl, head.start, head.end);
|
|
2622
2935
|
} else {
|
|
2623
2936
|
consumeTheRemnantsOfABadDeclaration(ts, nested);
|
|
2624
2937
|
return undefined;
|
|
@@ -2639,23 +2952,39 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2639
2952
|
// 4. Discard whitespace from input.
|
|
2640
2953
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
2641
2954
|
|
|
2955
|
+
// Step 8's custom-property test, computed early so the value parse can bail.
|
|
2956
|
+
const isCustomProperty = input.startsWith("--", start);
|
|
2957
|
+
|
|
2642
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.
|
|
2643
|
-
|
|
2644
|
-
|
|
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.
|
|
2645
2972
|
_setEnd(decl, ts.next().start);
|
|
2646
2973
|
|
|
2647
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.
|
|
2648
2975
|
{
|
|
2649
2976
|
let last = value.length - 1;
|
|
2650
|
-
while (last >= 0 &&
|
|
2977
|
+
while (last >= 0 && _nodeTypeOf(value[last]) === T_WHITESPACE) last--;
|
|
2651
2978
|
let prev = last - 1;
|
|
2652
|
-
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.
|
|
2653
2982
|
if (
|
|
2654
2983
|
prev >= 0 &&
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
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")
|
|
2659
2988
|
) {
|
|
2660
2989
|
_setImportant(decl);
|
|
2661
2990
|
value.length = prev;
|
|
@@ -2663,7 +2992,10 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2663
2992
|
}
|
|
2664
2993
|
|
|
2665
2994
|
// 7. While the last item in decl's value is a <whitespace-token>, remove that token.
|
|
2666
|
-
while (
|
|
2995
|
+
while (
|
|
2996
|
+
value.length > 0 &&
|
|
2997
|
+
_nodeTypeOf(value[value.length - 1]) === T_WHITESPACE
|
|
2998
|
+
) {
|
|
2667
2999
|
value.pop();
|
|
2668
3000
|
}
|
|
2669
3001
|
|
|
@@ -2671,17 +3003,17 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2671
3003
|
// Otherwise, if decl's value contains a top-level simple block with an associated token of <{-token>, return nothing.
|
|
2672
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.)
|
|
2673
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.)
|
|
2674
|
-
const isCustomProperty = input.startsWith("--", start);
|
|
2675
3006
|
if (!isCustomProperty) {
|
|
2676
3007
|
for (let i = 0; i < value.length; i++) {
|
|
2677
3008
|
const v = value[i];
|
|
2678
|
-
if (
|
|
3009
|
+
if (_nodeTypeOf(v) === T_SIMPLE_BLOCK && _nodeTokenOf(v) === "{") {
|
|
2679
3010
|
return undefined;
|
|
2680
3011
|
}
|
|
2681
3012
|
}
|
|
2682
3013
|
}
|
|
2683
3014
|
|
|
2684
3015
|
// 9. Return decl.
|
|
3016
|
+
_setValue(decl, value);
|
|
2685
3017
|
return decl;
|
|
2686
3018
|
};
|
|
2687
3019
|
|
|
@@ -2690,11 +3022,16 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2690
3022
|
* @param {TokenStream} ts token stream
|
|
2691
3023
|
* @param {number=} stopToken token type that terminates the list (left unconsumed)
|
|
2692
3024
|
* @param {boolean=} nested true inside a `{}` block — a top-level `}` ends the list (left unconsumed)
|
|
2693
|
-
* @
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
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() : []);
|
|
2698
3035
|
// Process input
|
|
2699
3036
|
for (;;) {
|
|
2700
3037
|
const t = ts.next();
|
|
@@ -2710,12 +3047,33 @@ const consumeAListOfComponentValues = (ts, stopToken, nested = false) => {
|
|
|
2710
3047
|
// Otherwise, this is a parse error. Consume a token from input and append the result to values.
|
|
2711
3048
|
if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2712
3049
|
if (nested) return values;
|
|
2713
|
-
|
|
3050
|
+
const closer = consumeATokenAsNode(ts);
|
|
3051
|
+
// Keep unless the type is explicitly marked skip (1); an out-of-range
|
|
3052
|
+
// lookup on a short `skip.types` yields `undefined`, which must not drop.
|
|
3053
|
+
if (!_skipActive || _skipTypes[_nodeTypeOf(closer)] !== 1) {
|
|
3054
|
+
values.push(closer);
|
|
3055
|
+
}
|
|
2714
3056
|
continue;
|
|
2715
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;
|
|
2716
3061
|
// anything else
|
|
2717
3062
|
// Consume a component value from input, and append the result to values.
|
|
2718
|
-
|
|
3063
|
+
// Skipped leaf types short-circuit before materializing: no SoA slot is
|
|
3064
|
+
// written and no node is built (blocks / functions never skip here).
|
|
3065
|
+
const tt = t.type;
|
|
3066
|
+
if (
|
|
3067
|
+
_skipActive &&
|
|
3068
|
+
tt !== TT_FUNCTION &&
|
|
3069
|
+
!(tt >= TT_LEFT_PARENTHESIS && tt <= TT_LEFT_CURLY_BRACKET) &&
|
|
3070
|
+
_skipTypes[_ttToNodeType[tt]] === 1
|
|
3071
|
+
) {
|
|
3072
|
+
ts.consume();
|
|
3073
|
+
continue;
|
|
3074
|
+
}
|
|
3075
|
+
const node = consumeAComponentValue(ts, t);
|
|
3076
|
+
if (!_skipActive || _skipTypes[_nodeTypeOf(node)] !== 1) values.push(node);
|
|
2719
3077
|
}
|
|
2720
3078
|
};
|
|
2721
3079
|
|
|
@@ -2761,11 +3119,11 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2761
3119
|
|
|
2762
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.
|
|
2763
3121
|
const block = /** @type {SimpleBlock} */ (
|
|
2764
|
-
|
|
3122
|
+
_makeContainer(T_SIMPLE_BLOCK, open.start, open.end)
|
|
2765
3123
|
);
|
|
2766
3124
|
_setToken(block, token);
|
|
2767
|
-
|
|
2768
|
-
|
|
3125
|
+
// Sealed (`_setValue`) at the return, once complete.
|
|
3126
|
+
const val = _soaActive ? _soaList() : [];
|
|
2769
3127
|
|
|
2770
3128
|
// Discard a token from input.
|
|
2771
3129
|
ts.discard();
|
|
@@ -2779,6 +3137,7 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2779
3137
|
// Discard a token from input. Return block.
|
|
2780
3138
|
if (t.type === TT_EOF || t.type === ending) {
|
|
2781
3139
|
ts.discard();
|
|
3140
|
+
_setValue(block, val);
|
|
2782
3141
|
_setEnd(block, t.end);
|
|
2783
3142
|
return block;
|
|
2784
3143
|
}
|
|
@@ -2795,16 +3154,15 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2795
3154
|
* @returns {FunctionNode | undefined} the consumed function node
|
|
2796
3155
|
*/
|
|
2797
3156
|
const consumeAFunction = (ts) => {
|
|
2798
|
-
const { input } = ts;
|
|
2799
3157
|
// Assert (spec): the next token is a <function-token>.
|
|
2800
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.
|
|
2801
3159
|
const tFn = ts.consume();
|
|
2802
3160
|
const fn = /** @type {FunctionNode} */ (
|
|
2803
|
-
|
|
3161
|
+
_makeContainer(T_FUNCTION, tFn.start, tFn.end)
|
|
2804
3162
|
);
|
|
2805
|
-
_setName(fn,
|
|
2806
|
-
|
|
2807
|
-
|
|
3163
|
+
_setName(fn, tFn.start, tFn.end - 1);
|
|
3164
|
+
// Sealed (`_setValue`) at the return, once complete.
|
|
3165
|
+
const val = _soaActive ? _soaList() : [];
|
|
2808
3166
|
|
|
2809
3167
|
// Process input
|
|
2810
3168
|
for (;;) {
|
|
@@ -2815,13 +3173,26 @@ const consumeAFunction = (ts) => {
|
|
|
2815
3173
|
// <)-token>
|
|
2816
3174
|
// Discard a token from input. Return function.
|
|
2817
3175
|
ts.discard();
|
|
3176
|
+
_setValue(fn, val);
|
|
2818
3177
|
_setEnd(fn, t.end);
|
|
2819
3178
|
return fn;
|
|
2820
3179
|
}
|
|
2821
3180
|
|
|
2822
3181
|
// anything else
|
|
2823
3182
|
// Consume a component value from input and append the result to function’s value.
|
|
2824
|
-
|
|
3183
|
+
// Same pre-materialization skip as `consumeAListOfComponentValues`.
|
|
3184
|
+
const tt = t.type;
|
|
3185
|
+
if (
|
|
3186
|
+
_skipActive &&
|
|
3187
|
+
tt !== TT_FUNCTION &&
|
|
3188
|
+
!(tt >= TT_LEFT_PARENTHESIS && tt <= TT_LEFT_CURLY_BRACKET) &&
|
|
3189
|
+
_skipTypes[_ttToNodeType[tt]] === 1
|
|
3190
|
+
) {
|
|
3191
|
+
ts.consume();
|
|
3192
|
+
continue;
|
|
3193
|
+
}
|
|
3194
|
+
const node = consumeAComponentValue(ts, t);
|
|
3195
|
+
if (!_skipActive || _skipTypes[_nodeTypeOf(node)] !== 1) val.push(node);
|
|
2825
3196
|
}
|
|
2826
3197
|
};
|
|
2827
3198
|
|
|
@@ -2879,13 +3250,18 @@ const _escapeIdentifier = (str) => {
|
|
|
2879
3250
|
}
|
|
2880
3251
|
output = lastFlush === 0 ? str : output + str.slice(lastFlush);
|
|
2881
3252
|
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
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
|
+
) {
|
|
2885
3261
|
output = `\\-${output.slice(1)}`;
|
|
2886
|
-
} else if (
|
|
3262
|
+
} else if (_isDigit(first)) {
|
|
2887
3263
|
// A leading digit becomes `\3<digit> `, another `\HEX ` run to clean up.
|
|
2888
|
-
output = `\\3${
|
|
3264
|
+
output = `\\3${str.charAt(0)} ${output.slice(1)}`;
|
|
2889
3265
|
needSpaceFix = true;
|
|
2890
3266
|
}
|
|
2891
3267
|
|
|
@@ -2919,7 +3295,6 @@ const _escapeIdentifier = (str) => {
|
|
|
2919
3295
|
*/
|
|
2920
3296
|
const gobbleHex = (str, start) => {
|
|
2921
3297
|
let hex = "";
|
|
2922
|
-
let spaceTerminated = false;
|
|
2923
3298
|
|
|
2924
3299
|
for (let i = 0; i < 6; i++) {
|
|
2925
3300
|
const code = str.charCodeAt(start + i);
|
|
@@ -2928,8 +3303,6 @@ const gobbleHex = (str, start) => {
|
|
|
2928
3303
|
(code >= 48 && code <= 57) ||
|
|
2929
3304
|
(code >= 65 && code <= 70) ||
|
|
2930
3305
|
(code >= 97 && code <= 102);
|
|
2931
|
-
// https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
|
|
2932
|
-
spaceTerminated = code === 32;
|
|
2933
3306
|
if (!valid) break;
|
|
2934
3307
|
// parseInt below is case-insensitive, so keep the original char.
|
|
2935
3308
|
hex += str[start + i];
|
|
@@ -2937,6 +3310,16 @@ const gobbleHex = (str, start) => {
|
|
|
2937
3310
|
|
|
2938
3311
|
if (hex.length === 0) return undefined;
|
|
2939
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
|
+
|
|
2940
3323
|
const codePoint = Number.parseInt(hex, 16);
|
|
2941
3324
|
const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff;
|
|
2942
3325
|
|
|
@@ -2944,13 +3327,10 @@ const gobbleHex = (str, start) => {
|
|
|
2944
3327
|
// "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
|
|
2945
3328
|
// https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
|
|
2946
3329
|
if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) {
|
|
2947
|
-
return ["�",
|
|
3330
|
+
return ["�", consumed];
|
|
2948
3331
|
}
|
|
2949
3332
|
|
|
2950
|
-
return [
|
|
2951
|
-
String.fromCodePoint(codePoint),
|
|
2952
|
-
hex.length + (spaceTerminated ? 1 : 0)
|
|
2953
|
-
];
|
|
3333
|
+
return [String.fromCodePoint(codePoint), consumed];
|
|
2954
3334
|
};
|
|
2955
3335
|
|
|
2956
3336
|
/**
|
|
@@ -2996,14 +3376,74 @@ const _unescapeIdentifier = (str) => {
|
|
|
2996
3376
|
const escapeIdentifier = makeCacheable(_escapeIdentifier);
|
|
2997
3377
|
const unescapeIdentifier = makeCacheable(_unescapeIdentifier);
|
|
2998
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
|
+
|
|
2999
3440
|
// CSS-typed views over the generic visitor machinery (`util/SourceProcessor`),
|
|
3000
3441
|
// re-exported so consumers keep importing them from this module.
|
|
3001
3442
|
/**
|
|
3002
|
-
* @typedef {import("../util/SourceProcessor").
|
|
3003
|
-
* @typedef {import("../util/SourceProcessor").
|
|
3004
|
-
* @typedef {import("../util/SourceProcessor").
|
|
3005
|
-
* @typedef {import("../util/SourceProcessor").
|
|
3006
|
-
* @typedef {import("../util/SourceProcessor").CompiledVisitorMap<Node>} CompiledVisitorMap
|
|
3443
|
+
* @typedef {import("../util/SourceProcessor").VisitorFn<CssPath>} VisitorFn
|
|
3444
|
+
* @typedef {import("../util/SourceProcessor").VisitorBucket<CssPath>} VisitorBucket
|
|
3445
|
+
* @typedef {import("../util/SourceProcessor").VisitorMap<CssPath>} VisitorMap
|
|
3446
|
+
* @typedef {import("../util/SourceProcessor").CompiledVisitorMap<CssPath>} CompiledVisitorMap
|
|
3007
3447
|
*/
|
|
3008
3448
|
|
|
3009
3449
|
/**
|
|
@@ -3027,10 +3467,149 @@ const TOP_LEVEL_CONSUMERS = {
|
|
|
3027
3467
|
/**
|
|
3028
3468
|
* @typedef {object} CssProcessOptions
|
|
3029
3469
|
* @property {LocConverter=} locConverter shared loc converter (default a fresh one over the input)
|
|
3030
|
-
* @property {((input: string, start: number, end: number) => number)=} comment comment-token callback
|
|
3031
3470
|
* @property {boolean=} recurseBlocks walk into block bodies' nested rules (default true)
|
|
3032
3471
|
* @property {("stylesheet" | "block-contents")=} as which top-level production to consume the source as (see `TOP_LEVEL_CONSUMERS`): `"stylesheet"` (default) or `"block-contents"` (a block's contents, e.g. an HTML `style` attribute)
|
|
3472
|
+
* @property {SkipOptions=} skip what the grammar may leave un-materialized to go faster — safe only for parts nothing reads in the active parse; default skip nothing
|
|
3473
|
+
*/
|
|
3474
|
+
|
|
3475
|
+
/**
|
|
3476
|
+
* `CssProcessOptions.skip`: two independent axes, so each reads unambiguously.
|
|
3477
|
+
* @typedef {object} SkipOptions
|
|
3478
|
+
* @property {Uint8Array=} types component-value node types to drop from declaration value / function-arg lists (indexed by `NodeType`, 1 = skip; build with `buildSkipSet`)
|
|
3479
|
+
* @property {boolean=} selectorPrelude drop qualified-rule (selector) preludes — the rule and its block are still produced (default false)
|
|
3480
|
+
* @property {boolean=} atRulePrelude drop at-rule preludes — the at-rule and its block are still produced (default false)
|
|
3481
|
+
*/
|
|
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
|
|
3033
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;
|
|
3034
3613
|
|
|
3035
3614
|
/**
|
|
3036
3615
|
* The CSS `SourceProcessor` grammar: consume top-level rules one at a time
|
|
@@ -3043,100 +3622,66 @@ const TOP_LEVEL_CONSUMERS = {
|
|
|
3043
3622
|
* @param {CssProcessOptions} options process options
|
|
3044
3623
|
*/
|
|
3045
3624
|
const grammar = (input, visitors, options) => {
|
|
3046
|
-
const { comment } = options;
|
|
3047
3625
|
const locConverter = options.locConverter || new LocConverter(input);
|
|
3048
3626
|
useSoaBackend(input, locConverter);
|
|
3049
|
-
const
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
};
|
|
3058
|
-
|
|
3059
|
-
/**
|
|
3060
|
-
* Walk a component-value subtree; children are already materialized. Fetches
|
|
3061
|
-
* the node's visitor bucket once (reused for enter + exit) and uses index
|
|
3062
|
-
* loops — `for…of` would allocate an iterator per node on this hot path.
|
|
3063
|
-
* @param {Node} node component-value root
|
|
3064
|
-
* @param {Node | null} parent enclosing node
|
|
3065
|
-
*/
|
|
3066
|
-
const walkValue = (node, parent) => {
|
|
3067
|
-
const ty = _soaTy[_ix(node)];
|
|
3068
|
-
const b = visitors[ty];
|
|
3069
|
-
let skip = false;
|
|
3070
|
-
if (b !== undefined && b.enter.length !== 0) {
|
|
3071
|
-
skipFlag = false;
|
|
3072
|
-
const e = b.enter;
|
|
3073
|
-
for (let i = 0; i < e.length; i++) e[i](node, parent, visitorCtx);
|
|
3074
|
-
skip = skipFlag;
|
|
3075
|
-
skipFlag = false;
|
|
3076
|
-
}
|
|
3077
|
-
if (!skip && (ty === T_FUNCTION || ty === T_SIMPLE_BLOCK)) {
|
|
3078
|
-
const v = /** @type {Node[]} */ (_soaL0[_ix(node)]);
|
|
3079
|
-
for (let i = 0; i < v.length; i++) walkValue(v[i], node);
|
|
3080
|
-
}
|
|
3081
|
-
if (b !== undefined) {
|
|
3082
|
-
const x = b.exit;
|
|
3083
|
-
for (let i = 0; i < x.length; i++) x[i](node, parent, visitorCtx);
|
|
3084
|
-
}
|
|
3085
|
-
};
|
|
3086
|
-
|
|
3087
|
-
/**
|
|
3088
|
-
* Walk a structural subtree; an at-rule / qualified-rule's block was parsed
|
|
3089
|
-
* eagerly (§5.4.4), so its `value` holds the nested rules / declarations.
|
|
3090
|
-
* @param {Node} node structural-tree root
|
|
3091
|
-
* @param {Node | null} parent enclosing node
|
|
3092
|
-
*/
|
|
3093
|
-
const walkRule = (node, parent) => {
|
|
3094
|
-
const i0 = _ix(node);
|
|
3095
|
-
const ty = _soaTy[i0];
|
|
3096
|
-
const b = visitors[ty];
|
|
3097
|
-
let skip = false;
|
|
3098
|
-
if (b !== undefined && b.enter.length !== 0) {
|
|
3099
|
-
skipFlag = false;
|
|
3100
|
-
const e = b.enter;
|
|
3101
|
-
for (let i = 0; i < e.length; i++) e[i](node, parent, visitorCtx);
|
|
3102
|
-
skip = skipFlag;
|
|
3103
|
-
skipFlag = false;
|
|
3104
|
-
}
|
|
3105
|
-
if (!skip) {
|
|
3106
|
-
if (ty === T_AT_RULE || ty === T_QUALIFIED_RULE) {
|
|
3107
|
-
const p = /** @type {Node[]} */ (_soaL0[i0]);
|
|
3108
|
-
for (let i = 0; i < p.length; i++) walkValue(p[i], node);
|
|
3109
|
-
if (recurseBlocks) {
|
|
3110
|
-
// Declarations then child rules — downstream consumers don't need them strictly interleaved in source order.
|
|
3111
|
-
const decls = _soaL1[i0];
|
|
3112
|
-
if (decls) {
|
|
3113
|
-
for (let i = 0; i < decls.length; i++) walkRule(decls[i], node);
|
|
3114
|
-
}
|
|
3115
|
-
const ch = _soaL2[i0];
|
|
3116
|
-
if (ch) for (let i = 0; i < ch.length; i++) walkRule(ch[i], node);
|
|
3117
|
-
}
|
|
3118
|
-
} else if (ty === T_DECLARATION) {
|
|
3119
|
-
const v = /** @type {Node[]} */ (_soaL0[i0]);
|
|
3120
|
-
for (let i = 0; i < v.length; i++) walkValue(v[i], node);
|
|
3121
|
-
}
|
|
3122
|
-
}
|
|
3123
|
-
if (b !== undefined) {
|
|
3124
|
-
const x = b.exit;
|
|
3125
|
-
for (let i = 0; i < x.length; i++) x[i](node, parent, visitorCtx);
|
|
3126
|
-
}
|
|
3127
|
-
};
|
|
3627
|
+
const skip = options.skip;
|
|
3628
|
+
_skipTypes = (skip && skip.types) || _NO_SKIP_TYPES;
|
|
3629
|
+
_skipActive = _skipTypes !== _NO_SKIP_TYPES;
|
|
3630
|
+
_skipSelectorPrelude = skip !== undefined && skip.selectorPrelude === true;
|
|
3631
|
+
_skipAtRulePrelude = skip !== undefined && skip.atRulePrelude === true;
|
|
3632
|
+
_recurseBlocks = options.recurseBlocks !== false;
|
|
3633
|
+
_visitors = visitors;
|
|
3634
|
+
_commentBucket = visitors[T_COMMENT];
|
|
3128
3635
|
|
|
3129
3636
|
// Stream each top-level node (selected by `as`) to the walker the moment it's
|
|
3130
3637
|
// consumed, rather than collecting them first — so the whole AST is never
|
|
3131
3638
|
// held at once; peak heap is ~one top-level node's subtree.
|
|
3132
|
-
const ts = new TokenStream(
|
|
3639
|
+
const ts = new TokenStream(
|
|
3640
|
+
input,
|
|
3641
|
+
0,
|
|
3642
|
+
locConverter,
|
|
3643
|
+
_commentBucket === undefined ? undefined : _grammarOnComment
|
|
3644
|
+
);
|
|
3133
3645
|
const consume =
|
|
3134
3646
|
TOP_LEVEL_CONSUMERS[options.as || "stylesheet"] ||
|
|
3135
3647
|
consumeAStylesheetsContents;
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3648
|
+
try {
|
|
3649
|
+
consume(ts, _walkTopLevel);
|
|
3650
|
+
} finally {
|
|
3651
|
+
// Drop the module-level SoA references so the last parsed source (and
|
|
3652
|
+
// its LocConverter / child lists / visitors) don't stay alive between
|
|
3653
|
+
// parses.
|
|
3654
|
+
_soaInput = "";
|
|
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;
|
|
3684
|
+
}
|
|
3140
3685
|
};
|
|
3141
3686
|
|
|
3142
3687
|
/**
|
|
@@ -3144,17 +3689,68 @@ const grammar = (input, visitors, options) => {
|
|
|
3144
3689
|
* `grammar`. Babel-style usage:
|
|
3145
3690
|
*
|
|
3146
3691
|
* ```
|
|
3147
|
-
*
|
|
3148
|
-
* processor.process(source);
|
|
3692
|
+
* new SourceProcessor({ skip }).use({ [NodeType.AtRule]: (path) => {} }).process(source);
|
|
3149
3693
|
* ```
|
|
3150
|
-
* @extends {GenericSourceProcessor<
|
|
3694
|
+
* @extends {GenericSourceProcessor<CssPath, CssProcessOptions>}
|
|
3151
3695
|
*/
|
|
3152
3696
|
class SourceProcessor extends GenericSourceProcessor {
|
|
3153
|
-
|
|
3154
|
-
|
|
3697
|
+
/**
|
|
3698
|
+
* @param {CssProcessOptions=} options default process options (`skip`, `as`, …) for every `process` call
|
|
3699
|
+
*/
|
|
3700
|
+
constructor(options) {
|
|
3701
|
+
super(grammar, options);
|
|
3155
3702
|
}
|
|
3156
3703
|
}
|
|
3157
3704
|
|
|
3705
|
+
/**
|
|
3706
|
+
* Build a `SkipOptions.types` set (drop these component-value node types from
|
|
3707
|
+
* value / function-arg lists) from a list of `NodeType`s. Preludes are separate
|
|
3708
|
+
* (`SkipOptions.selectorPrelude` / `atRulePrelude`). The caller owns the safety
|
|
3709
|
+
* contract: only pass types nothing reads in the intended parse. Two
|
|
3710
|
+
* grammar-internal caveats beyond consumer needs: dropping both `Delim` and
|
|
3711
|
+
* `Ident` loses `!important` detection, and dropping `SimpleBlock` loses the
|
|
3712
|
+
* custom-property `{}`-value check (and its subtree). Precompute once per
|
|
3713
|
+
* configuration and reuse across parses.
|
|
3714
|
+
* @param {number[]} nodeTypes component-value node types to drop
|
|
3715
|
+
* @returns {Uint8Array} skip-types set indexed by `NodeType`
|
|
3716
|
+
*/
|
|
3717
|
+
const buildSkipSet = (nodeTypes) => {
|
|
3718
|
+
const set = new Uint8Array(32);
|
|
3719
|
+
for (let i = 0; i < nodeTypes.length; i++) set[nodeTypes[i]] = 1;
|
|
3720
|
+
return set;
|
|
3721
|
+
};
|
|
3722
|
+
|
|
3723
|
+
/* eslint-disable jsdoc/require-template -- `A` below is the accessor const, not a type parameter */
|
|
3724
|
+
/**
|
|
3725
|
+
* The CSS path (Babel's `path` shape): the AST accessor with the walk's
|
|
3726
|
+
* current position on it — the single argument every visitor receives.
|
|
3727
|
+
* @typedef {typeof A} CssPath
|
|
3728
|
+
*/
|
|
3729
|
+
/* eslint-enable jsdoc/require-template */
|
|
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
|
+
|
|
3744
|
+
// Babel's `path.skip()`, children-only: set by `A.skipChildren()` during an
|
|
3745
|
+
// `enter` dispatch, consumed by the walk.
|
|
3746
|
+
let _walkSkip = false;
|
|
3747
|
+
// The walk's current position (`A.node` / `A.parent` read these; module-level
|
|
3748
|
+
// so the accessor methods' defaults avoid self-referential `this` typing).
|
|
3749
|
+
/** @type {Node} */
|
|
3750
|
+
let _currentNode = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
3751
|
+
/** @type {Node | null} */
|
|
3752
|
+
let _currentParent = null;
|
|
3753
|
+
|
|
3158
3754
|
// AST field-access seam. Every AST-node field read by `CssParser` goes through
|
|
3159
3755
|
// one of these accessors so the node representation can change underneath the
|
|
3160
3756
|
// consumer without touching it. Today they are backed by the `Node` / `Token` /
|
|
@@ -3163,51 +3759,104 @@ class SourceProcessor extends GenericSourceProcessor {
|
|
|
3163
3759
|
// consumer edit. `value` is the leaf-token string; container child lists are
|
|
3164
3760
|
// `children` / `prelude` / `declarations` / `childRules`.
|
|
3165
3761
|
const A = {
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3762
|
+
// === path position (rebound by the walk before every visitor call) ===
|
|
3763
|
+
/**
|
|
3764
|
+
* @returns {Node} current node — only valid during a visitor callback
|
|
3765
|
+
*/
|
|
3766
|
+
get node() {
|
|
3767
|
+
return _currentNode;
|
|
3768
|
+
},
|
|
3769
|
+
/**
|
|
3770
|
+
* @returns {Node | null} enclosing node (null = a top-level node)
|
|
3771
|
+
*/
|
|
3772
|
+
get parent() {
|
|
3773
|
+
return _currentParent;
|
|
3774
|
+
},
|
|
3775
|
+
/** Stop the walk descending into the current node (enter only). */
|
|
3776
|
+
skipChildren() {
|
|
3777
|
+
_walkSkip = true;
|
|
3176
3778
|
},
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3779
|
+
// === field reads — `n` defaults to the current node ===
|
|
3780
|
+
/**
|
|
3781
|
+
* @param {Node=} n node
|
|
3782
|
+
* @returns {number} node type
|
|
3783
|
+
*/
|
|
3784
|
+
type(n = _currentNode) {
|
|
3785
|
+
return _soaTypes[_nodeIndex(n)];
|
|
3786
|
+
},
|
|
3787
|
+
/**
|
|
3788
|
+
* @param {Node=} n node
|
|
3789
|
+
* @returns {number} start offset
|
|
3790
|
+
*/
|
|
3791
|
+
start(n = _currentNode) {
|
|
3792
|
+
return _soaStarts[_nodeIndex(n)];
|
|
3793
|
+
},
|
|
3794
|
+
/**
|
|
3795
|
+
* @param {Node=} n node
|
|
3796
|
+
* @returns {number} end offset
|
|
3797
|
+
*/
|
|
3798
|
+
end(n = _currentNode) {
|
|
3799
|
+
return _soaEnds[_nodeIndex(n)];
|
|
3800
|
+
},
|
|
3801
|
+
/**
|
|
3802
|
+
* @param {Node=} n node
|
|
3803
|
+
* @returns {[number, number]} start / end offsets
|
|
3804
|
+
*/
|
|
3805
|
+
range(n = _currentNode) {
|
|
3806
|
+
const i = _nodeIndex(n);
|
|
3807
|
+
return [_soaStarts[i], _soaEnds[i]];
|
|
3808
|
+
},
|
|
3809
|
+
/**
|
|
3810
|
+
* @param {Node=} n node
|
|
3811
|
+
* @returns {{ start: { line: number, column: number }, end: { line: number, column: number } }} source location
|
|
3812
|
+
*/
|
|
3813
|
+
loc(n = _currentNode) {
|
|
3814
|
+
const i = _nodeIndex(n);
|
|
3815
|
+
const lc = _soaLocConverter;
|
|
3816
|
+
const s = lc.get(_soaStarts[i]);
|
|
3182
3817
|
const sl = s.line;
|
|
3183
3818
|
const sc = s.column;
|
|
3184
|
-
const e = lc.get(
|
|
3819
|
+
const e = lc.get(_soaEnds[i]);
|
|
3185
3820
|
return {
|
|
3186
3821
|
start: { line: sl, column: sc },
|
|
3187
3822
|
end: { line: e.line, column: e.column }
|
|
3188
3823
|
};
|
|
3189
3824
|
},
|
|
3190
|
-
/**
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3825
|
+
/**
|
|
3826
|
+
* @param {Node=} n node
|
|
3827
|
+
* @returns {string} raw source slice
|
|
3828
|
+
*/
|
|
3829
|
+
source(n = _currentNode) {
|
|
3830
|
+
const i = _nodeIndex(n);
|
|
3831
|
+
return _soaInput.slice(_soaStarts[i], _soaEnds[i]);
|
|
3832
|
+
},
|
|
3833
|
+
/**
|
|
3834
|
+
* @param {Node=} n node
|
|
3835
|
+
* @returns {string} raw token value
|
|
3836
|
+
*/
|
|
3837
|
+
value(n = _currentNode) {
|
|
3838
|
+
return _soaValueOf(_nodeIndex(n));
|
|
3194
3839
|
},
|
|
3195
|
-
/**
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3840
|
+
/**
|
|
3841
|
+
* @param {Node=} n node
|
|
3842
|
+
* @returns {string} unescaped token value
|
|
3843
|
+
*/
|
|
3844
|
+
unescaped(n = _currentNode) {
|
|
3845
|
+
const i = _nodeIndex(n);
|
|
3200
3846
|
const v = _soaValueOf(i);
|
|
3201
|
-
return
|
|
3847
|
+
return _soaTypes[i] === T_STRING
|
|
3202
3848
|
? unescapeIdentifier(v.slice(1, -1))
|
|
3203
3849
|
: unescapeIdentifier(v);
|
|
3204
3850
|
},
|
|
3205
|
-
/**
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3851
|
+
/**
|
|
3852
|
+
* @param {Node=} n node
|
|
3853
|
+
* @returns {string} hash / numeric type flag
|
|
3854
|
+
*/
|
|
3855
|
+
typeFlag(n = _currentNode) {
|
|
3856
|
+
const i = _nodeIndex(n);
|
|
3857
|
+
if (_soaTypes[i] === T_HASH) {
|
|
3209
3858
|
const input = _soaInput;
|
|
3210
|
-
const p =
|
|
3859
|
+
const p = _soaStarts[i] + 1;
|
|
3211
3860
|
return _ifThreeCodePointsWouldStartAnIdentSequence(
|
|
3212
3861
|
input,
|
|
3213
3862
|
p,
|
|
@@ -3220,52 +3869,130 @@ const A = {
|
|
|
3220
3869
|
}
|
|
3221
3870
|
const v = _soaValueOf(i);
|
|
3222
3871
|
return _typeFlagOf(
|
|
3223
|
-
|
|
3872
|
+
_soaTypes[i] === T_DIMENSION ? v.slice(0, _consumeANumber(v, 0)) : v
|
|
3873
|
+
);
|
|
3874
|
+
},
|
|
3875
|
+
/**
|
|
3876
|
+
* @param {Node=} n node
|
|
3877
|
+
* @returns {number} url content start offset
|
|
3878
|
+
*/
|
|
3879
|
+
contentStart(n = _currentNode) {
|
|
3880
|
+
return _soaAux0[_nodeIndex(n)];
|
|
3881
|
+
},
|
|
3882
|
+
/**
|
|
3883
|
+
* @param {Node=} n node
|
|
3884
|
+
* @returns {number} url content end offset
|
|
3885
|
+
*/
|
|
3886
|
+
contentEnd(n = _currentNode) {
|
|
3887
|
+
return _soaAux1[_nodeIndex(n)];
|
|
3888
|
+
},
|
|
3889
|
+
/**
|
|
3890
|
+
* @param {Node=} n node
|
|
3891
|
+
* @returns {string} rule / declaration / function name
|
|
3892
|
+
*/
|
|
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]);
|
|
3898
|
+
},
|
|
3899
|
+
/**
|
|
3900
|
+
* @param {Node=} n node
|
|
3901
|
+
* @returns {number} name start offset
|
|
3902
|
+
*/
|
|
3903
|
+
nameStart(n = _currentNode) {
|
|
3904
|
+
return _soaStarts[_nodeIndex(n)];
|
|
3905
|
+
},
|
|
3906
|
+
/**
|
|
3907
|
+
* @param {Node=} n node
|
|
3908
|
+
* @returns {number} name end offset
|
|
3909
|
+
*/
|
|
3910
|
+
nameEnd(n = _currentNode) {
|
|
3911
|
+
return _soaAux0[_nodeIndex(n)];
|
|
3912
|
+
},
|
|
3913
|
+
/**
|
|
3914
|
+
* @param {Node=} n node
|
|
3915
|
+
* @returns {string} unescaped name
|
|
3916
|
+
*/
|
|
3917
|
+
unescapedName(n = _currentNode) {
|
|
3918
|
+
return unescapeIdentifier(A.name(n));
|
|
3919
|
+
},
|
|
3920
|
+
/**
|
|
3921
|
+
* @param {Node=} n node
|
|
3922
|
+
* @returns {ComponentValue[]} function / block children
|
|
3923
|
+
*/
|
|
3924
|
+
children(n = _currentNode) {
|
|
3925
|
+
return /** @type {ComponentValue[]} */ (_materializeList(n));
|
|
3926
|
+
},
|
|
3927
|
+
/**
|
|
3928
|
+
* @param {Node=} n node
|
|
3929
|
+
* @returns {ComponentValue[]} rule prelude
|
|
3930
|
+
*/
|
|
3931
|
+
prelude(n = _currentNode) {
|
|
3932
|
+
return /** @type {ComponentValue[]} */ (_materializeList(n));
|
|
3933
|
+
},
|
|
3934
|
+
/**
|
|
3935
|
+
* @param {Node=} n node
|
|
3936
|
+
* @returns {Declaration[] | null} block declarations
|
|
3937
|
+
*/
|
|
3938
|
+
declarations(n = _currentNode) {
|
|
3939
|
+
return /** @type {Declaration[] | null} */ (
|
|
3940
|
+
_soaDeclarationLists[_nodeIndex(n)]
|
|
3224
3941
|
);
|
|
3225
3942
|
},
|
|
3226
|
-
/**
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3943
|
+
/**
|
|
3944
|
+
* @param {Node=} n node
|
|
3945
|
+
* @returns {Rule[] | null} block child rules
|
|
3946
|
+
*/
|
|
3947
|
+
childRules(n = _currentNode) {
|
|
3948
|
+
return /** @type {Rule[] | null} */ (_soaChildRuleLists[_nodeIndex(n)]);
|
|
3949
|
+
},
|
|
3950
|
+
/**
|
|
3951
|
+
* @param {Node=} n node
|
|
3952
|
+
* @returns {number} block start offset
|
|
3953
|
+
*/
|
|
3954
|
+
blockStart(n = _currentNode) {
|
|
3955
|
+
return _soaAux1[_nodeIndex(n)];
|
|
3956
|
+
},
|
|
3957
|
+
/**
|
|
3958
|
+
* @param {Node=} n node
|
|
3959
|
+
* @returns {number} block end offset
|
|
3960
|
+
*/
|
|
3961
|
+
blockEnd(n = _currentNode) {
|
|
3962
|
+
return _soaAux2[_nodeIndex(n)];
|
|
3963
|
+
},
|
|
3964
|
+
/**
|
|
3965
|
+
* @param {Node=} n node
|
|
3966
|
+
* @returns {boolean} `!important` flag
|
|
3967
|
+
*/
|
|
3968
|
+
important(n = _currentNode) {
|
|
3969
|
+
return (_soaFlags[_nodeIndex(n)] & 1) !== 0;
|
|
3970
|
+
},
|
|
3971
|
+
/**
|
|
3972
|
+
* @param {Node=} n node
|
|
3973
|
+
* @returns {SimpleBlockToken} block opening token
|
|
3974
|
+
*/
|
|
3975
|
+
blockToken(n = _currentNode) {
|
|
3976
|
+
return /** @type {SimpleBlockToken} */ (
|
|
3977
|
+
_soaInput[_soaStarts[_nodeIndex(n)]]
|
|
3978
|
+
);
|
|
3236
3979
|
},
|
|
3237
|
-
/** @type {(n: Node) => number} */
|
|
3238
|
-
nameStart: (n) => _soaSt[_ix(n)],
|
|
3239
|
-
/** @type {(n: Node) => number} */
|
|
3240
|
-
nameEnd: (n) => _soaA0[_ix(n)],
|
|
3241
|
-
/** @type {(n: Node) => string} */
|
|
3242
|
-
unescapedName: (n) => unescapeIdentifier(A.name(n)),
|
|
3243
|
-
/** @type {(n: Node) => ComponentValue[]} */
|
|
3244
|
-
children: (n) => /** @type {ComponentValue[]} */ (_soaL0[_ix(n)]),
|
|
3245
|
-
/** @type {(n: Node) => ComponentValue[]} */
|
|
3246
|
-
prelude: (n) => /** @type {ComponentValue[]} */ (_soaL0[_ix(n)]),
|
|
3247
|
-
/** @type {(n: Node) => Declaration[] | null} */
|
|
3248
|
-
declarations: (n) => /** @type {Declaration[] | null} */ (_soaL1[_ix(n)]),
|
|
3249
|
-
/** @type {(n: Node) => Rule[] | null} */
|
|
3250
|
-
childRules: (n) => /** @type {Rule[] | null} */ (_soaL2[_ix(n)]),
|
|
3251
|
-
/** @type {(n: Node) => number} */
|
|
3252
|
-
blockStart: (n) => _soaA1[_ix(n)],
|
|
3253
|
-
/** @type {(n: Node) => number} */
|
|
3254
|
-
blockEnd: (n) => _soaA2[_ix(n)],
|
|
3255
|
-
/** @type {(n: Node) => boolean} */
|
|
3256
|
-
important: (n) => (_soaFl[_ix(n)] & 1) !== 0,
|
|
3257
|
-
/** @type {(n: Node) => SimpleBlockToken} */
|
|
3258
|
-
blockToken: (n) =>
|
|
3259
|
-
/** @type {SimpleBlockToken} */ (_soaInput[_soaSt[_ix(n)]]),
|
|
3260
3980
|
// Writers — `CssParser` rewrites a rule's end / block-end when it folds an
|
|
3261
|
-
// inline ICSS `:import` / `:export` body into a single dependency.
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3981
|
+
// inline ICSS `:import` / `:export` body into a single dependency. The
|
|
3982
|
+
// node stays explicit here: writes should never be implicit on position.
|
|
3983
|
+
/**
|
|
3984
|
+
* @param {Node} n node
|
|
3985
|
+
* @param {number} v new end offset
|
|
3986
|
+
*/
|
|
3987
|
+
setEnd(n, v) {
|
|
3988
|
+
_soaEnds[_nodeIndex(n)] = v;
|
|
3265
3989
|
},
|
|
3266
|
-
/**
|
|
3267
|
-
|
|
3268
|
-
|
|
3990
|
+
/**
|
|
3991
|
+
* @param {Node} n node
|
|
3992
|
+
* @param {number} v new block end offset
|
|
3993
|
+
*/
|
|
3994
|
+
setBlockEnd(n, v) {
|
|
3995
|
+
_soaAux2[_nodeIndex(n)] = v;
|
|
3269
3996
|
}
|
|
3270
3997
|
};
|
|
3271
3998
|
|
|
@@ -3307,8 +4034,14 @@ module.exports.TT_URL = TT_URL;
|
|
|
3307
4034
|
module.exports.TT_WHITESPACE = TT_WHITESPACE;
|
|
3308
4035
|
module.exports.Token = Token;
|
|
3309
4036
|
module.exports.TokenStream = TokenStream;
|
|
4037
|
+
module.exports.buildSkipSet = buildSkipSet;
|
|
3310
4038
|
module.exports.equalsLowerCase = equalsLowerCase;
|
|
3311
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;
|
|
3312
4045
|
module.exports.parseABlocksContents = parseABlocksContents;
|
|
3313
4046
|
module.exports.parseACommaSeparatedListOfComponentValues =
|
|
3314
4047
|
parseACommaSeparatedListOfComponentValues;
|
|
@@ -3318,5 +4051,8 @@ module.exports.parseAListOfComponentValues = parseAListOfComponentValues;
|
|
|
3318
4051
|
module.exports.parseARule = parseARule;
|
|
3319
4052
|
module.exports.parseAStylesheet = parseAStylesheet;
|
|
3320
4053
|
module.exports.parseAStylesheetsContents = parseAStylesheetsContents;
|
|
4054
|
+
module.exports.rangeEquals = rangeEquals;
|
|
4055
|
+
module.exports.rangeEqualsLowerCase = rangeEqualsLowerCase;
|
|
3321
4056
|
module.exports.readToken = readToken;
|
|
4057
|
+
module.exports.toLowerCaseIfNeeded = toLowerCaseIfNeeded;
|
|
3322
4058
|
module.exports.unescapeIdentifier = unescapeIdentifier;
|