webpack 5.108.2 → 5.108.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Compilation.js +1 -4
- package/lib/FlagDependencyExportsPlugin.js +55 -8
- package/lib/LazyBarrel.js +30 -3
- package/lib/NormalModule.js +8 -12
- package/lib/config/defaults.js +3 -3
- package/lib/css/CssGenerator.js +33 -42
- package/lib/css/CssModulesPlugin.js +79 -53
- package/lib/css/CssParser.js +699 -449
- package/lib/css/syntax.js +1146 -360
- package/lib/dependencies/CssIcssExportDependency.js +28 -16
- package/lib/dependencies/HarmonyAcceptDependency.js +8 -16
- package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +23 -96
- package/lib/dependencies/HarmonyImportGuard.js +192 -20
- package/lib/dependencies/ImportParserPlugin.js +7 -9
- package/lib/html/HtmlGenerator.js +53 -76
- package/lib/html/HtmlModule.js +1 -0
- package/lib/html/HtmlModulesPlugin.js +24 -13
- package/lib/html/HtmlParser.js +240 -124
- package/lib/html/syntax.js +1362 -683
- package/lib/javascript/JavascriptParser.js +10 -0
- package/lib/url/URLParserPlugin.js +33 -7
- package/lib/util/LocConverter.js +9 -8
- package/lib/util/SourceProcessor.js +41 -28
- package/package.json +3 -3
- package/types.d.ts +79 -221
package/lib/css/syntax.js
CHANGED
|
@@ -209,7 +209,6 @@ const CC_EXCLAMATION = "!".charCodeAt(0);
|
|
|
209
209
|
const CC_UPPER_A = "A".charCodeAt(0);
|
|
210
210
|
const CC_UPPER_F = "F".charCodeAt(0);
|
|
211
211
|
const CC_UPPER_E = "E".charCodeAt(0);
|
|
212
|
-
const CC_UPPER_U = "U".charCodeAt(0);
|
|
213
212
|
const CC_UPPER_Z = "Z".charCodeAt(0);
|
|
214
213
|
const CC_0 = "0".charCodeAt(0);
|
|
215
214
|
const CC_9 = "9".charCodeAt(0);
|
|
@@ -298,17 +297,14 @@ const _isSpace = (cc) => cc === CC_SPACE || cc === CC_TAB;
|
|
|
298
297
|
// rarer tab / newline tests.
|
|
299
298
|
const _isWhiteSpace = (cc) => _isSpace(cc) || _isNewline(cc);
|
|
300
299
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
(cc >= CC_UPPER_A && cc <= CC_UPPER_Z) ||
|
|
310
|
-
cc === CC_LOW_LINE ||
|
|
311
|
-
cc >= 0x80;
|
|
300
|
+
// Whitespace membership table for the run-consumption loop — one load instead
|
|
301
|
+
// of up to five compares per char. EOF (NaN) / non-ASCII index to undefined.
|
|
302
|
+
const _wsTable = new Uint8Array(128);
|
|
303
|
+
_wsTable[CC_SPACE] = 1;
|
|
304
|
+
_wsTable[CC_TAB] = 1;
|
|
305
|
+
_wsTable[CC_LINE_FEED] = 1;
|
|
306
|
+
_wsTable[CC_CARRIAGE_RETURN] = 1;
|
|
307
|
+
_wsTable[CC_FORM_FEED] = 1;
|
|
312
308
|
|
|
313
309
|
/**
|
|
314
310
|
* @param {number} cc char code
|
|
@@ -482,11 +478,17 @@ const _ifThreeCodePointsWouldStartANumber = (input, pos, f, s, t) => {
|
|
|
482
478
|
* @returns {number} position just past the last ident-sequence code point
|
|
483
479
|
*/
|
|
484
480
|
const _consumeAnIdentSequence = (input, pos) => {
|
|
481
|
+
// Hot loop (every ident, at-keyword, hash, function name, unit). Both checks
|
|
482
|
+
// are inlined from `_isIdentCodePoint` / `_ifTwoCodePointsAreValidEscape`: the
|
|
483
|
+
// ASCII ident test is a single table load, and the escape test reads the
|
|
484
|
+
// following code point only when `cc` is a `\` (rare) instead of eagerly.
|
|
485
485
|
for (;;) {
|
|
486
486
|
const cc = input.charCodeAt(pos);
|
|
487
487
|
pos++;
|
|
488
|
-
if (
|
|
489
|
-
|
|
488
|
+
if (cc < 128 ? _identCharTable[cc] === 1 : _isIdentStartCodePointCC(cc)) {
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
if (cc === CC_REVERSE_SOLIDUS && !_isNewline(input.charCodeAt(pos))) {
|
|
490
492
|
pos = _consumeAnEscapedCodePoint(input, pos);
|
|
491
493
|
continue;
|
|
492
494
|
}
|
|
@@ -619,7 +621,7 @@ const fill = (out, type, start, end) => {
|
|
|
619
621
|
*/
|
|
620
622
|
function consumeSpace(input, pos, out) {
|
|
621
623
|
const start = pos - 1;
|
|
622
|
-
while (
|
|
624
|
+
while (_wsTable[input.charCodeAt(pos)] === 1) pos++;
|
|
623
625
|
return fill(out, TT_WHITESPACE, start, pos);
|
|
624
626
|
}
|
|
625
627
|
|
|
@@ -950,6 +952,96 @@ function consumeReverseSolidus(input, pos, out) {
|
|
|
950
952
|
return fill(out, TT_DELIM, pos - 1, pos);
|
|
951
953
|
}
|
|
952
954
|
|
|
955
|
+
// `consumeAToken` dispatch: the §4 token rules keyed by the lead code point are
|
|
956
|
+
// === Tokenizer lead-character dispatch (CSS Syntax Level 3 §4 "consume a token") ===
|
|
957
|
+
//
|
|
958
|
+
// `consumeAToken` selects a sub-routine from the first ("lead") code point of each
|
|
959
|
+
// token. The §4 rules are keyed on specific code points (`"` `#` `(` digit
|
|
960
|
+
// ident-start …) that sit SPARSELY across the ASCII range, so a plain `switch (cc)`
|
|
961
|
+
// compiles to a jump table spanning U+0009..U+007D in which the most common lead —
|
|
962
|
+
// an ident-start letter — is not a case and reaches its handler only after the
|
|
963
|
+
// digit/whitespace tests miss. `_charClass` precomputes, for every ASCII code
|
|
964
|
+
// point, a dense handler id (`HC_*`, 0..12) so `consumeAToken` is one array load +
|
|
965
|
+
// a compact 13-entry jump table and idents dispatch directly. Non-ASCII
|
|
966
|
+
// (cc >= 128) is always ident-start per §4, so it skips the table.
|
|
967
|
+
//
|
|
968
|
+
// Extending for a spec change: repoint the code point in the build loop below; if
|
|
969
|
+
// it needs a new sub-routine, add an `HC_*` id, a `case` in `consumeAToken`, and a
|
|
970
|
+
// row here. This list is the authoritative "which lead code point dispatches
|
|
971
|
+
// where" map (§4 "consume a token", step by lead code point):
|
|
972
|
+
//
|
|
973
|
+
// HC_WHITESPACE whitespace U+0009 TAB U+000A LF U+000C FF U+000D CR U+0020 SPACE
|
|
974
|
+
// HC_STRING string start U+0022 " U+0027 '
|
|
975
|
+
// HC_SINGLE one-char token ( ) , : ; [ ] { } (its token type comes from `_singleTT`)
|
|
976
|
+
// HC_NUMBER_SIGN hash / delim U+0023 #
|
|
977
|
+
// HC_PLUS_SIGN number / delim U+002B +
|
|
978
|
+
// HC_HYPHEN_MINUS number / CDC / ident / delim U+002D -
|
|
979
|
+
// HC_FULL_STOP number / delim U+002E .
|
|
980
|
+
// HC_LESS_THAN CDO / delim U+003C <
|
|
981
|
+
// HC_AT_SIGN at-keyword / delim U+0040 @
|
|
982
|
+
// HC_REVERSE_SOLIDUS escape / delim U+005C \
|
|
983
|
+
// HC_DIGIT number U+0030..U+0039 0-9
|
|
984
|
+
// HC_IDENT ident-like U+0041..U+005A A-Z U+0061..U+007A a-z U+005F _ (plus cc >= 128)
|
|
985
|
+
// HC_DELIM anything else -> a single <delim-token>
|
|
986
|
+
//
|
|
987
|
+
// `_singleTT[cc]` is the token type for the HC_SINGLE code points (a second table
|
|
988
|
+
// so they share one handler instead of one `case` each). The default class 0 is
|
|
989
|
+
// the delim handler (anything not matched below), so it needs no named constant.
|
|
990
|
+
const HC_WHITESPACE = 1;
|
|
991
|
+
const HC_STRING = 2;
|
|
992
|
+
const HC_SINGLE = 3;
|
|
993
|
+
const HC_NUMBER_SIGN = 4;
|
|
994
|
+
const HC_PLUS_SIGN = 5;
|
|
995
|
+
const HC_HYPHEN_MINUS = 6;
|
|
996
|
+
const HC_FULL_STOP = 7;
|
|
997
|
+
const HC_LESS_THAN = 8;
|
|
998
|
+
const HC_AT_SIGN = 9;
|
|
999
|
+
const HC_REVERSE_SOLIDUS = 10;
|
|
1000
|
+
const HC_DIGIT = 11;
|
|
1001
|
+
const HC_IDENT = 12;
|
|
1002
|
+
const _charClass = new Uint8Array(128);
|
|
1003
|
+
const _singleTT = new Uint8Array(128);
|
|
1004
|
+
_singleTT[CC_LEFT_PARENTHESIS] = TT_LEFT_PARENTHESIS;
|
|
1005
|
+
_singleTT[CC_RIGHT_PARENTHESIS] = TT_RIGHT_PARENTHESIS;
|
|
1006
|
+
_singleTT[CC_COMMA] = TT_COMMA;
|
|
1007
|
+
_singleTT[CC_COLON] = TT_COLON;
|
|
1008
|
+
_singleTT[CC_SEMICOLON] = TT_SEMICOLON;
|
|
1009
|
+
_singleTT[CC_LEFT_SQUARE] = TT_LEFT_SQUARE_BRACKET;
|
|
1010
|
+
_singleTT[CC_RIGHT_SQUARE] = TT_RIGHT_SQUARE_BRACKET;
|
|
1011
|
+
_singleTT[CC_LEFT_CURLY] = TT_LEFT_CURLY_BRACKET;
|
|
1012
|
+
_singleTT[CC_RIGHT_CURLY] = TT_RIGHT_CURLY_BRACKET;
|
|
1013
|
+
// Each ASCII code point belongs to exactly one class; HC_SINGLE is seeded from
|
|
1014
|
+
// `_singleTT` above, the rest follow §4's lead-code-point rules, and everything
|
|
1015
|
+
// unmatched stays the delim class (0). Keep this in sync with the table above.
|
|
1016
|
+
for (let i = 0; i < 128; i++) {
|
|
1017
|
+
if (_singleTT[i] !== 0) {
|
|
1018
|
+
_charClass[i] = HC_SINGLE;
|
|
1019
|
+
} else if (_isWhiteSpace(i)) {
|
|
1020
|
+
_charClass[i] = HC_WHITESPACE;
|
|
1021
|
+
} else if (i === CC_QUOTATION_MARK || i === CC_APOSTROPHE) {
|
|
1022
|
+
_charClass[i] = HC_STRING;
|
|
1023
|
+
} else if (i === CC_NUMBER_SIGN) {
|
|
1024
|
+
_charClass[i] = HC_NUMBER_SIGN;
|
|
1025
|
+
} else if (i === CC_PLUS_SIGN) {
|
|
1026
|
+
_charClass[i] = HC_PLUS_SIGN;
|
|
1027
|
+
} else if (i === CC_HYPHEN_MINUS) {
|
|
1028
|
+
_charClass[i] = HC_HYPHEN_MINUS;
|
|
1029
|
+
} else if (i === CC_FULL_STOP) {
|
|
1030
|
+
_charClass[i] = HC_FULL_STOP;
|
|
1031
|
+
} else if (i === CC_LESS_THAN_SIGN) {
|
|
1032
|
+
_charClass[i] = HC_LESS_THAN;
|
|
1033
|
+
} else if (i === CC_AT_SIGN) {
|
|
1034
|
+
_charClass[i] = HC_AT_SIGN;
|
|
1035
|
+
} else if (i === CC_REVERSE_SOLIDUS) {
|
|
1036
|
+
_charClass[i] = HC_REVERSE_SOLIDUS;
|
|
1037
|
+
} else if (_isDigit(i)) {
|
|
1038
|
+
_charClass[i] = HC_DIGIT;
|
|
1039
|
+
} else if (_isIdentStartCodePointCC(i)) {
|
|
1040
|
+
_charClass[i] = HC_IDENT;
|
|
1041
|
+
}
|
|
1042
|
+
// else stays the delim class (0)
|
|
1043
|
+
}
|
|
1044
|
+
|
|
953
1045
|
/**
|
|
954
1046
|
* Per-character dispatcher. The outer loop has already advanced past
|
|
955
1047
|
* the lead code point (`pos - 1` is the lead).
|
|
@@ -960,65 +1052,50 @@ function consumeReverseSolidus(input, pos, out) {
|
|
|
960
1052
|
* @returns {MutableToken | undefined} the resulting token, or undefined at EOF
|
|
961
1053
|
*/
|
|
962
1054
|
function consumeAToken(input, pos, cc, out) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
case
|
|
968
|
-
case CC_SPACE:
|
|
1055
|
+
// `u` / `U` would start a unicode-range token in the spec; those are not
|
|
1056
|
+
// produced, so they map to HC_IDENT and fall through to ident-like.
|
|
1057
|
+
switch (cc < 128 ? _charClass[cc] : HC_IDENT) {
|
|
1058
|
+
// Run of whitespace → one <whitespace-token>.
|
|
1059
|
+
case HC_WHITESPACE:
|
|
969
1060
|
return consumeSpace(input, pos, out);
|
|
970
|
-
|
|
971
|
-
case
|
|
1061
|
+
// `"` / `'` → <string-token> (or <bad-string-token> on a raw newline).
|
|
1062
|
+
case HC_STRING:
|
|
972
1063
|
return consumeAStringToken(input, pos, out);
|
|
973
|
-
|
|
1064
|
+
// One-code-point token: its type is looked up in `_singleTT` (the `(` `)`
|
|
1065
|
+
// `,` `:` `;` `[` `]` `{` `}` set), so all of them share this arm.
|
|
1066
|
+
case HC_SINGLE:
|
|
1067
|
+
return fill(out, _singleTT[cc], pos - 1, pos);
|
|
1068
|
+
// `#` → <hash-token> if an ident/escape follows, else a <delim-token>.
|
|
1069
|
+
case HC_NUMBER_SIGN:
|
|
974
1070
|
return consumeNumberSign(input, pos, out);
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
case CC_RIGHT_PARENTHESIS:
|
|
978
|
-
return fill(out, TT_RIGHT_PARENTHESIS, pos - 1, pos);
|
|
979
|
-
case CC_PLUS_SIGN:
|
|
1071
|
+
// `+` → <number-token> if it starts a number, else a <delim-token>.
|
|
1072
|
+
case HC_PLUS_SIGN:
|
|
980
1073
|
return consumePlusSign(input, pos, out);
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
case CC_HYPHEN_MINUS:
|
|
1074
|
+
// `-` → number / <CDC-token> (`-->`) / ident / <delim-token>.
|
|
1075
|
+
case HC_HYPHEN_MINUS:
|
|
984
1076
|
return consumeHyphenMinus(input, pos, out);
|
|
985
|
-
|
|
1077
|
+
// `.` → <number-token> if a digit follows, else a <delim-token>.
|
|
1078
|
+
case HC_FULL_STOP:
|
|
986
1079
|
return consumeFullStop(input, pos, out);
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
case CC_SEMICOLON:
|
|
990
|
-
return fill(out, TT_SEMICOLON, pos - 1, pos);
|
|
991
|
-
case CC_LESS_THAN_SIGN:
|
|
1080
|
+
// `<` → <CDO-token> (`<!--`), else a <delim-token>.
|
|
1081
|
+
case HC_LESS_THAN:
|
|
992
1082
|
return consumeLessThan(input, pos, out);
|
|
993
|
-
|
|
1083
|
+
// `@` → <at-keyword-token> if an ident follows, else a <delim-token>.
|
|
1084
|
+
case HC_AT_SIGN:
|
|
994
1085
|
return consumeCommercialAt(input, pos, out);
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
case CC_REVERSE_SOLIDUS:
|
|
1086
|
+
// `\` → ident-like token if it's a valid escape, else a <delim-token>.
|
|
1087
|
+
case HC_REVERSE_SOLIDUS:
|
|
998
1088
|
return consumeReverseSolidus(input, pos, out);
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1089
|
+
// Digit → numeric token; `pos - 1` re-includes the digit the caller passed.
|
|
1090
|
+
case HC_DIGIT:
|
|
1091
|
+
return consumeANumericToken(input, pos - 1, out);
|
|
1092
|
+
// Ident-start (letter / `_` / non-ASCII, incl. `u`/`U`) → ident / function /
|
|
1093
|
+
// url token; `pos - 1` re-includes the lead code point.
|
|
1094
|
+
case HC_IDENT:
|
|
1095
|
+
return consumeAnIdentLikeToken(input, pos - 1, out);
|
|
1005
1096
|
default:
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
return consumeANumericToken(input, pos, out);
|
|
1009
|
-
}
|
|
1010
|
-
if (cc === CC_LOWER_U || cc === CC_UPPER_U) {
|
|
1011
|
-
// Unicode-range tokens are not produced — fall back to
|
|
1012
|
-
// ident-like to match the existing tokenizer's behaviour.
|
|
1013
|
-
pos--;
|
|
1014
|
-
return consumeAnIdentLikeToken(input, pos, out);
|
|
1015
|
-
}
|
|
1016
|
-
if (isIdentStartCodePoint(cc)) {
|
|
1017
|
-
pos--;
|
|
1018
|
-
return consumeAnIdentLikeToken(input, pos, out);
|
|
1019
|
-
}
|
|
1020
|
-
// EOF is impossible here (caller guarded with the outer
|
|
1021
|
-
// loop's `pos < input.length` check). Anything else: delim.
|
|
1097
|
+
// HC_DELIM. EOF is impossible here (caller guarded with the outer
|
|
1098
|
+
// loop's `pos < input.length` check). Anything else: a <delim-token>.
|
|
1022
1099
|
return fill(out, TT_DELIM, pos - 1, pos);
|
|
1023
1100
|
}
|
|
1024
1101
|
}
|
|
@@ -1043,18 +1120,16 @@ function readToken(input, pos, out) {
|
|
|
1043
1120
|
// Comment: `/*…*/` is yielded as a token (filtered by `next`).
|
|
1044
1121
|
if (cc === CC_SOLIDUS && input.charCodeAt(pos + 1) === CC_ASTERISK) {
|
|
1045
1122
|
const start = pos;
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
pos++;
|
|
1057
|
-
}
|
|
1123
|
+
// Jump to the closing `*/` in one native scan instead of a per-character
|
|
1124
|
+
// loop — comment bodies (license banners, source comments) can be long.
|
|
1125
|
+
// No close: unterminated comment runs to EOF so ranges cover all input.
|
|
1126
|
+
const close = input.indexOf("*/", pos + 2);
|
|
1127
|
+
return fill(
|
|
1128
|
+
out,
|
|
1129
|
+
TT_COMMENT,
|
|
1130
|
+
start,
|
|
1131
|
+
close === -1 ? input.length : close + 2
|
|
1132
|
+
);
|
|
1058
1133
|
}
|
|
1059
1134
|
// `consumeAToken` dispatches on the lead code point at `pos` (it expects the
|
|
1060
1135
|
// position just past the lead and the already-read lead code point).
|
|
@@ -1100,7 +1175,10 @@ const NodeType = {
|
|
|
1100
1175
|
Declaration: 23,
|
|
1101
1176
|
AtRule: 24,
|
|
1102
1177
|
QualifiedRule: 25,
|
|
1103
|
-
Stylesheet: 26
|
|
1178
|
+
Stylesheet: 26,
|
|
1179
|
+
// Comments are never tree nodes; this type exists only so a `NodeType.Comment`
|
|
1180
|
+
// visitor can be registered (fired during tokenization — see `grammar`).
|
|
1181
|
+
Comment: 27
|
|
1104
1182
|
};
|
|
1105
1183
|
const {
|
|
1106
1184
|
Ident: T_IDENT,
|
|
@@ -1128,7 +1206,8 @@ const {
|
|
|
1128
1206
|
Declaration: T_DECLARATION,
|
|
1129
1207
|
AtRule: T_AT_RULE,
|
|
1130
1208
|
QualifiedRule: T_QUALIFIED_RULE,
|
|
1131
|
-
Stylesheet: T_STYLESHEET
|
|
1209
|
+
Stylesheet: T_STYLESHEET,
|
|
1210
|
+
Comment: T_COMMENT
|
|
1132
1211
|
} = NodeType;
|
|
1133
1212
|
|
|
1134
1213
|
/**
|
|
@@ -1240,26 +1319,28 @@ class Token extends Node {
|
|
|
1240
1319
|
* @param {number} end byte offset just past the token's last code point
|
|
1241
1320
|
* @param {LocConverter} locConverter shared loc converter
|
|
1242
1321
|
*/
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
/** @type {string | undefined} */
|
|
1250
|
-
this._value = undefined;
|
|
1251
|
-
}
|
|
1322
|
+
// No own fields: a leaf token is exactly a `Node` plus the value getters
|
|
1323
|
+
// below. `value` is derived from the byte range on read instead of cached in
|
|
1324
|
+
// a `_value` slot — most tokens (whitespace, punctuation) never read it, and
|
|
1325
|
+
// dropping the slot is ~8 bytes saved on every token (the bulk of all nodes).
|
|
1326
|
+
// hash / at-keyword strip their `#` / `@` prefix; url uses its content range
|
|
1327
|
+
// (`contentStart` / `contentEnd`, the token's only own fields).
|
|
1252
1328
|
|
|
1253
1329
|
/**
|
|
1254
1330
|
* @returns {string} the token's value (raw source slice unless overridden)
|
|
1255
1331
|
*/
|
|
1256
1332
|
get value() {
|
|
1257
|
-
const
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
this.end
|
|
1262
|
-
|
|
1333
|
+
const input = this._locConverter._input;
|
|
1334
|
+
const type = this.type;
|
|
1335
|
+
// hash (`#name` → `name`) and at-keyword (`@name` → `name`) drop one char.
|
|
1336
|
+
if (type === T_HASH || type === T_AT_KEYWORD) {
|
|
1337
|
+
return input.slice(this.start + 1, this.end);
|
|
1338
|
+
}
|
|
1339
|
+
if (type === T_URL) {
|
|
1340
|
+
const u = /** @type {UrlToken} */ (/** @type {unknown} */ (this));
|
|
1341
|
+
return input.slice(u.contentStart, u.contentEnd);
|
|
1342
|
+
}
|
|
1343
|
+
return input.slice(this.start, this.end);
|
|
1263
1344
|
}
|
|
1264
1345
|
|
|
1265
1346
|
/**
|
|
@@ -1297,19 +1378,28 @@ class Token extends Node {
|
|
|
1297
1378
|
|
|
1298
1379
|
/**
|
|
1299
1380
|
* Spec type flag. For number / dimension tokens it's "integer" / "number"
|
|
1300
|
-
* (derived from `value`); for hash tokens it's "id" / "unrestricted" (
|
|
1301
|
-
*
|
|
1302
|
-
*
|
|
1381
|
+
* (derived from `value`); for hash tokens it's "id" / "unrestricted" (re-derived
|
|
1382
|
+
* from the source). The two senses share the name in the spec; both are computed
|
|
1383
|
+
* on read so every leaf token keeps the same object shape (no own `typeFlag`).
|
|
1303
1384
|
* @returns {"integer" | "number" | "id" | "unrestricted"} the spec type flag
|
|
1304
1385
|
*/
|
|
1305
1386
|
get typeFlag() {
|
|
1306
1387
|
if (this.type === T_HASH) {
|
|
1307
|
-
//
|
|
1308
|
-
//
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1388
|
+
// Re-derive id-ness from the source (whether the name after `#` starts an
|
|
1389
|
+
// ident sequence) rather than storing an `_isId` slot — keeps hash tokens
|
|
1390
|
+
// the same shape as every other leaf token. Matches the lexer's
|
|
1391
|
+
// `consumeNumberSign`, which sets `isId` from the same check.
|
|
1392
|
+
const input = this._locConverter._input;
|
|
1393
|
+
const p = this.start + 1;
|
|
1394
|
+
return _ifThreeCodePointsWouldStartAnIdentSequence(
|
|
1395
|
+
input,
|
|
1396
|
+
p,
|
|
1397
|
+
input.charCodeAt(p),
|
|
1398
|
+
input.charCodeAt(p + 1),
|
|
1399
|
+
input.charCodeAt(p + 2)
|
|
1400
|
+
)
|
|
1401
|
+
? "id"
|
|
1402
|
+
: "unrestricted";
|
|
1313
1403
|
}
|
|
1314
1404
|
const v = this.value;
|
|
1315
1405
|
return _typeFlagOf(
|
|
@@ -1333,6 +1423,54 @@ class Token extends Node {
|
|
|
1333
1423
|
}
|
|
1334
1424
|
}
|
|
1335
1425
|
|
|
1426
|
+
/**
|
|
1427
|
+
* The non-leaf `Node` subclass: functions, simple blocks, declarations, at-rules
|
|
1428
|
+
* and qualified rules. It declares the union of every container field up front so
|
|
1429
|
+
* all five share **one** hidden class — the consume algorithms only overwrite the
|
|
1430
|
+
* slots relevant to their type, never adding a property, so the shape never
|
|
1431
|
+
* transitions. This caps the shape count: the walker's hot `.type` / `.value` /
|
|
1432
|
+
* `.prelude` loads otherwise see eight distinct node maps (five container types
|
|
1433
|
+
* plus the token / hash / url token maps), tipping V8's inline cache into the
|
|
1434
|
+
* slow megamorphic path; folding the five containers into one leaves four maps
|
|
1435
|
+
* (token, hash, url, container) — within the polymorphic limit, so those loads
|
|
1436
|
+
* stay inline-cached. Unused-for-the-type slots keep their defaults (the field
|
|
1437
|
+
* typedefs below document which fields each type uses). The stylesheet node is
|
|
1438
|
+
* rare (one per parse) and stays a bare `Node`.
|
|
1439
|
+
*/
|
|
1440
|
+
class Container extends Node {
|
|
1441
|
+
/**
|
|
1442
|
+
* @param {number} type node type
|
|
1443
|
+
* @param {number} start byte offset of the node's first code point
|
|
1444
|
+
* @param {number} end byte offset just past the node's last code point
|
|
1445
|
+
* @param {LocConverter} locConverter shared loc converter
|
|
1446
|
+
*/
|
|
1447
|
+
constructor(type, start, end, locConverter) {
|
|
1448
|
+
super(type, start, end, locConverter);
|
|
1449
|
+
/** @type {string} name (function / at-rule / declaration) */
|
|
1450
|
+
this.name = "";
|
|
1451
|
+
/** @type {number} */
|
|
1452
|
+
this.nameStart = start;
|
|
1453
|
+
/** @type {number} */
|
|
1454
|
+
this.nameEnd = start;
|
|
1455
|
+
/** @type {ComponentValue[] | null} component values (function / block / declaration) */
|
|
1456
|
+
this.value = null;
|
|
1457
|
+
/** @type {ComponentValue[] | null} prelude (at-rule / qualified rule) */
|
|
1458
|
+
this.prelude = null;
|
|
1459
|
+
/** @type {Declaration[] | null} */
|
|
1460
|
+
this.declarations = null;
|
|
1461
|
+
/** @type {Rule[] | null} */
|
|
1462
|
+
this.childRules = null;
|
|
1463
|
+
/** @type {number} `{` start offset, or -1 (at-rule / qualified rule) */
|
|
1464
|
+
this.blockStart = -1;
|
|
1465
|
+
/** @type {number} `}` end offset, or -1 (at-rule / qualified rule) */
|
|
1466
|
+
this.blockEnd = -1;
|
|
1467
|
+
/** @type {boolean} stripped `!important` (declaration) */
|
|
1468
|
+
this.important = false;
|
|
1469
|
+
/** @type {SimpleBlockToken | undefined} opening char (simple block) */
|
|
1470
|
+
this.token = undefined;
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1336
1474
|
/**
|
|
1337
1475
|
* 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`).
|
|
1338
1476
|
* @typedef {Token & { numericValue: number, typeFlag: "integer" | "number", sign: "+" | "-" | "" }} NumberToken
|
|
@@ -1430,82 +1568,350 @@ class Token extends Node {
|
|
|
1430
1568
|
* @typedef {Node & { rules: Rule[] }} Stylesheet
|
|
1431
1569
|
*/
|
|
1432
1570
|
|
|
1571
|
+
// Lexer-token-type → AST-node-type map. A single \`new Token\` construct site
|
|
1572
|
+
// (vs a ~20-case switch with a \`new Token\` in each arm) keeps V8 on the fast
|
|
1573
|
+
// monomorphic allocation path — the switch form showed up as generic construct
|
|
1574
|
+
// stubs in profiles. URL is the one type with extra own state, handled first.
|
|
1575
|
+
const _ttToNodeType = new Uint8Array(27);
|
|
1576
|
+
_ttToNodeType[TT_WHITESPACE] = T_WHITESPACE;
|
|
1577
|
+
_ttToNodeType[TT_IDENTIFIER] = T_IDENT;
|
|
1578
|
+
_ttToNodeType[TT_STRING] = T_STRING;
|
|
1579
|
+
_ttToNodeType[TT_DELIM] = T_DELIM;
|
|
1580
|
+
_ttToNodeType[TT_NUMBER] = T_NUMBER;
|
|
1581
|
+
_ttToNodeType[TT_PERCENTAGE] = T_PERCENTAGE;
|
|
1582
|
+
_ttToNodeType[TT_DIMENSION] = T_DIMENSION;
|
|
1583
|
+
_ttToNodeType[TT_HASH] = T_HASH;
|
|
1584
|
+
_ttToNodeType[TT_AT_KEYWORD] = T_AT_KEYWORD;
|
|
1585
|
+
_ttToNodeType[TT_BAD_STRING_TOKEN] = T_BAD_STRING;
|
|
1586
|
+
_ttToNodeType[TT_BAD_URL_TOKEN] = T_BAD_URL;
|
|
1587
|
+
_ttToNodeType[TT_COLON] = T_COLON;
|
|
1588
|
+
_ttToNodeType[TT_COMMA] = T_COMMA;
|
|
1589
|
+
_ttToNodeType[TT_SEMICOLON] = T_SEMICOLON;
|
|
1590
|
+
_ttToNodeType[TT_RIGHT_PARENTHESIS] = T_RIGHT_PARENTHESIS;
|
|
1591
|
+
_ttToNodeType[TT_RIGHT_SQUARE_BRACKET] = T_RIGHT_SQUARE_BRACKET;
|
|
1592
|
+
_ttToNodeType[TT_RIGHT_CURLY_BRACKET] = T_RIGHT_CURLY_BRACKET;
|
|
1593
|
+
_ttToNodeType[TT_CDO] = T_CDO;
|
|
1594
|
+
_ttToNodeType[TT_CDC] = T_CDC;
|
|
1595
|
+
|
|
1596
|
+
// === AST construction backend ===
|
|
1597
|
+
// The consume algorithms build nodes through these module-level primitives
|
|
1598
|
+
// rather than `new Token` / `new Container` directly, so the node
|
|
1599
|
+
// representation can be swapped under the parser. The object backend below
|
|
1600
|
+
// builds the retainable `Node` / `Token` / `Container` tree the `parseA*`
|
|
1601
|
+
// entry points return; the Struct-of-Arrays backend (added separately) writes
|
|
1602
|
+
// the same nodes into reused typed arrays for the streaming `grammar`, where
|
|
1603
|
+
// per-node allocation dominates cost. Child lists are plain arrays in both
|
|
1604
|
+
// backends (a node ref is an object or an integer index); only node creation /
|
|
1605
|
+
// field access differs, so only those ops are swapped.
|
|
1606
|
+
|
|
1607
|
+
// Current loc converter for the active parse (object backend reads it).
|
|
1608
|
+
let _lc = /** @type {LocConverter} */ (/** @type {unknown} */ (null));
|
|
1609
|
+
|
|
1610
|
+
// Active skip state (from `CssProcessOptions.skip`), applied by the grammar.
|
|
1611
|
+
// `_skipTypes` is indexed by `NodeType` (1 = skip): drop that component-value
|
|
1612
|
+
// leaf / container from declaration value and function-arg lists. The two
|
|
1613
|
+
// prelude flags scan a rule's prelude without materializing its tree (url tokens
|
|
1614
|
+
// / functions kept, so `url()` in a selector or `@import url(…)` still resolves).
|
|
1615
|
+
// A skipped node is still tokenized (positions stay correct) but never pushed,
|
|
1616
|
+
// so it is never walked or read — the caller must only skip what nothing reads.
|
|
1617
|
+
// `useObjectBackend` restores the defaults so `parseA*` build the full tree.
|
|
1618
|
+
const _NO_SKIP_TYPES = new Uint8Array(32);
|
|
1619
|
+
/** @type {Uint8Array} */
|
|
1620
|
+
let _skipTypes = _NO_SKIP_TYPES;
|
|
1621
|
+
// Fast-path flag: true only when a real skip set is active, so the (dominant)
|
|
1622
|
+
// no-skip parses pay one boolean test instead of a node-type lookup per value.
|
|
1623
|
+
let _skipActive = false;
|
|
1624
|
+
let _skipSelectorPrelude = false;
|
|
1625
|
+
let _skipAtRulePrelude = false;
|
|
1626
|
+
|
|
1627
|
+
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1628
|
+
let _mkLeaf;
|
|
1629
|
+
/** @type {(start: number, end: number, contentStart: number, contentEnd: number) => Node} */
|
|
1630
|
+
let _mkUrl;
|
|
1631
|
+
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1632
|
+
let _mkContainer;
|
|
1633
|
+
/** @type {(start: number) => Node} */
|
|
1634
|
+
let _mkStylesheet;
|
|
1635
|
+
/** @type {(r: Node, v: string, nameStart: number, nameEnd: number) => void} */
|
|
1636
|
+
let _setName;
|
|
1637
|
+
/** @type {(r: Node, v: number) => void} */
|
|
1638
|
+
let _setEnd;
|
|
1639
|
+
/** @type {(r: Node, blockStart: number, blockEnd: number) => void} */
|
|
1640
|
+
let _setBlock;
|
|
1641
|
+
/** @type {(r: Node) => void} */
|
|
1642
|
+
let _setImportant;
|
|
1643
|
+
/** @type {(r: Node, ch: SimpleBlockToken) => void} */
|
|
1644
|
+
let _setToken;
|
|
1645
|
+
/** @type {(r: Node, list: Node[]) => void} */
|
|
1646
|
+
let _setValue;
|
|
1647
|
+
/** @type {(r: Node, list: Node[]) => void} */
|
|
1648
|
+
let _setPrelude;
|
|
1649
|
+
/** @type {(r: Node, decls: Node[], childRules: Node[]) => void} */
|
|
1650
|
+
let _setBody;
|
|
1651
|
+
/** @type {(r: Node, list: Node[]) => void} */
|
|
1652
|
+
let _setRules;
|
|
1653
|
+
/** @type {(r: Node) => number} */
|
|
1654
|
+
let _nType;
|
|
1655
|
+
/** @type {(r: Node) => number} */
|
|
1656
|
+
let _nStart;
|
|
1657
|
+
/** @type {(r: Node) => string} */
|
|
1658
|
+
let _nValue;
|
|
1659
|
+
/** @type {(r: Node) => SimpleBlockToken} */
|
|
1660
|
+
let _nToken;
|
|
1661
|
+
|
|
1662
|
+
// Child lists are plain arrays in every backend; refs are pushed by value.
|
|
1663
|
+
const _list = () => /** @type {Node[]} */ ([]);
|
|
1664
|
+
|
|
1665
|
+
// -- object backend: builds the retainable class-instance tree --
|
|
1666
|
+
/** @type {typeof _mkLeaf} */
|
|
1667
|
+
const _objLeaf = (type, start, end) => new Token(type, start, end, _lc);
|
|
1668
|
+
/** @type {typeof _mkUrl} */
|
|
1669
|
+
const _objUrl = (start, end, cs, ce) => {
|
|
1670
|
+
const u = /** @type {UrlToken} */ (new Token(T_URL, start, end, _lc));
|
|
1671
|
+
u.contentStart = cs;
|
|
1672
|
+
u.contentEnd = ce;
|
|
1673
|
+
return u;
|
|
1674
|
+
};
|
|
1675
|
+
/** @type {typeof _mkContainer} */
|
|
1676
|
+
const _objContainer = (type, start, end) =>
|
|
1677
|
+
new Container(type, start, end, _lc);
|
|
1678
|
+
/** @type {typeof _mkStylesheet} */
|
|
1679
|
+
const _objStylesheet = (start) => {
|
|
1680
|
+
const s = /** @type {Stylesheet} */ (
|
|
1681
|
+
new Node(T_STYLESHEET, start, start, _lc)
|
|
1682
|
+
);
|
|
1683
|
+
s.rules = [];
|
|
1684
|
+
return s;
|
|
1685
|
+
};
|
|
1686
|
+
/** @param {LocConverter} lc loc converter for this parse */
|
|
1687
|
+
const useObjectBackend = (lc) => {
|
|
1688
|
+
_lc = lc;
|
|
1689
|
+
// `parseA*` return the full tree, so nothing is skipped.
|
|
1690
|
+
_skipTypes = _NO_SKIP_TYPES;
|
|
1691
|
+
_skipActive = false;
|
|
1692
|
+
_skipSelectorPrelude = false;
|
|
1693
|
+
_skipAtRulePrelude = false;
|
|
1694
|
+
_mkLeaf = _objLeaf;
|
|
1695
|
+
_mkUrl = _objUrl;
|
|
1696
|
+
_mkContainer = _objContainer;
|
|
1697
|
+
_mkStylesheet = _objStylesheet;
|
|
1698
|
+
_setName = (r, v, ns, ne) => {
|
|
1699
|
+
const c = /** @type {Container} */ (r);
|
|
1700
|
+
c.name = v;
|
|
1701
|
+
c.nameStart = ns;
|
|
1702
|
+
c.nameEnd = ne;
|
|
1703
|
+
};
|
|
1704
|
+
_setEnd = (r, v) => {
|
|
1705
|
+
r.end = v;
|
|
1706
|
+
};
|
|
1707
|
+
_setBlock = (r, bs, be) => {
|
|
1708
|
+
const c = /** @type {Container} */ (r);
|
|
1709
|
+
c.blockStart = bs;
|
|
1710
|
+
c.blockEnd = be;
|
|
1711
|
+
};
|
|
1712
|
+
_setImportant = (r) => {
|
|
1713
|
+
/** @type {Container} */ (r).important = true;
|
|
1714
|
+
};
|
|
1715
|
+
_setToken = (r, ch) => {
|
|
1716
|
+
/** @type {Container} */ (r).token = ch;
|
|
1717
|
+
};
|
|
1718
|
+
_setValue = (r, list) => {
|
|
1719
|
+
/** @type {Container} */ (r).value = /** @type {ComponentValue[]} */ (list);
|
|
1720
|
+
};
|
|
1721
|
+
_setPrelude = (r, list) => {
|
|
1722
|
+
/** @type {Container} */ (r).prelude = /** @type {ComponentValue[]} */ (
|
|
1723
|
+
list
|
|
1724
|
+
);
|
|
1725
|
+
};
|
|
1726
|
+
_setBody = (r, decls, childRules) => {
|
|
1727
|
+
const c = /** @type {Container} */ (r);
|
|
1728
|
+
c.declarations = /** @type {Declaration[]} */ (decls);
|
|
1729
|
+
c.childRules = /** @type {Rule[]} */ (childRules);
|
|
1730
|
+
};
|
|
1731
|
+
_setRules = (r, list) => {
|
|
1732
|
+
/** @type {Stylesheet} */ (r).rules = /** @type {Rule[]} */ (list);
|
|
1733
|
+
};
|
|
1734
|
+
_nType = (r) => r.type;
|
|
1735
|
+
_nStart = (r) => r.start;
|
|
1736
|
+
_nValue = (r) => /** @type {Token} */ (r).value;
|
|
1737
|
+
_nToken = (r) =>
|
|
1738
|
+
/** @type {SimpleBlockToken} */ (/** @type {Container} */ (r).token);
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
// -- struct-of-arrays backend: writes nodes into reused typed arrays --
|
|
1742
|
+
// A node ref is its integer id; fields live in parallel arrays indexed by id.
|
|
1743
|
+
// Three reused int slots (`_soaA0/1/2`) plus a flags byte carry the per-type
|
|
1744
|
+
// extras; child lists hang off three object arrays. Slot meaning by type:
|
|
1745
|
+
// url: a0 contentStart, a1 contentEnd
|
|
1746
|
+
// function: a0 nameEnd
|
|
1747
|
+
// declaration: a0 nameEnd, flags bit0 important
|
|
1748
|
+
// at-rule: a0 nameEnd, a1 blockStart, a2 blockEnd
|
|
1749
|
+
// qualified: a1 blockStart, a2 blockEnd
|
|
1750
|
+
// `name` / `nameStart` / a simple block's `token` are derived from the source
|
|
1751
|
+
// on read (see the SoA accessors), so they need no slot. Lists: l0 =
|
|
1752
|
+
// value | prelude | stylesheet rules, l1 = declarations, l2 = childRules.
|
|
1753
|
+
// `grammar` resets `_soaN` to 0 after each top-level rule's walk, so the
|
|
1754
|
+
// buffers are reused across rules and the parse allocates almost nothing.
|
|
1755
|
+
let _soaCap = 0;
|
|
1756
|
+
let _soaN = 0;
|
|
1757
|
+
let _soaTy = new Uint8Array(0);
|
|
1758
|
+
let _soaSt = new Int32Array(0);
|
|
1759
|
+
let _soaEn = new Int32Array(0);
|
|
1760
|
+
let _soaA0 = new Int32Array(0);
|
|
1761
|
+
let _soaA1 = new Int32Array(0);
|
|
1762
|
+
let _soaA2 = new Int32Array(0);
|
|
1763
|
+
let _soaFl = new Uint8Array(0);
|
|
1764
|
+
/** @type {(Node[] | null)[]} */
|
|
1765
|
+
const _soaL0 = [];
|
|
1766
|
+
/** @type {(Node[] | null)[]} */
|
|
1767
|
+
const _soaL1 = [];
|
|
1768
|
+
/** @type {(Node[] | null)[]} */
|
|
1769
|
+
const _soaL2 = [];
|
|
1770
|
+
let _soaInput = "";
|
|
1771
|
+
let _soaLC = /** @type {LocConverter} */ (/** @type {unknown} */ (null));
|
|
1772
|
+
|
|
1773
|
+
// Node refs are integers here but typed `Node` across the parser; these are
|
|
1774
|
+
// identity casts that just satisfy the type system at the boundary.
|
|
1775
|
+
/** @type {(n: Node) => number} */
|
|
1776
|
+
const _ix = (n) => /** @type {number} */ (/** @type {unknown} */ (n));
|
|
1777
|
+
/** @type {(i: number) => Node} */
|
|
1778
|
+
const _ref = (i) => /** @type {Node} */ (/** @type {unknown} */ (i));
|
|
1779
|
+
|
|
1780
|
+
/** @param {number} need minimum capacity */
|
|
1781
|
+
const _soaGrow = (need) => {
|
|
1782
|
+
let cap = _soaCap || 4096;
|
|
1783
|
+
while (cap < need) cap *= 2;
|
|
1784
|
+
const ty = new Uint8Array(cap);
|
|
1785
|
+
ty.set(_soaTy);
|
|
1786
|
+
_soaTy = ty;
|
|
1787
|
+
const st = new Int32Array(cap);
|
|
1788
|
+
st.set(_soaSt);
|
|
1789
|
+
_soaSt = st;
|
|
1790
|
+
const en = new Int32Array(cap);
|
|
1791
|
+
en.set(_soaEn);
|
|
1792
|
+
_soaEn = en;
|
|
1793
|
+
const a0 = new Int32Array(cap);
|
|
1794
|
+
a0.set(_soaA0);
|
|
1795
|
+
_soaA0 = a0;
|
|
1796
|
+
const a1 = new Int32Array(cap);
|
|
1797
|
+
a1.set(_soaA1);
|
|
1798
|
+
_soaA1 = a1;
|
|
1799
|
+
const a2 = new Int32Array(cap);
|
|
1800
|
+
a2.set(_soaA2);
|
|
1801
|
+
_soaA2 = a2;
|
|
1802
|
+
const fl = new Uint8Array(cap);
|
|
1803
|
+
fl.set(_soaFl);
|
|
1804
|
+
_soaFl = fl;
|
|
1805
|
+
_soaCap = cap;
|
|
1806
|
+
};
|
|
1807
|
+
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1808
|
+
const _soaAlloc = (type, start, end) => {
|
|
1809
|
+
// Ids are 1-based: a node ref is used in truthiness checks (`if (!parent)`),
|
|
1810
|
+
// so 0 must stay reserved for "no node".
|
|
1811
|
+
// Leaves never read the flag / list slots — `_soaAllocContainer` clears
|
|
1812
|
+
// them instead, keeping the dominant leaf allocation at three writes.
|
|
1813
|
+
const i = _soaN + 1;
|
|
1814
|
+
if (i >= _soaCap) _soaGrow(i + 1);
|
|
1815
|
+
_soaTy[i] = type;
|
|
1816
|
+
_soaSt[i] = start;
|
|
1817
|
+
_soaEn[i] = end;
|
|
1818
|
+
_soaN = i;
|
|
1819
|
+
return _ref(i);
|
|
1820
|
+
};
|
|
1821
|
+
/** @type {(type: number, start: number, end: number) => Node} */
|
|
1822
|
+
const _soaAllocContainer = (type, start, end) => {
|
|
1823
|
+
const r = _soaAlloc(type, start, end);
|
|
1824
|
+
const i = _ix(r);
|
|
1825
|
+
_soaFl[i] = 0;
|
|
1826
|
+
// Clear list slots so a reused id never exposes a previous node's children.
|
|
1827
|
+
_soaL0[i] = null;
|
|
1828
|
+
_soaL1[i] = null;
|
|
1829
|
+
_soaL2[i] = null;
|
|
1830
|
+
return r;
|
|
1831
|
+
};
|
|
1832
|
+
// Raw token value (the lazy `Token.value` form): hash / at-keyword drop their
|
|
1833
|
+
// one-char prefix, url uses its content range. Shared by the parser's
|
|
1834
|
+
// mid-parse reads and the SoA accessor.
|
|
1835
|
+
/**
|
|
1836
|
+
* @param {number} i node id
|
|
1837
|
+
* @returns {string} raw token value
|
|
1838
|
+
*/
|
|
1839
|
+
const _soaValueOf = (i) => {
|
|
1840
|
+
const ty = _soaTy[i];
|
|
1841
|
+
if (ty === T_HASH || ty === T_AT_KEYWORD) {
|
|
1842
|
+
return _soaInput.slice(_soaSt[i] + 1, _soaEn[i]);
|
|
1843
|
+
}
|
|
1844
|
+
if (ty === T_URL) return _soaInput.slice(_soaA0[i], _soaA1[i]);
|
|
1845
|
+
return _soaInput.slice(_soaSt[i], _soaEn[i]);
|
|
1846
|
+
};
|
|
1847
|
+
/**
|
|
1848
|
+
* @param {string} input source
|
|
1849
|
+
* @param {LocConverter} lc loc converter
|
|
1850
|
+
*/
|
|
1851
|
+
const useSoaBackend = (input, lc) => {
|
|
1852
|
+
_soaInput = input;
|
|
1853
|
+
_soaLC = lc;
|
|
1854
|
+
_soaN = 0;
|
|
1855
|
+
_mkLeaf = (type, start, end) => _soaAlloc(type, start, end);
|
|
1856
|
+
_mkUrl = (start, end, cs, ce) => {
|
|
1857
|
+
const r = _soaAlloc(T_URL, start, end);
|
|
1858
|
+
_soaA0[_ix(r)] = cs;
|
|
1859
|
+
_soaA1[_ix(r)] = ce;
|
|
1860
|
+
return r;
|
|
1861
|
+
};
|
|
1862
|
+
_mkContainer = (type, start, end) => _soaAllocContainer(type, start, end);
|
|
1863
|
+
_mkStylesheet = (start) => _soaAllocContainer(T_STYLESHEET, start, start);
|
|
1864
|
+
// name / nameStart are derived from start + nameEnd; only nameEnd is stored.
|
|
1865
|
+
_setName = (r, v, ns, ne) => {
|
|
1866
|
+
_soaA0[_ix(r)] = ne;
|
|
1867
|
+
};
|
|
1868
|
+
_setEnd = (r, v) => {
|
|
1869
|
+
_soaEn[_ix(r)] = v;
|
|
1870
|
+
};
|
|
1871
|
+
_setBlock = (r, bs, be) => {
|
|
1872
|
+
const i = _ix(r);
|
|
1873
|
+
_soaA1[i] = bs;
|
|
1874
|
+
_soaA2[i] = be;
|
|
1875
|
+
};
|
|
1876
|
+
_setImportant = (r) => {
|
|
1877
|
+
_soaFl[_ix(r)] |= 1;
|
|
1878
|
+
};
|
|
1879
|
+
// A simple block's token is derived from its opening char on read.
|
|
1880
|
+
_setToken = (r, ch) => {};
|
|
1881
|
+
_setValue = (r, list) => {
|
|
1882
|
+
_soaL0[_ix(r)] = list;
|
|
1883
|
+
};
|
|
1884
|
+
_setPrelude = (r, list) => {
|
|
1885
|
+
_soaL0[_ix(r)] = list;
|
|
1886
|
+
};
|
|
1887
|
+
_setBody = (r, decls, childRules) => {
|
|
1888
|
+
const i = _ix(r);
|
|
1889
|
+
_soaL1[i] = decls;
|
|
1890
|
+
_soaL2[i] = childRules;
|
|
1891
|
+
};
|
|
1892
|
+
_setRules = (r, list) => {
|
|
1893
|
+
_soaL0[_ix(r)] = list;
|
|
1894
|
+
};
|
|
1895
|
+
_nType = (r) => _soaTy[_ix(r)];
|
|
1896
|
+
_nStart = (r) => _soaSt[_ix(r)];
|
|
1897
|
+
_nValue = (r) => _soaValueOf(_ix(r));
|
|
1898
|
+
_nToken = (r) => /** @type {SimpleBlockToken} */ (_soaInput[_soaSt[_ix(r)]]);
|
|
1899
|
+
};
|
|
1900
|
+
|
|
1433
1901
|
/**
|
|
1434
1902
|
* Materialize a single non-block, non-function lexer token as its leaf AST node — the spec's "consume a token" result (§5.4.8 "anything else"), preserving stray closers / CDO / CDC.
|
|
1435
1903
|
* @param {MutableToken} t token from the lexer
|
|
1436
|
-
* @
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
return new Token(T_IDENT, t.start, t.end, locConverter);
|
|
1446
|
-
case TT_STRING:
|
|
1447
|
-
return new Token(T_STRING, t.start, t.end, locConverter);
|
|
1448
|
-
case TT_DELIM:
|
|
1449
|
-
return new Token(T_DELIM, t.start, t.end, locConverter);
|
|
1450
|
-
// Numeric tokens: construct only — `numericValue` / `typeFlag` / `sign` /
|
|
1451
|
-
// `unit` are lazy `Token` getters, so the `Number()` parse + slices +
|
|
1452
|
-
// `toLowerCase` are skipped for the (common) tokens never read numerically.
|
|
1453
|
-
case TT_NUMBER:
|
|
1454
|
-
return new Token(T_NUMBER, t.start, t.end, locConverter);
|
|
1455
|
-
case TT_PERCENTAGE:
|
|
1456
|
-
return new Token(T_PERCENTAGE, t.start, t.end, locConverter);
|
|
1457
|
-
case TT_DIMENSION:
|
|
1458
|
-
return new Token(T_DIMENSION, t.start, t.end, locConverter);
|
|
1459
|
-
case TT_HASH: {
|
|
1460
|
-
const hash = new Token(T_HASH, t.start, t.end, locConverter);
|
|
1461
|
-
hash._value = input.slice(t.start + 1, t.end);
|
|
1462
|
-
// Store the raw id-ness; the `typeFlag` getter maps it to "id" /
|
|
1463
|
-
// "unrestricted". Kept off `Token`'s declared fields so non-hash
|
|
1464
|
-
// tokens don't carry the slot.
|
|
1465
|
-
/** @type {Token & { _isId: boolean }} */ (hash)._isId = t.isId;
|
|
1466
|
-
return hash;
|
|
1467
|
-
}
|
|
1468
|
-
case TT_AT_KEYWORD: {
|
|
1469
|
-
const at = new Token(T_AT_KEYWORD, t.start, t.end, locConverter);
|
|
1470
|
-
at._value = input.slice(t.start + 1, t.end);
|
|
1471
|
-
return at;
|
|
1472
|
-
}
|
|
1473
|
-
case TT_URL: {
|
|
1474
|
-
const ut = /** @type {CssUrlToken} */ (t);
|
|
1475
|
-
const url = /** @type {UrlToken} */ (
|
|
1476
|
-
new Token(T_URL, t.start, t.end, locConverter)
|
|
1477
|
-
);
|
|
1478
|
-
url._value = input.slice(ut.contentStart, ut.contentEnd);
|
|
1479
|
-
url.contentStart = ut.contentStart;
|
|
1480
|
-
url.contentEnd = ut.contentEnd;
|
|
1481
|
-
return url;
|
|
1482
|
-
}
|
|
1483
|
-
case TT_BAD_STRING_TOKEN:
|
|
1484
|
-
return new Token(T_BAD_STRING, t.start, t.end, locConverter);
|
|
1485
|
-
case TT_BAD_URL_TOKEN:
|
|
1486
|
-
return new Token(T_BAD_URL, t.start, t.end, locConverter);
|
|
1487
|
-
case TT_COLON:
|
|
1488
|
-
return new Token(T_COLON, t.start, t.end, locConverter);
|
|
1489
|
-
case TT_COMMA:
|
|
1490
|
-
return new Token(T_COMMA, t.start, t.end, locConverter);
|
|
1491
|
-
case TT_SEMICOLON:
|
|
1492
|
-
return new Token(T_SEMICOLON, t.start, t.end, locConverter);
|
|
1493
|
-
// Stray closers / CDO / CDC reach here only on malformed input; the spec
|
|
1494
|
-
// preserves them as component values ("consume a token and return it").
|
|
1495
|
-
case TT_RIGHT_PARENTHESIS:
|
|
1496
|
-
return new Token(T_RIGHT_PARENTHESIS, t.start, t.end, locConverter);
|
|
1497
|
-
case TT_RIGHT_SQUARE_BRACKET:
|
|
1498
|
-
return new Token(T_RIGHT_SQUARE_BRACKET, t.start, t.end, locConverter);
|
|
1499
|
-
case TT_RIGHT_CURLY_BRACKET:
|
|
1500
|
-
return new Token(T_RIGHT_CURLY_BRACKET, t.start, t.end, locConverter);
|
|
1501
|
-
case TT_CDO:
|
|
1502
|
-
return new Token(T_CDO, t.start, t.end, locConverter);
|
|
1503
|
-
case TT_CDC:
|
|
1504
|
-
return new Token(T_CDC, t.start, t.end, locConverter);
|
|
1505
|
-
/* istanbul ignore next -- @preserve: unreachable; blocks/functions are routed earlier, comments filtered, EOF guarded by callers */
|
|
1506
|
-
default:
|
|
1507
|
-
throw new Error(`Unexpected token type "${t.type}"`);
|
|
1904
|
+
* @returns {Node} the leaf token node
|
|
1905
|
+
*/
|
|
1906
|
+
const tokenToNode = (t) => {
|
|
1907
|
+
const tt = t.type;
|
|
1908
|
+
// URL is the only leaf with own state (its content range); all others are a
|
|
1909
|
+
// plain leaf whose node type comes from the map.
|
|
1910
|
+
if (tt === TT_URL) {
|
|
1911
|
+
const ut = /** @type {CssUrlToken} */ (t);
|
|
1912
|
+
return _mkUrl(t.start, t.end, ut.contentStart, ut.contentEnd);
|
|
1508
1913
|
}
|
|
1914
|
+
return _mkLeaf(_ttToNodeType[tt], t.start, t.end);
|
|
1509
1915
|
};
|
|
1510
1916
|
|
|
1511
1917
|
/**
|
|
@@ -1555,12 +1961,12 @@ class TokenStream {
|
|
|
1555
1961
|
/** @type {number} */
|
|
1556
1962
|
this._commentHigh = pos;
|
|
1557
1963
|
// Single reused token the lexer writes into on the `next` path — see
|
|
1558
|
-
// `MutableToken`. `
|
|
1559
|
-
//
|
|
1964
|
+
// `MutableToken`. `_hasNext` marks it cached — a boolean instead of an
|
|
1965
|
+
// object slot, so caching a token never pays a GC write barrier.
|
|
1560
1966
|
/** @type {MutableToken} */
|
|
1561
1967
|
this._tok = createToken();
|
|
1562
|
-
/** @type {
|
|
1563
|
-
this.
|
|
1968
|
+
/** @type {boolean} whether `_tok` holds the (lazily tokenized) next token */
|
|
1969
|
+
this._hasNext = false;
|
|
1564
1970
|
/** @type {number[]} byte offsets to rewind to */
|
|
1565
1971
|
this._marks = [];
|
|
1566
1972
|
}
|
|
@@ -1574,14 +1980,14 @@ class TokenStream {
|
|
|
1574
1980
|
* @returns {MutableToken} the next token
|
|
1575
1981
|
*/
|
|
1576
1982
|
next() {
|
|
1577
|
-
if (this.
|
|
1983
|
+
if (!this._hasNext) {
|
|
1578
1984
|
const input = this.input;
|
|
1579
1985
|
const tok = this._tok;
|
|
1580
1986
|
let pos = this._pos;
|
|
1581
1987
|
for (;;) {
|
|
1582
1988
|
const t = readToken(input, pos, tok);
|
|
1583
1989
|
if (t === undefined) {
|
|
1584
|
-
|
|
1990
|
+
fill(tok, TT_EOF, input.length, input.length);
|
|
1585
1991
|
break;
|
|
1586
1992
|
}
|
|
1587
1993
|
if (t.type === TT_COMMENT) {
|
|
@@ -1592,11 +1998,11 @@ class TokenStream {
|
|
|
1592
1998
|
pos = t.end;
|
|
1593
1999
|
continue;
|
|
1594
2000
|
}
|
|
1595
|
-
this._next = t;
|
|
1596
2001
|
break;
|
|
1597
2002
|
}
|
|
2003
|
+
this._hasNext = true;
|
|
1598
2004
|
}
|
|
1599
|
-
return
|
|
2005
|
+
return this._tok;
|
|
1600
2006
|
}
|
|
1601
2007
|
|
|
1602
2008
|
/**
|
|
@@ -1609,7 +2015,7 @@ class TokenStream {
|
|
|
1609
2015
|
const t = this.next();
|
|
1610
2016
|
if (t.type !== TT_EOF) {
|
|
1611
2017
|
this._pos = t.end;
|
|
1612
|
-
this.
|
|
2018
|
+
this._hasNext = false;
|
|
1613
2019
|
}
|
|
1614
2020
|
return t;
|
|
1615
2021
|
}
|
|
@@ -1623,7 +2029,7 @@ class TokenStream {
|
|
|
1623
2029
|
const t = this.next();
|
|
1624
2030
|
if (t.type !== TT_EOF) {
|
|
1625
2031
|
this._pos = t.end;
|
|
1626
|
-
this.
|
|
2032
|
+
this._hasNext = false;
|
|
1627
2033
|
}
|
|
1628
2034
|
}
|
|
1629
2035
|
|
|
@@ -1643,7 +2049,7 @@ class TokenStream {
|
|
|
1643
2049
|
*/
|
|
1644
2050
|
restoreMark() {
|
|
1645
2051
|
this._pos = /** @type {number} */ (this._marks.pop());
|
|
1646
|
-
this.
|
|
2052
|
+
this._hasNext = false;
|
|
1647
2053
|
}
|
|
1648
2054
|
|
|
1649
2055
|
/**
|
|
@@ -1697,15 +2103,13 @@ const parseAStylesheet = (input, comment) => {
|
|
|
1697
2103
|
// 1. If input is a byte stream for a stylesheet, decode bytes from input, and set input to the result.
|
|
1698
2104
|
// 2. Normalize input, and set input to the result.
|
|
1699
2105
|
const ts = normalizeIntoTokenStream(input, 0, comment);
|
|
2106
|
+
useObjectBackend(ts.locConverter);
|
|
1700
2107
|
// 3. Create a new stylesheet, with its location set to location (or null, if location was not passed).
|
|
1701
2108
|
const start = ts.next().start;
|
|
1702
|
-
const stylesheet = /** @type {Stylesheet} */ (
|
|
1703
|
-
new Node(T_STYLESHEET, start, start, ts.locConverter)
|
|
1704
|
-
);
|
|
1705
|
-
stylesheet.rules = /** @type {Rule[]} */ ([]);
|
|
2109
|
+
const stylesheet = /** @type {Stylesheet} */ (_mkStylesheet(start));
|
|
1706
2110
|
// 4. Consume a stylesheet's contents from input, and set the stylesheet's rules to the result.
|
|
1707
|
-
stylesheet
|
|
1708
|
-
stylesheet
|
|
2111
|
+
_setRules(stylesheet, consumeAStylesheetsContents(ts));
|
|
2112
|
+
_setEnd(stylesheet, ts.next().start);
|
|
1709
2113
|
// 5. Return the stylesheet.
|
|
1710
2114
|
return stylesheet;
|
|
1711
2115
|
};
|
|
@@ -1723,6 +2127,7 @@ const parseAStylesheet = (input, comment) => {
|
|
|
1723
2127
|
const parseAStylesheetsContents = (input, comment) => {
|
|
1724
2128
|
// 1. Normalize input, and set input to the result.
|
|
1725
2129
|
const ts = normalizeIntoTokenStream(input, 0, comment);
|
|
2130
|
+
useObjectBackend(ts.locConverter);
|
|
1726
2131
|
// 2. Consume a stylesheet’s contents from input, and return the result.
|
|
1727
2132
|
return consumeAStylesheetsContents(ts);
|
|
1728
2133
|
};
|
|
@@ -1738,6 +2143,7 @@ const parseAStylesheetsContents = (input, comment) => {
|
|
|
1738
2143
|
const parseABlocksContents = (input, pos, comment) => {
|
|
1739
2144
|
// 1. Normalize input, and set input to the result.
|
|
1740
2145
|
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2146
|
+
useObjectBackend(ts.locConverter);
|
|
1741
2147
|
// 2. Consume a block’s contents from input, and return the result.
|
|
1742
2148
|
return consumeABlocksContents(ts);
|
|
1743
2149
|
};
|
|
@@ -1755,6 +2161,7 @@ const parseABlocksContents = (input, pos, comment) => {
|
|
|
1755
2161
|
const parseARule = (input, pos, comment) => {
|
|
1756
2162
|
// 1. Normalize input, and set input to the result.
|
|
1757
2163
|
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2164
|
+
useObjectBackend(ts.locConverter);
|
|
1758
2165
|
// 2. Discard whitespace from input.
|
|
1759
2166
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
1760
2167
|
// 3. If the next token from input is an <EOF-token>, return a syntax error.
|
|
@@ -1785,6 +2192,7 @@ const parseARule = (input, pos, comment) => {
|
|
|
1785
2192
|
const parseADeclaration = (input, pos, comment) => {
|
|
1786
2193
|
// 1. Normalize input, and set input to the result.
|
|
1787
2194
|
const ts = normalizeIntoTokenStream(input, pos, comment);
|
|
2195
|
+
useObjectBackend(ts.locConverter);
|
|
1788
2196
|
// 2. Discard whitespace from input.
|
|
1789
2197
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
1790
2198
|
// 3. Consume a declaration from input. If anything was returned, return it. Otherwise, return a syntax error.
|
|
@@ -1801,6 +2209,7 @@ const parseADeclaration = (input, pos, comment) => {
|
|
|
1801
2209
|
const parseAComponentValue = (input, pos, options = {}) => {
|
|
1802
2210
|
// 1. Normalize input, and set input to the result.
|
|
1803
2211
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2212
|
+
useObjectBackend(ts.locConverter);
|
|
1804
2213
|
// 2. Discard whitespace from input.
|
|
1805
2214
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
1806
2215
|
// 3. If input is empty, return a syntax error.
|
|
@@ -1825,6 +2234,7 @@ const parseAComponentValue = (input, pos, options = {}) => {
|
|
|
1825
2234
|
const parseAListOfComponentValues = (input, pos, options = {}) => {
|
|
1826
2235
|
// 1. Normalize input, and set input to the result.
|
|
1827
2236
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2237
|
+
useObjectBackend(ts.locConverter);
|
|
1828
2238
|
// 2. Consume a list of component values from input, and return the result.
|
|
1829
2239
|
return consumeAListOfComponentValues(ts);
|
|
1830
2240
|
};
|
|
@@ -1843,6 +2253,7 @@ const parseACommaSeparatedListOfComponentValues = (
|
|
|
1843
2253
|
) => {
|
|
1844
2254
|
// 1. Normalize input, and set input to the result.
|
|
1845
2255
|
const ts = normalizeIntoTokenStream(input, pos, options.comment);
|
|
2256
|
+
useObjectBackend(ts.locConverter);
|
|
1846
2257
|
// 2. Let groups be an empty list.
|
|
1847
2258
|
/** @type {ComponentValue[][]} */
|
|
1848
2259
|
const groups = [];
|
|
@@ -1924,17 +2335,23 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
1924
2335
|
// Consume a token from input, and let rule be a new at-rule with its name set to the returned token’s value, its prelude initially set to an empty list, and no declarations or child rules.
|
|
1925
2336
|
const head = ts.consume();
|
|
1926
2337
|
const rule = /** @type {AtRule} */ (
|
|
1927
|
-
|
|
2338
|
+
_mkContainer(T_AT_RULE, head.start, head.end)
|
|
1928
2339
|
);
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
rule
|
|
1937
|
-
|
|
2340
|
+
_setName(
|
|
2341
|
+
rule,
|
|
2342
|
+
ts.input.slice(head.start + 1, head.end),
|
|
2343
|
+
head.start,
|
|
2344
|
+
head.end
|
|
2345
|
+
);
|
|
2346
|
+
const prelude = _list();
|
|
2347
|
+
_setPrelude(rule, prelude);
|
|
2348
|
+
// declarations / childRules / blockStart (-1) / blockEnd (-1) keep their
|
|
2349
|
+
// defaults (no block: the `;` / EOF / nested-`}` at-rule forms).
|
|
2350
|
+
|
|
2351
|
+
// Like `consumeAQualifiedRule`: skip mode scans the prelude without
|
|
2352
|
+
// materializing it (url tokens / functions kept so `@import url(…)` still
|
|
2353
|
+
// resolves); the block boundary is found by scanning, not the prelude nodes.
|
|
2354
|
+
const skip = _skipAtRulePrelude;
|
|
1938
2355
|
|
|
1939
2356
|
// Process input
|
|
1940
2357
|
for (;;) {
|
|
@@ -1945,7 +2362,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
1945
2362
|
// Discard a token from input. If rule is valid in the current context, return it; otherwise return nothing.
|
|
1946
2363
|
if (t.type === TT_SEMICOLON || t.type === TT_EOF) {
|
|
1947
2364
|
ts.discard();
|
|
1948
|
-
rule
|
|
2365
|
+
_setEnd(rule, t.start);
|
|
1949
2366
|
return rule;
|
|
1950
2367
|
}
|
|
1951
2368
|
// <}-token>
|
|
@@ -1953,27 +2370,31 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
1953
2370
|
// Otherwise, consume a token and append the result to rule’s prelude.
|
|
1954
2371
|
else if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
1955
2372
|
if (nested) {
|
|
1956
|
-
rule
|
|
2373
|
+
_setEnd(rule, t.start);
|
|
1957
2374
|
return rule;
|
|
1958
2375
|
}
|
|
1959
|
-
|
|
2376
|
+
const node = consumeATokenAsNode(ts);
|
|
2377
|
+
if (!skip) prelude.push(node);
|
|
1960
2378
|
continue;
|
|
1961
2379
|
}
|
|
1962
2380
|
// <{-token>
|
|
1963
2381
|
// Consume a block from input, and assign the result to rule's declarations and child rules.
|
|
1964
2382
|
else if (t.type === TT_LEFT_CURLY_BRACKET) {
|
|
1965
2383
|
const block = consumeABlock(ts);
|
|
1966
|
-
rule.
|
|
1967
|
-
rule.
|
|
1968
|
-
rule
|
|
1969
|
-
rule.blockEnd = block.blockEnd;
|
|
1970
|
-
rule.end = block.blockEnd;
|
|
2384
|
+
_setBody(rule, block.decls, block.rules);
|
|
2385
|
+
_setBlock(rule, block.blockStart, block.blockEnd);
|
|
2386
|
+
_setEnd(rule, block.blockEnd);
|
|
1971
2387
|
return rule;
|
|
1972
2388
|
}
|
|
1973
2389
|
|
|
1974
2390
|
// anything else
|
|
1975
2391
|
// Consume a component value from input and append the returned value to rule’s prelude.
|
|
1976
|
-
|
|
2392
|
+
const node = consumeAComponentValue(ts, t);
|
|
2393
|
+
if (!skip) {
|
|
2394
|
+
prelude.push(node);
|
|
2395
|
+
} else if (_nType(node) === T_FUNCTION || _nType(node) === T_URL) {
|
|
2396
|
+
prelude.push(node);
|
|
2397
|
+
}
|
|
1977
2398
|
}
|
|
1978
2399
|
};
|
|
1979
2400
|
|
|
@@ -1988,7 +2409,7 @@ const consumeAnAtRule = (ts, nested = false) => {
|
|
|
1988
2409
|
*/
|
|
1989
2410
|
const consumeATokenAsNode = (ts) => {
|
|
1990
2411
|
const t = ts.consume();
|
|
1991
|
-
return tokenToNode(t
|
|
2412
|
+
return /** @type {Token} */ (tokenToNode(t));
|
|
1992
2413
|
};
|
|
1993
2414
|
|
|
1994
2415
|
/**
|
|
@@ -2002,14 +2423,19 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2002
2423
|
const start = ts.next().start;
|
|
2003
2424
|
// Let rule be a new qualified rule with its prelude, declarations, and child rules all initially set to empty lists.
|
|
2004
2425
|
const rule = /** @type {QualifiedRule} */ (
|
|
2005
|
-
|
|
2426
|
+
_mkContainer(T_QUALIFIED_RULE, start, start)
|
|
2006
2427
|
);
|
|
2007
|
-
|
|
2008
|
-
rule
|
|
2009
|
-
|
|
2010
|
-
//
|
|
2011
|
-
|
|
2012
|
-
|
|
2428
|
+
const prelude = _list();
|
|
2429
|
+
_setPrelude(rule, prelude);
|
|
2430
|
+
// declarations / childRules / blockStart (-1) / blockEnd (-1) keep their
|
|
2431
|
+
// defaults (no block until a `{` is reached).
|
|
2432
|
+
|
|
2433
|
+
// Skip mode leaves `prelude` empty (selector text is recovered from the
|
|
2434
|
+
// rule's byte range, not its nodes); `first`/`second` still track the first
|
|
2435
|
+
// two non-whitespace tokens the `--foo: {` disambiguation below needs.
|
|
2436
|
+
const skip = _skipSelectorPrelude;
|
|
2437
|
+
let first = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
2438
|
+
let second = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
2013
2439
|
|
|
2014
2440
|
// Process input
|
|
2015
2441
|
for (;;) {
|
|
@@ -2024,7 +2450,13 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2024
2450
|
// This is a parse error. If nested is true, return nothing. Otherwise, consume a token and append the result to rule’s prelude.
|
|
2025
2451
|
else if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2026
2452
|
if (nested) return undefined;
|
|
2027
|
-
|
|
2453
|
+
const node = consumeATokenAsNode(ts);
|
|
2454
|
+
if (skip) {
|
|
2455
|
+
if (!first) first = node;
|
|
2456
|
+
else if (!second) second = node;
|
|
2457
|
+
} else {
|
|
2458
|
+
prelude.push(node);
|
|
2459
|
+
}
|
|
2028
2460
|
continue;
|
|
2029
2461
|
}
|
|
2030
2462
|
// <{-token>
|
|
@@ -2034,31 +2466,33 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2034
2466
|
// (This disambiguates custom-property declarations from nested qualified rules — `--foo: { … }` at top level of a block is a declaration, not a rule.)
|
|
2035
2467
|
// Otherwise, consume a block from input, and let child rules be the result.
|
|
2036
2468
|
else if (t.type === TT_LEFT_CURLY_BRACKET) {
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2469
|
+
if (!skip) {
|
|
2470
|
+
let firstIdx = 0;
|
|
2471
|
+
/* istanbul ignore next -- @preserve: leading whitespace is discarded before the rule, so the prelude never starts with it */
|
|
2472
|
+
while (
|
|
2473
|
+
firstIdx < prelude.length &&
|
|
2474
|
+
_nType(prelude[firstIdx]) === T_WHITESPACE
|
|
2475
|
+
) {
|
|
2476
|
+
firstIdx++;
|
|
2477
|
+
}
|
|
2478
|
+
let secondIdx = firstIdx + 1;
|
|
2479
|
+
while (
|
|
2480
|
+
secondIdx < prelude.length &&
|
|
2481
|
+
_nType(prelude[secondIdx]) === T_WHITESPACE
|
|
2482
|
+
) {
|
|
2483
|
+
secondIdx++;
|
|
2484
|
+
}
|
|
2485
|
+
first = prelude[firstIdx];
|
|
2486
|
+
second = prelude[secondIdx];
|
|
2051
2487
|
}
|
|
2052
|
-
const first = rule.prelude[firstIdx];
|
|
2053
|
-
const second = rule.prelude[secondIdx];
|
|
2054
2488
|
if (
|
|
2055
2489
|
first &&
|
|
2056
|
-
first
|
|
2490
|
+
_nType(first) === T_IDENT &&
|
|
2057
2491
|
// Test the source bytes directly — avoids forcing the lazy `value`
|
|
2058
2492
|
// slice just to check the `--` custom-property prefix.
|
|
2059
|
-
ts.input.startsWith("--", first
|
|
2493
|
+
ts.input.startsWith("--", _nStart(first)) &&
|
|
2060
2494
|
second &&
|
|
2061
|
-
second
|
|
2495
|
+
_nType(second) === T_COLON
|
|
2062
2496
|
) {
|
|
2063
2497
|
/* istanbul ignore if -- @preserve: when nested, `declarationStartLikely` routes every `--x:` to consumeADeclaration (which accepts custom properties), so this fallthrough is unreachable */
|
|
2064
2498
|
if (nested) {
|
|
@@ -2069,17 +2503,29 @@ const consumeAQualifiedRule = (ts, stopToken, nested = false) => {
|
|
|
2069
2503
|
return undefined;
|
|
2070
2504
|
}
|
|
2071
2505
|
const block = consumeABlock(ts);
|
|
2072
|
-
rule.
|
|
2073
|
-
rule.
|
|
2074
|
-
rule
|
|
2075
|
-
rule.blockEnd = block.blockEnd;
|
|
2076
|
-
rule.end = block.blockEnd;
|
|
2506
|
+
_setBody(rule, block.decls, block.rules);
|
|
2507
|
+
_setBlock(rule, block.blockStart, block.blockEnd);
|
|
2508
|
+
_setEnd(rule, block.blockEnd);
|
|
2077
2509
|
return rule;
|
|
2078
2510
|
}
|
|
2079
2511
|
|
|
2080
2512
|
// anything else
|
|
2081
2513
|
// Consume a component value from input and append the result to rule’s prelude.
|
|
2082
|
-
|
|
2514
|
+
const node = consumeAComponentValue(ts, t);
|
|
2515
|
+
if (skip) {
|
|
2516
|
+
// Keep only url-bearing nodes (url tokens, or functions that may hold a
|
|
2517
|
+
// url like `:unknown(url(x))`) so the url visitor still rewrites them;
|
|
2518
|
+
// other selector tokens have no non-modules consumer, so drop them.
|
|
2519
|
+
const ty = _nType(node);
|
|
2520
|
+
if (ty === T_FUNCTION || ty === T_URL) prelude.push(node);
|
|
2521
|
+
// Track the first two non-whitespace tokens for the disambiguation above.
|
|
2522
|
+
if (t.type !== TT_WHITESPACE) {
|
|
2523
|
+
if (!first) first = node;
|
|
2524
|
+
else if (!second) second = node;
|
|
2525
|
+
}
|
|
2526
|
+
} else {
|
|
2527
|
+
prelude.push(node);
|
|
2528
|
+
}
|
|
2083
2529
|
}
|
|
2084
2530
|
};
|
|
2085
2531
|
|
|
@@ -2103,7 +2549,8 @@ const consumeABlock = (ts) => {
|
|
|
2103
2549
|
};
|
|
2104
2550
|
|
|
2105
2551
|
/**
|
|
2106
|
-
* 2-token lookahead: is the next non-whitespace pair `<ident> <colon
|
|
2552
|
+
* 2-token lookahead: is the next non-whitespace pair `<ident> <colon>`?
|
|
2553
|
+
* Peeks raw code points without advancing; comments still fire `onComment` later.
|
|
2107
2554
|
* @param {TokenStream} ts token stream
|
|
2108
2555
|
* @returns {boolean} true if consume-a-declaration's step 1 + step 3 would both succeed on the current input
|
|
2109
2556
|
*/
|
|
@@ -2245,22 +2692,17 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2245
2692
|
const { input } = ts;
|
|
2246
2693
|
// Let decl be a new declaration, with an initially empty name and a value set to an empty list.
|
|
2247
2694
|
const start = ts.next().start;
|
|
2695
|
+
// name "" / nameStart / nameEnd (= start) / important (false) keep their
|
|
2696
|
+
// `Container` defaults; `value` is set unconditionally at step 5 below.
|
|
2248
2697
|
const decl = /** @type {Declaration} */ (
|
|
2249
|
-
|
|
2698
|
+
_mkContainer(T_DECLARATION, start, start)
|
|
2250
2699
|
);
|
|
2251
|
-
decl.name = "";
|
|
2252
|
-
decl.nameStart = start;
|
|
2253
|
-
decl.nameEnd = start;
|
|
2254
|
-
decl.value = /** @type {ComponentValue[]} */ ([]);
|
|
2255
|
-
decl.important = false;
|
|
2256
2700
|
|
|
2257
2701
|
// 1. If the next token is an <ident-token>, consume a token from input and set decl's name to the returned token's value.
|
|
2258
2702
|
// Otherwise, consume the remnants of a bad declaration from input, with nested, and return nothing.
|
|
2259
2703
|
if (ts.next().type === TT_IDENTIFIER) {
|
|
2260
2704
|
const head = ts.consume();
|
|
2261
|
-
decl.
|
|
2262
|
-
decl.nameEnd = head.end;
|
|
2263
|
-
decl.name = input.slice(head.start, head.end);
|
|
2705
|
+
_setName(decl, input.slice(head.start, head.end), head.start, head.end);
|
|
2264
2706
|
} else {
|
|
2265
2707
|
consumeTheRemnantsOfABadDeclaration(ts, nested);
|
|
2266
2708
|
return undefined;
|
|
@@ -2282,51 +2724,42 @@ const consumeADeclaration = (ts, nested = false) => {
|
|
|
2282
2724
|
while (ts.next().type === TT_WHITESPACE) ts.discard();
|
|
2283
2725
|
|
|
2284
2726
|
// 5. Consume a list of component values from input, with nested, and with <semicolon-token> as the stop token, and set decl's value to the result.
|
|
2285
|
-
|
|
2286
|
-
decl
|
|
2727
|
+
const value = consumeAListOfComponentValues(ts, TT_SEMICOLON, nested);
|
|
2728
|
+
_setValue(decl, value);
|
|
2729
|
+
_setEnd(decl, ts.next().start);
|
|
2287
2730
|
|
|
2288
2731
|
// 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.
|
|
2289
2732
|
{
|
|
2290
|
-
let last =
|
|
2291
|
-
while (last >= 0 &&
|
|
2733
|
+
let last = value.length - 1;
|
|
2734
|
+
while (last >= 0 && _nType(value[last]) === T_WHITESPACE) last--;
|
|
2292
2735
|
let prev = last - 1;
|
|
2293
|
-
while (prev >= 0 &&
|
|
2736
|
+
while (prev >= 0 && _nType(value[prev]) === T_WHITESPACE) prev--;
|
|
2294
2737
|
if (
|
|
2295
2738
|
prev >= 0 &&
|
|
2296
|
-
|
|
2297
|
-
equalsLowerCase(
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
) &&
|
|
2301
|
-
decl.value[prev].type === T_DELIM &&
|
|
2302
|
-
input.charCodeAt(decl.value[prev].start) === CC_EXCLAMATION
|
|
2739
|
+
_nType(value[last]) === T_IDENT &&
|
|
2740
|
+
equalsLowerCase(_nValue(value[last]), "important") &&
|
|
2741
|
+
_nType(value[prev]) === T_DELIM &&
|
|
2742
|
+
input.charCodeAt(_nStart(value[prev])) === CC_EXCLAMATION
|
|
2303
2743
|
) {
|
|
2304
|
-
decl
|
|
2305
|
-
|
|
2744
|
+
_setImportant(decl);
|
|
2745
|
+
value.length = prev;
|
|
2306
2746
|
}
|
|
2307
2747
|
}
|
|
2308
2748
|
|
|
2309
2749
|
// 7. While the last item in decl's value is a <whitespace-token>, remove that token.
|
|
2310
|
-
while (
|
|
2311
|
-
|
|
2312
|
-
decl.value[decl.value.length - 1].type === T_WHITESPACE
|
|
2313
|
-
) {
|
|
2314
|
-
decl.value.pop();
|
|
2750
|
+
while (value.length > 0 && _nType(value[value.length - 1]) === T_WHITESPACE) {
|
|
2751
|
+
value.pop();
|
|
2315
2752
|
}
|
|
2316
2753
|
|
|
2317
2754
|
// 8. If decl's name starts with "--" (a custom property), it can contain any value (including a top-level `{}` block) — accept it.
|
|
2318
2755
|
// Otherwise, if decl's value contains a top-level simple block with an associated token of <{-token>, return nothing.
|
|
2319
2756
|
// (That is, a top-level {}-block is only allowed as the entire value of a non-custom property — for CSS Nesting, `consumeABlocksContents`'s `mark` / `restore a mark` will retry the input as a qualified rule.)
|
|
2320
2757
|
// Otherwise, accept the declaration. (The spec also checks "contains any non-whitespace-tokens at the top level" → return nothing; we keep empty-value declarations because callers — e.g. `@value name:;` — rely on them.)
|
|
2321
|
-
const isCustomProperty = input.startsWith("--",
|
|
2758
|
+
const isCustomProperty = input.startsWith("--", start);
|
|
2322
2759
|
if (!isCustomProperty) {
|
|
2323
|
-
const value = decl.value;
|
|
2324
2760
|
for (let i = 0; i < value.length; i++) {
|
|
2325
2761
|
const v = value[i];
|
|
2326
|
-
if (
|
|
2327
|
-
v.type === T_SIMPLE_BLOCK &&
|
|
2328
|
-
/** @type {SimpleBlock} */ (v).token === "{"
|
|
2329
|
-
) {
|
|
2762
|
+
if (_nType(v) === T_SIMPLE_BLOCK && _nToken(v) === "{") {
|
|
2330
2763
|
return undefined;
|
|
2331
2764
|
}
|
|
2332
2765
|
}
|
|
@@ -2361,22 +2794,40 @@ const consumeAListOfComponentValues = (ts, stopToken, nested = false) => {
|
|
|
2361
2794
|
// Otherwise, this is a parse error. Consume a token from input and append the result to values.
|
|
2362
2795
|
if (t.type === TT_RIGHT_CURLY_BRACKET) {
|
|
2363
2796
|
if (nested) return values;
|
|
2364
|
-
|
|
2797
|
+
const closer = consumeATokenAsNode(ts);
|
|
2798
|
+
// Keep unless the type is explicitly marked skip (1); an out-of-range
|
|
2799
|
+
// lookup on a short `skip.types` yields `undefined`, which must not drop.
|
|
2800
|
+
if (!_skipActive || _skipTypes[_nType(closer)] !== 1) values.push(closer);
|
|
2365
2801
|
continue;
|
|
2366
2802
|
}
|
|
2367
2803
|
// anything else
|
|
2368
2804
|
// Consume a component value from input, and append the result to values.
|
|
2369
|
-
|
|
2805
|
+
// Skipped leaf types short-circuit before materializing: no SoA slot is
|
|
2806
|
+
// written and no node is built (blocks / functions never skip here).
|
|
2807
|
+
const tt = t.type;
|
|
2808
|
+
if (
|
|
2809
|
+
_skipActive &&
|
|
2810
|
+
tt !== TT_FUNCTION &&
|
|
2811
|
+
!(tt >= TT_LEFT_PARENTHESIS && tt <= TT_LEFT_CURLY_BRACKET) &&
|
|
2812
|
+
_skipTypes[_ttToNodeType[tt]] === 1
|
|
2813
|
+
) {
|
|
2814
|
+
ts.consume();
|
|
2815
|
+
continue;
|
|
2816
|
+
}
|
|
2817
|
+
const node = consumeAComponentValue(ts, t);
|
|
2818
|
+
if (!_skipActive || _skipTypes[_nType(node)] !== 1) values.push(node);
|
|
2370
2819
|
}
|
|
2371
2820
|
};
|
|
2372
2821
|
|
|
2373
2822
|
/**
|
|
2374
2823
|
* Consume a component value, CSS Syntax Level 3 [§5.4.8](https://drafts.csswg.org/css-syntax/#consume-component-value) — consumes the next value (simple block, function, or single token); callers guard against EOF before calling.
|
|
2375
2824
|
* @param {TokenStream} ts token stream
|
|
2825
|
+
* @param {MutableToken=} t the next token, if the caller already peeked it (defaults to `ts.next()`)
|
|
2376
2826
|
* @returns {SimpleBlock | FunctionNode | ComponentValue} the consumed component value
|
|
2377
2827
|
*/
|
|
2378
|
-
const consumeAComponentValue = (ts) => {
|
|
2379
|
-
|
|
2828
|
+
const consumeAComponentValue = (ts, t = ts.next()) => {
|
|
2829
|
+
// `t` is the next token; hot callers already peeked it and pass it in to
|
|
2830
|
+
// skip a redundant `ts.next()` per component value.
|
|
2380
2831
|
// <{-token> / <[-token> / <(-token> (the three contiguous opening brackets)
|
|
2381
2832
|
// Consume a simple block from input and return the result.
|
|
2382
2833
|
if (t.type >= TT_LEFT_PARENTHESIS && t.type <= TT_LEFT_CURLY_BRACKET) {
|
|
@@ -2389,7 +2840,11 @@ const consumeAComponentValue = (ts) => {
|
|
|
2389
2840
|
}
|
|
2390
2841
|
// anything else
|
|
2391
2842
|
// Consume a token from input and return the result. (Asserted: not EOF.)
|
|
2392
|
-
|
|
2843
|
+
// Inlined `consumeATokenAsNode`: `t` is already the peeked next token, so
|
|
2844
|
+
// advance past it and materialize it directly — one fewer call per leaf
|
|
2845
|
+
// component value (the bulk of the nodes on a large stylesheet).
|
|
2846
|
+
ts.consume();
|
|
2847
|
+
return /** @type {ComponentValue} */ (tokenToNode(t));
|
|
2393
2848
|
};
|
|
2394
2849
|
|
|
2395
2850
|
/**
|
|
@@ -2406,10 +2861,11 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2406
2861
|
|
|
2407
2862
|
// Let block be a new simple block with its associated token set to the next token and with its value initially set to an empty list.
|
|
2408
2863
|
const block = /** @type {SimpleBlock} */ (
|
|
2409
|
-
|
|
2864
|
+
_mkContainer(T_SIMPLE_BLOCK, open.start, open.end)
|
|
2410
2865
|
);
|
|
2411
|
-
block
|
|
2412
|
-
|
|
2866
|
+
_setToken(block, token);
|
|
2867
|
+
const val = _list();
|
|
2868
|
+
_setValue(block, val);
|
|
2413
2869
|
|
|
2414
2870
|
// Discard a token from input.
|
|
2415
2871
|
ts.discard();
|
|
@@ -2423,13 +2879,13 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2423
2879
|
// Discard a token from input. Return block.
|
|
2424
2880
|
if (t.type === TT_EOF || t.type === ending) {
|
|
2425
2881
|
ts.discard();
|
|
2426
|
-
block
|
|
2882
|
+
_setEnd(block, t.end);
|
|
2427
2883
|
return block;
|
|
2428
2884
|
}
|
|
2429
2885
|
|
|
2430
2886
|
// anything else
|
|
2431
2887
|
// Consume a component value from input and append the result to block’s value.
|
|
2432
|
-
|
|
2888
|
+
val.push(consumeAComponentValue(ts, t));
|
|
2433
2889
|
}
|
|
2434
2890
|
};
|
|
2435
2891
|
|
|
@@ -2439,17 +2895,16 @@ const consumeASimpleBlock = (ts) => {
|
|
|
2439
2895
|
* @returns {FunctionNode | undefined} the consumed function node
|
|
2440
2896
|
*/
|
|
2441
2897
|
const consumeAFunction = (ts) => {
|
|
2442
|
-
const { input
|
|
2898
|
+
const { input } = ts;
|
|
2443
2899
|
// Assert (spec): the next token is a <function-token>.
|
|
2444
2900
|
// Consume a token from input, and let function be a new function with its name equal the returned token’s value, and a value set to an empty list.
|
|
2445
2901
|
const tFn = ts.consume();
|
|
2446
2902
|
const fn = /** @type {FunctionNode} */ (
|
|
2447
|
-
|
|
2903
|
+
_mkContainer(T_FUNCTION, tFn.start, tFn.end)
|
|
2448
2904
|
);
|
|
2449
|
-
fn
|
|
2450
|
-
|
|
2451
|
-
fn
|
|
2452
|
-
fn.value = /** @type {ComponentValue[]} */ ([]);
|
|
2905
|
+
_setName(fn, input.slice(tFn.start, tFn.end - 1), tFn.start, tFn.end - 1);
|
|
2906
|
+
const val = _list();
|
|
2907
|
+
_setValue(fn, val);
|
|
2453
2908
|
|
|
2454
2909
|
// Process input
|
|
2455
2910
|
for (;;) {
|
|
@@ -2460,13 +2915,25 @@ const consumeAFunction = (ts) => {
|
|
|
2460
2915
|
// <)-token>
|
|
2461
2916
|
// Discard a token from input. Return function.
|
|
2462
2917
|
ts.discard();
|
|
2463
|
-
fn
|
|
2918
|
+
_setEnd(fn, t.end);
|
|
2464
2919
|
return fn;
|
|
2465
2920
|
}
|
|
2466
2921
|
|
|
2467
2922
|
// anything else
|
|
2468
2923
|
// Consume a component value from input and append the result to function’s value.
|
|
2469
|
-
|
|
2924
|
+
// Same pre-materialization skip as `consumeAListOfComponentValues`.
|
|
2925
|
+
const tt = t.type;
|
|
2926
|
+
if (
|
|
2927
|
+
_skipActive &&
|
|
2928
|
+
tt !== TT_FUNCTION &&
|
|
2929
|
+
!(tt >= TT_LEFT_PARENTHESIS && tt <= TT_LEFT_CURLY_BRACKET) &&
|
|
2930
|
+
_skipTypes[_ttToNodeType[tt]] === 1
|
|
2931
|
+
) {
|
|
2932
|
+
ts.consume();
|
|
2933
|
+
continue;
|
|
2934
|
+
}
|
|
2935
|
+
const node = consumeAComponentValue(ts, t);
|
|
2936
|
+
if (!_skipActive || _skipTypes[_nType(node)] !== 1) val.push(node);
|
|
2470
2937
|
}
|
|
2471
2938
|
};
|
|
2472
2939
|
|
|
@@ -2644,11 +3111,10 @@ const unescapeIdentifier = makeCacheable(_unescapeIdentifier);
|
|
|
2644
3111
|
// CSS-typed views over the generic visitor machinery (`util/SourceProcessor`),
|
|
2645
3112
|
// re-exported so consumers keep importing them from this module.
|
|
2646
3113
|
/**
|
|
2647
|
-
* @typedef {import("../util/SourceProcessor").
|
|
2648
|
-
* @typedef {import("../util/SourceProcessor").
|
|
2649
|
-
* @typedef {import("../util/SourceProcessor").
|
|
2650
|
-
* @typedef {import("../util/SourceProcessor").
|
|
2651
|
-
* @typedef {import("../util/SourceProcessor").CompiledVisitorMap<Node>} CompiledVisitorMap
|
|
3114
|
+
* @typedef {import("../util/SourceProcessor").VisitorFn<CssPath>} VisitorFn
|
|
3115
|
+
* @typedef {import("../util/SourceProcessor").VisitorBucket<CssPath>} VisitorBucket
|
|
3116
|
+
* @typedef {import("../util/SourceProcessor").VisitorMap<CssPath>} VisitorMap
|
|
3117
|
+
* @typedef {import("../util/SourceProcessor").CompiledVisitorMap<CssPath>} CompiledVisitorMap
|
|
2652
3118
|
*/
|
|
2653
3119
|
|
|
2654
3120
|
/**
|
|
@@ -2672,9 +3138,17 @@ const TOP_LEVEL_CONSUMERS = {
|
|
|
2672
3138
|
/**
|
|
2673
3139
|
* @typedef {object} CssProcessOptions
|
|
2674
3140
|
* @property {LocConverter=} locConverter shared loc converter (default a fresh one over the input)
|
|
2675
|
-
* @property {((input: string, start: number, end: number) => number)=} comment comment-token callback
|
|
2676
3141
|
* @property {boolean=} recurseBlocks walk into block bodies' nested rules (default true)
|
|
2677
3142
|
* @property {("stylesheet" | "block-contents")=} as which top-level production to consume the source as (see `TOP_LEVEL_CONSUMERS`): `"stylesheet"` (default) or `"block-contents"` (a block's contents, e.g. an HTML `style` attribute)
|
|
3143
|
+
* @property {SkipOptions=} skip what the grammar may leave un-materialized to go faster — safe only for parts nothing reads in the active parse; default skip nothing
|
|
3144
|
+
*/
|
|
3145
|
+
|
|
3146
|
+
/**
|
|
3147
|
+
* `CssProcessOptions.skip`: two independent axes, so each reads unambiguously.
|
|
3148
|
+
* @typedef {object} SkipOptions
|
|
3149
|
+
* @property {Uint8Array=} types component-value node types to drop from declaration value / function-arg lists (indexed by `NodeType`, 1 = skip; build with `buildSkipSet`)
|
|
3150
|
+
* @property {boolean=} selectorPrelude drop qualified-rule (selector) preludes — the rule and its block are still produced (default false)
|
|
3151
|
+
* @property {boolean=} atRulePrelude drop at-rule preludes — the at-rule and its block are still produced (default false)
|
|
2678
3152
|
*/
|
|
2679
3153
|
|
|
2680
3154
|
/**
|
|
@@ -2688,17 +3162,36 @@ const TOP_LEVEL_CONSUMERS = {
|
|
|
2688
3162
|
* @param {CssProcessOptions} options process options
|
|
2689
3163
|
*/
|
|
2690
3164
|
const grammar = (input, visitors, options) => {
|
|
2691
|
-
const { comment } = options;
|
|
2692
3165
|
const locConverter = options.locConverter || new LocConverter(input);
|
|
3166
|
+
useSoaBackend(input, locConverter);
|
|
3167
|
+
const skip = options.skip;
|
|
3168
|
+
_skipTypes = (skip && skip.types) || _NO_SKIP_TYPES;
|
|
3169
|
+
_skipActive = _skipTypes !== _NO_SKIP_TYPES;
|
|
3170
|
+
_skipSelectorPrelude = skip !== undefined && skip.selectorPrelude === true;
|
|
3171
|
+
_skipAtRulePrelude = skip !== undefined && skip.atRulePrelude === true;
|
|
2693
3172
|
const recurseBlocks = options.recurseBlocks !== false;
|
|
2694
3173
|
|
|
2695
|
-
//
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
3174
|
+
// Comments reach the visitor map through `NodeType.Comment` instead of a
|
|
3175
|
+
// side callback. They fire during tokenization — in source order among
|
|
3176
|
+
// comments, not interleaved with the node walk — on a transient SoA node so
|
|
3177
|
+
// `A.start`/`end`/`loc`/`source` work. No comment visitor → no callback →
|
|
3178
|
+
// the tokenizer skips comments with zero overhead.
|
|
3179
|
+
const commentBucket = visitors[T_COMMENT];
|
|
3180
|
+
const onComment =
|
|
3181
|
+
commentBucket === undefined
|
|
3182
|
+
? undefined
|
|
3183
|
+
: /** @type {(input: string, start: number, end: number) => number} */ (
|
|
3184
|
+
(_input, start, end) => {
|
|
3185
|
+
const node = _soaAlloc(T_COMMENT, start, end);
|
|
3186
|
+
_curNode = node;
|
|
3187
|
+
_curParent = null;
|
|
3188
|
+
const e = commentBucket.enter;
|
|
3189
|
+
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3190
|
+
const x = commentBucket.exit;
|
|
3191
|
+
for (let i = 0; i < x.length; i++) x[i](A);
|
|
3192
|
+
return end;
|
|
3193
|
+
}
|
|
3194
|
+
);
|
|
2702
3195
|
|
|
2703
3196
|
/**
|
|
2704
3197
|
* Walk a component-value subtree; children are already materialized. Fetches
|
|
@@ -2708,29 +3201,28 @@ const grammar = (input, visitors, options) => {
|
|
|
2708
3201
|
* @param {Node | null} parent enclosing node
|
|
2709
3202
|
*/
|
|
2710
3203
|
const walkValue = (node, parent) => {
|
|
2711
|
-
const
|
|
3204
|
+
const ty = _soaTy[_ix(node)];
|
|
3205
|
+
const b = visitors[ty];
|
|
2712
3206
|
let skip = false;
|
|
2713
3207
|
if (b !== undefined && b.enter.length !== 0) {
|
|
2714
|
-
|
|
3208
|
+
_walkSkip = false;
|
|
3209
|
+
_curNode = node;
|
|
3210
|
+
_curParent = parent;
|
|
2715
3211
|
const e = b.enter;
|
|
2716
|
-
for (let i = 0; i < e.length; i++) e[i](
|
|
2717
|
-
skip =
|
|
2718
|
-
|
|
3212
|
+
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3213
|
+
skip = _walkSkip;
|
|
3214
|
+
_walkSkip = false;
|
|
2719
3215
|
}
|
|
2720
|
-
if (!skip) {
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
case T_SIMPLE_BLOCK: {
|
|
2724
|
-
const v = /** @type {FunctionNode | SimpleBlock} */ (node).value;
|
|
2725
|
-
for (let i = 0; i < v.length; i++) walkValue(v[i], node);
|
|
2726
|
-
break;
|
|
2727
|
-
}
|
|
2728
|
-
// All other types are leaf tokens.
|
|
2729
|
-
}
|
|
3216
|
+
if (!skip && (ty === T_FUNCTION || ty === T_SIMPLE_BLOCK)) {
|
|
3217
|
+
const v = /** @type {Node[]} */ (_soaL0[_ix(node)]);
|
|
3218
|
+
for (let i = 0; i < v.length; i++) walkValue(v[i], node);
|
|
2730
3219
|
}
|
|
2731
3220
|
if (b !== undefined) {
|
|
3221
|
+
// Rebind: descending into children moved the path.
|
|
3222
|
+
_curNode = node;
|
|
3223
|
+
_curParent = parent;
|
|
2732
3224
|
const x = b.exit;
|
|
2733
|
-
for (let i = 0; i < x.length; i++) x[i](
|
|
3225
|
+
for (let i = 0; i < x.length; i++) x[i](A);
|
|
2734
3226
|
}
|
|
2735
3227
|
};
|
|
2736
3228
|
|
|
@@ -2741,54 +3233,67 @@ const grammar = (input, visitors, options) => {
|
|
|
2741
3233
|
* @param {Node | null} parent enclosing node
|
|
2742
3234
|
*/
|
|
2743
3235
|
const walkRule = (node, parent) => {
|
|
2744
|
-
const
|
|
3236
|
+
const i0 = _ix(node);
|
|
3237
|
+
const ty = _soaTy[i0];
|
|
3238
|
+
const b = visitors[ty];
|
|
2745
3239
|
let skip = false;
|
|
2746
3240
|
if (b !== undefined && b.enter.length !== 0) {
|
|
2747
|
-
|
|
3241
|
+
_walkSkip = false;
|
|
3242
|
+
_curNode = node;
|
|
3243
|
+
_curParent = parent;
|
|
2748
3244
|
const e = b.enter;
|
|
2749
|
-
for (let i = 0; i < e.length; i++) e[i](
|
|
2750
|
-
skip =
|
|
2751
|
-
|
|
3245
|
+
for (let i = 0; i < e.length; i++) e[i](A);
|
|
3246
|
+
skip = _walkSkip;
|
|
3247
|
+
_walkSkip = false;
|
|
2752
3248
|
}
|
|
2753
3249
|
if (!skip) {
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
if (
|
|
2761
|
-
|
|
2762
|
-
const decls = r.declarations;
|
|
2763
|
-
if (decls) {
|
|
2764
|
-
for (let i = 0; i < decls.length; i++) walkRule(decls[i], node);
|
|
2765
|
-
}
|
|
2766
|
-
const ch = r.childRules;
|
|
2767
|
-
if (ch) for (let i = 0; i < ch.length; i++) walkRule(ch[i], node);
|
|
3250
|
+
if (ty === T_AT_RULE || ty === T_QUALIFIED_RULE) {
|
|
3251
|
+
const p = /** @type {Node[]} */ (_soaL0[i0]);
|
|
3252
|
+
for (let i = 0; i < p.length; i++) walkValue(p[i], node);
|
|
3253
|
+
if (recurseBlocks) {
|
|
3254
|
+
// Declarations then child rules — downstream consumers don't need them strictly interleaved in source order.
|
|
3255
|
+
const decls = _soaL1[i0];
|
|
3256
|
+
if (decls) {
|
|
3257
|
+
for (let i = 0; i < decls.length; i++) walkRule(decls[i], node);
|
|
2768
3258
|
}
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
case T_DECLARATION: {
|
|
2772
|
-
const v = /** @type {Declaration} */ (node).value;
|
|
2773
|
-
for (let i = 0; i < v.length; i++) walkValue(v[i], node);
|
|
2774
|
-
break;
|
|
3259
|
+
const ch = _soaL2[i0];
|
|
3260
|
+
if (ch) for (let i = 0; i < ch.length; i++) walkRule(ch[i], node);
|
|
2775
3261
|
}
|
|
3262
|
+
} else if (ty === T_DECLARATION) {
|
|
3263
|
+
const v = /** @type {Node[]} */ (_soaL0[i0]);
|
|
3264
|
+
for (let i = 0; i < v.length; i++) walkValue(v[i], node);
|
|
2776
3265
|
}
|
|
2777
3266
|
}
|
|
2778
3267
|
if (b !== undefined) {
|
|
3268
|
+
// Rebind: descending into children moved the path.
|
|
3269
|
+
_curNode = node;
|
|
3270
|
+
_curParent = parent;
|
|
2779
3271
|
const x = b.exit;
|
|
2780
|
-
for (let i = 0; i < x.length; i++) x[i](
|
|
3272
|
+
for (let i = 0; i < x.length; i++) x[i](A);
|
|
2781
3273
|
}
|
|
2782
3274
|
};
|
|
2783
3275
|
|
|
2784
3276
|
// Stream each top-level node (selected by `as`) to the walker the moment it's
|
|
2785
3277
|
// consumed, rather than collecting them first — so the whole AST is never
|
|
2786
3278
|
// held at once; peak heap is ~one top-level node's subtree.
|
|
2787
|
-
const ts = new TokenStream(input, 0, locConverter,
|
|
3279
|
+
const ts = new TokenStream(input, 0, locConverter, onComment);
|
|
2788
3280
|
const consume =
|
|
2789
3281
|
TOP_LEVEL_CONSUMERS[options.as || "stylesheet"] ||
|
|
2790
3282
|
consumeAStylesheetsContents;
|
|
2791
|
-
|
|
3283
|
+
try {
|
|
3284
|
+
consume(ts, (node) => {
|
|
3285
|
+
walkRule(node, null);
|
|
3286
|
+
_soaN = 0;
|
|
3287
|
+
});
|
|
3288
|
+
} finally {
|
|
3289
|
+
// Drop the module-level SoA references so the last parsed source (and
|
|
3290
|
+
// its LocConverter / child lists) don't stay alive between parses.
|
|
3291
|
+
_soaInput = "";
|
|
3292
|
+
_soaLC = /** @type {LocConverter} */ (/** @type {unknown} */ (null));
|
|
3293
|
+
_soaL0.length = 0;
|
|
3294
|
+
_soaL1.length = 0;
|
|
3295
|
+
_soaL2.length = 0;
|
|
3296
|
+
}
|
|
2792
3297
|
};
|
|
2793
3298
|
|
|
2794
3299
|
/**
|
|
@@ -2796,23 +3301,303 @@ const grammar = (input, visitors, options) => {
|
|
|
2796
3301
|
* `grammar`. Babel-style usage:
|
|
2797
3302
|
*
|
|
2798
3303
|
* ```
|
|
2799
|
-
*
|
|
2800
|
-
* processor.process(source);
|
|
3304
|
+
* new SourceProcessor({ skip }).use({ [NodeType.AtRule]: (path) => {} }).process(source);
|
|
2801
3305
|
* ```
|
|
2802
|
-
* @extends {GenericSourceProcessor<
|
|
3306
|
+
* @extends {GenericSourceProcessor<CssPath, CssProcessOptions>}
|
|
2803
3307
|
*/
|
|
2804
3308
|
class SourceProcessor extends GenericSourceProcessor {
|
|
2805
|
-
|
|
2806
|
-
|
|
3309
|
+
/**
|
|
3310
|
+
* @param {CssProcessOptions=} options default process options (`skip`, `as`, …) for every `process` call
|
|
3311
|
+
*/
|
|
3312
|
+
constructor(options) {
|
|
3313
|
+
super(grammar, options);
|
|
2807
3314
|
}
|
|
2808
3315
|
}
|
|
2809
3316
|
|
|
3317
|
+
/**
|
|
3318
|
+
* Build a `SkipOptions.types` set (drop these component-value node types from
|
|
3319
|
+
* value / function-arg lists) from a list of `NodeType`s. Preludes are separate
|
|
3320
|
+
* (`SkipOptions.selectorPrelude` / `atRulePrelude`). The caller owns the safety
|
|
3321
|
+
* contract: only pass types nothing reads in the intended parse. Two
|
|
3322
|
+
* grammar-internal caveats beyond consumer needs: dropping both `Delim` and
|
|
3323
|
+
* `Ident` loses `!important` detection, and dropping `SimpleBlock` loses the
|
|
3324
|
+
* custom-property `{}`-value check (and its subtree). Precompute once per
|
|
3325
|
+
* configuration and reuse across parses.
|
|
3326
|
+
* @param {number[]} nodeTypes component-value node types to drop
|
|
3327
|
+
* @returns {Uint8Array} skip-types set indexed by `NodeType`
|
|
3328
|
+
*/
|
|
3329
|
+
const buildSkipSet = (nodeTypes) => {
|
|
3330
|
+
const set = new Uint8Array(32);
|
|
3331
|
+
for (let i = 0; i < nodeTypes.length; i++) set[nodeTypes[i]] = 1;
|
|
3332
|
+
return set;
|
|
3333
|
+
};
|
|
3334
|
+
|
|
3335
|
+
/* eslint-disable jsdoc/require-template -- `A` below is the accessor const, not a type parameter */
|
|
3336
|
+
/**
|
|
3337
|
+
* The CSS path (Babel's `path` shape): the AST accessor with the walk's
|
|
3338
|
+
* current position on it — the single argument every visitor receives.
|
|
3339
|
+
* @typedef {typeof A} CssPath
|
|
3340
|
+
*/
|
|
3341
|
+
/* eslint-enable jsdoc/require-template */
|
|
3342
|
+
|
|
3343
|
+
// Babel's `path.skip()`, children-only: set by `A.skipChildren()` during an
|
|
3344
|
+
// `enter` dispatch, consumed by the walk.
|
|
3345
|
+
let _walkSkip = false;
|
|
3346
|
+
// The walk's current position (`A.node` / `A.parent` read these; module-level
|
|
3347
|
+
// so the accessor methods' defaults avoid self-referential `this` typing).
|
|
3348
|
+
/** @type {Node} */
|
|
3349
|
+
let _curNode = /** @type {Node} */ (/** @type {unknown} */ (0));
|
|
3350
|
+
/** @type {Node | null} */
|
|
3351
|
+
let _curParent = null;
|
|
3352
|
+
|
|
3353
|
+
// AST field-access seam. Every AST-node field read by `CssParser` goes through
|
|
3354
|
+
// one of these accessors so the node representation can change underneath the
|
|
3355
|
+
// consumer without touching it. Today they are backed by the `Node` / `Token` /
|
|
3356
|
+
// `Container` objects (`n` is a node); the Struct-of-Arrays migration rewrites
|
|
3357
|
+
// the bodies to index typed arrays (`n` becomes an integer node id) without any
|
|
3358
|
+
// consumer edit. `value` is the leaf-token string; container child lists are
|
|
3359
|
+
// `children` / `prelude` / `declarations` / `childRules`.
|
|
3360
|
+
const A = {
|
|
3361
|
+
// === path position (rebound by the walk before every visitor call) ===
|
|
3362
|
+
/**
|
|
3363
|
+
* @returns {Node} current node — only valid during a visitor callback
|
|
3364
|
+
*/
|
|
3365
|
+
get node() {
|
|
3366
|
+
return _curNode;
|
|
3367
|
+
},
|
|
3368
|
+
/**
|
|
3369
|
+
* @returns {Node | null} enclosing node (null = a top-level node)
|
|
3370
|
+
*/
|
|
3371
|
+
get parent() {
|
|
3372
|
+
return _curParent;
|
|
3373
|
+
},
|
|
3374
|
+
/** Stop the walk descending into the current node (enter only). */
|
|
3375
|
+
skipChildren() {
|
|
3376
|
+
_walkSkip = true;
|
|
3377
|
+
},
|
|
3378
|
+
// === field reads — `n` defaults to the current node ===
|
|
3379
|
+
/**
|
|
3380
|
+
* @param {Node=} n node
|
|
3381
|
+
* @returns {number} node type
|
|
3382
|
+
*/
|
|
3383
|
+
type(n = _curNode) {
|
|
3384
|
+
return _soaTy[_ix(n)];
|
|
3385
|
+
},
|
|
3386
|
+
/**
|
|
3387
|
+
* @param {Node=} n node
|
|
3388
|
+
* @returns {number} start offset
|
|
3389
|
+
*/
|
|
3390
|
+
start(n = _curNode) {
|
|
3391
|
+
return _soaSt[_ix(n)];
|
|
3392
|
+
},
|
|
3393
|
+
/**
|
|
3394
|
+
* @param {Node=} n node
|
|
3395
|
+
* @returns {number} end offset
|
|
3396
|
+
*/
|
|
3397
|
+
end(n = _curNode) {
|
|
3398
|
+
return _soaEn[_ix(n)];
|
|
3399
|
+
},
|
|
3400
|
+
/**
|
|
3401
|
+
* @param {Node=} n node
|
|
3402
|
+
* @returns {[number, number]} start / end offsets
|
|
3403
|
+
*/
|
|
3404
|
+
range(n = _curNode) {
|
|
3405
|
+
const i = _ix(n);
|
|
3406
|
+
return [_soaSt[i], _soaEn[i]];
|
|
3407
|
+
},
|
|
3408
|
+
/**
|
|
3409
|
+
* @param {Node=} n node
|
|
3410
|
+
* @returns {{ start: { line: number, column: number }, end: { line: number, column: number } }} source location
|
|
3411
|
+
*/
|
|
3412
|
+
loc(n = _curNode) {
|
|
3413
|
+
const i = _ix(n);
|
|
3414
|
+
const lc = _soaLC;
|
|
3415
|
+
const s = lc.get(_soaSt[i]);
|
|
3416
|
+
const sl = s.line;
|
|
3417
|
+
const sc = s.column;
|
|
3418
|
+
const e = lc.get(_soaEn[i]);
|
|
3419
|
+
return {
|
|
3420
|
+
start: { line: sl, column: sc },
|
|
3421
|
+
end: { line: e.line, column: e.column }
|
|
3422
|
+
};
|
|
3423
|
+
},
|
|
3424
|
+
/**
|
|
3425
|
+
* @param {Node=} n node
|
|
3426
|
+
* @returns {string} raw source slice
|
|
3427
|
+
*/
|
|
3428
|
+
source(n = _curNode) {
|
|
3429
|
+
const i = _ix(n);
|
|
3430
|
+
return _soaInput.slice(_soaSt[i], _soaEn[i]);
|
|
3431
|
+
},
|
|
3432
|
+
/**
|
|
3433
|
+
* @param {Node=} n node
|
|
3434
|
+
* @returns {string} raw token value
|
|
3435
|
+
*/
|
|
3436
|
+
value(n = _curNode) {
|
|
3437
|
+
return _soaValueOf(_ix(n));
|
|
3438
|
+
},
|
|
3439
|
+
/**
|
|
3440
|
+
* @param {Node=} n node
|
|
3441
|
+
* @returns {string} unescaped token value
|
|
3442
|
+
*/
|
|
3443
|
+
unescaped(n = _curNode) {
|
|
3444
|
+
const i = _ix(n);
|
|
3445
|
+
const v = _soaValueOf(i);
|
|
3446
|
+
return _soaTy[i] === T_STRING
|
|
3447
|
+
? unescapeIdentifier(v.slice(1, -1))
|
|
3448
|
+
: unescapeIdentifier(v);
|
|
3449
|
+
},
|
|
3450
|
+
/**
|
|
3451
|
+
* @param {Node=} n node
|
|
3452
|
+
* @returns {string} hash / numeric type flag
|
|
3453
|
+
*/
|
|
3454
|
+
typeFlag(n = _curNode) {
|
|
3455
|
+
const i = _ix(n);
|
|
3456
|
+
if (_soaTy[i] === T_HASH) {
|
|
3457
|
+
const input = _soaInput;
|
|
3458
|
+
const p = _soaSt[i] + 1;
|
|
3459
|
+
return _ifThreeCodePointsWouldStartAnIdentSequence(
|
|
3460
|
+
input,
|
|
3461
|
+
p,
|
|
3462
|
+
input.charCodeAt(p),
|
|
3463
|
+
input.charCodeAt(p + 1),
|
|
3464
|
+
input.charCodeAt(p + 2)
|
|
3465
|
+
)
|
|
3466
|
+
? "id"
|
|
3467
|
+
: "unrestricted";
|
|
3468
|
+
}
|
|
3469
|
+
const v = _soaValueOf(i);
|
|
3470
|
+
return _typeFlagOf(
|
|
3471
|
+
_soaTy[i] === T_DIMENSION ? v.slice(0, _consumeANumber(v, 0)) : v
|
|
3472
|
+
);
|
|
3473
|
+
},
|
|
3474
|
+
/**
|
|
3475
|
+
* @param {Node=} n node
|
|
3476
|
+
* @returns {number} url content start offset
|
|
3477
|
+
*/
|
|
3478
|
+
contentStart(n = _curNode) {
|
|
3479
|
+
return _soaA0[_ix(n)];
|
|
3480
|
+
},
|
|
3481
|
+
/**
|
|
3482
|
+
* @param {Node=} n node
|
|
3483
|
+
* @returns {number} url content end offset
|
|
3484
|
+
*/
|
|
3485
|
+
contentEnd(n = _curNode) {
|
|
3486
|
+
return _soaA1[_ix(n)];
|
|
3487
|
+
},
|
|
3488
|
+
/**
|
|
3489
|
+
* @param {Node=} n node
|
|
3490
|
+
* @returns {string} rule / declaration / function name
|
|
3491
|
+
*/
|
|
3492
|
+
name(n = _curNode) {
|
|
3493
|
+
const i = _ix(n);
|
|
3494
|
+
return _soaTy[i] === T_AT_RULE
|
|
3495
|
+
? _soaInput.slice(_soaSt[i] + 1, _soaA0[i])
|
|
3496
|
+
: _soaInput.slice(_soaSt[i], _soaA0[i]);
|
|
3497
|
+
},
|
|
3498
|
+
/**
|
|
3499
|
+
* @param {Node=} n node
|
|
3500
|
+
* @returns {number} name start offset
|
|
3501
|
+
*/
|
|
3502
|
+
nameStart(n = _curNode) {
|
|
3503
|
+
return _soaSt[_ix(n)];
|
|
3504
|
+
},
|
|
3505
|
+
/**
|
|
3506
|
+
* @param {Node=} n node
|
|
3507
|
+
* @returns {number} name end offset
|
|
3508
|
+
*/
|
|
3509
|
+
nameEnd(n = _curNode) {
|
|
3510
|
+
return _soaA0[_ix(n)];
|
|
3511
|
+
},
|
|
3512
|
+
/**
|
|
3513
|
+
* @param {Node=} n node
|
|
3514
|
+
* @returns {string} unescaped name
|
|
3515
|
+
*/
|
|
3516
|
+
unescapedName(n = _curNode) {
|
|
3517
|
+
return unescapeIdentifier(A.name(n));
|
|
3518
|
+
},
|
|
3519
|
+
/**
|
|
3520
|
+
* @param {Node=} n node
|
|
3521
|
+
* @returns {ComponentValue[]} function / block children
|
|
3522
|
+
*/
|
|
3523
|
+
children(n = _curNode) {
|
|
3524
|
+
return /** @type {ComponentValue[]} */ (_soaL0[_ix(n)]);
|
|
3525
|
+
},
|
|
3526
|
+
/**
|
|
3527
|
+
* @param {Node=} n node
|
|
3528
|
+
* @returns {ComponentValue[]} rule prelude
|
|
3529
|
+
*/
|
|
3530
|
+
prelude(n = _curNode) {
|
|
3531
|
+
return /** @type {ComponentValue[]} */ (_soaL0[_ix(n)]);
|
|
3532
|
+
},
|
|
3533
|
+
/**
|
|
3534
|
+
* @param {Node=} n node
|
|
3535
|
+
* @returns {Declaration[] | null} block declarations
|
|
3536
|
+
*/
|
|
3537
|
+
declarations(n = _curNode) {
|
|
3538
|
+
return /** @type {Declaration[] | null} */ (_soaL1[_ix(n)]);
|
|
3539
|
+
},
|
|
3540
|
+
/**
|
|
3541
|
+
* @param {Node=} n node
|
|
3542
|
+
* @returns {Rule[] | null} block child rules
|
|
3543
|
+
*/
|
|
3544
|
+
childRules(n = _curNode) {
|
|
3545
|
+
return /** @type {Rule[] | null} */ (_soaL2[_ix(n)]);
|
|
3546
|
+
},
|
|
3547
|
+
/**
|
|
3548
|
+
* @param {Node=} n node
|
|
3549
|
+
* @returns {number} block start offset
|
|
3550
|
+
*/
|
|
3551
|
+
blockStart(n = _curNode) {
|
|
3552
|
+
return _soaA1[_ix(n)];
|
|
3553
|
+
},
|
|
3554
|
+
/**
|
|
3555
|
+
* @param {Node=} n node
|
|
3556
|
+
* @returns {number} block end offset
|
|
3557
|
+
*/
|
|
3558
|
+
blockEnd(n = _curNode) {
|
|
3559
|
+
return _soaA2[_ix(n)];
|
|
3560
|
+
},
|
|
3561
|
+
/**
|
|
3562
|
+
* @param {Node=} n node
|
|
3563
|
+
* @returns {boolean} `!important` flag
|
|
3564
|
+
*/
|
|
3565
|
+
important(n = _curNode) {
|
|
3566
|
+
return (_soaFl[_ix(n)] & 1) !== 0;
|
|
3567
|
+
},
|
|
3568
|
+
/**
|
|
3569
|
+
* @param {Node=} n node
|
|
3570
|
+
* @returns {SimpleBlockToken} block opening token
|
|
3571
|
+
*/
|
|
3572
|
+
blockToken(n = _curNode) {
|
|
3573
|
+
return /** @type {SimpleBlockToken} */ (_soaInput[_soaSt[_ix(n)]]);
|
|
3574
|
+
},
|
|
3575
|
+
// Writers — `CssParser` rewrites a rule's end / block-end when it folds an
|
|
3576
|
+
// inline ICSS `:import` / `:export` body into a single dependency. The
|
|
3577
|
+
// node stays explicit here: writes should never be implicit on position.
|
|
3578
|
+
/**
|
|
3579
|
+
* @param {Node} n node
|
|
3580
|
+
* @param {number} v new end offset
|
|
3581
|
+
*/
|
|
3582
|
+
setEnd(n, v) {
|
|
3583
|
+
_soaEn[_ix(n)] = v;
|
|
3584
|
+
},
|
|
3585
|
+
/**
|
|
3586
|
+
* @param {Node} n node
|
|
3587
|
+
* @param {number} v new block end offset
|
|
3588
|
+
*/
|
|
3589
|
+
setBlockEnd(n, v) {
|
|
3590
|
+
_soaA2[_ix(n)] = v;
|
|
3591
|
+
}
|
|
3592
|
+
};
|
|
3593
|
+
|
|
2810
3594
|
// The two AST runtime classes — `Node` and its sole subclass `Token` (the
|
|
2811
3595
|
// other node shapes are `@typedef`s over `Node`, exported as types only). Plus
|
|
2812
3596
|
// the full CSS-Syntax-3 §5.3 `parseA*` entry-point surface, `consumeASimpleBlock`
|
|
2813
3597
|
// (the one §5.4 algorithm exposed as a byte entry point for `CssParser`), the
|
|
2814
3598
|
// `TokenStream` (so callers can pass a pre-built stream to any `parseA*`), and
|
|
2815
3599
|
// the `escape` / `unescapeIdentifier` string utils.
|
|
3600
|
+
module.exports.A = A;
|
|
2816
3601
|
module.exports.Node = Node;
|
|
2817
3602
|
module.exports.NodeType = NodeType;
|
|
2818
3603
|
module.exports.SourceProcessor = SourceProcessor;
|
|
@@ -2844,6 +3629,7 @@ module.exports.TT_URL = TT_URL;
|
|
|
2844
3629
|
module.exports.TT_WHITESPACE = TT_WHITESPACE;
|
|
2845
3630
|
module.exports.Token = Token;
|
|
2846
3631
|
module.exports.TokenStream = TokenStream;
|
|
3632
|
+
module.exports.buildSkipSet = buildSkipSet;
|
|
2847
3633
|
module.exports.equalsLowerCase = equalsLowerCase;
|
|
2848
3634
|
module.exports.escapeIdentifier = escapeIdentifier;
|
|
2849
3635
|
module.exports.parseABlocksContents = parseABlocksContents;
|