@ismail-elkorchi/css-parser 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +67 -69
  2. package/dist/internal/cssom/declarations.d.ts +36 -0
  3. package/dist/internal/cssom/declarations.js +177 -0
  4. package/dist/internal/generated/css-data.d.ts +2 -0
  5. package/dist/internal/generated/css-data.js +16229 -0
  6. package/dist/internal/grammar/catalog-types.d.ts +26 -0
  7. package/dist/internal/grammar/value-definition.d.ts +63 -0
  8. package/dist/internal/grammar/value-definition.js +439 -0
  9. package/dist/internal/properties/matcher.d.ts +29 -0
  10. package/dist/internal/properties/matcher.js +791 -0
  11. package/dist/internal/properties/registry.d.ts +24 -0
  12. package/dist/internal/properties/registry.js +52 -0
  13. package/dist/internal/selectors/matcher.d.ts +96 -0
  14. package/dist/internal/selectors/matcher.js +616 -0
  15. package/dist/internal/selectors/parser.d.ts +2 -0
  16. package/dist/internal/selectors/parser.js +702 -0
  17. package/dist/internal/selectors/specificity.d.ts +3 -0
  18. package/dist/internal/selectors/specificity.js +77 -0
  19. package/dist/internal/selectors/types.d.ts +110 -0
  20. package/dist/internal/syntax/ast.d.ts +70 -0
  21. package/dist/internal/syntax/ast.js +1 -0
  22. package/dist/internal/syntax/characters.d.ts +8 -0
  23. package/dist/internal/syntax/characters.js +45 -0
  24. package/dist/internal/syntax/encoding.d.ts +15 -0
  25. package/dist/internal/syntax/encoding.js +161 -0
  26. package/dist/internal/syntax/input.d.ts +23 -0
  27. package/dist/internal/syntax/input.js +184 -0
  28. package/dist/internal/syntax/parser.d.ts +26 -0
  29. package/dist/internal/syntax/parser.js +581 -0
  30. package/dist/internal/syntax/resources.d.ts +28 -0
  31. package/dist/internal/syntax/resources.js +135 -0
  32. package/dist/internal/syntax/serialize.d.ts +10 -0
  33. package/dist/internal/syntax/serialize.js +630 -0
  34. package/dist/internal/syntax/token-stream.d.ts +16 -0
  35. package/dist/internal/syntax/token-stream.js +66 -0
  36. package/dist/internal/syntax/tokenizer.d.ts +21 -0
  37. package/dist/internal/syntax/tokenizer.js +571 -0
  38. package/dist/internal/syntax/tokens.d.ts +115 -0
  39. package/dist/internal/syntax/tokens.js +1 -0
  40. package/dist/internal/syntax/types.d.ts +46 -0
  41. package/dist/internal/syntax/types.js +1 -0
  42. package/dist/mod.d.ts +7 -1
  43. package/dist/mod.js +7 -1
  44. package/dist/public/edits.d.ts +12 -0
  45. package/dist/public/edits.js +195 -0
  46. package/dist/public/mod.d.ts +13 -35
  47. package/dist/public/mod.js +12 -1740
  48. package/dist/public/parse.d.ts +37 -0
  49. package/dist/public/parse.js +298 -0
  50. package/dist/public/traversal.d.ts +13 -0
  51. package/dist/public/traversal.js +96 -0
  52. package/dist/public/types.d.ts +78 -264
  53. package/package.json +32 -53
  54. package/THIRD_PARTY_NOTICES.md +0 -19
  55. package/dist/internal/csstree-runtime.d.ts +0 -20
  56. package/dist/internal/csstree-runtime.js +0 -21
  57. package/dist/internal/encoding/mod.d.ts +0 -1
  58. package/dist/internal/encoding/mod.js +0 -1
  59. package/dist/internal/encoding/sniff.d.ts +0 -14
  60. package/dist/internal/encoding/sniff.js +0 -95
  61. package/dist/internal/serializer/mod.d.ts +0 -1
  62. package/dist/internal/serializer/mod.js +0 -1
  63. package/dist/internal/serializer/serialize.d.ts +0 -3
  64. package/dist/internal/serializer/serialize.js +0 -89
  65. package/dist/internal/tokenizer/mod.d.ts +0 -2
  66. package/dist/internal/tokenizer/mod.js +0 -1
  67. package/dist/internal/tokenizer/tokenize.d.ts +0 -2
  68. package/dist/internal/tokenizer/tokenize.js +0 -39
  69. package/dist/internal/tokenizer/tokens.d.ts +0 -23
  70. package/dist/internal/tree/build.d.ts +0 -2
  71. package/dist/internal/tree/build.js +0 -85
  72. package/dist/internal/tree/mod.d.ts +0 -2
  73. package/dist/internal/tree/mod.js +0 -1
  74. package/dist/internal/tree/types.d.ts +0 -25
  75. package/dist/internal/vendor/csstree/LICENSE +0 -19
  76. package/dist/internal/vendor/csstree/csstree.esm.js +0 -12
  77. package/dist/internal/version.d.ts +0 -1
  78. package/dist/internal/version.js +0 -1
  79. package/dist/public/index.d.ts +0 -1
  80. package/dist/public/index.js +0 -1
  81. /package/dist/internal/{tokenizer/tokens.js → grammar/catalog-types.js} +0 -0
  82. /package/dist/internal/{tree → selectors}/types.js +0 -0
@@ -0,0 +1,66 @@
1
+ function position(offset, line, column) {
2
+ return Object.freeze({ offset, line, column });
3
+ }
4
+ function eofAt(tokens, explicitEnd) {
5
+ const last = tokens.at(-1);
6
+ const end = explicitEnd ?? last?.span.end ?? position(0, 1, 1);
7
+ return Object.freeze({
8
+ kind: "eof",
9
+ span: Object.freeze({ start: end, end })
10
+ });
11
+ }
12
+ export class TokenStream {
13
+ #tokens;
14
+ #eof;
15
+ #marks = [];
16
+ #index = 0;
17
+ constructor(tokens, end) {
18
+ this.#tokens = tokens;
19
+ this.#eof = eofAt(tokens, end);
20
+ }
21
+ get index() {
22
+ return this.#index;
23
+ }
24
+ get empty() {
25
+ return this.next.kind === "eof";
26
+ }
27
+ get next() {
28
+ return this.#tokens[this.#index] ?? this.#eof;
29
+ }
30
+ consume() {
31
+ const token = this.next;
32
+ if (token.kind !== "eof")
33
+ this.#index += 1;
34
+ return token;
35
+ }
36
+ discard() {
37
+ if (!this.empty)
38
+ this.#index += 1;
39
+ }
40
+ discardWhitespace() {
41
+ while (this.next.kind === "whitespace")
42
+ this.#index += 1;
43
+ }
44
+ mark() {
45
+ this.#marks.push(this.#index);
46
+ }
47
+ restore() {
48
+ const index = this.#marks.pop();
49
+ if (index === undefined)
50
+ throw new Error("no token-stream mark is available to restore");
51
+ this.#index = index;
52
+ }
53
+ discardMark() {
54
+ if (this.#marks.pop() === undefined) {
55
+ throw new Error("no token-stream mark is available to discard");
56
+ }
57
+ }
58
+ spanFrom(startIndex) {
59
+ if (!Number.isSafeInteger(startIndex) || startIndex < 0 || startIndex > this.#tokens.length) {
60
+ throw new RangeError("invalid token-stream start index");
61
+ }
62
+ const start = this.#tokens[startIndex]?.span.start ?? this.#eof.span.start;
63
+ const end = this.#tokens[this.#index - 1]?.span.end ?? start;
64
+ return Object.freeze({ start, end });
65
+ }
66
+ }
@@ -0,0 +1,21 @@
1
+ import { ResourceGuard } from "./resources.js";
2
+ import type { CssToken, TokenizationResult, TokenizerDiagnostic } from "./tokens.js";
3
+ import type { ResourceUsage, TokenizerResourceLimits } from "./types.js";
4
+ export interface TokenizerOptions {
5
+ readonly limits?: TokenizerResourceLimits;
6
+ readonly signal?: AbortSignal;
7
+ }
8
+ interface TokenizerDriverOptions extends TokenizerOptions {
9
+ readonly unicodeRanges?: boolean;
10
+ readonly guard?: ResourceGuard;
11
+ }
12
+ export declare class CssTokenizer {
13
+ #private;
14
+ constructor(input: string, options?: TokenizerDriverOptions);
15
+ get errors(): readonly TokenizerDiagnostic[];
16
+ get usage(): ResourceUsage;
17
+ next(): CssToken;
18
+ tokenize(): TokenizationResult;
19
+ }
20
+ export declare function tokenizeCss(input: string, options?: TokenizerOptions): TokenizationResult;
21
+ export {};
@@ -0,0 +1,571 @@
1
+ import { InputCursor, utf8ByteLength } from "./input.js";
2
+ import { ResourceGuard } from "./resources.js";
3
+ import { isDigit, isHexDigit, isIdent, isIdentStart, isNonPrintable, isWhitespace } from "./characters.js";
4
+ const CSS_SYNTAX = "https://drafts.csswg.org/css-syntax/";
5
+ const REPLACEMENT_CHARACTER = 0xfffd;
6
+ function asciiLower(value) {
7
+ return value.replace(/[A-Z]/gu, (character) => character.toLowerCase());
8
+ }
9
+ function span(start, end) {
10
+ return Object.freeze({
11
+ start: Object.freeze({ ...start }),
12
+ end
13
+ });
14
+ }
15
+ function fixedToken(kind, start, end) {
16
+ return Object.freeze({ kind, span: span(start, end) });
17
+ }
18
+ function validEscape(first, second) {
19
+ return first === 0x5c && second !== null && second !== 0x0a;
20
+ }
21
+ function wouldStartIdent(first, second, third) {
22
+ if (first === 0x2d) {
23
+ return isIdentStart(second) || second === 0x2d || validEscape(second, third);
24
+ }
25
+ if (isIdentStart(first)) {
26
+ return true;
27
+ }
28
+ return validEscape(first, second);
29
+ }
30
+ function wouldStartNumber(first, second, third) {
31
+ if (first === 0x2b || first === 0x2d) {
32
+ return isDigit(second) || (second === 0x2e && isDigit(third));
33
+ }
34
+ if (first === 0x2e) {
35
+ return isDigit(second);
36
+ }
37
+ return isDigit(first);
38
+ }
39
+ function wouldStartUnicodeRange(first, second, third) {
40
+ return ((first === 0x55 || first === 0x75) &&
41
+ second === 0x2b &&
42
+ (isHexDigit(third) || third === 0x3f));
43
+ }
44
+ function hexValue(value) {
45
+ if (value >= 0x30 && value <= 0x39)
46
+ return value - 0x30;
47
+ if (value >= 0x41 && value <= 0x46)
48
+ return value - 0x41 + 10;
49
+ return value - 0x61 + 10;
50
+ }
51
+ function numericValue(representation, numberType) {
52
+ return Object.freeze({
53
+ value: Number(representation),
54
+ numberType,
55
+ representation,
56
+ sign: representation.startsWith("+") ? "+" : representation.startsWith("-") ? "-" : null
57
+ });
58
+ }
59
+ export class CssTokenizer {
60
+ #cursor;
61
+ #guard;
62
+ #unicodeRanges;
63
+ #errors = [];
64
+ #emittedEof = false;
65
+ constructor(input, options = {}) {
66
+ this.#guard = options.guard ?? new ResourceGuard(options.limits, options.signal);
67
+ this.#guard.setInputBytes(utf8ByteLength(input));
68
+ this.#cursor = new InputCursor(input, this.#guard);
69
+ this.#unicodeRanges = options.unicodeRanges ?? false;
70
+ }
71
+ get errors() {
72
+ return this.#errors;
73
+ }
74
+ get usage() {
75
+ return this.#guard.snapshot();
76
+ }
77
+ next() {
78
+ if (this.#emittedEof) {
79
+ const position = this.#cursor.position();
80
+ return fixedToken("eof", position, position);
81
+ }
82
+ this.#consumeComments();
83
+ const start = this.#cursor.mark();
84
+ const current = this.#cursor.consume();
85
+ if (current === null) {
86
+ this.#emittedEof = true;
87
+ return fixedToken("eof", start, this.#cursor.position());
88
+ }
89
+ const value = current.value;
90
+ let token;
91
+ if (isWhitespace(value)) {
92
+ while (isWhitespace(this.#cursor.peek()))
93
+ this.#cursor.consume();
94
+ token = fixedToken("whitespace", start, this.#cursor.position());
95
+ }
96
+ else if (value === 0x22 || value === 0x27) {
97
+ token = this.#consumeString(value, start);
98
+ }
99
+ else if (value === 0x23) {
100
+ token = this.#consumeHash(start);
101
+ }
102
+ else if (value === 0x28) {
103
+ token = fixedToken("open-paren", start, this.#cursor.position());
104
+ }
105
+ else if (value === 0x29) {
106
+ token = fixedToken("close-paren", start, this.#cursor.position());
107
+ }
108
+ else if (value === 0x2b) {
109
+ token = wouldStartNumber(value, this.#cursor.peek(), this.#cursor.peek(1))
110
+ ? this.#consumeNumeric(start, true)
111
+ : this.#delim(value, start);
112
+ }
113
+ else if (value === 0x2c) {
114
+ token = fixedToken("comma", start, this.#cursor.position());
115
+ }
116
+ else if (value === 0x2d) {
117
+ if (wouldStartNumber(value, this.#cursor.peek(), this.#cursor.peek(1))) {
118
+ token = this.#consumeNumeric(start, true);
119
+ }
120
+ else if (this.#cursor.peek() === 0x2d && this.#cursor.peek(1) === 0x3e) {
121
+ this.#cursor.consume();
122
+ this.#cursor.consume();
123
+ token = fixedToken("cdc", start, this.#cursor.position());
124
+ }
125
+ else if (wouldStartIdent(value, this.#cursor.peek(), this.#cursor.peek(1))) {
126
+ this.#cursor.reconsume();
127
+ token = this.#consumeIdentLike(start);
128
+ }
129
+ else {
130
+ token = this.#delim(value, start);
131
+ }
132
+ }
133
+ else if (value === 0x2e) {
134
+ token = wouldStartNumber(value, this.#cursor.peek(), this.#cursor.peek(1))
135
+ ? this.#consumeNumeric(start, true)
136
+ : this.#delim(value, start);
137
+ }
138
+ else if (value === 0x2f) {
139
+ token = this.#delim(value, start);
140
+ }
141
+ else if (value === 0x3a) {
142
+ token = fixedToken("colon", start, this.#cursor.position());
143
+ }
144
+ else if (value === 0x3b) {
145
+ token = fixedToken("semicolon", start, this.#cursor.position());
146
+ }
147
+ else if (value === 0x3c) {
148
+ if (this.#cursor.peek() === 0x21 &&
149
+ this.#cursor.peek(1) === 0x2d &&
150
+ this.#cursor.peek(2) === 0x2d) {
151
+ this.#cursor.consume();
152
+ this.#cursor.consume();
153
+ this.#cursor.consume();
154
+ token = fixedToken("cdo", start, this.#cursor.position());
155
+ }
156
+ else {
157
+ token = this.#delim(value, start);
158
+ }
159
+ }
160
+ else if (value === 0x40) {
161
+ token = this.#consumeAtKeyword(start);
162
+ }
163
+ else if (value === 0x5b) {
164
+ token = fixedToken("open-square", start, this.#cursor.position());
165
+ }
166
+ else if (value === 0x5c) {
167
+ if (validEscape(value, this.#cursor.peek())) {
168
+ this.#cursor.reconsume();
169
+ token = this.#consumeIdentLike(start);
170
+ }
171
+ else {
172
+ this.#diagnostic("invalid-escape", "A reverse solidus followed by a newline does not start an escape.", start, "#consume-token");
173
+ token = this.#delim(value, start);
174
+ }
175
+ }
176
+ else if (value === 0x5d) {
177
+ token = fixedToken("close-square", start, this.#cursor.position());
178
+ }
179
+ else if (value === 0x7b) {
180
+ token = fixedToken("open-curly", start, this.#cursor.position());
181
+ }
182
+ else if (value === 0x7d) {
183
+ token = fixedToken("close-curly", start, this.#cursor.position());
184
+ }
185
+ else if (this.#unicodeRanges &&
186
+ (value === 0x55 || value === 0x75) &&
187
+ wouldStartUnicodeRange(value, this.#cursor.peek(), this.#cursor.peek(1))) {
188
+ token = this.#consumeUnicodeRange(start);
189
+ }
190
+ else if (isDigit(value)) {
191
+ token = this.#consumeNumeric(start, true);
192
+ }
193
+ else if (isIdentStart(value)) {
194
+ this.#cursor.reconsume();
195
+ token = this.#consumeIdentLike(start);
196
+ }
197
+ else {
198
+ token = this.#delim(value, start);
199
+ }
200
+ this.#guard.emitToken();
201
+ return token;
202
+ }
203
+ tokenize() {
204
+ const tokens = [];
205
+ for (;;) {
206
+ const token = this.next();
207
+ if (token.kind === "eof")
208
+ break;
209
+ tokens.push(token);
210
+ }
211
+ return Object.freeze({
212
+ tokens: Object.freeze(tokens),
213
+ errors: Object.freeze([...this.#errors]),
214
+ end: this.#cursor.position(),
215
+ usage: this.#guard.snapshot()
216
+ });
217
+ }
218
+ #consumeComments() {
219
+ for (;;) {
220
+ if (this.#cursor.peek() !== 0x2f || this.#cursor.peek(1) !== 0x2a) {
221
+ return;
222
+ }
223
+ const start = this.#cursor.mark();
224
+ this.#cursor.consume();
225
+ this.#cursor.consume();
226
+ for (;;) {
227
+ const next = this.#cursor.consume();
228
+ if (next === null) {
229
+ this.#diagnostic("unexpected-eof-in-comment", "The input ended before the comment was closed.", start, "#consume-comments");
230
+ return;
231
+ }
232
+ if (next.value === 0x2a && this.#cursor.peek() === 0x2f) {
233
+ this.#cursor.consume();
234
+ break;
235
+ }
236
+ }
237
+ }
238
+ }
239
+ #consumeHash(start) {
240
+ const first = this.#cursor.peek();
241
+ const second = this.#cursor.peek(1);
242
+ const third = this.#cursor.peek(2);
243
+ if (!isIdent(first) && !validEscape(first, second)) {
244
+ return this.#delim(0x23, start);
245
+ }
246
+ const hashType = wouldStartIdent(first, second, third) ? "id" : "unrestricted";
247
+ const value = this.#consumeIdentSequence();
248
+ return Object.freeze({
249
+ kind: "hash",
250
+ value,
251
+ hashType,
252
+ span: span(start, this.#cursor.position())
253
+ });
254
+ }
255
+ #consumeAtKeyword(start) {
256
+ if (!wouldStartIdent(this.#cursor.peek(), this.#cursor.peek(1), this.#cursor.peek(2))) {
257
+ return this.#delim(0x40, start);
258
+ }
259
+ return Object.freeze({
260
+ kind: "at-keyword",
261
+ value: this.#consumeIdentSequence(),
262
+ span: span(start, this.#cursor.position())
263
+ });
264
+ }
265
+ #consumeIdentSequence() {
266
+ let result = "";
267
+ for (;;) {
268
+ const next = this.#cursor.peek();
269
+ if (isIdent(next)) {
270
+ const consumed = this.#cursor.consume();
271
+ if (consumed !== null)
272
+ result += String.fromCodePoint(consumed.value);
273
+ }
274
+ else if (validEscape(next, this.#cursor.peek(1))) {
275
+ this.#cursor.consume();
276
+ result += String.fromCodePoint(this.#consumeEscapedCodePoint());
277
+ }
278
+ else {
279
+ return result;
280
+ }
281
+ }
282
+ }
283
+ #consumeEscapedCodePoint() {
284
+ const start = this.#cursor.mark();
285
+ const next = this.#cursor.consume();
286
+ if (next === null) {
287
+ this.#diagnostic("unexpected-eof-in-escape", "The input ended in an escape sequence.", start, "#consume-escaped-code-point");
288
+ return REPLACEMENT_CHARACTER;
289
+ }
290
+ if (!isHexDigit(next.value)) {
291
+ return next.value;
292
+ }
293
+ let value = hexValue(next.value);
294
+ let digits = 1;
295
+ while (digits < 6 && isHexDigit(this.#cursor.peek())) {
296
+ const digit = this.#cursor.consume();
297
+ if (digit === null)
298
+ break;
299
+ value = (value * 16) + hexValue(digit.value);
300
+ digits += 1;
301
+ }
302
+ if (isWhitespace(this.#cursor.peek()))
303
+ this.#cursor.consume();
304
+ if (value === 0 || value > 0x10ffff || (value >= 0xd800 && value <= 0xdfff)) {
305
+ return REPLACEMENT_CHARACTER;
306
+ }
307
+ return value;
308
+ }
309
+ #consumeIdentLike(start) {
310
+ const value = this.#consumeIdentSequence();
311
+ if (asciiLower(value) === "url" && this.#cursor.peek() === 0x28) {
312
+ this.#cursor.consume();
313
+ while (isWhitespace(this.#cursor.peek()) && isWhitespace(this.#cursor.peek(1))) {
314
+ this.#cursor.consume();
315
+ }
316
+ const next = this.#cursor.peek();
317
+ if (next === 0x22 ||
318
+ next === 0x27 ||
319
+ (isWhitespace(next) && (this.#cursor.peek(1) === 0x22 || this.#cursor.peek(1) === 0x27))) {
320
+ return Object.freeze({
321
+ kind: "function",
322
+ value,
323
+ span: span(start, this.#cursor.position())
324
+ });
325
+ }
326
+ return this.#consumeUrl(start);
327
+ }
328
+ if (this.#cursor.peek() === 0x28) {
329
+ this.#cursor.consume();
330
+ return Object.freeze({
331
+ kind: "function",
332
+ value,
333
+ span: span(start, this.#cursor.position())
334
+ });
335
+ }
336
+ return Object.freeze({
337
+ kind: "ident",
338
+ value,
339
+ span: span(start, this.#cursor.position())
340
+ });
341
+ }
342
+ #consumeString(ending, start) {
343
+ let value = "";
344
+ for (;;) {
345
+ const next = this.#cursor.consume();
346
+ if (next === null) {
347
+ this.#diagnostic("unexpected-eof-in-string", "The input ended before the string was closed.", start, "#consume-string-token");
348
+ return Object.freeze({
349
+ kind: "string",
350
+ value,
351
+ span: span(start, this.#cursor.position())
352
+ });
353
+ }
354
+ if (next.value === ending) {
355
+ return Object.freeze({
356
+ kind: "string",
357
+ value,
358
+ span: span(start, this.#cursor.position())
359
+ });
360
+ }
361
+ if (next.value === 0x0a) {
362
+ this.#diagnostic("newline-in-string", "An unescaped newline ended the string.", next.span.start, "#consume-string-token");
363
+ this.#cursor.reconsume();
364
+ return fixedToken("bad-string", start, this.#cursor.position());
365
+ }
366
+ if (next.value === 0x5c) {
367
+ const following = this.#cursor.peek();
368
+ if (following === null) {
369
+ continue;
370
+ }
371
+ if (following === 0x0a) {
372
+ this.#cursor.consume();
373
+ continue;
374
+ }
375
+ value += String.fromCodePoint(this.#consumeEscapedCodePoint());
376
+ continue;
377
+ }
378
+ value += String.fromCodePoint(next.value);
379
+ }
380
+ }
381
+ #consumeUrl(start) {
382
+ let value = "";
383
+ for (;;) {
384
+ const next = this.#cursor.consume();
385
+ if (next === null) {
386
+ this.#diagnostic("unexpected-eof-in-url", "The input ended before the URL token was closed.", start, "#consume-url-token");
387
+ return Object.freeze({
388
+ kind: "url",
389
+ value,
390
+ span: span(start, this.#cursor.position())
391
+ });
392
+ }
393
+ if (next.value === 0x29) {
394
+ return Object.freeze({
395
+ kind: "url",
396
+ value,
397
+ span: span(start, this.#cursor.position())
398
+ });
399
+ }
400
+ if (isWhitespace(next.value)) {
401
+ while (isWhitespace(this.#cursor.peek()))
402
+ this.#cursor.consume();
403
+ if (this.#cursor.peek() === 0x29) {
404
+ this.#cursor.consume();
405
+ return Object.freeze({
406
+ kind: "url",
407
+ value,
408
+ span: span(start, this.#cursor.position())
409
+ });
410
+ }
411
+ if (this.#cursor.peek() === null) {
412
+ this.#diagnostic("unexpected-eof-in-url", "The input ended before the URL token was closed.", start, "#consume-url-token");
413
+ return Object.freeze({
414
+ kind: "url",
415
+ value,
416
+ span: span(start, this.#cursor.position())
417
+ });
418
+ }
419
+ return this.#badUrl(start, "Whitespace was followed by content instead of a closing parenthesis.");
420
+ }
421
+ if (next.value === 0x22 ||
422
+ next.value === 0x27 ||
423
+ next.value === 0x28 ||
424
+ isNonPrintable(next.value)) {
425
+ return this.#badUrl(start, "The URL token contained a forbidden code point.");
426
+ }
427
+ if (next.value === 0x5c) {
428
+ if (!validEscape(next.value, this.#cursor.peek())) {
429
+ return this.#badUrl(start, "The URL token contained an invalid escape.");
430
+ }
431
+ value += String.fromCodePoint(this.#consumeEscapedCodePoint());
432
+ }
433
+ else {
434
+ value += String.fromCodePoint(next.value);
435
+ }
436
+ }
437
+ }
438
+ #badUrl(start, message) {
439
+ this.#diagnostic("invalid-url", message, start, "#consume-url-token");
440
+ for (;;) {
441
+ const next = this.#cursor.consume();
442
+ if (next === null || next.value === 0x29)
443
+ break;
444
+ if (validEscape(next.value, this.#cursor.peek())) {
445
+ this.#consumeEscapedCodePoint();
446
+ }
447
+ }
448
+ return fixedToken("bad-url", start, this.#cursor.position());
449
+ }
450
+ #consumeNumber() {
451
+ const start = this.#cursor.offset;
452
+ let numberType = "integer";
453
+ if (this.#cursor.peek() === 0x2b || this.#cursor.peek() === 0x2d)
454
+ this.#cursor.consume();
455
+ while (isDigit(this.#cursor.peek()))
456
+ this.#cursor.consume();
457
+ if (this.#cursor.peek() === 0x2e && isDigit(this.#cursor.peek(1))) {
458
+ numberType = "number";
459
+ this.#cursor.consume();
460
+ while (isDigit(this.#cursor.peek()))
461
+ this.#cursor.consume();
462
+ }
463
+ const exponent = this.#cursor.peek();
464
+ const exponentSecond = this.#cursor.peek(1);
465
+ const exponentThird = this.#cursor.peek(2);
466
+ if ((exponent === 0x45 || exponent === 0x65) &&
467
+ (isDigit(exponentSecond) ||
468
+ ((exponentSecond === 0x2b || exponentSecond === 0x2d) && isDigit(exponentThird)))) {
469
+ numberType = "number";
470
+ this.#cursor.consume();
471
+ if (this.#cursor.peek() === 0x2b || this.#cursor.peek() === 0x2d)
472
+ this.#cursor.consume();
473
+ while (isDigit(this.#cursor.peek()))
474
+ this.#cursor.consume();
475
+ }
476
+ return numericValue(this.#cursor.slice(start, this.#cursor.offset), numberType);
477
+ }
478
+ #consumeNumeric(start, reconsume) {
479
+ if (reconsume)
480
+ this.#cursor.reconsume();
481
+ const number = this.#consumeNumber();
482
+ if (wouldStartIdent(this.#cursor.peek(), this.#cursor.peek(1), this.#cursor.peek(2))) {
483
+ return Object.freeze({
484
+ kind: "dimension",
485
+ ...number,
486
+ unit: this.#consumeIdentSequence(),
487
+ span: span(start, this.#cursor.position())
488
+ });
489
+ }
490
+ if (this.#cursor.peek() === 0x25) {
491
+ this.#cursor.consume();
492
+ return Object.freeze({
493
+ kind: "percentage",
494
+ ...number,
495
+ span: span(start, this.#cursor.position())
496
+ });
497
+ }
498
+ return Object.freeze({
499
+ kind: "number",
500
+ ...number,
501
+ span: span(start, this.#cursor.position())
502
+ });
503
+ }
504
+ #consumeUnicodeRange(start) {
505
+ this.#cursor.consume();
506
+ let startValue = 0;
507
+ let digits = 0;
508
+ while (digits < 6 && isHexDigit(this.#cursor.peek())) {
509
+ const next = this.#cursor.consume();
510
+ if (next === null)
511
+ break;
512
+ startValue = (startValue * 16) + hexValue(next.value);
513
+ digits += 1;
514
+ }
515
+ let questionMarks = 0;
516
+ while (digits + questionMarks < 6 && this.#cursor.peek() === 0x3f) {
517
+ this.#cursor.consume();
518
+ questionMarks += 1;
519
+ }
520
+ if (questionMarks > 0) {
521
+ const multiplier = 16 ** questionMarks;
522
+ return Object.freeze({
523
+ kind: "unicode-range",
524
+ start: startValue * multiplier,
525
+ end: (startValue * multiplier) + multiplier - 1,
526
+ span: span(start, this.#cursor.position())
527
+ });
528
+ }
529
+ let endValue = startValue;
530
+ if (this.#cursor.peek() === 0x2d && isHexDigit(this.#cursor.peek(1))) {
531
+ this.#cursor.consume();
532
+ endValue = 0;
533
+ let endDigits = 0;
534
+ while (endDigits < 6 && isHexDigit(this.#cursor.peek())) {
535
+ const next = this.#cursor.consume();
536
+ if (next === null)
537
+ break;
538
+ endValue = (endValue * 16) + hexValue(next.value);
539
+ endDigits += 1;
540
+ }
541
+ }
542
+ return Object.freeze({
543
+ kind: "unicode-range",
544
+ start: startValue,
545
+ end: endValue,
546
+ span: span(start, this.#cursor.position())
547
+ });
548
+ }
549
+ #delim(value, start) {
550
+ return Object.freeze({
551
+ kind: "delim",
552
+ value,
553
+ span: span(start, this.#cursor.position())
554
+ });
555
+ }
556
+ #diagnostic(code, message, start, anchor) {
557
+ this.#errors.push(Object.freeze({
558
+ kind: "tokenizer",
559
+ code,
560
+ message,
561
+ span: span(start, this.#cursor.position()),
562
+ specRef: `${CSS_SYNTAX}${anchor}`
563
+ }));
564
+ }
565
+ }
566
+ export function tokenizeCss(input, options = {}) {
567
+ return new CssTokenizer(input, {
568
+ ...(options.limits === undefined ? {} : { limits: options.limits }),
569
+ ...(options.signal === undefined ? {} : { signal: options.signal })
570
+ }).tokenize();
571
+ }