@depup/webpack 5.109.0-depup.0 → 5.109.2-depup.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/README.md +4 -4
- package/bin/webpack.js +12 -0
- package/changes.json +4 -4
- package/lib/ChunkGraph.js +17 -15
- package/lib/CleanPlugin.js +11 -9
- package/lib/CompatibilityPlugin.js +31 -0
- package/lib/Compilation.js +21 -5
- package/lib/Compiler.js +16 -4
- package/lib/DefinePlugin.js +47 -20
- package/lib/ExportsInfo.js +22 -5
- package/lib/ExternalModule.js +10 -7
- package/lib/FileSystemInfo.js +126 -26
- package/lib/FlagDependencyUsagePlugin.js +33 -12
- package/lib/InitFragment.js +20 -32
- package/lib/ModuleFilenameHelpers.js +13 -2
- package/lib/NormalModule.js +80 -71
- package/lib/NormalModuleFactory.js +8 -2
- package/lib/RuntimePlugin.js +40 -113
- package/lib/Template.js +23 -3
- package/lib/cache/PackFileCacheStrategy.js +259 -5
- package/lib/config/defaults.js +21 -2
- package/lib/container/HoistContainerReferencesPlugin.js +47 -55
- package/lib/container/ModuleFederationPlugin.js +15 -9
- package/lib/css/CssGenerator.js +8 -2
- package/lib/css/CssLoadingRuntimeModule.js +25 -12
- package/lib/css/CssModulesPlugin.js +29 -18
- package/lib/css/CssParser.js +135 -50
- package/lib/css/syntax.js +862 -765
- package/lib/dependencies/CommonJsExportRequireDependency.js +19 -0
- package/lib/dependencies/CommonJsFullRequireDependency.js +33 -4
- package/lib/dependencies/CommonJsImportsParserPlugin.js +2 -0
- package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +2 -1
- package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +17 -9
- package/lib/dependencies/HarmonyImportDependency.js +1 -1
- package/lib/dependencies/HarmonyImportGuard.js +2 -1
- package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -7
- package/lib/esm/ModuleChunkLoadingRuntimeModule.js +15 -9
- package/lib/hmr/lazyCompilationBackend.js +19 -4
- package/lib/html/HtmlGenerator.js +23 -3
- package/lib/html/HtmlModulesPlugin.js +30 -12
- package/lib/html/syntax.js +2485 -2399
- package/lib/ids/IdHelpers.js +24 -10
- package/lib/javascript/JavascriptModulesPlugin.js +83 -49
- package/lib/javascript/JavascriptParser.js +49 -13
- package/lib/javascript/syntax.js +700 -43
- package/lib/node/ReadFileCompileAsyncWasmPlugin.js +10 -2
- package/lib/node/ReadFileCompileWasmPlugin.js +10 -2
- package/lib/optimize/AggressiveMergingPlugin.js +19 -15
- package/lib/optimize/ConcatenatedModule.js +19 -15
- package/lib/optimize/ModuleConcatenationPlugin.js +73 -24
- package/lib/optimize/RealContentHashPlugin.js +10 -7
- package/lib/optimize/SplitChunksPlugin.js +146 -44
- package/lib/prefetch/ResourceHintPlugin.js +96 -72
- package/lib/serialization/BinaryMiddleware.js +270 -938
- package/lib/serialization/FileMiddleware.js +299 -60
- package/lib/stats/DefaultStatsFactoryPlugin.js +204 -198
- package/lib/stats/DefaultStatsPrinterPlugin.js +24 -166
- package/lib/stats/StatsFactory.js +31 -9
- package/lib/util/identifier.js +21 -1
- package/package.json +10 -10
- package/schemas/WebpackOptions.json +110 -55
- package/types.d.ts +832 -247
package/lib/css/syntax.js
CHANGED
|
@@ -683,6 +683,15 @@ function consumeSpace(input, pos, out) {
|
|
|
683
683
|
return fill(out, TT_WHITESPACE, start, pos);
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
+
// Sticky fast-forward classes: a native run-skip over the ordinary characters of
|
|
687
|
+
// a string / url token, so long values (data: URIs, base64) don't cost one JS
|
|
688
|
+
// char read each. The negated classes match exactly the per-char terminators the
|
|
689
|
+
// loops below handle (quotes / backslash / newlines for strings; plus `(`, `)`,
|
|
690
|
+
// whitespace and non-printable code points for urls).
|
|
691
|
+
const _STRING_SAFE = /[^"'\\\n\r\f]+/y;
|
|
692
|
+
// eslint-disable-next-line no-control-regex -- url terminators include the control range and DEL (they make a bad-url)
|
|
693
|
+
const _URL_SAFE = /[^\u0000-\u0020\u007F"'()\\]+/y;
|
|
694
|
+
|
|
686
695
|
/**
|
|
687
696
|
* Consume a string token. Caller advanced past the opening quote so
|
|
688
697
|
* `pos - 1` holds the ending code point and `pos - 1` is the start.
|
|
@@ -695,6 +704,8 @@ function consumeAStringToken(input, pos, out) {
|
|
|
695
704
|
const start = pos - 1;
|
|
696
705
|
const endingCodePoint = input.charCodeAt(pos - 1);
|
|
697
706
|
for (;;) {
|
|
707
|
+
_STRING_SAFE.lastIndex = pos;
|
|
708
|
+
if (_STRING_SAFE.test(input)) pos = _STRING_SAFE.lastIndex;
|
|
698
709
|
if (pos === input.length) {
|
|
699
710
|
return fill(out, TT_STRING, start, pos);
|
|
700
711
|
}
|
|
@@ -874,6 +885,8 @@ function consumeAUrlToken(input, pos, fnStart, out) {
|
|
|
874
885
|
const contentStart = pos;
|
|
875
886
|
out.contentStart = contentStart;
|
|
876
887
|
for (;;) {
|
|
888
|
+
_URL_SAFE.lastIndex = pos;
|
|
889
|
+
if (_URL_SAFE.test(input)) pos = _URL_SAFE.lastIndex;
|
|
877
890
|
if (pos === input.length) {
|
|
878
891
|
out.contentEnd = pos;
|
|
879
892
|
return fill(out, TT_URL, fnStart, pos);
|
|
@@ -1289,78 +1302,23 @@ const {
|
|
|
1289
1302
|
} = NodeType;
|
|
1290
1303
|
|
|
1291
1304
|
/**
|
|
1292
|
-
* Base AST node
|
|
1293
|
-
*
|
|
1294
|
-
* of the source slice
|
|
1295
|
-
* shared `LocConverter
|
|
1296
|
-
*
|
|
1305
|
+
* Base AST node — the property-accessor view the `parseA*` entry points return
|
|
1306
|
+
* (see `_makeReader`). Every concrete node carries the `[start, end)` byte
|
|
1307
|
+
* `range` of the source slice it covers; `loc` is computed on demand from the
|
|
1308
|
+
* shared `LocConverter`, so line/column conversion is only paid when a consumer
|
|
1309
|
+
* needs it. The concrete node typedefs below extend this via `&`.
|
|
1310
|
+
*
|
|
1311
|
+
* Inside the parser a node ref is an integer id into the columns; the reader
|
|
1312
|
+
* exposes this property shape over a retained snapshot of those columns.
|
|
1313
|
+
* @typedef {object} Node
|
|
1314
|
+
* @property {number} type node-type discriminator
|
|
1315
|
+
* @property {number} start byte offset of the node's first code point
|
|
1316
|
+
* @property {number} end byte offset just past the node's last code point
|
|
1317
|
+
* @property {[number, number]} range the `[start, end)` byte range
|
|
1318
|
+
* @property {{ start: { line: number, column: number }, end: { line: number, column: number } }} loc source location (1-based line, 0-based column)
|
|
1319
|
+
* @property {() => string} toString source slice for this node
|
|
1320
|
+
* @property {string} unescapedName name with CSS escapes resolved (name-bearing nodes only)
|
|
1297
1321
|
*/
|
|
1298
|
-
class Node {
|
|
1299
|
-
/**
|
|
1300
|
-
* @param {number} type node type discriminator
|
|
1301
|
-
* @param {number} start byte offset of the node's first code point
|
|
1302
|
-
* @param {number} end byte offset just past the node's last code point
|
|
1303
|
-
* @param {LocConverter} locConverter shared loc converter
|
|
1304
|
-
*/
|
|
1305
|
-
constructor(type, start, end, locConverter) {
|
|
1306
|
-
/** @type {number} */
|
|
1307
|
-
this.type = type;
|
|
1308
|
-
// Byte range as two inline fields rather than a `[start, end]` array —
|
|
1309
|
-
// one fewer allocation per node and ~56 bytes lighter (×100k+ nodes).
|
|
1310
|
-
/** @type {number} */
|
|
1311
|
-
this.start = start;
|
|
1312
|
-
/** @type {number} */
|
|
1313
|
-
this.end = end;
|
|
1314
|
-
/** @type {LocConverter} */
|
|
1315
|
-
this._locConverter = locConverter;
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
/**
|
|
1319
|
-
* The `[start, end)` byte range as a tuple — compatibility view over
|
|
1320
|
-
* `start` / `end` (builds the array lazily; hot code reads the
|
|
1321
|
-
* fields directly).
|
|
1322
|
-
* @returns {[number, number]} the byte range
|
|
1323
|
-
*/
|
|
1324
|
-
get range() {
|
|
1325
|
-
return [this.start, this.end];
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
get loc() {
|
|
1329
|
-
const lc = this._locConverter;
|
|
1330
|
-
// `LocConverter#get` mutates and returns the converter itself, so we
|
|
1331
|
-
// must snapshot `line`/`column` between the two calls.
|
|
1332
|
-
const s = lc.get(this.start);
|
|
1333
|
-
const sl = s.line;
|
|
1334
|
-
const sc = s.column;
|
|
1335
|
-
const e = lc.get(this.end);
|
|
1336
|
-
return {
|
|
1337
|
-
start: { line: sl, column: sc },
|
|
1338
|
-
end: { line: e.line, column: e.column }
|
|
1339
|
-
};
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
/**
|
|
1343
|
-
* Serialize back to source — re-slices the original input (zero-alloc for
|
|
1344
|
-
* untouched nodes).
|
|
1345
|
-
* @returns {string} the source slice for this node
|
|
1346
|
-
*/
|
|
1347
|
-
toString() {
|
|
1348
|
-
return this._locConverter._input.slice(this.start, this.end);
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
/**
|
|
1352
|
-
* For name-bearing nodes (function / at-rule): the `name` with CSS escapes
|
|
1353
|
-
* resolved, for case-insensitive keyword matching (`\75 rl` → `url`). Computed
|
|
1354
|
-
* on read via `unescapeIdentifier`'s no-escape fast path. Callers without a
|
|
1355
|
-
* `name` must not read this.
|
|
1356
|
-
* @returns {string} the unescaped name
|
|
1357
|
-
*/
|
|
1358
|
-
get unescapedName() {
|
|
1359
|
-
return unescapeIdentifier(
|
|
1360
|
-
/** @type {{ name: string }} */ (/** @type {unknown} */ (this)).name
|
|
1361
|
-
);
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
1322
|
|
|
1365
1323
|
/**
|
|
1366
1324
|
* @param {string} s numeric text
|
|
@@ -1379,175 +1337,15 @@ const _typeFlagOf = (s) =>
|
|
|
1379
1337
|
s.includes(".") || s.includes("e") || s.includes("E") ? "number" : "integer";
|
|
1380
1338
|
|
|
1381
1339
|
/**
|
|
1382
|
-
* Leaf token node
|
|
1340
|
+
* Leaf token node (property-accessor view) — `value` is the raw source slice
|
|
1383
1341
|
* (identifier text, quoted string including quotes, a dimension's full `123px`,
|
|
1384
|
-
*
|
|
1385
|
-
* `NumberToken` / `DimensionToken`
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
1389
|
-
*
|
|
1390
|
-
* token keeps a single object shape so the walker's `node.type` dispatch stays
|
|
1391
|
-
* monomorphic. They are only meaningful on the matching token type.
|
|
1342
|
+
* …; hash / at-keyword drop their `#` / `@` prefix, url uses its content range).
|
|
1343
|
+
* The `NumberToken` / `HashToken` / `UrlToken` / `DimensionToken` typedefs below
|
|
1344
|
+
* narrow the value accessors. `numericValue` / `typeFlag` / `sign` / `unit` are
|
|
1345
|
+
* derived from the source on read and are only meaningful on the matching token
|
|
1346
|
+
* type; `contentStart` / `contentEnd` mark a url token's inner content range.
|
|
1347
|
+
* @typedef {Node & { value: string, unescaped: string, numericValue: number, typeFlag: "integer" | "number" | "id" | "unrestricted", sign: "+" | "-" | "", unit: string, contentStart: number, contentEnd: number }} Token
|
|
1392
1348
|
*/
|
|
1393
|
-
class Token extends Node {
|
|
1394
|
-
/**
|
|
1395
|
-
* @param {number} type node type
|
|
1396
|
-
* @param {number} start byte offset of the token's first code point
|
|
1397
|
-
* @param {number} end byte offset just past the token's last code point
|
|
1398
|
-
* @param {LocConverter} locConverter shared loc converter
|
|
1399
|
-
*/
|
|
1400
|
-
// No own fields: a leaf token is exactly a `Node` plus the value getters
|
|
1401
|
-
// below. `value` is derived from the byte range on read instead of cached in
|
|
1402
|
-
// a `_value` slot — most tokens (whitespace, punctuation) never read it, and
|
|
1403
|
-
// dropping the slot is ~8 bytes saved on every token (the bulk of all nodes).
|
|
1404
|
-
// hash / at-keyword strip their `#` / `@` prefix; url uses its content range
|
|
1405
|
-
// (`contentStart` / `contentEnd`, the token's only own fields).
|
|
1406
|
-
|
|
1407
|
-
/**
|
|
1408
|
-
* @returns {string} the token's value (raw source slice unless overridden)
|
|
1409
|
-
*/
|
|
1410
|
-
get value() {
|
|
1411
|
-
const input = this._locConverter._input;
|
|
1412
|
-
const type = this.type;
|
|
1413
|
-
// hash (`#name` → `name`) and at-keyword (`@name` → `name`) drop one char.
|
|
1414
|
-
if (type === T_HASH || type === T_AT_KEYWORD) {
|
|
1415
|
-
return input.slice(this.start + 1, this.end);
|
|
1416
|
-
}
|
|
1417
|
-
if (type === T_URL) {
|
|
1418
|
-
const u = /** @type {UrlToken} */ (/** @type {unknown} */ (this));
|
|
1419
|
-
return input.slice(u.contentStart, u.contentEnd);
|
|
1420
|
-
}
|
|
1421
|
-
return input.slice(this.start, this.end);
|
|
1422
|
-
}
|
|
1423
|
-
|
|
1424
|
-
/**
|
|
1425
|
-
* The token's value with CSS escapes resolved (`\2d` → `-`, `\75 rl` → `url`),
|
|
1426
|
-
* per https://www.w3.org/TR/css-syntax-3/#consume-escaped-code-point — the
|
|
1427
|
-
* form to match keywords / export as a CSS-Modules name against. For a string
|
|
1428
|
-
* token it is the content between the quotes (the spec string value). Computed
|
|
1429
|
-
* on read; `unescapeIdentifier` fast-returns the value unchanged when it has no
|
|
1430
|
-
* escapes (the common case), so nothing is stored per token.
|
|
1431
|
-
* @returns {string} the unescaped value
|
|
1432
|
-
*/
|
|
1433
|
-
get unescaped() {
|
|
1434
|
-
const v = this.value;
|
|
1435
|
-
// A string token's `value` carries its delimiting quotes; its value is the content between them.
|
|
1436
|
-
return this.type === T_STRING
|
|
1437
|
-
? unescapeIdentifier(v.slice(1, -1))
|
|
1438
|
-
: unescapeIdentifier(v);
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
/**
|
|
1442
|
-
* Parsed numeric value (number / percentage / dimension tokens). Derived from
|
|
1443
|
-
* `value` on access — the `%` is dropped for percentages and the unit for
|
|
1444
|
-
* dimensions (split with `_consumeANumber`, recomputed here so nothing is
|
|
1445
|
-
* stored per token).
|
|
1446
|
-
* @returns {number} the parsed numeric value
|
|
1447
|
-
*/
|
|
1448
|
-
get numericValue() {
|
|
1449
|
-
const v = this.value;
|
|
1450
|
-
if (this.type === T_DIMENSION) {
|
|
1451
|
-
return Number(v.slice(0, _consumeANumber(v, 0)));
|
|
1452
|
-
}
|
|
1453
|
-
if (this.type === T_PERCENTAGE) return Number(v.slice(0, -1));
|
|
1454
|
-
return Number(v);
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
/**
|
|
1458
|
-
* Spec type flag. For number / dimension tokens it's "integer" / "number"
|
|
1459
|
-
* (derived from `value`); for hash tokens it's "id" / "unrestricted" (re-derived
|
|
1460
|
-
* from the source). The two senses share the name in the spec; both are computed
|
|
1461
|
-
* on read so every leaf token keeps the same object shape (no own `typeFlag`).
|
|
1462
|
-
* @returns {"integer" | "number" | "id" | "unrestricted"} the spec type flag
|
|
1463
|
-
*/
|
|
1464
|
-
get typeFlag() {
|
|
1465
|
-
if (this.type === T_HASH) {
|
|
1466
|
-
// Re-derive id-ness from the source (whether the name after `#` starts an
|
|
1467
|
-
// ident sequence) rather than storing an `_isId` slot — keeps hash tokens
|
|
1468
|
-
// the same shape as every other leaf token. Matches the lexer's
|
|
1469
|
-
// `consumeNumberSign`, which sets `isId` from the same check.
|
|
1470
|
-
const input = this._locConverter._input;
|
|
1471
|
-
const p = this.start + 1;
|
|
1472
|
-
return _ifThreeCodePointsWouldStartAnIdentSequence(
|
|
1473
|
-
input,
|
|
1474
|
-
p,
|
|
1475
|
-
input.charCodeAt(p),
|
|
1476
|
-
input.charCodeAt(p + 1),
|
|
1477
|
-
input.charCodeAt(p + 2)
|
|
1478
|
-
)
|
|
1479
|
-
? "id"
|
|
1480
|
-
: "unrestricted";
|
|
1481
|
-
}
|
|
1482
|
-
const v = this.value;
|
|
1483
|
-
return _typeFlagOf(
|
|
1484
|
-
this.type === T_DIMENSION ? v.slice(0, _consumeANumber(v, 0)) : v
|
|
1485
|
-
);
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
/**
|
|
1489
|
-
* @returns {"+" | "-" | ""} the spec sign (number / percentage / dimension tokens)
|
|
1490
|
-
*/
|
|
1491
|
-
get sign() {
|
|
1492
|
-
return _signOf(this.value);
|
|
1493
|
-
}
|
|
1494
|
-
|
|
1495
|
-
/**
|
|
1496
|
-
* @returns {string} the unit, lower-cased per spec (dimension tokens)
|
|
1497
|
-
*/
|
|
1498
|
-
get unit() {
|
|
1499
|
-
const v = this.value;
|
|
1500
|
-
return v.slice(_consumeANumber(v, 0)).toLowerCase();
|
|
1501
|
-
}
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
/**
|
|
1505
|
-
* The non-leaf `Node` subclass: functions, simple blocks, declarations, at-rules
|
|
1506
|
-
* and qualified rules. It declares the union of every container field up front so
|
|
1507
|
-
* all five share **one** hidden class — the consume algorithms only overwrite the
|
|
1508
|
-
* slots relevant to their type, never adding a property, so the shape never
|
|
1509
|
-
* transitions. This caps the shape count: the walker's hot `.type` / `.value` /
|
|
1510
|
-
* `.prelude` loads otherwise see eight distinct node maps (five container types
|
|
1511
|
-
* plus the token / hash / url token maps), tipping V8's inline cache into the
|
|
1512
|
-
* slow megamorphic path; folding the five containers into one leaves four maps
|
|
1513
|
-
* (token, hash, url, container) — within the polymorphic limit, so those loads
|
|
1514
|
-
* stay inline-cached. Unused-for-the-type slots keep their defaults (the field
|
|
1515
|
-
* typedefs below document which fields each type uses). The stylesheet node is
|
|
1516
|
-
* rare (one per parse) and stays a bare `Node`.
|
|
1517
|
-
*/
|
|
1518
|
-
class Container extends Node {
|
|
1519
|
-
/**
|
|
1520
|
-
* @param {number} type node type
|
|
1521
|
-
* @param {number} start byte offset of the node's first code point
|
|
1522
|
-
* @param {number} end byte offset just past the node's last code point
|
|
1523
|
-
* @param {LocConverter} locConverter shared loc converter
|
|
1524
|
-
*/
|
|
1525
|
-
constructor(type, start, end, locConverter) {
|
|
1526
|
-
super(type, start, end, locConverter);
|
|
1527
|
-
/** @type {string} name (function / at-rule / declaration) */
|
|
1528
|
-
this.name = "";
|
|
1529
|
-
/** @type {number} */
|
|
1530
|
-
this.nameStart = start;
|
|
1531
|
-
/** @type {number} */
|
|
1532
|
-
this.nameEnd = start;
|
|
1533
|
-
/** @type {ComponentValue[] | null} component values (function / block / declaration) */
|
|
1534
|
-
this.value = null;
|
|
1535
|
-
/** @type {ComponentValue[] | null} prelude (at-rule / qualified rule) */
|
|
1536
|
-
this.prelude = null;
|
|
1537
|
-
/** @type {Declaration[] | null} */
|
|
1538
|
-
this.declarations = null;
|
|
1539
|
-
/** @type {Rule[] | null} */
|
|
1540
|
-
this.childRules = null;
|
|
1541
|
-
/** @type {number} `{` start offset, or -1 (at-rule / qualified rule) */
|
|
1542
|
-
this.blockStart = -1;
|
|
1543
|
-
/** @type {number} `}` end offset, or -1 (at-rule / qualified rule) */
|
|
1544
|
-
this.blockEnd = -1;
|
|
1545
|
-
/** @type {boolean} stripped `!important` (declaration) */
|
|
1546
|
-
this.important = false;
|
|
1547
|
-
/** @type {SimpleBlockToken | undefined} opening char (simple block) */
|
|
1548
|
-
this.token = undefined;
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
1349
|
|
|
1552
1350
|
/**
|
|
1553
1351
|
* Number token (`123`, `-1.5`, `+2e3`). `value` is the raw source slice (the spec's "value"); `numericValue` / `typeFlag` / `sign` are lazy getters derived from it (see `Token`).
|
|
@@ -1646,10 +1444,10 @@ class Container extends Node {
|
|
|
1646
1444
|
* @typedef {Node & { rules: Rule[] }} Stylesheet
|
|
1647
1445
|
*/
|
|
1648
1446
|
|
|
1649
|
-
// Lexer-token-type → AST-node-type map. A single
|
|
1650
|
-
//
|
|
1651
|
-
//
|
|
1652
|
-
//
|
|
1447
|
+
// Lexer-token-type → AST-node-type map. A single `_makeLeaf` call site (vs a
|
|
1448
|
+
// ~20-case switch with an alloc in each arm) keeps V8 on the fast monomorphic
|
|
1449
|
+
// path — the switch form showed up as generic stubs in profiles. URL is the one
|
|
1450
|
+
// type with extra own state, handled first.
|
|
1653
1451
|
const _ttToNodeType = new Uint8Array(27);
|
|
1654
1452
|
_ttToNodeType[TT_WHITESPACE] = T_WHITESPACE;
|
|
1655
1453
|
_ttToNodeType[TT_IDENTIFIER] = T_IDENT;
|
|
@@ -1671,21 +1469,14 @@ _ttToNodeType[TT_RIGHT_CURLY_BRACKET] = T_RIGHT_CURLY_BRACKET;
|
|
|
1671
1469
|
_ttToNodeType[TT_CDO] = T_CDO;
|
|
1672
1470
|
_ttToNodeType[TT_CDC] = T_CDC;
|
|
1673
1471
|
|
|
1674
|
-
// === AST construction
|
|
1675
|
-
//
|
|
1676
|
-
//
|
|
1677
|
-
//
|
|
1678
|
-
//
|
|
1679
|
-
//
|
|
1680
|
-
// the
|
|
1681
|
-
//
|
|
1682
|
-
// backends (a node ref is an object or an integer index); only node creation /
|
|
1683
|
-
// field access differs, so only those ops are swapped.
|
|
1684
|
-
|
|
1685
|
-
// Current loc converter for the active parse (object backend reads it).
|
|
1686
|
-
let _objLocConverter = /** @type {LocConverter} */ (
|
|
1687
|
-
/** @type {unknown} */ (null)
|
|
1688
|
-
);
|
|
1472
|
+
// === AST construction ===
|
|
1473
|
+
// Nodes live in one struct-of-arrays node store: a node ref is an integer id
|
|
1474
|
+
// into parallel typed-array columns, so per-node allocation is avoided entirely.
|
|
1475
|
+
// The consume algorithms build nodes through the `_make*` / `_set*` primitives
|
|
1476
|
+
// below, which write those columns directly. The streaming `grammar` walks each
|
|
1477
|
+
// top-level node and recycles the columns; the `parseA*` entry points instead
|
|
1478
|
+
// retain the columns as a snapshot and hand back property-accessor nodes over it
|
|
1479
|
+
// (see `_makeReader`). Child lists are plain arrays of node ids in both modes.
|
|
1689
1480
|
|
|
1690
1481
|
// Active skip state (from `CssProcessOptions.skip`), applied by the grammar.
|
|
1691
1482
|
// `_skipTypes` is indexed by `NodeType` (1 = skip): drop that component-value
|
|
@@ -1694,7 +1485,7 @@ let _objLocConverter = /** @type {LocConverter} */ (
|
|
|
1694
1485
|
// / functions kept, so `url()` in a selector or `@import url(…)` still resolves).
|
|
1695
1486
|
// A skipped node is still tokenized (positions stay correct) but never pushed,
|
|
1696
1487
|
// so it is never walked or read — the caller must only skip what nothing reads.
|
|
1697
|
-
// `
|
|
1488
|
+
// `parseA*` leave these at their no-skip defaults so they build the full tree.
|
|
1698
1489
|
const _NO_SKIP_TYPES = new Uint8Array(32);
|
|
1699
1490
|
// Shared frozen empty list for block bodies with no decls / no child rules (the
|
|
1700
1491
|
// common case — most rules carry only declarations). Every consumer reads these
|
|
@@ -1711,227 +1502,71 @@ let _skipActive = false;
|
|
|
1711
1502
|
let _skipSelectorPrelude = false;
|
|
1712
1503
|
let _skipAtRulePrelude = false;
|
|
1713
1504
|
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1719
|
-
let _makeContainer;
|
|
1720
|
-
/** @type {(start: number) => Node} */
|
|
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} */
|
|
1726
|
-
let _setName;
|
|
1727
|
-
/** @type {(r: Node, v: number) => void} */
|
|
1728
|
-
let _setEnd;
|
|
1729
|
-
/** @type {(r: Node, blockStart: number, blockEnd: number) => void} */
|
|
1730
|
-
let _setBlock;
|
|
1731
|
-
/** @type {(r: Node) => void} */
|
|
1732
|
-
let _setImportant;
|
|
1733
|
-
/** @type {(r: Node, ch: SimpleBlockToken) => void} */
|
|
1734
|
-
let _setToken;
|
|
1735
|
-
/** @type {(r: Node, list: Node[]) => void} */
|
|
1736
|
-
let _setValue;
|
|
1737
|
-
/** @type {(r: Node, list: Node[]) => void} */
|
|
1738
|
-
let _setPrelude;
|
|
1739
|
-
/** @type {(r: Node, decls: Node[], childRules: Node[]) => void} */
|
|
1740
|
-
let _setBody;
|
|
1741
|
-
/** @type {(r: Node, list: Node[]) => void} */
|
|
1742
|
-
let _setRules;
|
|
1743
|
-
/** @type {(r: Node) => number} */
|
|
1744
|
-
let _nodeTypeOf;
|
|
1745
|
-
/** @type {(r: Node) => number} */
|
|
1746
|
-
let _nodeStartOf;
|
|
1747
|
-
/** @type {(r: Node) => string} */
|
|
1748
|
-
let _nodeValueOf;
|
|
1749
|
-
/** @type {(r: Node) => SimpleBlockToken} */
|
|
1750
|
-
let _nodeTokenOf;
|
|
1751
|
-
|
|
1752
|
-
// Child lists are plain arrays in every backend; refs are pushed by value.
|
|
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;
|
|
1505
|
+
// Scratch content-list pool: a container's value / prelude is built in a plain
|
|
1506
|
+
// array, then sealed into the flat value buffer by `_setValue`, which returns
|
|
1507
|
+
// the array to the pool — so a parse allocates almost no per-list arrays. An
|
|
1508
|
+
// abandoned (never-sealed) list simply falls out of the pool.
|
|
1761
1509
|
/** @type {Node[][]} */
|
|
1762
1510
|
const _listPool = [];
|
|
1763
|
-
const
|
|
1511
|
+
const _takeList = () =>
|
|
1764
1512
|
_listPool.length > 0
|
|
1765
1513
|
? /** @type {Node[]} */ (_listPool.pop())
|
|
1766
1514
|
: /** @type {Node[]} */ ([]);
|
|
1767
1515
|
|
|
1768
|
-
// --
|
|
1769
|
-
/** @type {typeof _makeLeaf} */
|
|
1770
|
-
const _objLeaf = (type, start, end) =>
|
|
1771
|
-
new Token(type, start, end, _objLocConverter);
|
|
1772
|
-
/** @type {typeof _makeUrl} */
|
|
1773
|
-
const _objUrl = (start, end, cs, ce) => {
|
|
1774
|
-
const u = /** @type {UrlToken} */ (
|
|
1775
|
-
new Token(T_URL, start, end, _objLocConverter)
|
|
1776
|
-
);
|
|
1777
|
-
u.contentStart = cs;
|
|
1778
|
-
u.contentEnd = ce;
|
|
1779
|
-
return u;
|
|
1780
|
-
};
|
|
1781
|
-
/** @type {typeof _makeContainer} */
|
|
1782
|
-
const _objContainer = (type, start, end) =>
|
|
1783
|
-
new Container(type, start, end, _objLocConverter);
|
|
1784
|
-
/** @type {typeof _makeStylesheet} */
|
|
1785
|
-
const _objStylesheet = (start) => {
|
|
1786
|
-
const s = /** @type {Stylesheet} */ (
|
|
1787
|
-
new Node(T_STYLESHEET, start, start, _objLocConverter)
|
|
1788
|
-
);
|
|
1789
|
-
s.rules = [];
|
|
1790
|
-
return s;
|
|
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);
|
|
1852
|
-
/** @param {LocConverter} lc loc converter for this parse */
|
|
1853
|
-
const useObjectBackend = (lc) => {
|
|
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;
|
|
1878
|
-
};
|
|
1879
|
-
|
|
1880
|
-
// -- struct-of-arrays backend: writes nodes into reused typed arrays --
|
|
1516
|
+
// -- struct-of-arrays store: nodes live in reused typed-array columns --
|
|
1881
1517
|
// A node ref is its integer id; fields live in parallel arrays indexed by id.
|
|
1882
|
-
//
|
|
1518
|
+
// Two reused int slots (`_aux0/1`) plus a flags byte carry the per-type
|
|
1883
1519
|
// extras; child lists hang off three object arrays. Aux slot meaning by type:
|
|
1884
1520
|
// url: aux0 contentStart, aux1 contentEnd
|
|
1885
1521
|
// function: aux0 nameEnd
|
|
1886
1522
|
// declaration: aux0 nameEnd, flags bit0 important
|
|
1887
|
-
// at-rule: aux0 nameEnd, aux1 blockStart
|
|
1888
|
-
// qualified: aux1 blockStart
|
|
1523
|
+
// at-rule: aux0 nameEnd, aux1 blockStart (blockEnd == end)
|
|
1524
|
+
// qualified: aux1 blockStart (blockEnd == end)
|
|
1889
1525
|
// `name` / `nameStart` / a simple block's `token` are derived from the source
|
|
1890
|
-
// on read (see the
|
|
1891
|
-
// (value | prelude | stylesheet rules) is a `
|
|
1892
|
-
// `grammar` resets `
|
|
1526
|
+
// on read (see the accessors), so they need no slot. A node's main content
|
|
1527
|
+
// (value | prelude | stylesheet rules) is a `_flat` span (see below).
|
|
1528
|
+
// `grammar` resets `_nodeCount` to 0 after each top-level rule's walk, so the
|
|
1893
1529
|
// buffers are reused across rules and the parse allocates almost nothing.
|
|
1894
|
-
let
|
|
1895
|
-
let
|
|
1896
|
-
let
|
|
1897
|
-
let
|
|
1898
|
-
let
|
|
1899
|
-
let
|
|
1900
|
-
let
|
|
1901
|
-
let
|
|
1902
|
-
|
|
1903
|
-
// Content-list spans: a container's value / prelude is `_soaFlat[start, start+len)`
|
|
1530
|
+
let _capacity = 0;
|
|
1531
|
+
let _nodeCount = 0;
|
|
1532
|
+
let _types = new Uint8Array(0);
|
|
1533
|
+
let _starts = new Int32Array(0);
|
|
1534
|
+
let _ends = new Int32Array(0);
|
|
1535
|
+
let _aux0 = new Int32Array(0);
|
|
1536
|
+
let _aux1 = new Int32Array(0);
|
|
1537
|
+
let _flags = new Uint8Array(0);
|
|
1538
|
+
// Content-list spans: a container's value / prelude is `_flat[start, start+len)`
|
|
1904
1539
|
// (node refs), recycled per top-level rule like the node columns.
|
|
1905
|
-
let
|
|
1906
|
-
let
|
|
1907
|
-
let
|
|
1908
|
-
let
|
|
1540
|
+
let _listStarts = new Int32Array(0);
|
|
1541
|
+
let _listLens = new Int32Array(0);
|
|
1542
|
+
let _flat = new Int32Array(0);
|
|
1543
|
+
let _flatTop = 0;
|
|
1909
1544
|
// Peak usage of the current parse, and use-once regrow hints: after an
|
|
1910
1545
|
// over-capacity shrink the next grow jumps straight back to the previous
|
|
1911
1546
|
// parse's peak (one exact-fit allocation instead of re-doubling up).
|
|
1912
|
-
let
|
|
1913
|
-
let
|
|
1914
|
-
let
|
|
1915
|
-
let
|
|
1547
|
+
let _peak = 0;
|
|
1548
|
+
let _flatPeak = 0;
|
|
1549
|
+
let _growHint = 0;
|
|
1550
|
+
let _flatGrowHint = 0;
|
|
1916
1551
|
|
|
1917
1552
|
/** @param {number} need minimum flat-buffer capacity */
|
|
1918
|
-
const
|
|
1919
|
-
let cap =
|
|
1920
|
-
if (
|
|
1921
|
-
|
|
1553
|
+
const _flatGrow = (need) => {
|
|
1554
|
+
let cap = _flat.length || 4096;
|
|
1555
|
+
if (_flatGrowHint > cap) cap = _flatGrowHint;
|
|
1556
|
+
_flatGrowHint = 0;
|
|
1922
1557
|
while (cap < need) cap *= 2;
|
|
1923
1558
|
const next = new Int32Array(cap);
|
|
1924
|
-
next.set(
|
|
1925
|
-
|
|
1559
|
+
next.set(_flat);
|
|
1560
|
+
_flat = next;
|
|
1926
1561
|
};
|
|
1562
|
+
// Reassigned (not mutated) when a `parseA*` parse hands its columns to a
|
|
1563
|
+
// retained snapshot, so the next parse starts on fresh arrays.
|
|
1927
1564
|
/** @type {(Node[] | null)[]} */
|
|
1928
|
-
|
|
1565
|
+
let _declarationLists = [];
|
|
1929
1566
|
/** @type {(Node[] | null)[]} */
|
|
1930
|
-
|
|
1931
|
-
let
|
|
1932
|
-
let
|
|
1933
|
-
/** @type {unknown} */ (null)
|
|
1934
|
-
);
|
|
1567
|
+
let _childRuleLists = [];
|
|
1568
|
+
let _input = "";
|
|
1569
|
+
let _locConverter = /** @type {LocConverter} */ (/** @type {unknown} */ (null));
|
|
1935
1570
|
|
|
1936
1571
|
// Node refs are integers here but typed `Node` across the parser; these are
|
|
1937
1572
|
// identity casts that just satisfy the type system at the boundary.
|
|
@@ -1941,175 +1576,181 @@ const _nodeIndex = (n) => /** @type {number} */ (/** @type {unknown} */ (n));
|
|
|
1941
1576
|
const _nodeRef = (i) => /** @type {Node} */ (/** @type {unknown} */ (i));
|
|
1942
1577
|
|
|
1943
1578
|
/** @param {number} need minimum capacity */
|
|
1944
|
-
const
|
|
1945
|
-
let cap =
|
|
1946
|
-
if (
|
|
1947
|
-
|
|
1579
|
+
const _grow = (need) => {
|
|
1580
|
+
let cap = _capacity || 4096;
|
|
1581
|
+
if (_growHint > cap) cap = _growHint;
|
|
1582
|
+
_growHint = 0;
|
|
1948
1583
|
while (cap < need) cap *= 2;
|
|
1949
1584
|
const ty = new Uint8Array(cap);
|
|
1950
|
-
ty.set(
|
|
1951
|
-
|
|
1585
|
+
ty.set(_types);
|
|
1586
|
+
_types = ty;
|
|
1952
1587
|
const st = new Int32Array(cap);
|
|
1953
|
-
st.set(
|
|
1954
|
-
|
|
1588
|
+
st.set(_starts);
|
|
1589
|
+
_starts = st;
|
|
1955
1590
|
const en = new Int32Array(cap);
|
|
1956
|
-
en.set(
|
|
1957
|
-
|
|
1591
|
+
en.set(_ends);
|
|
1592
|
+
_ends = en;
|
|
1958
1593
|
const a0 = new Int32Array(cap);
|
|
1959
|
-
a0.set(
|
|
1960
|
-
|
|
1594
|
+
a0.set(_aux0);
|
|
1595
|
+
_aux0 = a0;
|
|
1961
1596
|
const a1 = new Int32Array(cap);
|
|
1962
|
-
a1.set(
|
|
1963
|
-
|
|
1964
|
-
const a2 = new Int32Array(cap);
|
|
1965
|
-
a2.set(_soaAux2);
|
|
1966
|
-
_soaAux2 = a2;
|
|
1597
|
+
a1.set(_aux1);
|
|
1598
|
+
_aux1 = a1;
|
|
1967
1599
|
const fl = new Uint8Array(cap);
|
|
1968
|
-
fl.set(
|
|
1969
|
-
|
|
1600
|
+
fl.set(_flags);
|
|
1601
|
+
_flags = fl;
|
|
1970
1602
|
const ls = new Int32Array(cap);
|
|
1971
|
-
ls.set(
|
|
1972
|
-
|
|
1603
|
+
ls.set(_listStarts);
|
|
1604
|
+
_listStarts = ls;
|
|
1973
1605
|
const ll = new Int32Array(cap);
|
|
1974
|
-
ll.set(
|
|
1975
|
-
|
|
1976
|
-
|
|
1606
|
+
ll.set(_listLens);
|
|
1607
|
+
_listLens = ll;
|
|
1608
|
+
_capacity = cap;
|
|
1977
1609
|
};
|
|
1978
1610
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1979
|
-
const
|
|
1611
|
+
const _makeLeaf = (type, start, end) => {
|
|
1980
1612
|
// Ids are 1-based: a node ref is used in truthiness checks (`if (!parent)`),
|
|
1981
1613
|
// so 0 must stay reserved for "no node".
|
|
1982
|
-
// Leaves never read the flag / list slots — `
|
|
1614
|
+
// Leaves never read the flag / list slots — `_makeContainer` clears
|
|
1983
1615
|
// them instead, keeping the dominant leaf allocation at three writes.
|
|
1984
|
-
const i =
|
|
1985
|
-
if (i >=
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1616
|
+
const i = _nodeCount + 1;
|
|
1617
|
+
if (i >= _capacity) _grow(i + 1);
|
|
1618
|
+
_types[i] = type;
|
|
1619
|
+
_starts[i] = start;
|
|
1620
|
+
_ends[i] = end;
|
|
1621
|
+
_nodeCount = i;
|
|
1990
1622
|
return _nodeRef(i);
|
|
1991
1623
|
};
|
|
1992
1624
|
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1993
|
-
const
|
|
1994
|
-
const r =
|
|
1625
|
+
const _makeContainer = (type, start, end) => {
|
|
1626
|
+
const r = _makeLeaf(type, start, end);
|
|
1995
1627
|
const i = _nodeIndex(r);
|
|
1996
|
-
|
|
1997
|
-
// Clear
|
|
1998
|
-
// (content lists are flat spans, so zeroing the length
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
1628
|
+
_flags[i] = 0;
|
|
1629
|
+
// Clear the content-span length so a reused id never exposes a previous
|
|
1630
|
+
// node's children (content lists are flat spans, so zeroing the length
|
|
1631
|
+
// suffices). `blockStart` (aux1) is NOT defaulted here: only at-rules and
|
|
1632
|
+
// qualified rules carry a block, and they set it on every return path
|
|
1633
|
+
// (`_setBlock`, or `-1` for the no-block forms). `blockEnd` is not stored — a
|
|
1634
|
+
// block rule's `end` is its `blockEnd` (see `_setBlock`), else it is `-1`.
|
|
1635
|
+
_listLens[i] = 0;
|
|
1636
|
+
// The decl / child-rule slots are read only for rules (the walk guards on
|
|
1637
|
+
// type; readers treat a missing slot as `null`), so only rules clear them —
|
|
1638
|
+
// clearing every container would write these `id`-indexed arrays at scattered
|
|
1639
|
+
// ids and degrade them to slow dictionary mode on large non-recycling parses.
|
|
1640
|
+
if (type === T_AT_RULE || type === T_QUALIFIED_RULE) {
|
|
1641
|
+
_declarationLists[i] = null;
|
|
1642
|
+
_childRuleLists[i] = null;
|
|
1643
|
+
}
|
|
2002
1644
|
return r;
|
|
2003
1645
|
};
|
|
2004
1646
|
// Raw token value (the lazy `Token.value` form): hash / at-keyword drop their
|
|
2005
1647
|
// one-char prefix, url uses its content range. Shared by the parser's
|
|
2006
|
-
// mid-parse reads and the
|
|
1648
|
+
// mid-parse reads and the accessor.
|
|
2007
1649
|
/**
|
|
2008
1650
|
* @param {number} i node id
|
|
2009
1651
|
* @returns {string} raw token value
|
|
2010
1652
|
*/
|
|
2011
|
-
const
|
|
2012
|
-
const ty =
|
|
1653
|
+
const _valueOf = (i) => {
|
|
1654
|
+
const ty = _types[i];
|
|
2013
1655
|
if (ty === T_HASH || ty === T_AT_KEYWORD) {
|
|
2014
|
-
return
|
|
1656
|
+
return _input.slice(_starts[i] + 1, _ends[i]);
|
|
2015
1657
|
}
|
|
2016
|
-
if (ty === T_URL) return
|
|
2017
|
-
return
|
|
1658
|
+
if (ty === T_URL) return _input.slice(_aux0[i], _aux1[i]);
|
|
1659
|
+
return _input.slice(_starts[i], _ends[i]);
|
|
2018
1660
|
};
|
|
2019
|
-
// Module-level constants
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
1661
|
+
// Module-level constants (not per-parse closures), so each consume-algorithm
|
|
1662
|
+
// call site keeps one function identity and stays monomorphic.
|
|
1663
|
+
/** @type {(start: number, end: number, contentStart: number, contentEnd: number) => Node} */
|
|
1664
|
+
const _makeUrl = (start, end, cs, ce) => {
|
|
1665
|
+
const r = _makeLeaf(T_URL, start, end);
|
|
1666
|
+
_aux0[_nodeIndex(r)] = cs;
|
|
1667
|
+
_aux1[_nodeIndex(r)] = ce;
|
|
2025
1668
|
return r;
|
|
2026
1669
|
};
|
|
2027
|
-
/** @type {
|
|
2028
|
-
const
|
|
2029
|
-
_soaAllocContainer(T_STYLESHEET, start, start);
|
|
1670
|
+
/** @type {(start: number) => Node} */
|
|
1671
|
+
const _makeStylesheet = (start) => _makeContainer(T_STYLESHEET, start, start);
|
|
2030
1672
|
// name / nameStart are derived from start + nameEnd; only nameEnd is stored.
|
|
2031
|
-
/** @type {
|
|
2032
|
-
const
|
|
2033
|
-
|
|
1673
|
+
/** @type {(r: Node, nameStart: number, nameEnd: number) => void} */
|
|
1674
|
+
const _setName = (r, ns, ne) => {
|
|
1675
|
+
_aux0[_nodeIndex(r)] = ne;
|
|
2034
1676
|
};
|
|
2035
|
-
/** @type {
|
|
2036
|
-
const
|
|
2037
|
-
|
|
1677
|
+
/** @type {(r: Node, v: number) => void} */
|
|
1678
|
+
const _setEnd = (r, v) => {
|
|
1679
|
+
_ends[_nodeIndex(r)] = v;
|
|
2038
1680
|
};
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
1681
|
+
// `blockEnd` is not stored: for a block rule the parser sets `end` to the
|
|
1682
|
+
// block end right after this (so `A.blockEnd` reads `end`); a `-1` blockStart
|
|
1683
|
+
// marks the no-block forms, where `blockEnd` derives to `-1`.
|
|
1684
|
+
/** @type {(r: Node, blockStart: number) => void} */
|
|
1685
|
+
const _setBlock = (r, bs) => {
|
|
1686
|
+
_aux1[_nodeIndex(r)] = bs;
|
|
2044
1687
|
};
|
|
2045
|
-
/** @type {
|
|
2046
|
-
const
|
|
2047
|
-
|
|
1688
|
+
/** @type {(r: Node) => void} */
|
|
1689
|
+
const _setImportant = (r) => {
|
|
1690
|
+
_flags[_nodeIndex(r)] |= 1;
|
|
2048
1691
|
};
|
|
2049
1692
|
// A simple block's token is derived from its opening char on read.
|
|
2050
|
-
/** @type {
|
|
2051
|
-
const
|
|
2052
|
-
/** @type {
|
|
2053
|
-
const
|
|
1693
|
+
/** @type {(r: Node, ch: SimpleBlockToken) => void} */
|
|
1694
|
+
const _setToken = (r, ch) => {};
|
|
1695
|
+
/** @type {(r: Node, list: Node[]) => void} */
|
|
1696
|
+
const _setValue = (r, list) => {
|
|
2054
1697
|
// Seal the finished list: copy its refs into the flat buffer and hand the
|
|
2055
1698
|
// scratch array back to the pool. The caller never touches `list` again.
|
|
2056
|
-
const i = _nodeIndex(r);
|
|
2057
1699
|
const len = list.length;
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
1700
|
+
// Empty seal (common in non-modules skip mode, where value/prelude leaves are
|
|
1701
|
+
// dropped): `_makeContainer` already left `_listLens` at 0, so skip the flat
|
|
1702
|
+
// writes entirely and just recycle the scratch array.
|
|
1703
|
+
if (len !== 0) {
|
|
1704
|
+
const i = _nodeIndex(r);
|
|
1705
|
+
const start = _flatTop;
|
|
1706
|
+
if (start + len > _flat.length) _flatGrow(start + len);
|
|
1707
|
+
for (let k = 0; k < len; k++) {
|
|
1708
|
+
_flat[start + k] = _nodeIndex(list[k]);
|
|
1709
|
+
}
|
|
1710
|
+
_flatTop = start + len;
|
|
1711
|
+
_listStarts[i] = start;
|
|
1712
|
+
_listLens[i] = len;
|
|
1713
|
+
list.length = 0;
|
|
1714
|
+
}
|
|
2067
1715
|
_listPool.push(list);
|
|
2068
1716
|
};
|
|
2069
|
-
/** @type {
|
|
2070
|
-
const
|
|
1717
|
+
/** @type {(r: Node, decls: Node[], childRules: Node[]) => void} */
|
|
1718
|
+
const _setBody = (r, decls, childRules) => {
|
|
2071
1719
|
const i = _nodeIndex(r);
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
};
|
|
2075
|
-
/** @type {
|
|
2076
|
-
const
|
|
2077
|
-
/** @type {
|
|
2078
|
-
const
|
|
2079
|
-
/** @type {
|
|
2080
|
-
const
|
|
2081
|
-
/** @type {
|
|
2082
|
-
const
|
|
2083
|
-
/** @type {SimpleBlockToken} */ (
|
|
2084
|
-
|
|
1720
|
+
_declarationLists[i] = decls;
|
|
1721
|
+
_childRuleLists[i] = childRules;
|
|
1722
|
+
};
|
|
1723
|
+
/** @type {(r: Node) => number} */
|
|
1724
|
+
const _nodeTypeOf = (r) => _types[_nodeIndex(r)];
|
|
1725
|
+
/** @type {(r: Node) => number} */
|
|
1726
|
+
const _nodeStartOf = (r) => _starts[_nodeIndex(r)];
|
|
1727
|
+
/** @type {(r: Node) => string} */
|
|
1728
|
+
const _nodeValueOf = (r) => _valueOf(_nodeIndex(r));
|
|
1729
|
+
/** @type {(r: Node) => SimpleBlockToken} */
|
|
1730
|
+
const _nodeTokenOf = (r) =>
|
|
1731
|
+
/** @type {SimpleBlockToken} */ (_input[_starts[_nodeIndex(r)]]);
|
|
1732
|
+
// A container's value, a rule's prelude, and a stylesheet's rules all seal into
|
|
1733
|
+
// the one content-list writer, so `_setPrelude` / `_setRules` are named views of
|
|
1734
|
+
// `_setValue` that keep the consume algorithms reading in spec terms.
|
|
1735
|
+
const _setPrelude = _setValue;
|
|
1736
|
+
const _setRules = _setValue;
|
|
1737
|
+
|
|
1738
|
+
/**
|
|
1739
|
+
* Start a parse into the store: point it at this source, reset the node /
|
|
1740
|
+
* flat cursors so nodes accumulate from id 1, and clear skip state (`grammar`
|
|
1741
|
+
* sets its own afterwards; `parseA*` leave it off to build the full tree).
|
|
2085
1742
|
* @param {string} input source
|
|
2086
1743
|
* @param {LocConverter} lc loc converter
|
|
2087
1744
|
*/
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
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;
|
|
1745
|
+
const _setupParse = (input, lc) => {
|
|
1746
|
+
_input = input;
|
|
1747
|
+
_locConverter = lc;
|
|
1748
|
+
_nodeCount = 0;
|
|
1749
|
+
_flatTop = 0;
|
|
1750
|
+
_skipTypes = _NO_SKIP_TYPES;
|
|
1751
|
+
_skipActive = false;
|
|
1752
|
+
_skipSelectorPrelude = false;
|
|
1753
|
+
_skipAtRulePrelude = false;
|
|
2113
1754
|
};
|
|
2114
1755
|
|
|
2115
1756
|
/**
|
|
@@ -2247,6 +1888,20 @@ class TokenStream {
|
|
|
2247
1888
|
}
|
|
2248
1889
|
}
|
|
2249
1890
|
|
|
1891
|
+
/**
|
|
1892
|
+
* Advance past the already-peeked next token, skipping the redundant `next()`
|
|
1893
|
+
* re-check `consume` / `discard` pay. Precondition: the caller has just called
|
|
1894
|
+
* `next()` (so `_tok` is the cached next token and `_hasNext` is true) and that
|
|
1895
|
+
* token is not the `<eof-token>` — the hot "peek, decide, advance" sites where
|
|
1896
|
+
* both always hold. Callers that can't guarantee a non-EOF cached token use
|
|
1897
|
+
* `consume` / `discard` instead.
|
|
1898
|
+
* @returns {void}
|
|
1899
|
+
*/
|
|
1900
|
+
advance() {
|
|
1901
|
+
this._pos = this._tok.end;
|
|
1902
|
+
this._hasNext = false;
|
|
1903
|
+
}
|
|
1904
|
+
|
|
2250
1905
|
/**
|
|
2251
1906
|
* Mark (CSS Syntax §3 "mark") — push the current cursor position.
|
|
2252
1907
|
* @returns {void}
|
|
@@ -2306,6 +1961,353 @@ const normalizeIntoTokenStream = (input, pos, onComment) =>
|
|
|
2306
1961
|
* @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
|
|
2307
1962
|
*/
|
|
2308
1963
|
|
|
1964
|
+
// === parseA* retained store + property-accessor readers ===
|
|
1965
|
+
// `grammar` recycles the columns per top-level rule, but the `parseA*` entry
|
|
1966
|
+
// points must hand back a tree that outlives the parse. Each `parseA*` run parses
|
|
1967
|
+
// into the columns without recycling, then `_finishStore` hands them to a
|
|
1968
|
+
// snapshot object and resets the module columns to fresh arrays so the next parse
|
|
1969
|
+
// can't clobber it. Nodes are exposed as `parseA*` readers: plain objects sharing
|
|
1970
|
+
// one module-level prototype whose getters index the reader's own snapshot by node
|
|
1971
|
+
// id — no per-node class, no eager string slices, child readers built lazily on
|
|
1972
|
+
// access. A single shared prototype (rather than one per store) keeps the readers
|
|
1973
|
+
// monomorphic across parses, so `_readerAt` and every getter stay on the fast path.
|
|
1974
|
+
|
|
1975
|
+
/**
|
|
1976
|
+
* @typedef {object} NodeReader
|
|
1977
|
+
* @property {NodeStore} _store snapshot this reader indexes
|
|
1978
|
+
* @property {number} _i node id into the snapshot columns
|
|
1979
|
+
* @property {number} type node type
|
|
1980
|
+
* @property {number} start start offset
|
|
1981
|
+
* @property {number} end end offset
|
|
1982
|
+
* @property {[number, number]} range start / end offsets
|
|
1983
|
+
* @property {{ start: { line: number, column: number }, end: { line: number, column: number } }} loc source location
|
|
1984
|
+
* @property {() => string} toString source slice
|
|
1985
|
+
* @property {string | ComponentValue[]} value token value (leaf) or component-value list (function / block / declaration)
|
|
1986
|
+
* @property {string} unescaped unescaped token value
|
|
1987
|
+
* @property {number} numericValue parsed numeric value
|
|
1988
|
+
* @property {"integer" | "number" | "id" | "unrestricted"} typeFlag spec type flag
|
|
1989
|
+
* @property {"+" | "-" | ""} sign spec sign
|
|
1990
|
+
* @property {string} unit dimension unit (lower-cased)
|
|
1991
|
+
* @property {number} contentStart url content start offset
|
|
1992
|
+
* @property {number} contentEnd url content end offset
|
|
1993
|
+
* @property {string} name rule / declaration / function name
|
|
1994
|
+
* @property {number} nameStart name start offset
|
|
1995
|
+
* @property {number} nameEnd name end offset
|
|
1996
|
+
* @property {string} unescapedName unescaped name
|
|
1997
|
+
* @property {ComponentValue[]} prelude rule prelude
|
|
1998
|
+
* @property {Declaration[] | null} declarations block declarations
|
|
1999
|
+
* @property {Rule[] | null} childRules block child rules
|
|
2000
|
+
* @property {number} blockStart `{` start offset
|
|
2001
|
+
* @property {number} blockEnd `}` end offset
|
|
2002
|
+
* @property {boolean} important `!important` flag
|
|
2003
|
+
* @property {SimpleBlockToken} token simple-block opening char
|
|
2004
|
+
* @property {Rule[]} rules stylesheet rules
|
|
2005
|
+
*/
|
|
2006
|
+
|
|
2007
|
+
/**
|
|
2008
|
+
* @typedef {object} NodeStore
|
|
2009
|
+
* @property {string} input source
|
|
2010
|
+
* @property {LocConverter} lc loc converter
|
|
2011
|
+
* @property {Uint8Array} types node-type column
|
|
2012
|
+
* @property {Int32Array} starts start-offset column
|
|
2013
|
+
* @property {Int32Array} ends end-offset column
|
|
2014
|
+
* @property {Int32Array} aux0 aux slot 0
|
|
2015
|
+
* @property {Int32Array} aux1 aux slot 1
|
|
2016
|
+
* @property {Uint8Array} flags flags column
|
|
2017
|
+
* @property {Int32Array} listStarts content-span start column
|
|
2018
|
+
* @property {Int32Array} listLens content-span length column
|
|
2019
|
+
* @property {Int32Array} flat flat node-ref buffer
|
|
2020
|
+
* @property {(Node[] | null)[]} declLists per-node declaration lists
|
|
2021
|
+
* @property {(Node[] | null)[]} childLists per-node child-rule lists
|
|
2022
|
+
*/
|
|
2023
|
+
|
|
2024
|
+
// Shared frozen empty list for a block body with no declarations / child rules,
|
|
2025
|
+
// so equal-empty reads return one reference (mirrors `_EMPTY_LIST`).
|
|
2026
|
+
const _READER_EMPTY = /** @type {Rule[]} */ (
|
|
2027
|
+
/** @type {unknown} */ (Object.freeze([]))
|
|
2028
|
+
);
|
|
2029
|
+
|
|
2030
|
+
/**
|
|
2031
|
+
* Raw token value over a snapshot (the lazy `Token.value` form): hash / at-keyword
|
|
2032
|
+
* drop their one-char prefix, url uses its content range.
|
|
2033
|
+
* @param {NodeStore} store snapshot
|
|
2034
|
+
* @param {number} i node id
|
|
2035
|
+
* @returns {string} raw token value
|
|
2036
|
+
*/
|
|
2037
|
+
const _storeValueOf = (store, i) => {
|
|
2038
|
+
const ty = store.types[i];
|
|
2039
|
+
if (ty === T_HASH || ty === T_AT_KEYWORD) {
|
|
2040
|
+
return store.input.slice(store.starts[i] + 1, store.ends[i]);
|
|
2041
|
+
}
|
|
2042
|
+
if (ty === T_URL) return store.input.slice(store.aux0[i], store.aux1[i]);
|
|
2043
|
+
return store.input.slice(store.starts[i], store.ends[i]);
|
|
2044
|
+
};
|
|
2045
|
+
|
|
2046
|
+
/**
|
|
2047
|
+
* @param {NodeStore} store snapshot
|
|
2048
|
+
* @param {number} i node id
|
|
2049
|
+
* @returns {Node} reader over node `i`
|
|
2050
|
+
*/
|
|
2051
|
+
const _readerAt = (store, i) => {
|
|
2052
|
+
const o = Object.create(NODE_READER_PROTO);
|
|
2053
|
+
o._store = store;
|
|
2054
|
+
o._i = i;
|
|
2055
|
+
return /** @type {Node} */ (o);
|
|
2056
|
+
};
|
|
2057
|
+
|
|
2058
|
+
/**
|
|
2059
|
+
* Readers over a container's flat content span (value / prelude / rules).
|
|
2060
|
+
* @param {NodeStore} store snapshot
|
|
2061
|
+
* @param {number} i container id
|
|
2062
|
+
* @returns {Node[]} child readers
|
|
2063
|
+
*/
|
|
2064
|
+
const _readList = (store, i) => {
|
|
2065
|
+
const s = store.listStarts[i];
|
|
2066
|
+
const len = store.listLens[i];
|
|
2067
|
+
const flat = store.flat;
|
|
2068
|
+
/** @type {Node[]} */
|
|
2069
|
+
const out = [];
|
|
2070
|
+
for (let k = 0; k < len; k++) out.push(_readerAt(store, flat[s + k]));
|
|
2071
|
+
return out;
|
|
2072
|
+
};
|
|
2073
|
+
|
|
2074
|
+
/**
|
|
2075
|
+
* Readers over a node-id list (declarations / child rules).
|
|
2076
|
+
* @param {NodeStore} store snapshot
|
|
2077
|
+
* @param {Node[]} list node-id list
|
|
2078
|
+
* @returns {Node[]} child readers
|
|
2079
|
+
*/
|
|
2080
|
+
const _readRefList = (store, list) => {
|
|
2081
|
+
/** @type {Node[]} */
|
|
2082
|
+
const out = [];
|
|
2083
|
+
for (let k = 0; k < list.length; k++) {
|
|
2084
|
+
out.push(_readerAt(store, _nodeIndex(list[k])));
|
|
2085
|
+
}
|
|
2086
|
+
return out;
|
|
2087
|
+
};
|
|
2088
|
+
|
|
2089
|
+
// Module-level reader prototype shared by every `parseA*` reader. Getters index
|
|
2090
|
+
// the reader's own `_store` snapshot by its `_i` node id; because the prototype is
|
|
2091
|
+
// created once (not per store), all readers share one hidden map and stay
|
|
2092
|
+
// monomorphic across parses.
|
|
2093
|
+
const NODE_READER_PROTO = /** @type {NodeReader} */ ({
|
|
2094
|
+
_store: /** @type {NodeStore} */ (/** @type {unknown} */ (null)),
|
|
2095
|
+
_i: 0,
|
|
2096
|
+
get type() {
|
|
2097
|
+
return this._store.types[this._i];
|
|
2098
|
+
},
|
|
2099
|
+
get start() {
|
|
2100
|
+
return this._store.starts[this._i];
|
|
2101
|
+
},
|
|
2102
|
+
get end() {
|
|
2103
|
+
return this._store.ends[this._i];
|
|
2104
|
+
},
|
|
2105
|
+
get range() {
|
|
2106
|
+
const store = this._store;
|
|
2107
|
+
const i = this._i;
|
|
2108
|
+
return /** @type {[number, number]} */ ([store.starts[i], store.ends[i]]);
|
|
2109
|
+
},
|
|
2110
|
+
get loc() {
|
|
2111
|
+
const store = this._store;
|
|
2112
|
+
const i = this._i;
|
|
2113
|
+
const lc = store.lc;
|
|
2114
|
+
// `LocConverter#get` mutates and returns itself, so snapshot the first.
|
|
2115
|
+
const s = lc.get(store.starts[i]);
|
|
2116
|
+
const sl = s.line;
|
|
2117
|
+
const sc = s.column;
|
|
2118
|
+
const e = lc.get(store.ends[i]);
|
|
2119
|
+
return {
|
|
2120
|
+
start: { line: sl, column: sc },
|
|
2121
|
+
end: { line: e.line, column: e.column }
|
|
2122
|
+
};
|
|
2123
|
+
},
|
|
2124
|
+
toString() {
|
|
2125
|
+
const store = this._store;
|
|
2126
|
+
const i = this._i;
|
|
2127
|
+
return store.input.slice(store.starts[i], store.ends[i]);
|
|
2128
|
+
},
|
|
2129
|
+
get value() {
|
|
2130
|
+
const store = this._store;
|
|
2131
|
+
const i = this._i;
|
|
2132
|
+
const ty = store.types[i];
|
|
2133
|
+
// function / simple-block / declaration expose their component-value
|
|
2134
|
+
// list; every leaf token exposes its raw string value.
|
|
2135
|
+
return ty === T_FUNCTION || ty === T_SIMPLE_BLOCK || ty === T_DECLARATION
|
|
2136
|
+
? /** @type {ComponentValue[]} */ (_readList(store, i))
|
|
2137
|
+
: _storeValueOf(store, i);
|
|
2138
|
+
},
|
|
2139
|
+
get unescaped() {
|
|
2140
|
+
const store = this._store;
|
|
2141
|
+
const i = this._i;
|
|
2142
|
+
const v = _storeValueOf(store, i);
|
|
2143
|
+
return store.types[i] === T_STRING
|
|
2144
|
+
? unescapeIdentifier(v.slice(1, -1))
|
|
2145
|
+
: unescapeIdentifier(v);
|
|
2146
|
+
},
|
|
2147
|
+
get numericValue() {
|
|
2148
|
+
const store = this._store;
|
|
2149
|
+
const i = this._i;
|
|
2150
|
+
const v = _storeValueOf(store, i);
|
|
2151
|
+
if (store.types[i] === T_DIMENSION) {
|
|
2152
|
+
return Number(v.slice(0, _consumeANumber(v, 0)));
|
|
2153
|
+
}
|
|
2154
|
+
if (store.types[i] === T_PERCENTAGE) return Number(v.slice(0, -1));
|
|
2155
|
+
return Number(v);
|
|
2156
|
+
},
|
|
2157
|
+
get typeFlag() {
|
|
2158
|
+
const store = this._store;
|
|
2159
|
+
const i = this._i;
|
|
2160
|
+
if (store.types[i] === T_HASH) {
|
|
2161
|
+
const input = store.input;
|
|
2162
|
+
const p = store.starts[i] + 1;
|
|
2163
|
+
return _ifThreeCodePointsWouldStartAnIdentSequence(
|
|
2164
|
+
input,
|
|
2165
|
+
p,
|
|
2166
|
+
input.charCodeAt(p),
|
|
2167
|
+
input.charCodeAt(p + 1),
|
|
2168
|
+
input.charCodeAt(p + 2)
|
|
2169
|
+
)
|
|
2170
|
+
? "id"
|
|
2171
|
+
: "unrestricted";
|
|
2172
|
+
}
|
|
2173
|
+
const v = _storeValueOf(store, i);
|
|
2174
|
+
return _typeFlagOf(
|
|
2175
|
+
store.types[i] === T_DIMENSION ? v.slice(0, _consumeANumber(v, 0)) : v
|
|
2176
|
+
);
|
|
2177
|
+
},
|
|
2178
|
+
get sign() {
|
|
2179
|
+
return _signOf(_storeValueOf(this._store, this._i));
|
|
2180
|
+
},
|
|
2181
|
+
get unit() {
|
|
2182
|
+
const v = _storeValueOf(this._store, this._i);
|
|
2183
|
+
return v.slice(_consumeANumber(v, 0)).toLowerCase();
|
|
2184
|
+
},
|
|
2185
|
+
get contentStart() {
|
|
2186
|
+
return this._store.aux0[this._i];
|
|
2187
|
+
},
|
|
2188
|
+
get contentEnd() {
|
|
2189
|
+
return this._store.aux1[this._i];
|
|
2190
|
+
},
|
|
2191
|
+
get name() {
|
|
2192
|
+
const store = this._store;
|
|
2193
|
+
const i = this._i;
|
|
2194
|
+
// An at-rule's name skips its `@`; others start at the node.
|
|
2195
|
+
return store.types[i] === T_AT_RULE
|
|
2196
|
+
? store.input.slice(store.starts[i] + 1, store.aux0[i])
|
|
2197
|
+
: store.input.slice(store.starts[i], store.aux0[i]);
|
|
2198
|
+
},
|
|
2199
|
+
get nameStart() {
|
|
2200
|
+
return this._store.starts[this._i];
|
|
2201
|
+
},
|
|
2202
|
+
get nameEnd() {
|
|
2203
|
+
return this._store.aux0[this._i];
|
|
2204
|
+
},
|
|
2205
|
+
get unescapedName() {
|
|
2206
|
+
return unescapeIdentifier(this.name);
|
|
2207
|
+
},
|
|
2208
|
+
get prelude() {
|
|
2209
|
+
return /** @type {ComponentValue[]} */ (_readList(this._store, this._i));
|
|
2210
|
+
},
|
|
2211
|
+
get declarations() {
|
|
2212
|
+
const store = this._store;
|
|
2213
|
+
// Non-rule containers never populate this slot (see `_makeContainer`), so a
|
|
2214
|
+
// missing entry (`undefined`) is normalized to `null` — same as a rule with
|
|
2215
|
+
// no block.
|
|
2216
|
+
const list = store.declLists[this._i] || null;
|
|
2217
|
+
return list === null
|
|
2218
|
+
? null
|
|
2219
|
+
: /** @type {Declaration[]} */ (
|
|
2220
|
+
list.length > 0 ? _readRefList(store, list) : _READER_EMPTY
|
|
2221
|
+
);
|
|
2222
|
+
},
|
|
2223
|
+
get childRules() {
|
|
2224
|
+
const store = this._store;
|
|
2225
|
+
const list = store.childLists[this._i] || null;
|
|
2226
|
+
return list === null
|
|
2227
|
+
? null
|
|
2228
|
+
: /** @type {Rule[]} */ (
|
|
2229
|
+
list.length > 0 ? _readRefList(store, list) : _READER_EMPTY
|
|
2230
|
+
);
|
|
2231
|
+
},
|
|
2232
|
+
get blockStart() {
|
|
2233
|
+
return this._store.aux1[this._i];
|
|
2234
|
+
},
|
|
2235
|
+
get blockEnd() {
|
|
2236
|
+
const store = this._store;
|
|
2237
|
+
const i = this._i;
|
|
2238
|
+
return store.aux1[i] !== -1 ? store.ends[i] : -1;
|
|
2239
|
+
},
|
|
2240
|
+
get important() {
|
|
2241
|
+
return (this._store.flags[this._i] & 1) !== 0;
|
|
2242
|
+
},
|
|
2243
|
+
get token() {
|
|
2244
|
+
const store = this._store;
|
|
2245
|
+
return /** @type {SimpleBlockToken} */ (store.input[store.starts[this._i]]);
|
|
2246
|
+
},
|
|
2247
|
+
get rules() {
|
|
2248
|
+
return /** @type {Rule[]} */ (_readList(this._store, this._i));
|
|
2249
|
+
}
|
|
2250
|
+
});
|
|
2251
|
+
|
|
2252
|
+
/**
|
|
2253
|
+
* Hand the module's live columns to a retained snapshot, then reset the
|
|
2254
|
+
* module columns to fresh empty arrays so the next parse starts clean and can't
|
|
2255
|
+
* mutate this store. Called once per `parseA*` after all consuming is done.
|
|
2256
|
+
* @returns {NodeStore} the retained snapshot
|
|
2257
|
+
*/
|
|
2258
|
+
const _finishStore = () => {
|
|
2259
|
+
/** @type {NodeStore} */
|
|
2260
|
+
const store = {
|
|
2261
|
+
input: _input,
|
|
2262
|
+
lc: _locConverter,
|
|
2263
|
+
types: _types,
|
|
2264
|
+
starts: _starts,
|
|
2265
|
+
ends: _ends,
|
|
2266
|
+
aux0: _aux0,
|
|
2267
|
+
aux1: _aux1,
|
|
2268
|
+
flags: _flags,
|
|
2269
|
+
listStarts: _listStarts,
|
|
2270
|
+
listLens: _listLens,
|
|
2271
|
+
flat: _flat,
|
|
2272
|
+
declLists: _declarationLists,
|
|
2273
|
+
childLists: _childRuleLists
|
|
2274
|
+
};
|
|
2275
|
+
// Carry this parse's size forward as a one-shot grow hint so the next parse
|
|
2276
|
+
// allocates its columns once instead of re-doubling from scratch (node ids
|
|
2277
|
+
// are 1-based, so `+1`).
|
|
2278
|
+
_growHint = _nodeCount + 1;
|
|
2279
|
+
_flatGrowHint = _flatTop;
|
|
2280
|
+
// Reset module state — the columns now belong to `store`.
|
|
2281
|
+
_capacity = 0;
|
|
2282
|
+
_nodeCount = 0;
|
|
2283
|
+
_flatTop = 0;
|
|
2284
|
+
_types = new Uint8Array(0);
|
|
2285
|
+
_starts = new Int32Array(0);
|
|
2286
|
+
_ends = new Int32Array(0);
|
|
2287
|
+
_aux0 = new Int32Array(0);
|
|
2288
|
+
_aux1 = new Int32Array(0);
|
|
2289
|
+
_flags = new Uint8Array(0);
|
|
2290
|
+
_listStarts = new Int32Array(0);
|
|
2291
|
+
_listLens = new Int32Array(0);
|
|
2292
|
+
_flat = new Int32Array(0);
|
|
2293
|
+
_declarationLists = [];
|
|
2294
|
+
_childRuleLists = [];
|
|
2295
|
+
_listPool.length = 0;
|
|
2296
|
+
_input = "";
|
|
2297
|
+
_locConverter = /** @type {LocConverter} */ (/** @type {unknown} */ (null));
|
|
2298
|
+
return store;
|
|
2299
|
+
};
|
|
2300
|
+
|
|
2301
|
+
/**
|
|
2302
|
+
* Finish the parse and wrap one consumed node ref as a `parseA*` reader, keeping
|
|
2303
|
+
* the caller's node type.
|
|
2304
|
+
* @template {Node} T
|
|
2305
|
+
* @param {T} ref consumed node ref
|
|
2306
|
+
* @returns {T} reader over the retained snapshot
|
|
2307
|
+
*/
|
|
2308
|
+
const _finishOne = (ref) =>
|
|
2309
|
+
/** @type {T} */ (_readerAt(_finishStore(), _nodeIndex(ref)));
|
|
2310
|
+
|
|
2309
2311
|
/**
|
|
2310
2312
|
* Parse a stylesheet, CSS Syntax Level 3
|
|
2311
2313
|
* [§5.3.4](https://drafts.csswg.org/css-syntax/#parse-stylesheet).
|
|
@@ -2318,15 +2320,17 @@ const parseAStylesheet = (input, pos = 0, options = {}) => {
|
|
|
2318
2320
|
// 1. If input is a byte stream for a stylesheet, decode bytes from input, and set input to the result.
|
|
2319
2321
|
// 2. Normalize input, and set input to the result.
|
|
2320
2322
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2321
|
-
|
|
2323
|
+
_setupParse(ts.input, ts.locConverter);
|
|
2322
2324
|
// 3. Create a new stylesheet, with its location set to location (or null, if location was not passed).
|
|
2323
2325
|
const start = ts.next().start;
|
|
2324
|
-
const stylesheet =
|
|
2326
|
+
const stylesheet = _makeStylesheet(start);
|
|
2325
2327
|
// 4. Consume a stylesheet's contents from input, and set the stylesheet's rules to the result.
|
|
2326
2328
|
_setRules(stylesheet, consumeAStylesheetsContents(ts));
|
|
2327
2329
|
_setEnd(stylesheet, ts.next().start);
|
|
2328
2330
|
// 5. Return the stylesheet.
|
|
2329
|
-
return
|
|
2331
|
+
return /** @type {Stylesheet} */ (
|
|
2332
|
+
_readerAt(_finishStore(), _nodeIndex(stylesheet))
|
|
2333
|
+
);
|
|
2330
2334
|
};
|
|
2331
2335
|
|
|
2332
2336
|
/**
|
|
@@ -2343,9 +2347,11 @@ const parseAStylesheet = (input, pos = 0, options = {}) => {
|
|
|
2343
2347
|
const parseAStylesheetsContents = (input, pos = 0, options = {}) => {
|
|
2344
2348
|
// 1. Normalize input, and set input to the result.
|
|
2345
2349
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2346
|
-
|
|
2350
|
+
_setupParse(ts.input, ts.locConverter);
|
|
2347
2351
|
// 2. Consume a stylesheet’s contents from input, and return the result.
|
|
2348
|
-
|
|
2352
|
+
const rules = consumeAStylesheetsContents(ts);
|
|
2353
|
+
const store = _finishStore();
|
|
2354
|
+
return /** @type {Rule[]} */ (_readRefList(store, rules));
|
|
2349
2355
|
};
|
|
2350
2356
|
|
|
2351
2357
|
/**
|
|
@@ -2359,9 +2365,17 @@ const parseAStylesheetsContents = (input, pos = 0, options = {}) => {
|
|
|
2359
2365
|
const parseABlocksContents = (input, pos = 0, options = {}) => {
|
|
2360
2366
|
// 1. Normalize input, and set input to the result.
|
|
2361
2367
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2362
|
-
|
|
2368
|
+
_setupParse(ts.input, ts.locConverter);
|
|
2363
2369
|
// 2. Consume a block’s contents from input, and return the result.
|
|
2364
|
-
|
|
2370
|
+
const { decls, rules } = consumeABlocksContents(ts);
|
|
2371
|
+
const store = _finishStore();
|
|
2372
|
+
return {
|
|
2373
|
+
decls: /** @type {Declaration[]} */ (_readRefList(store, decls)),
|
|
2374
|
+
rules:
|
|
2375
|
+
rules.length > 0
|
|
2376
|
+
? /** @type {Rule[]} */ (_readRefList(store, rules))
|
|
2377
|
+
: _READER_EMPTY
|
|
2378
|
+
};
|
|
2365
2379
|
};
|
|
2366
2380
|
|
|
2367
2381
|
/**
|
|
@@ -2377,9 +2391,9 @@ const parseABlocksContents = (input, pos = 0, options = {}) => {
|
|
|
2377
2391
|
const parseARule = (input, pos = 0, options = {}) => {
|
|
2378
2392
|
// 1. Normalize input, and set input to the result.
|
|
2379
2393
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2380
|
-
|
|
2394
|
+
_setupParse(ts.input, ts.locConverter);
|
|
2381
2395
|
// 2. Discard whitespace from input.
|
|
2382
|
-
while (ts.next().type === TT_WHITESPACE) ts.
|
|
2396
|
+
while (ts.next().type === TT_WHITESPACE) ts.advance();
|
|
2383
2397
|
// 3. If the next token from input is an <EOF-token>, return a syntax error.
|
|
2384
2398
|
// Otherwise, if the next token from input is an <at-keyword-token>, consume an at-rule from input, and let rule be the return value.
|
|
2385
2399
|
// Otherwise, consume a qualified rule from input and let rule be the return value.
|
|
@@ -2392,9 +2406,9 @@ const parseARule = (input, pos = 0, options = {}) => {
|
|
|
2392
2406
|
: consumeAQualifiedRule(ts);
|
|
2393
2407
|
if (!rule) return undefined;
|
|
2394
2408
|
// 4. Discard whitespace from input.
|
|
2395
|
-
while (ts.next().type === TT_WHITESPACE) ts.
|
|
2409
|
+
while (ts.next().type === TT_WHITESPACE) ts.advance();
|
|
2396
2410
|
// 5. If the next token from input is an <EOF-token>, return rule. Otherwise, return a syntax error.
|
|
2397
|
-
return ts.next().type === TT_EOF ? rule : undefined;
|
|
2411
|
+
return ts.next().type === TT_EOF ? _finishOne(rule) : undefined;
|
|
2398
2412
|
};
|
|
2399
2413
|
|
|
2400
2414
|
/**
|
|
@@ -2408,11 +2422,12 @@ const parseARule = (input, pos = 0, options = {}) => {
|
|
|
2408
2422
|
const parseADeclaration = (input, pos = 0, options = {}) => {
|
|
2409
2423
|
// 1. Normalize input, and set input to the result.
|
|
2410
2424
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2411
|
-
|
|
2425
|
+
_setupParse(ts.input, ts.locConverter);
|
|
2412
2426
|
// 2. Discard whitespace from input.
|
|
2413
|
-
while (ts.next().type === TT_WHITESPACE) ts.
|
|
2427
|
+
while (ts.next().type === TT_WHITESPACE) ts.advance();
|
|
2414
2428
|
// 3. Consume a declaration from input. If anything was returned, return it. Otherwise, return a syntax error.
|
|
2415
|
-
|
|
2429
|
+
const decl = consumeADeclaration(ts);
|
|
2430
|
+
return decl === undefined ? undefined : _finishOne(decl);
|
|
2416
2431
|
};
|
|
2417
2432
|
|
|
2418
2433
|
/**
|
|
@@ -2425,18 +2440,17 @@ const parseADeclaration = (input, pos = 0, options = {}) => {
|
|
|
2425
2440
|
const parseAComponentValue = (input, pos = 0, options = {}) => {
|
|
2426
2441
|
// 1. Normalize input, and set input to the result.
|
|
2427
2442
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2428
|
-
|
|
2443
|
+
_setupParse(ts.input, ts.locConverter);
|
|
2429
2444
|
// 2. Discard whitespace from input.
|
|
2430
|
-
while (ts.next().type === TT_WHITESPACE) ts.
|
|
2445
|
+
while (ts.next().type === TT_WHITESPACE) ts.advance();
|
|
2431
2446
|
// 3. If input is empty, return a syntax error.
|
|
2432
2447
|
if (ts.next().type === TT_EOF) return undefined;
|
|
2433
2448
|
// 4. Consume a component value from input and let value be the return value.
|
|
2434
2449
|
const result = consumeAComponentValue(ts);
|
|
2435
2450
|
// 5. Discard whitespace from input.
|
|
2436
|
-
while (ts.next().type === TT_WHITESPACE) ts.
|
|
2451
|
+
while (ts.next().type === TT_WHITESPACE) ts.advance();
|
|
2437
2452
|
// 6. If input is empty, return value. Otherwise, return a syntax error.
|
|
2438
|
-
|
|
2439
|
-
return undefined;
|
|
2453
|
+
return ts.next().type === TT_EOF ? _finishOne(result) : undefined;
|
|
2440
2454
|
};
|
|
2441
2455
|
|
|
2442
2456
|
/**
|
|
@@ -2450,10 +2464,12 @@ const parseAComponentValue = (input, pos = 0, options = {}) => {
|
|
|
2450
2464
|
const parseAListOfComponentValues = (input, pos = 0, options = {}) => {
|
|
2451
2465
|
// 1. Normalize input, and set input to the result.
|
|
2452
2466
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2453
|
-
|
|
2467
|
+
_setupParse(ts.input, ts.locConverter);
|
|
2454
2468
|
// 2. Consume a list of component values from input, and return the result.
|
|
2455
2469
|
// (`null` needs `bailOnCurly`, which is not passed here.)
|
|
2456
|
-
|
|
2470
|
+
const values = /** @type {Node[]} */ (consumeAListOfComponentValues(ts));
|
|
2471
|
+
const store = _finishStore();
|
|
2472
|
+
return /** @type {ComponentValue[]} */ (_readRefList(store, values));
|
|
2457
2473
|
};
|
|
2458
2474
|
|
|
2459
2475
|
/**
|
|
@@ -2470,23 +2486,24 @@ const parseACommaSeparatedListOfComponentValues = (
|
|
|
2470
2486
|
) => {
|
|
2471
2487
|
// 1. Normalize input, and set input to the result.
|
|
2472
2488
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2473
|
-
|
|
2489
|
+
_setupParse(ts.input, ts.locConverter);
|
|
2474
2490
|
// 2. Let groups be an empty list.
|
|
2475
|
-
/** @type {
|
|
2491
|
+
/** @type {Node[][]} */
|
|
2476
2492
|
const groups = [];
|
|
2477
2493
|
// 3. While input is not empty:
|
|
2478
2494
|
while (ts.next().type !== TT_EOF) {
|
|
2479
2495
|
// 3.1. Consume a list of component values from input, with <comma-token> as the stop token, and append the result to groups.
|
|
2480
2496
|
groups.push(
|
|
2481
|
-
/** @type {
|
|
2482
|
-
consumeAListOfComponentValues(ts, TT_COMMA)
|
|
2483
|
-
)
|
|
2497
|
+
/** @type {Node[]} */ (consumeAListOfComponentValues(ts, TT_COMMA))
|
|
2484
2498
|
);
|
|
2485
2499
|
// 3.2 Discard a token from input.
|
|
2486
2500
|
ts.discard();
|
|
2487
2501
|
}
|
|
2488
|
-
// 4. Return groups.
|
|
2489
|
-
|
|
2502
|
+
// 4. Return groups — wrap each group's refs against the retained snapshot.
|
|
2503
|
+
const store = _finishStore();
|
|
2504
|
+
return groups.map(
|
|
2505
|
+
(g) => /** @type {ComponentValue[]} */ (_readRefList(store, g))
|
|
2506
|
+
);
|
|
2490
2507
|
};
|
|
2491
2508
|
|
|
2492
2509
|
// === Parser algorithms (CSS Syntax Level 3 §5.4) ===
|
|
@@ -2559,11 +2576,11 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2559
2576
|
_makeContainer(T_AT_RULE, head.start, head.end)
|
|
2560
2577
|
);
|
|
2561
2578
|
_setName(rule, head.start, head.end);
|
|
2562
|
-
// Sealed (`_setPrelude`) at each return — the
|
|
2579
|
+
// Sealed (`_setPrelude`) at each return — the store consumes the
|
|
2563
2580
|
// scratch array when sealing, so it must be complete by then.
|
|
2564
|
-
const prelude =
|
|
2565
|
-
// declarations / childRules
|
|
2566
|
-
//
|
|
2581
|
+
const prelude = _takeList();
|
|
2582
|
+
// declarations / childRules stay null (no block); the `;` / EOF / nested-`}`
|
|
2583
|
+
// forms set blockStart / blockEnd to -1 explicitly at their return below.
|
|
2567
2584
|
|
|
2568
2585
|
// Like `consumeAQualifiedRule`: skip mode scans the prelude without
|
|
2569
2586
|
// materializing it (url tokens / functions kept so `@import url(…)` still
|
|
@@ -2580,6 +2597,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2580
2597
|
if (t.type === TT_SEMICOLON || t.type === TT_EOF) {
|
|
2581
2598
|
ts.discard();
|
|
2582
2599
|
_setPrelude(rule, prelude);
|
|
2600
|
+
_setBlock(rule, -1);
|
|
2583
2601
|
_setEnd(rule, t.start);
|
|
2584
2602
|
return rule;
|
|
2585
2603
|
}
|
|
@@ -2589,6 +2607,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2589
2607
|
else if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2590
2608
|
if (nested) {
|
|
2591
2609
|
_setPrelude(rule, prelude);
|
|
2610
|
+
_setBlock(rule, -1);
|
|
2592
2611
|
_setEnd(rule, t.start);
|
|
2593
2612
|
return rule;
|
|
2594
2613
|
}
|
|
@@ -2602,7 +2621,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
2602
2621
|
_setPrelude(rule, prelude);
|
|
2603
2622
|
const block = consumeABlock(ts);
|
|
2604
2623
|
_setBody(rule, block.decls, block.rules);
|
|
2605
|
-
_setBlock(rule, block.blockStart
|
|
2624
|
+
_setBlock(rule, block.blockStart);
|
|
2606
2625
|
_setEnd(rule, block.blockEnd);
|
|
2607
2626
|
return rule;
|
|
2608
2627
|
}
|
|
@@ -2649,17 +2668,28 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2649
2668
|
_makeContainer(T_QUALIFIED_RULE, start, start)
|
|
2650
2669
|
);
|
|
2651
2670
|
// Sealed (`_setPrelude`) at the `{` exit — the only path that returns the
|
|
2652
|
-
// rule; the parse-error exits abandon the scratch unsealed.
|
|
2653
|
-
|
|
2654
|
-
//
|
|
2655
|
-
//
|
|
2671
|
+
// rule; the parse-error exits abandon the scratch unsealed. Allocated lazily
|
|
2672
|
+
// on first push: skip mode usually pushes nothing (empty selector prelude),
|
|
2673
|
+
// and `_makeContainer` already zeroed the rule's list length, so a null
|
|
2674
|
+
// prelude reads as empty in the walk.
|
|
2675
|
+
/** @type {Node[] | null} */
|
|
2676
|
+
let prelude = null;
|
|
2677
|
+
// A returned qualified rule always has a block (only the `{` exit returns a
|
|
2678
|
+
// rule), so `_setBlock` always runs — no blockStart / blockEnd default needed.
|
|
2656
2679
|
|
|
2657
2680
|
// Skip mode leaves `prelude` empty (selector text is recovered from the
|
|
2658
2681
|
// rule's byte range, not its nodes); `first`/`second` still track the first
|
|
2659
2682
|
// two non-whitespace tokens the `--foo: {` disambiguation below needs.
|
|
2660
2683
|
const skip = _skipSelectorPrelude;
|
|
2684
|
+
// Non-skip: first two non-whitespace prelude nodes (computed at the `{`).
|
|
2661
2685
|
let first = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
2662
2686
|
let second = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
2687
|
+
// Skip mode: the same two tokens tracked by token type + start, so a dropped
|
|
2688
|
+
// leaf selector token needs no materialized node just for the disambiguation.
|
|
2689
|
+
// `0` (no token type) = unset.
|
|
2690
|
+
let firstTT = 0;
|
|
2691
|
+
let firstStart = 0;
|
|
2692
|
+
let secondTT = 0;
|
|
2663
2693
|
|
|
2664
2694
|
// Process input
|
|
2665
2695
|
for (;;) {
|
|
@@ -2674,12 +2704,17 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2674
2704
|
// This is a parse error. If nested is true, return nothing. Otherwise, consume a token and append the result to rule’s prelude.
|
|
2675
2705
|
else if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2676
2706
|
if (nested) return undefined;
|
|
2677
|
-
const node = consumeATokenAsNode(ts);
|
|
2678
2707
|
if (skip) {
|
|
2679
|
-
|
|
2680
|
-
|
|
2708
|
+
// Stray `}` is a non-ws token; record its type/start, drop the node.
|
|
2709
|
+
if (firstTT === 0) {
|
|
2710
|
+
firstTT = t.type;
|
|
2711
|
+
firstStart = t.start;
|
|
2712
|
+
} else if (secondTT === 0) {
|
|
2713
|
+
secondTT = t.type;
|
|
2714
|
+
}
|
|
2715
|
+
ts.advance();
|
|
2681
2716
|
} else {
|
|
2682
|
-
prelude.push(
|
|
2717
|
+
(prelude || (prelude = _takeList())).push(consumeATokenAsNode(ts));
|
|
2683
2718
|
}
|
|
2684
2719
|
continue;
|
|
2685
2720
|
}
|
|
@@ -2690,7 +2725,18 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2690
2725
|
// (This disambiguates custom-property declarations from nested qualified rules — `--foo: { … }` at top level of a block is a declaration, not a rule.)
|
|
2691
2726
|
// Otherwise, consume a block from input, and let child rules be the result.
|
|
2692
2727
|
else if (t.type === TT_LEFT_CURLY_BRACKET) {
|
|
2693
|
-
|
|
2728
|
+
// `--foo: {` disambiguation: are the first two non-ws prelude tokens an
|
|
2729
|
+
// ident starting with `--` followed by a colon? Skip mode reads the
|
|
2730
|
+
// tracked token type/start; non-skip reads the materialized prelude.
|
|
2731
|
+
let dashedDeclaration;
|
|
2732
|
+
if (skip) {
|
|
2733
|
+
dashedDeclaration =
|
|
2734
|
+
firstTT === TT_IDENTIFIER &&
|
|
2735
|
+
ts.input.startsWith("--", firstStart) &&
|
|
2736
|
+
secondTT === TT_COLON;
|
|
2737
|
+
} else if (prelude === null) {
|
|
2738
|
+
dashedDeclaration = false;
|
|
2739
|
+
} else {
|
|
2694
2740
|
let firstIdx = 0;
|
|
2695
2741
|
/* istanbul ignore next -- @preserve: leading whitespace is discarded before the rule, so the prelude never starts with it */
|
|
2696
2742
|
while (
|
|
@@ -2708,16 +2754,16 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2708
2754
|
}
|
|
2709
2755
|
first = prelude[firstIdx];
|
|
2710
2756
|
second = prelude[secondIdx];
|
|
2757
|
+
dashedDeclaration =
|
|
2758
|
+
first &&
|
|
2759
|
+
_nodeTypeOf(first) === T_IDENT &&
|
|
2760
|
+
// Test the source bytes directly — avoids forcing the lazy `value`
|
|
2761
|
+
// slice just to check the `--` custom-property prefix.
|
|
2762
|
+
ts.input.startsWith("--", _nodeStartOf(first)) &&
|
|
2763
|
+
second &&
|
|
2764
|
+
_nodeTypeOf(second) === T_COLON;
|
|
2711
2765
|
}
|
|
2712
|
-
if (
|
|
2713
|
-
first &&
|
|
2714
|
-
_nodeTypeOf(first) === T_IDENT &&
|
|
2715
|
-
// Test the source bytes directly — avoids forcing the lazy `value`
|
|
2716
|
-
// slice just to check the `--` custom-property prefix.
|
|
2717
|
-
ts.input.startsWith("--", _nodeStartOf(first)) &&
|
|
2718
|
-
second &&
|
|
2719
|
-
_nodeTypeOf(second) === T_COLON
|
|
2720
|
-
) {
|
|
2766
|
+
if (dashedDeclaration) {
|
|
2721
2767
|
/* istanbul ignore if -- @preserve: when nested, `declarationStartLikely` routes every `--x:` to consumeADeclaration (which accepts custom properties), so this fallthrough is unreachable */
|
|
2722
2768
|
if (nested) {
|
|
2723
2769
|
consumeTheRemnantsOfABadDeclaration(ts, true);
|
|
@@ -2726,30 +2772,45 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2726
2772
|
}
|
|
2727
2773
|
return undefined;
|
|
2728
2774
|
}
|
|
2729
|
-
_setPrelude(rule, prelude);
|
|
2775
|
+
if (prelude !== null) _setPrelude(rule, prelude);
|
|
2730
2776
|
const block = consumeABlock(ts);
|
|
2731
2777
|
_setBody(rule, block.decls, block.rules);
|
|
2732
|
-
_setBlock(rule, block.blockStart
|
|
2778
|
+
_setBlock(rule, block.blockStart);
|
|
2733
2779
|
_setEnd(rule, block.blockEnd);
|
|
2734
2780
|
return rule;
|
|
2735
2781
|
}
|
|
2736
2782
|
|
|
2737
2783
|
// anything else
|
|
2738
2784
|
// Consume a component value from input and append the result to rule’s prelude.
|
|
2739
|
-
const node = consumeAComponentValue(ts, t);
|
|
2740
2785
|
if (skip) {
|
|
2741
|
-
|
|
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);
|
|
2786
|
+
const tt = t.type;
|
|
2746
2787
|
// Track the first two non-whitespace tokens for the disambiguation above.
|
|
2747
|
-
if (
|
|
2748
|
-
if (
|
|
2749
|
-
|
|
2788
|
+
if (tt !== TT_WHITESPACE) {
|
|
2789
|
+
if (firstTT === 0) {
|
|
2790
|
+
firstTT = tt;
|
|
2791
|
+
firstStart = t.start;
|
|
2792
|
+
} else if (secondTT === 0) {
|
|
2793
|
+
secondTT = tt;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
// Only functions (which may hold a url like `:unknown(url(x))`) and the
|
|
2797
|
+
// `(` / `[` blocks that must be balanced are materialized; the url
|
|
2798
|
+
// visitor keeps url / function nodes. Every other selector leaf token has
|
|
2799
|
+
// no non-modules consumer — drop it without building a node.
|
|
2800
|
+
if (
|
|
2801
|
+
tt === TT_FUNCTION ||
|
|
2802
|
+
(tt >= TT_LEFT_PARENTHESIS && tt <= TT_LEFT_CURLY_BRACKET)
|
|
2803
|
+
) {
|
|
2804
|
+
const node = consumeAComponentValue(ts, t);
|
|
2805
|
+
const ty = _nodeTypeOf(node);
|
|
2806
|
+
if (ty === T_FUNCTION || ty === T_URL) {
|
|
2807
|
+
(prelude || (prelude = _takeList())).push(node);
|
|
2808
|
+
}
|
|
2809
|
+
} else {
|
|
2810
|
+
ts.advance();
|
|
2750
2811
|
}
|
|
2751
2812
|
} else {
|
|
2752
|
-
prelude.push(
|
|
2813
|
+
(prelude || (prelude = _takeList())).push(consumeAComponentValue(ts, t));
|
|
2753
2814
|
}
|
|
2754
2815
|
}
|
|
2755
2816
|
};
|
|
@@ -2838,9 +2899,9 @@ const consumeABlocksContents = (ts, onNode) => {
|
|
|
2838
2899
|
const t = ts.next();
|
|
2839
2900
|
|
|
2840
2901
|
// <whitespace-token> / <semicolon-token>
|
|
2841
|
-
// Discard a token from input.
|
|
2902
|
+
// Discard a token from input (`t` was just peeked and is non-EOF).
|
|
2842
2903
|
if (t.type === TT_WHITESPACE || t.type === TT_SEMICOLON) {
|
|
2843
|
-
ts.
|
|
2904
|
+
ts.advance();
|
|
2844
2905
|
}
|
|
2845
2906
|
// <EOF-token> / <}-token>
|
|
2846
2907
|
// Return decls and rules.
|
|
@@ -2921,8 +2982,8 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2921
2982
|
const { input } = ts;
|
|
2922
2983
|
// Let decl be a new declaration, with an initially empty name and a value set to an empty list.
|
|
2923
2984
|
const start = ts.next().start;
|
|
2924
|
-
//
|
|
2925
|
-
// `
|
|
2985
|
+
// nameEnd (= start) / important (unset) keep their container defaults;
|
|
2986
|
+
// `value` is set unconditionally at step 5 below.
|
|
2926
2987
|
const decl = /** @type {Declaration} */ (
|
|
2927
2988
|
_makeContainer(T_DECLARATION, start, start)
|
|
2928
2989
|
);
|
|
@@ -2938,19 +2999,19 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2938
2999
|
}
|
|
2939
3000
|
|
|
2940
3001
|
// 2. Discard whitespace from input.
|
|
2941
|
-
while (ts.next().type === TT_WHITESPACE) ts.
|
|
3002
|
+
while (ts.next().type === TT_WHITESPACE) ts.advance();
|
|
2942
3003
|
|
|
2943
3004
|
// 3. If the next token is a <colon-token>, discard a token from input.
|
|
2944
3005
|
// Otherwise, consume the remnants of a bad declaration from input, with nested, and return nothing.
|
|
2945
3006
|
if (ts.next().type === TT_COLON) {
|
|
2946
|
-
ts.
|
|
3007
|
+
ts.advance();
|
|
2947
3008
|
} else {
|
|
2948
3009
|
consumeTheRemnantsOfABadDeclaration(ts, nested);
|
|
2949
3010
|
return undefined;
|
|
2950
3011
|
}
|
|
2951
3012
|
|
|
2952
3013
|
// 4. Discard whitespace from input.
|
|
2953
|
-
while (ts.next().type === TT_WHITESPACE) ts.
|
|
3014
|
+
while (ts.next().type === TT_WHITESPACE) ts.advance();
|
|
2954
3015
|
|
|
2955
3016
|
// Step 8's custom-property test, computed early so the value parse can bail.
|
|
2956
3017
|
const isCustomProperty = input.startsWith("--", start);
|
|
@@ -2968,7 +3029,7 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2968
3029
|
);
|
|
2969
3030
|
if (value === null) return undefined;
|
|
2970
3031
|
// `_setValue` waits until step 9: steps 6-8 still trim / scan the scratch,
|
|
2971
|
-
// and the
|
|
3032
|
+
// and the store consumes it when sealing.
|
|
2972
3033
|
_setEnd(decl, ts.next().start);
|
|
2973
3034
|
|
|
2974
3035
|
// 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.
|
|
@@ -3031,7 +3092,7 @@ const consumeAListOfComponentValues = (
|
|
|
3031
3092
|
nested = false,
|
|
3032
3093
|
bailOnCurly = false
|
|
3033
3094
|
) => {
|
|
3034
|
-
const values = /** @type {ComponentValue[]} */ (
|
|
3095
|
+
const values = /** @type {ComponentValue[]} */ (_takeList());
|
|
3035
3096
|
// Process input
|
|
3036
3097
|
for (;;) {
|
|
3037
3098
|
const t = ts.next();
|
|
@@ -3060,7 +3121,7 @@ const consumeAListOfComponentValues = (
|
|
|
3060
3121
|
if (bailOnCurly && t.type === TT_LEFT_CURLY_BRACKET) return null;
|
|
3061
3122
|
// anything else
|
|
3062
3123
|
// Consume a component value from input, and append the result to values.
|
|
3063
|
-
// Skipped leaf types short-circuit before materializing: no
|
|
3124
|
+
// Skipped leaf types short-circuit before materializing: no column slot is
|
|
3064
3125
|
// written and no node is built (blocks / functions never skip here).
|
|
3065
3126
|
const tt = t.type;
|
|
3066
3127
|
if (
|
|
@@ -3069,7 +3130,8 @@ const consumeAListOfComponentValues = (
|
|
|
3069
3130
|
!(tt >= TT_LEFT_PARENTHESIS && tt <= TT_LEFT_CURLY_BRACKET) &&
|
|
3070
3131
|
_skipTypes[_ttToNodeType[tt]] === 1
|
|
3071
3132
|
) {
|
|
3072
|
-
|
|
3133
|
+
// `t` was just peeked and is a skipped value leaf (never EOF).
|
|
3134
|
+
ts.advance();
|
|
3073
3135
|
continue;
|
|
3074
3136
|
}
|
|
3075
3137
|
const node = consumeAComponentValue(ts, t);
|
|
@@ -3098,10 +3160,11 @@ const consumeAComponentValue = (ts, t = ts.next()) => {
|
|
|
3098
3160
|
}
|
|
3099
3161
|
// anything else
|
|
3100
3162
|
// Consume a token from input and return the result. (Asserted: not EOF.)
|
|
3101
|
-
// Inlined `consumeATokenAsNode`: `t` is already the peeked next token
|
|
3102
|
-
// advance past it and materialize it directly —
|
|
3103
|
-
// component value (the bulk of the nodes
|
|
3104
|
-
|
|
3163
|
+
// Inlined `consumeATokenAsNode`: `t` is already the peeked next token (and not
|
|
3164
|
+
// EOF), so `advance` past it and materialize it directly — no redundant
|
|
3165
|
+
// `next()` and one fewer call per leaf component value (the bulk of the nodes
|
|
3166
|
+
// on a large stylesheet).
|
|
3167
|
+
ts.advance();
|
|
3105
3168
|
return /** @type {ComponentValue} */ (tokenToNode(t));
|
|
3106
3169
|
};
|
|
3107
3170
|
|
|
@@ -3123,7 +3186,7 @@ const consumeASimpleBlock = (ts) => {
|
|
|
3123
3186
|
);
|
|
3124
3187
|
_setToken(block, token);
|
|
3125
3188
|
// Sealed (`_setValue`) at the return, once complete.
|
|
3126
|
-
const val =
|
|
3189
|
+
const val = _takeList();
|
|
3127
3190
|
|
|
3128
3191
|
// Discard a token from input.
|
|
3129
3192
|
ts.discard();
|
|
@@ -3162,7 +3225,7 @@ const consumeAFunction = (ts) => {
|
|
|
3162
3225
|
);
|
|
3163
3226
|
_setName(fn, tFn.start, tFn.end - 1);
|
|
3164
3227
|
// Sealed (`_setValue`) at the return, once complete.
|
|
3165
|
-
const val =
|
|
3228
|
+
const val = _takeList();
|
|
3166
3229
|
|
|
3167
3230
|
// Process input
|
|
3168
3231
|
for (;;) {
|
|
@@ -3188,7 +3251,8 @@ const consumeAFunction = (ts) => {
|
|
|
3188
3251
|
!(tt >= TT_LEFT_PARENTHESIS && tt <= TT_LEFT_CURLY_BRACKET) &&
|
|
3189
3252
|
_skipTypes[_ttToNodeType[tt]] === 1
|
|
3190
3253
|
) {
|
|
3191
|
-
|
|
3254
|
+
// `t` was just peeked and is a skipped value leaf (never EOF).
|
|
3255
|
+
ts.advance();
|
|
3192
3256
|
continue;
|
|
3193
3257
|
}
|
|
3194
3258
|
const node = consumeAComponentValue(ts, t);
|
|
@@ -3493,14 +3557,15 @@ let _commentBucket;
|
|
|
3493
3557
|
|
|
3494
3558
|
// Comments reach the visitor map through `NodeType.Comment` instead of a
|
|
3495
3559
|
// side callback. They fire during tokenization — in source order among
|
|
3496
|
-
// comments, not interleaved with the node walk — on a transient
|
|
3560
|
+
// comments, not interleaved with the node walk — on a transient store node so
|
|
3497
3561
|
// `A.start`/`end`/`loc`/`source` work. No comment visitor → no callback →
|
|
3498
3562
|
// the tokenizer skips comments with zero overhead.
|
|
3499
3563
|
/** @type {(input: string, start: number, end: number) => number} */
|
|
3500
|
-
const _grammarOnComment = (
|
|
3501
|
-
const node =
|
|
3564
|
+
const _grammarOnComment = (_src, start, end) => {
|
|
3565
|
+
const node = _makeLeaf(T_COMMENT, start, end);
|
|
3502
3566
|
_currentNode = node;
|
|
3503
3567
|
_currentParent = null;
|
|
3568
|
+
_currentIndex = 0;
|
|
3504
3569
|
const bucket = /** @type {CompiledVisitorBucket} */ (_commentBucket);
|
|
3505
3570
|
const e = bucket.enter;
|
|
3506
3571
|
for (let i = 0; i < e.length; i++) e[i](A);
|
|
@@ -3515,15 +3580,17 @@ const _grammarOnComment = (_input, start, end) => {
|
|
|
3515
3580
|
* loops — `for…of` would allocate an iterator per node on this hot path.
|
|
3516
3581
|
* @param {Node} node component-value root
|
|
3517
3582
|
* @param {Node | null} parent enclosing node
|
|
3583
|
+
* @param {number} index node's index within its sibling list
|
|
3518
3584
|
*/
|
|
3519
|
-
const _walkValue = (node, parent) => {
|
|
3520
|
-
const ty =
|
|
3585
|
+
const _walkValue = (node, parent, index) => {
|
|
3586
|
+
const ty = _types[_nodeIndex(node)];
|
|
3521
3587
|
const b = _visitors[ty];
|
|
3522
3588
|
let skip = false;
|
|
3523
3589
|
if (b !== undefined && b.enter.length !== 0) {
|
|
3524
3590
|
_walkSkip = false;
|
|
3525
3591
|
_currentNode = node;
|
|
3526
3592
|
_currentParent = parent;
|
|
3593
|
+
_currentIndex = index;
|
|
3527
3594
|
const e = b.enter;
|
|
3528
3595
|
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3529
3596
|
skip = _walkSkip;
|
|
@@ -3531,14 +3598,15 @@ const _walkValue = (node, parent) => {
|
|
|
3531
3598
|
}
|
|
3532
3599
|
if (!skip && (ty === T_FUNCTION || ty === T_SIMPLE_BLOCK)) {
|
|
3533
3600
|
const i0 = _nodeIndex(node);
|
|
3534
|
-
const vs =
|
|
3535
|
-
const ve = vs +
|
|
3536
|
-
for (let i = vs; i < ve; i++) _walkValue(_nodeRef(
|
|
3601
|
+
const vs = _listStarts[i0];
|
|
3602
|
+
const ve = vs + _listLens[i0];
|
|
3603
|
+
for (let i = vs; i < ve; i++) _walkValue(_nodeRef(_flat[i]), node, i - vs);
|
|
3537
3604
|
}
|
|
3538
3605
|
if (b !== undefined) {
|
|
3539
3606
|
// Rebind: descending into children moved the path.
|
|
3540
3607
|
_currentNode = node;
|
|
3541
3608
|
_currentParent = parent;
|
|
3609
|
+
_currentIndex = index;
|
|
3542
3610
|
const x = b.exit;
|
|
3543
3611
|
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3544
3612
|
}
|
|
@@ -3549,16 +3617,18 @@ const _walkValue = (node, parent) => {
|
|
|
3549
3617
|
* eagerly (§5.4.4), so its `value` holds the nested rules / declarations.
|
|
3550
3618
|
* @param {Node} node structural-tree root
|
|
3551
3619
|
* @param {Node | null} parent enclosing node
|
|
3620
|
+
* @param {number} index node's index within its sibling list (declarations and child rules index independently)
|
|
3552
3621
|
*/
|
|
3553
|
-
const _walkRule = (node, parent) => {
|
|
3622
|
+
const _walkRule = (node, parent, index) => {
|
|
3554
3623
|
const i0 = _nodeIndex(node);
|
|
3555
|
-
const ty =
|
|
3624
|
+
const ty = _types[i0];
|
|
3556
3625
|
const b = _visitors[ty];
|
|
3557
3626
|
let skip = false;
|
|
3558
3627
|
if (b !== undefined && b.enter.length !== 0) {
|
|
3559
3628
|
_walkSkip = false;
|
|
3560
3629
|
_currentNode = node;
|
|
3561
3630
|
_currentParent = parent;
|
|
3631
|
+
_currentIndex = index;
|
|
3562
3632
|
const e = b.enter;
|
|
3563
3633
|
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3564
3634
|
skip = _walkSkip;
|
|
@@ -3566,50 +3636,55 @@ const _walkRule = (node, parent) => {
|
|
|
3566
3636
|
}
|
|
3567
3637
|
if (!skip) {
|
|
3568
3638
|
if (ty === T_AT_RULE || ty === T_QUALIFIED_RULE) {
|
|
3569
|
-
const ps =
|
|
3570
|
-
const pe = ps +
|
|
3571
|
-
for (let i = ps; i < pe; i++)
|
|
3639
|
+
const ps = _listStarts[i0];
|
|
3640
|
+
const pe = ps + _listLens[i0];
|
|
3641
|
+
for (let i = ps; i < pe; i++) {
|
|
3642
|
+
_walkValue(_nodeRef(_flat[i]), node, i - ps);
|
|
3643
|
+
}
|
|
3572
3644
|
if (_recurseBlocks) {
|
|
3573
3645
|
// Declarations then child rules — downstream consumers don't need them strictly interleaved in source order.
|
|
3574
|
-
const decls =
|
|
3646
|
+
const decls = _declarationLists[i0];
|
|
3575
3647
|
if (decls) {
|
|
3576
|
-
for (let i = 0; i < decls.length; i++) _walkRule(decls[i], node);
|
|
3648
|
+
for (let i = 0; i < decls.length; i++) _walkRule(decls[i], node, i);
|
|
3577
3649
|
}
|
|
3578
|
-
const ch =
|
|
3579
|
-
if (ch) for (let i = 0; i < ch.length; i++) _walkRule(ch[i], node);
|
|
3650
|
+
const ch = _childRuleLists[i0];
|
|
3651
|
+
if (ch) for (let i = 0; i < ch.length; i++) _walkRule(ch[i], node, i);
|
|
3580
3652
|
}
|
|
3581
3653
|
} else if (ty === T_DECLARATION) {
|
|
3582
|
-
const vs =
|
|
3583
|
-
const ve = vs +
|
|
3584
|
-
for (let i = vs; i < ve; i++)
|
|
3654
|
+
const vs = _listStarts[i0];
|
|
3655
|
+
const ve = vs + _listLens[i0];
|
|
3656
|
+
for (let i = vs; i < ve; i++) {
|
|
3657
|
+
_walkValue(_nodeRef(_flat[i]), node, i - vs);
|
|
3658
|
+
}
|
|
3585
3659
|
}
|
|
3586
3660
|
}
|
|
3587
3661
|
if (b !== undefined) {
|
|
3588
3662
|
// Rebind: descending into children moved the path.
|
|
3589
3663
|
_currentNode = node;
|
|
3590
3664
|
_currentParent = parent;
|
|
3665
|
+
_currentIndex = index;
|
|
3591
3666
|
const x = b.exit;
|
|
3592
3667
|
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3593
3668
|
}
|
|
3594
3669
|
};
|
|
3595
3670
|
|
|
3596
3671
|
/**
|
|
3597
|
-
* The `grammar` streaming sink: walk one top-level node, then recycle the
|
|
3672
|
+
* The `grammar` streaming sink: walk one top-level node, then recycle the
|
|
3598
3673
|
* buffers for the next.
|
|
3599
3674
|
* @param {Rule | Declaration} node top-level node
|
|
3600
3675
|
*/
|
|
3601
3676
|
const _walkTopLevel = (node) => {
|
|
3602
|
-
_walkRule(node, null);
|
|
3603
|
-
if (
|
|
3604
|
-
if (
|
|
3605
|
-
|
|
3606
|
-
|
|
3677
|
+
_walkRule(node, null, 0);
|
|
3678
|
+
if (_nodeCount > _peak) _peak = _nodeCount;
|
|
3679
|
+
if (_flatTop > _flatPeak) _flatPeak = _flatTop;
|
|
3680
|
+
_nodeCount = 0;
|
|
3681
|
+
_flatTop = 0;
|
|
3607
3682
|
};
|
|
3608
3683
|
|
|
3609
|
-
// The
|
|
3684
|
+
// The store buffers grow to the largest single top-level rule ever parsed and
|
|
3610
3685
|
// live at module level; above this capacity they are re-shrunk after a parse
|
|
3611
3686
|
// so one pathological rule can't pin megabytes for the process lifetime.
|
|
3612
|
-
const
|
|
3687
|
+
const _SHRINK_CAPACITY = 65536;
|
|
3613
3688
|
|
|
3614
3689
|
/**
|
|
3615
3690
|
* The CSS `SourceProcessor` grammar: consume top-level rules one at a time
|
|
@@ -3623,7 +3698,7 @@ const _SOA_SHRINK_CAPACITY = 65536;
|
|
|
3623
3698
|
*/
|
|
3624
3699
|
const grammar = (input, visitors, options) => {
|
|
3625
3700
|
const locConverter = options.locConverter || new LocConverter(input);
|
|
3626
|
-
|
|
3701
|
+
_setupParse(input, locConverter);
|
|
3627
3702
|
const skip = options.skip;
|
|
3628
3703
|
_skipTypes = (skip && skip.types) || _NO_SKIP_TYPES;
|
|
3629
3704
|
_skipActive = _skipTypes !== _NO_SKIP_TYPES;
|
|
@@ -3648,39 +3723,36 @@ const grammar = (input, visitors, options) => {
|
|
|
3648
3723
|
try {
|
|
3649
3724
|
consume(ts, _walkTopLevel);
|
|
3650
3725
|
} finally {
|
|
3651
|
-
// Drop the module-level
|
|
3726
|
+
// Drop the module-level column references so the last parsed source (and
|
|
3652
3727
|
// its LocConverter / child lists / visitors) don't stay alive between
|
|
3653
3728
|
// parses.
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
_soaChildRuleLists.length = 0;
|
|
3660
|
-
_soaFlatTop = 0;
|
|
3729
|
+
_input = "";
|
|
3730
|
+
_locConverter = /** @type {LocConverter} */ (/** @type {unknown} */ (null));
|
|
3731
|
+
_declarationLists.length = 0;
|
|
3732
|
+
_childRuleLists.length = 0;
|
|
3733
|
+
_flatTop = 0;
|
|
3661
3734
|
_listPool.length = 0;
|
|
3662
|
-
if (
|
|
3663
|
-
|
|
3664
|
-
|
|
3735
|
+
if (_flat.length > _SHRINK_CAPACITY) {
|
|
3736
|
+
_flatGrowHint = _flatPeak;
|
|
3737
|
+
_flat = new Int32Array(0);
|
|
3665
3738
|
}
|
|
3666
3739
|
_visitors = /** @type {CompiledVisitorMap} */ (/** @type {unknown} */ ([]));
|
|
3667
3740
|
_commentBucket = undefined;
|
|
3668
|
-
if (
|
|
3741
|
+
if (_capacity > _SHRINK_CAPACITY) {
|
|
3669
3742
|
// +1: node ids are 1-based and grow fires at `id >= capacity`.
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
_soaListLens = new Int32Array(0);
|
|
3743
|
+
_growHint = _peak + 1;
|
|
3744
|
+
_capacity = 0;
|
|
3745
|
+
_types = new Uint8Array(0);
|
|
3746
|
+
_starts = new Int32Array(0);
|
|
3747
|
+
_ends = new Int32Array(0);
|
|
3748
|
+
_aux0 = new Int32Array(0);
|
|
3749
|
+
_aux1 = new Int32Array(0);
|
|
3750
|
+
_flags = new Uint8Array(0);
|
|
3751
|
+
_listStarts = new Int32Array(0);
|
|
3752
|
+
_listLens = new Int32Array(0);
|
|
3681
3753
|
}
|
|
3682
|
-
|
|
3683
|
-
|
|
3754
|
+
_peak = 0;
|
|
3755
|
+
_flatPeak = 0;
|
|
3684
3756
|
}
|
|
3685
3757
|
};
|
|
3686
3758
|
|
|
@@ -3733,11 +3805,11 @@ const buildSkipSet = (nodeTypes) => {
|
|
|
3733
3805
|
/** @type {(n: Node) => Node[]} */
|
|
3734
3806
|
const _materializeList = (n) => {
|
|
3735
3807
|
const i = _nodeIndex(n);
|
|
3736
|
-
const start =
|
|
3737
|
-
const len =
|
|
3808
|
+
const start = _listStarts[i];
|
|
3809
|
+
const len = _listLens[i];
|
|
3738
3810
|
/** @type {Node[]} */
|
|
3739
3811
|
const out = [];
|
|
3740
|
-
for (let k = 0; k < len; k++) out.push(_nodeRef(
|
|
3812
|
+
for (let k = 0; k < len; k++) out.push(_nodeRef(_flat[start + k]));
|
|
3741
3813
|
return out;
|
|
3742
3814
|
};
|
|
3743
3815
|
|
|
@@ -3750,14 +3822,15 @@ let _walkSkip = false;
|
|
|
3750
3822
|
let _currentNode = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
3751
3823
|
/** @type {Node | null} */
|
|
3752
3824
|
let _currentParent = null;
|
|
3825
|
+
// Index of the current node within its sibling list (a rule body's declarations
|
|
3826
|
+
// and child rules are separate lists, so each indexes from 0 independently).
|
|
3827
|
+
let _currentIndex = 0;
|
|
3753
3828
|
|
|
3754
3829
|
// AST field-access seam. Every AST-node field read by `CssParser` goes through
|
|
3755
|
-
// one of these accessors
|
|
3756
|
-
//
|
|
3757
|
-
//
|
|
3758
|
-
//
|
|
3759
|
-
// consumer edit. `value` is the leaf-token string; container child lists are
|
|
3760
|
-
// `children` / `prelude` / `declarations` / `childRules`.
|
|
3830
|
+
// one of these accessors (`n` is an integer node id into the columns), so
|
|
3831
|
+
// the storage stays behind the accessor without any consumer edit. `value` is
|
|
3832
|
+
// the leaf-token string; container child lists are `children` / `prelude` /
|
|
3833
|
+
// `declarations` / `childRules`.
|
|
3761
3834
|
const A = {
|
|
3762
3835
|
// === path position (rebound by the walk before every visitor call) ===
|
|
3763
3836
|
/**
|
|
@@ -3772,6 +3845,12 @@ const A = {
|
|
|
3772
3845
|
get parent() {
|
|
3773
3846
|
return _currentParent;
|
|
3774
3847
|
},
|
|
3848
|
+
/**
|
|
3849
|
+
* @returns {number} index of the current node within its sibling list (0 for a top-level node) — only valid during a visitor callback
|
|
3850
|
+
*/
|
|
3851
|
+
get index() {
|
|
3852
|
+
return _currentIndex;
|
|
3853
|
+
},
|
|
3775
3854
|
/** Stop the walk descending into the current node (enter only). */
|
|
3776
3855
|
skipChildren() {
|
|
3777
3856
|
_walkSkip = true;
|
|
@@ -3782,21 +3861,21 @@ const A = {
|
|
|
3782
3861
|
* @returns {number} node type
|
|
3783
3862
|
*/
|
|
3784
3863
|
type(n = _currentNode) {
|
|
3785
|
-
return
|
|
3864
|
+
return _types[_nodeIndex(n)];
|
|
3786
3865
|
},
|
|
3787
3866
|
/**
|
|
3788
3867
|
* @param {Node=} n node
|
|
3789
3868
|
* @returns {number} start offset
|
|
3790
3869
|
*/
|
|
3791
3870
|
start(n = _currentNode) {
|
|
3792
|
-
return
|
|
3871
|
+
return _starts[_nodeIndex(n)];
|
|
3793
3872
|
},
|
|
3794
3873
|
/**
|
|
3795
3874
|
* @param {Node=} n node
|
|
3796
3875
|
* @returns {number} end offset
|
|
3797
3876
|
*/
|
|
3798
3877
|
end(n = _currentNode) {
|
|
3799
|
-
return
|
|
3878
|
+
return _ends[_nodeIndex(n)];
|
|
3800
3879
|
},
|
|
3801
3880
|
/**
|
|
3802
3881
|
* @param {Node=} n node
|
|
@@ -3804,7 +3883,7 @@ const A = {
|
|
|
3804
3883
|
*/
|
|
3805
3884
|
range(n = _currentNode) {
|
|
3806
3885
|
const i = _nodeIndex(n);
|
|
3807
|
-
return [
|
|
3886
|
+
return [_starts[i], _ends[i]];
|
|
3808
3887
|
},
|
|
3809
3888
|
/**
|
|
3810
3889
|
* @param {Node=} n node
|
|
@@ -3812,11 +3891,11 @@ const A = {
|
|
|
3812
3891
|
*/
|
|
3813
3892
|
loc(n = _currentNode) {
|
|
3814
3893
|
const i = _nodeIndex(n);
|
|
3815
|
-
const lc =
|
|
3816
|
-
const s = lc.get(
|
|
3894
|
+
const lc = _locConverter;
|
|
3895
|
+
const s = lc.get(_starts[i]);
|
|
3817
3896
|
const sl = s.line;
|
|
3818
3897
|
const sc = s.column;
|
|
3819
|
-
const e = lc.get(
|
|
3898
|
+
const e = lc.get(_ends[i]);
|
|
3820
3899
|
return {
|
|
3821
3900
|
start: { line: sl, column: sc },
|
|
3822
3901
|
end: { line: e.line, column: e.column }
|
|
@@ -3828,14 +3907,14 @@ const A = {
|
|
|
3828
3907
|
*/
|
|
3829
3908
|
source(n = _currentNode) {
|
|
3830
3909
|
const i = _nodeIndex(n);
|
|
3831
|
-
return
|
|
3910
|
+
return _input.slice(_starts[i], _ends[i]);
|
|
3832
3911
|
},
|
|
3833
3912
|
/**
|
|
3834
3913
|
* @param {Node=} n node
|
|
3835
3914
|
* @returns {string} raw token value
|
|
3836
3915
|
*/
|
|
3837
3916
|
value(n = _currentNode) {
|
|
3838
|
-
return
|
|
3917
|
+
return _valueOf(_nodeIndex(n));
|
|
3839
3918
|
},
|
|
3840
3919
|
/**
|
|
3841
3920
|
* @param {Node=} n node
|
|
@@ -3843,8 +3922,8 @@ const A = {
|
|
|
3843
3922
|
*/
|
|
3844
3923
|
unescaped(n = _currentNode) {
|
|
3845
3924
|
const i = _nodeIndex(n);
|
|
3846
|
-
const v =
|
|
3847
|
-
return
|
|
3925
|
+
const v = _valueOf(i);
|
|
3926
|
+
return _types[i] === T_STRING
|
|
3848
3927
|
? unescapeIdentifier(v.slice(1, -1))
|
|
3849
3928
|
: unescapeIdentifier(v);
|
|
3850
3929
|
},
|
|
@@ -3854,9 +3933,9 @@ const A = {
|
|
|
3854
3933
|
*/
|
|
3855
3934
|
typeFlag(n = _currentNode) {
|
|
3856
3935
|
const i = _nodeIndex(n);
|
|
3857
|
-
if (
|
|
3858
|
-
const input =
|
|
3859
|
-
const p =
|
|
3936
|
+
if (_types[i] === T_HASH) {
|
|
3937
|
+
const input = _input;
|
|
3938
|
+
const p = _starts[i] + 1;
|
|
3860
3939
|
return _ifThreeCodePointsWouldStartAnIdentSequence(
|
|
3861
3940
|
input,
|
|
3862
3941
|
p,
|
|
@@ -3867,9 +3946,9 @@ const A = {
|
|
|
3867
3946
|
? "id"
|
|
3868
3947
|
: "unrestricted";
|
|
3869
3948
|
}
|
|
3870
|
-
const v =
|
|
3949
|
+
const v = _valueOf(i);
|
|
3871
3950
|
return _typeFlagOf(
|
|
3872
|
-
|
|
3951
|
+
_types[i] === T_DIMENSION ? v.slice(0, _consumeANumber(v, 0)) : v
|
|
3873
3952
|
);
|
|
3874
3953
|
},
|
|
3875
3954
|
/**
|
|
@@ -3877,14 +3956,14 @@ const A = {
|
|
|
3877
3956
|
* @returns {number} url content start offset
|
|
3878
3957
|
*/
|
|
3879
3958
|
contentStart(n = _currentNode) {
|
|
3880
|
-
return
|
|
3959
|
+
return _aux0[_nodeIndex(n)];
|
|
3881
3960
|
},
|
|
3882
3961
|
/**
|
|
3883
3962
|
* @param {Node=} n node
|
|
3884
3963
|
* @returns {number} url content end offset
|
|
3885
3964
|
*/
|
|
3886
3965
|
contentEnd(n = _currentNode) {
|
|
3887
|
-
return
|
|
3966
|
+
return _aux1[_nodeIndex(n)];
|
|
3888
3967
|
},
|
|
3889
3968
|
/**
|
|
3890
3969
|
* @param {Node=} n node
|
|
@@ -3892,23 +3971,23 @@ const A = {
|
|
|
3892
3971
|
*/
|
|
3893
3972
|
name(n = _currentNode) {
|
|
3894
3973
|
const i = _nodeIndex(n);
|
|
3895
|
-
return
|
|
3896
|
-
?
|
|
3897
|
-
:
|
|
3974
|
+
return _types[i] === T_AT_RULE
|
|
3975
|
+
? _input.slice(_starts[i] + 1, _aux0[i])
|
|
3976
|
+
: _input.slice(_starts[i], _aux0[i]);
|
|
3898
3977
|
},
|
|
3899
3978
|
/**
|
|
3900
3979
|
* @param {Node=} n node
|
|
3901
3980
|
* @returns {number} name start offset
|
|
3902
3981
|
*/
|
|
3903
3982
|
nameStart(n = _currentNode) {
|
|
3904
|
-
return
|
|
3983
|
+
return _starts[_nodeIndex(n)];
|
|
3905
3984
|
},
|
|
3906
3985
|
/**
|
|
3907
3986
|
* @param {Node=} n node
|
|
3908
3987
|
* @returns {number} name end offset
|
|
3909
3988
|
*/
|
|
3910
3989
|
nameEnd(n = _currentNode) {
|
|
3911
|
-
return
|
|
3990
|
+
return _aux0[_nodeIndex(n)];
|
|
3912
3991
|
},
|
|
3913
3992
|
/**
|
|
3914
3993
|
* @param {Node=} n node
|
|
@@ -3931,13 +4010,32 @@ const A = {
|
|
|
3931
4010
|
prelude(n = _currentNode) {
|
|
3932
4011
|
return /** @type {ComponentValue[]} */ (_materializeList(n));
|
|
3933
4012
|
},
|
|
4013
|
+
/**
|
|
4014
|
+
* @param {Node=} n node
|
|
4015
|
+
* @returns {number} number of children (value / prelude) without materializing the list
|
|
4016
|
+
*/
|
|
4017
|
+
childCount(n = _currentNode) {
|
|
4018
|
+
return _listLens[_nodeIndex(n)];
|
|
4019
|
+
},
|
|
4020
|
+
/**
|
|
4021
|
+
* @param {Node} n node
|
|
4022
|
+
* @param {number} i child index (`0 <= i < childCount(n)`)
|
|
4023
|
+
* @returns {ComponentValue} i-th child (value / prelude) without materializing the list
|
|
4024
|
+
*/
|
|
4025
|
+
childAt(n, i) {
|
|
4026
|
+
return /** @type {ComponentValue} */ (
|
|
4027
|
+
_nodeRef(_flat[_listStarts[_nodeIndex(n)] + i])
|
|
4028
|
+
);
|
|
4029
|
+
},
|
|
3934
4030
|
/**
|
|
3935
4031
|
* @param {Node=} n node
|
|
3936
4032
|
* @returns {Declaration[] | null} block declarations
|
|
3937
4033
|
*/
|
|
3938
4034
|
declarations(n = _currentNode) {
|
|
4035
|
+
// `_makeContainer` only populates this slot for rules; a non-rule node
|
|
4036
|
+
// reads `undefined`, normalized to `null` to keep the documented contract.
|
|
3939
4037
|
return /** @type {Declaration[] | null} */ (
|
|
3940
|
-
|
|
4038
|
+
_declarationLists[_nodeIndex(n)] || null
|
|
3941
4039
|
);
|
|
3942
4040
|
},
|
|
3943
4041
|
/**
|
|
@@ -3945,65 +4043,65 @@ const A = {
|
|
|
3945
4043
|
* @returns {Rule[] | null} block child rules
|
|
3946
4044
|
*/
|
|
3947
4045
|
childRules(n = _currentNode) {
|
|
3948
|
-
return /** @type {Rule[] | null} */ (
|
|
4046
|
+
return /** @type {Rule[] | null} */ (
|
|
4047
|
+
_childRuleLists[_nodeIndex(n)] || null
|
|
4048
|
+
);
|
|
3949
4049
|
},
|
|
3950
4050
|
/**
|
|
3951
4051
|
* @param {Node=} n node
|
|
3952
4052
|
* @returns {number} block start offset
|
|
3953
4053
|
*/
|
|
3954
4054
|
blockStart(n = _currentNode) {
|
|
3955
|
-
return
|
|
4055
|
+
return _aux1[_nodeIndex(n)];
|
|
3956
4056
|
},
|
|
3957
4057
|
/**
|
|
3958
4058
|
* @param {Node=} n node
|
|
3959
4059
|
* @returns {number} block end offset
|
|
3960
4060
|
*/
|
|
3961
4061
|
blockEnd(n = _currentNode) {
|
|
3962
|
-
|
|
4062
|
+
const i = _nodeIndex(n);
|
|
4063
|
+
return _aux1[i] !== -1 ? _ends[i] : -1;
|
|
3963
4064
|
},
|
|
3964
4065
|
/**
|
|
3965
4066
|
* @param {Node=} n node
|
|
3966
4067
|
* @returns {boolean} `!important` flag
|
|
3967
4068
|
*/
|
|
3968
4069
|
important(n = _currentNode) {
|
|
3969
|
-
return (
|
|
4070
|
+
return (_flags[_nodeIndex(n)] & 1) !== 0;
|
|
3970
4071
|
},
|
|
3971
4072
|
/**
|
|
3972
4073
|
* @param {Node=} n node
|
|
3973
4074
|
* @returns {SimpleBlockToken} block opening token
|
|
3974
4075
|
*/
|
|
3975
4076
|
blockToken(n = _currentNode) {
|
|
3976
|
-
return /** @type {SimpleBlockToken} */ (
|
|
3977
|
-
_soaInput[_soaStarts[_nodeIndex(n)]]
|
|
3978
|
-
);
|
|
4077
|
+
return /** @type {SimpleBlockToken} */ (_input[_starts[_nodeIndex(n)]]);
|
|
3979
4078
|
},
|
|
3980
4079
|
// Writers — `CssParser` rewrites a rule's end / block-end when it folds an
|
|
3981
|
-
// inline ICSS `:import` / `:export` body into a single dependency.
|
|
3982
|
-
//
|
|
4080
|
+
// inline ICSS `:import` / `:export` body into a single dependency. A block
|
|
4081
|
+
// rule's `blockEnd` is its `end`, so `setBlockEnd` writes the same `end` slot.
|
|
3983
4082
|
/**
|
|
3984
4083
|
* @param {Node} n node
|
|
3985
4084
|
* @param {number} v new end offset
|
|
3986
4085
|
*/
|
|
3987
4086
|
setEnd(n, v) {
|
|
3988
|
-
|
|
4087
|
+
_ends[_nodeIndex(n)] = v;
|
|
3989
4088
|
},
|
|
3990
4089
|
/**
|
|
3991
4090
|
* @param {Node} n node
|
|
3992
4091
|
* @param {number} v new block end offset
|
|
3993
4092
|
*/
|
|
3994
4093
|
setBlockEnd(n, v) {
|
|
3995
|
-
|
|
4094
|
+
_ends[_nodeIndex(n)] = v;
|
|
3996
4095
|
}
|
|
3997
4096
|
};
|
|
3998
4097
|
|
|
3999
|
-
// The
|
|
4000
|
-
//
|
|
4001
|
-
//
|
|
4002
|
-
//
|
|
4003
|
-
//
|
|
4004
|
-
//
|
|
4098
|
+
// The AST node shapes (`Node`, `Token`, and the container typedefs) are types
|
|
4099
|
+
// only — nodes are integer ids into the store, surfaced by the `A` visitor
|
|
4100
|
+
// accessor and the `parseA*` readers. Runtime exports: the `A` accessor, the
|
|
4101
|
+
// full CSS-Syntax-3 §5.3 `parseA*` entry-point surface, the `TokenStream` (so
|
|
4102
|
+
// callers can pass a pre-built stream to any `parseA*`), and the `escape` /
|
|
4103
|
+
// `unescapeIdentifier` string utils.
|
|
4005
4104
|
module.exports.A = A;
|
|
4006
|
-
module.exports.Node = Node;
|
|
4007
4105
|
module.exports.NodeType = NodeType;
|
|
4008
4106
|
module.exports.SourceProcessor = SourceProcessor;
|
|
4009
4107
|
module.exports.TT_AT_KEYWORD = TT_AT_KEYWORD;
|
|
@@ -4032,7 +4130,6 @@ module.exports.TT_SEMICOLON = TT_SEMICOLON;
|
|
|
4032
4130
|
module.exports.TT_STRING = TT_STRING;
|
|
4033
4131
|
module.exports.TT_URL = TT_URL;
|
|
4034
4132
|
module.exports.TT_WHITESPACE = TT_WHITESPACE;
|
|
4035
|
-
module.exports.Token = Token;
|
|
4036
4133
|
module.exports.TokenStream = TokenStream;
|
|
4037
4134
|
module.exports.buildSkipSet = buildSkipSet;
|
|
4038
4135
|
module.exports.equalsLowerCase = equalsLowerCase;
|