@navecss/core 0.1.1 → 0.3.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.
@@ -0,0 +1,747 @@
1
+ // src/directive/expand-text-diagnostics.ts
2
+ function lineStartOffsets(text) {
3
+ const starts = [0];
4
+ let i = 0;
5
+ while (i < text.length) {
6
+ const c = text[i];
7
+ if (c === "\r") {
8
+ i++;
9
+ if (text[i] === "\n") i++;
10
+ starts.push(i);
11
+ continue;
12
+ }
13
+ if (c === "\n" || c === "\f") {
14
+ i++;
15
+ starts.push(i);
16
+ continue;
17
+ }
18
+ i++;
19
+ }
20
+ return starts;
21
+ }
22
+ function lastAtOrBefore(starts, offset) {
23
+ let low = 0;
24
+ let high = starts.length - 1;
25
+ while (low < high) {
26
+ const mid = low + high + 1 >> 1;
27
+ if (starts[mid] <= offset) low = mid;
28
+ else high = mid - 1;
29
+ }
30
+ return low;
31
+ }
32
+ function createPositionFinder(text) {
33
+ const starts = lineStartOffsets(text);
34
+ return (offset) => {
35
+ const line = lastAtOrBefore(starts, offset);
36
+ return { line: line + 1, column: offset - starts[line] };
37
+ };
38
+ }
39
+ function reportDiagnostics(diagnostics, css, options) {
40
+ const onUnknown = options.onUnknown ?? "error";
41
+ if (onUnknown === "ignore") return [];
42
+ const severity = onUnknown === "warn" ? "warning" : "error";
43
+ const positionAt = createPositionFinder(css);
44
+ return diagnostics.map((diagnostic) => {
45
+ const position = positionAt(diagnostic.offset);
46
+ return {
47
+ ...diagnostic,
48
+ severity,
49
+ ...options.from !== void 0 && { file: options.from },
50
+ line: position.line,
51
+ column: position.column + 1
52
+ };
53
+ });
54
+ }
55
+
56
+ // src/directive/bracket-match.ts
57
+ var OPENERS = /* @__PURE__ */ new Set(["(-token", "[-token", "function-token", "{-token"]);
58
+ var CLOSERS = /* @__PURE__ */ new Set([")-token", "]-token", "}-token"]);
59
+ function isMirrorCloser(openerType, closerType) {
60
+ if (closerType === ")-token") return openerType === "(-token" || openerType === "function-token";
61
+ if (closerType === "]-token") return openerType === "[-token";
62
+ return openerType === "{-token";
63
+ }
64
+ function matchBrackets(tokens) {
65
+ const closerFor = new Int32Array(tokens.length).fill(-1);
66
+ const opens = [];
67
+ for (const [i, token] of tokens.entries()) {
68
+ const { type } = token;
69
+ if (OPENERS.has(type)) {
70
+ opens.push(i);
71
+ continue;
72
+ }
73
+ if (!CLOSERS.has(type)) continue;
74
+ const openIndex = opens.at(-1);
75
+ if (openIndex === void 0 || !isMirrorCloser(tokens[openIndex].type, type)) continue;
76
+ opens.pop();
77
+ closerFor[openIndex] = i;
78
+ }
79
+ return closerFor;
80
+ }
81
+ function matchedCloseWithin(closerFor, openIndex, limit) {
82
+ const closeIndex = closerFor[openIndex];
83
+ return closeIndex !== -1 && closeIndex < limit ? closeIndex : void 0;
84
+ }
85
+
86
+ // src/directive/block-reader.ts
87
+ function scanItem(tokens, start, limit, closerFor) {
88
+ let i = start;
89
+ while (i < limit) {
90
+ const type = tokens[i].type;
91
+ if (OPENERS.has(type)) {
92
+ const closeIndex = matchedCloseWithin(closerFor, i, limit);
93
+ if (type === "{-token") {
94
+ return {
95
+ end: closeIndex === void 0 ? limit : closeIndex + 1,
96
+ consumedSemicolon: false,
97
+ trailingBlock: { openIndex: i, closeIndex }
98
+ };
99
+ }
100
+ i = closeIndex === void 0 ? limit : closeIndex + 1;
101
+ continue;
102
+ }
103
+ if (CLOSERS.has(type)) {
104
+ if (i === start) return { end: i + 1, consumedSemicolon: false };
105
+ i++;
106
+ continue;
107
+ }
108
+ if (type === "semicolon-token") return { end: i + 1, consumedSemicolon: true };
109
+ i++;
110
+ }
111
+ return { end: i, consumedSemicolon: false };
112
+ }
113
+ function scanCustomPropertyValue(tokens, start, limit, closerFor) {
114
+ let i = start;
115
+ while (i < limit) {
116
+ const type = tokens[i].type;
117
+ if (OPENERS.has(type)) {
118
+ const closeIndex = matchedCloseWithin(closerFor, i, limit);
119
+ i = closeIndex === void 0 ? limit : closeIndex + 1;
120
+ continue;
121
+ }
122
+ if (type === "semicolon-token") return i + 1;
123
+ i++;
124
+ }
125
+ return i;
126
+ }
127
+ function isColonSoonAfter(tokens, i, limit) {
128
+ let j = i;
129
+ while (j < limit && (tokens[j].type === "whitespace-token" || tokens[j].type === "comment")) j++;
130
+ return tokens[j]?.type === "colon-token";
131
+ }
132
+ function isCustomPropertyName(token) {
133
+ return token.type === "ident-token" && (token.structured?.value).startsWith("--");
134
+ }
135
+ function atKeywordName(token) {
136
+ return (token.structured?.value).toLowerCase();
137
+ }
138
+ function toDeclarationItem(start, end) {
139
+ return { kind: "declaration", start, end };
140
+ }
141
+ function preludeEndOffset(tokens, start, scan, preludeStart) {
142
+ if (scan.trailingBlock) return tokens[scan.trailingBlock.openIndex].startIndex;
143
+ const lastContentIndex = scan.consumedSemicolon ? scan.end - 2 : scan.end - 1;
144
+ return lastContentIndex >= start ? tokens[lastContentIndex].endIndex : preludeStart;
145
+ }
146
+ function toAtRuleItem(tokens, start, scan) {
147
+ const nameToken = tokens[start];
148
+ const preludeStart = tokens[start + 1]?.startIndex ?? nameToken.endIndex;
149
+ return {
150
+ kind: "at-rule",
151
+ start,
152
+ end: scan.end,
153
+ atKeyword: atKeywordName(nameToken),
154
+ preludeOffset: preludeStart,
155
+ preludeEndOffset: preludeEndOffset(tokens, start + 1, scan, preludeStart),
156
+ ...scan.trailingBlock && {
157
+ blockStart: scan.trailingBlock.openIndex + 1,
158
+ blockEnd: scan.trailingBlock.closeIndex ?? scan.end
159
+ }
160
+ };
161
+ }
162
+ function toRuleOrInvalidItem(start, scan) {
163
+ if (!scan.trailingBlock) return { kind: "invalid", start, end: scan.end };
164
+ return {
165
+ kind: "rule",
166
+ start,
167
+ end: scan.end,
168
+ blockStart: scan.trailingBlock.openIndex + 1,
169
+ blockEnd: scan.trailingBlock.closeIndex ?? scan.end
170
+ };
171
+ }
172
+ function readItem(tokens, start, limit, closerFor) {
173
+ const first = tokens[start];
174
+ if (first.type === "at-keyword-token") {
175
+ return toAtRuleItem(tokens, start, scanItem(tokens, start + 1, limit, closerFor));
176
+ }
177
+ if (first.type === "ident-token" && isColonSoonAfter(tokens, start + 1, limit)) {
178
+ if (isCustomPropertyName(first)) {
179
+ return toDeclarationItem(start, scanCustomPropertyValue(tokens, start + 1, limit, closerFor));
180
+ }
181
+ const scan = scanItem(tokens, start + 1, limit, closerFor);
182
+ if (!scan.trailingBlock) return toDeclarationItem(start, scan.end);
183
+ return toRuleOrInvalidItem(start, scan);
184
+ }
185
+ return toRuleOrInvalidItem(start, scanItem(tokens, start, limit, closerFor));
186
+ }
187
+ function isNaveAtKeyword(token) {
188
+ return token.type === "at-keyword-token" && atKeywordName(token) === "nave";
189
+ }
190
+
191
+ // src/directive/tokenizer/comment.ts
192
+ function tryConsumeComment(s, start) {
193
+ if (s.peek() !== "/" || s.peek(1) !== "*") return void 0;
194
+ s.advance(2);
195
+ for (; ; ) {
196
+ if (s.eof()) break;
197
+ if (s.peek() === "*" && s.peek(1) === "/") {
198
+ s.advance(2);
199
+ break;
200
+ }
201
+ s.advance();
202
+ }
203
+ return {
204
+ type: "comment",
205
+ raw: s.input.slice(start, s.pos),
206
+ startIndex: start,
207
+ endIndex: s.pos,
208
+ // eslint-disable-next-line unicorn/no-null -- the corpus's format carries null (not undefined) for a comment's structured field
209
+ structured: null
210
+ };
211
+ }
212
+
213
+ // src/directive/tokenizer/scanner.ts
214
+ function isDigit(c) {
215
+ return c !== void 0 && c >= "0" && c <= "9";
216
+ }
217
+ function isHexDigit(c) {
218
+ if (isDigit(c)) return true;
219
+ return c !== void 0 && (c >= "a" && c <= "f" || c >= "A" && c <= "F");
220
+ }
221
+ function isLetter(c) {
222
+ return c !== void 0 && (c >= "a" && c <= "z" || c >= "A" && c <= "Z");
223
+ }
224
+ var NON_ASCII_IDENT_RANGES = [
225
+ [0, 0],
226
+ // NUL, read back as U+FFFD by consumeLiteralCodePoint
227
+ [183, 183],
228
+ [192, 214],
229
+ [216, 246],
230
+ [248, 893],
231
+ [895, 8191],
232
+ [8204, 8205],
233
+ [8255, 8256],
234
+ [8304, 8591],
235
+ [11264, 12271],
236
+ [12289, 55295],
237
+ [55296, 57343],
238
+ // any surrogate code unit, lone or half of a pair
239
+ [63744, 64975],
240
+ [65008, 65533]
241
+ ];
242
+ function isNonAsciiIdentCodePoint(c) {
243
+ if (c === void 0) return false;
244
+ const cp = c.codePointAt(0);
245
+ return NON_ASCII_IDENT_RANGES.some(([low, high]) => cp >= low && cp <= high);
246
+ }
247
+ function isIdentStartCodePoint(c) {
248
+ return c !== void 0 && (isLetter(c) || isNonAsciiIdentCodePoint(c) || c === "_");
249
+ }
250
+ function isIdentCodePoint(c) {
251
+ return c !== void 0 && (isIdentStartCodePoint(c) || isDigit(c) || c === "-");
252
+ }
253
+ var WHITESPACE_CHARS = /* @__PURE__ */ new Set([" ", " ", "\n", "\r", "\f"]);
254
+ function isWhitespace(c) {
255
+ return c !== void 0 && WHITESPACE_CHARS.has(c);
256
+ }
257
+ var NON_PRINTABLE_RANGES = [
258
+ [0, 8],
259
+ [11, 11],
260
+ [14, 31],
261
+ [127, 127]
262
+ ];
263
+ function isNonPrintable(c) {
264
+ if (c === void 0) return false;
265
+ const cp = c.codePointAt(0);
266
+ return NON_PRINTABLE_RANGES.some(([low, high]) => cp >= low && cp <= high);
267
+ }
268
+ var Scanner = class {
269
+ input;
270
+ pos = 0;
271
+ constructor(input) {
272
+ this.input = input;
273
+ }
274
+ advance(count = 1) {
275
+ this.pos += count;
276
+ }
277
+ eof(offset = 0) {
278
+ return this.pos + offset >= this.input.length;
279
+ }
280
+ peek(offset = 0) {
281
+ return this.input[this.pos + offset];
282
+ }
283
+ };
284
+ function isValidEscapeAt(s, offset = 0) {
285
+ if (s.peek(offset) !== "\\") return false;
286
+ const next = s.peek(offset + 1);
287
+ return next !== "\n" && next !== "\r" && next !== "\f";
288
+ }
289
+ function isIdentSequenceStartAt(s, offset = 0) {
290
+ const c0 = s.peek(offset);
291
+ if (c0 === "-") {
292
+ const c1 = s.peek(offset + 1);
293
+ if (c1 === "-" || isIdentStartCodePoint(c1)) return true;
294
+ return isValidEscapeAt(s, offset + 1);
295
+ }
296
+ if (isIdentStartCodePoint(c0)) return true;
297
+ return isValidEscapeAt(s, offset);
298
+ }
299
+ function isNumberStartAt(s, offset = 0) {
300
+ const c0 = s.peek(offset);
301
+ if (c0 === "+" || c0 === "-") {
302
+ const c1 = s.peek(offset + 1);
303
+ if (isDigit(c1)) return true;
304
+ return c1 === "." && isDigit(s.peek(offset + 2));
305
+ }
306
+ if (c0 === ".") return isDigit(s.peek(offset + 1));
307
+ return isDigit(c0);
308
+ }
309
+ function didConsumeNewline(s) {
310
+ if (s.peek() === "\r" && s.peek(1) === "\n") {
311
+ s.advance(2);
312
+ return true;
313
+ }
314
+ if (s.peek() === "\n" || s.peek() === "\r" || s.peek() === "\f") {
315
+ s.advance(1);
316
+ return true;
317
+ }
318
+ return false;
319
+ }
320
+ function consumeLiteralCodePoint(s) {
321
+ const cp = s.input.codePointAt(s.pos) ?? 0;
322
+ if (cp === 0 || cp >= 55296 && cp <= 57343) {
323
+ s.advance();
324
+ return "\uFFFD";
325
+ }
326
+ const width = cp > 65535 ? 2 : 1;
327
+ const text = s.input.slice(s.pos, s.pos + width);
328
+ s.advance(width);
329
+ return text;
330
+ }
331
+
332
+ // src/directive/tokenizer/escape.ts
333
+ function consumeEscapeHexDigits(s) {
334
+ let hex = s.peek() ?? "";
335
+ s.advance();
336
+ for (let i = 0; i < 5 && isHexDigit(s.peek()); i++) {
337
+ hex += s.peek();
338
+ s.advance();
339
+ }
340
+ return hex;
341
+ }
342
+ function isMaxAllowedCodePoint(value) {
343
+ return value !== 0 && !(value >= 55296 && value <= 57343) && value <= 1114111;
344
+ }
345
+ function consumeEscapedCodePoint(s) {
346
+ if (s.eof()) return "\uFFFD";
347
+ if (!isHexDigit(s.peek())) return consumeLiteralCodePoint(s);
348
+ const hex = consumeEscapeHexDigits(s);
349
+ if (!didConsumeNewline(s) && (s.peek() === " " || s.peek() === " ")) s.advance();
350
+ const value = Number.parseInt(hex, 16);
351
+ return isMaxAllowedCodePoint(value) ? String.fromCodePoint(value) : "\uFFFD";
352
+ }
353
+ function consumeName(s) {
354
+ let result = "";
355
+ for (; ; ) {
356
+ if (isIdentCodePoint(s.peek())) {
357
+ result += consumeLiteralCodePoint(s);
358
+ continue;
359
+ }
360
+ if (!isValidEscapeAt(s)) return result;
361
+ s.advance();
362
+ result += consumeEscapedCodePoint(s);
363
+ }
364
+ }
365
+
366
+ // src/directive/tokenizer/url.ts
367
+ var URL_BAD_DELIMITERS = /* @__PURE__ */ new Set(['"', "'", "("]);
368
+ function isUrlBadCharacter(c) {
369
+ if (c !== void 0 && URL_BAD_DELIMITERS.has(c)) return true;
370
+ return isNonPrintable(c);
371
+ }
372
+ function consumeBadUrlRemnants(s) {
373
+ for (; ; ) {
374
+ if (s.eof()) return;
375
+ if (s.peek() === ")") {
376
+ s.advance();
377
+ return;
378
+ }
379
+ if (isValidEscapeAt(s)) {
380
+ s.advance();
381
+ consumeEscapedCodePoint(s);
382
+ continue;
383
+ }
384
+ s.advance();
385
+ }
386
+ }
387
+ function bad(s) {
388
+ consumeBadUrlRemnants(s);
389
+ return { value: "", bad: true };
390
+ }
391
+ function consumeUrlTrailingWhitespace(s, value) {
392
+ while (isWhitespace(s.peek())) s.advance();
393
+ if (s.eof()) return { value, bad: false };
394
+ if (s.peek() === ")") {
395
+ s.advance();
396
+ return { value, bad: false };
397
+ }
398
+ return bad(s);
399
+ }
400
+ function consumeUrlBackslash(s, value) {
401
+ if (isValidEscapeAt(s)) {
402
+ s.advance();
403
+ return { value: value + consumeEscapedCodePoint(s), bad: false };
404
+ }
405
+ return bad(s);
406
+ }
407
+ function consumeUrlToken(s) {
408
+ let value = "";
409
+ while (isWhitespace(s.peek())) s.advance();
410
+ for (; ; ) {
411
+ if (s.eof()) return { value, bad: false };
412
+ const c = s.peek();
413
+ if (c === ")") {
414
+ s.advance();
415
+ return { value, bad: false };
416
+ }
417
+ if (isWhitespace(c)) return consumeUrlTrailingWhitespace(s, value);
418
+ if (isUrlBadCharacter(c)) return bad(s);
419
+ if (c === "\\") {
420
+ const result = consumeUrlBackslash(s, value);
421
+ if (result.bad) return result;
422
+ value = result.value;
423
+ continue;
424
+ }
425
+ value += consumeLiteralCodePoint(s);
426
+ }
427
+ }
428
+
429
+ // src/directive/tokenizer/ident-like.ts
430
+ function isFollowedByQuote(s) {
431
+ let lookahead = 0;
432
+ while (isWhitespace(s.peek(lookahead))) lookahead++;
433
+ const next = s.peek(lookahead);
434
+ return next === '"' || next === "'";
435
+ }
436
+ function consumeUrlOrFunctionToken(s, start) {
437
+ s.advance();
438
+ if (isFollowedByQuote(s)) {
439
+ return {
440
+ type: "function-token",
441
+ raw: s.input.slice(start, s.pos),
442
+ startIndex: start,
443
+ endIndex: s.pos,
444
+ structured: { value: "url" }
445
+ };
446
+ }
447
+ const { value, bad: bad2 } = consumeUrlToken(s);
448
+ return {
449
+ type: bad2 ? "bad-url-token" : "url-token",
450
+ raw: s.input.slice(start, s.pos),
451
+ startIndex: start,
452
+ endIndex: s.pos,
453
+ // eslint-disable-next-line unicorn/no-null -- the corpus's format carries null (not undefined) for a bad-url-token's structured field
454
+ structured: bad2 ? null : { value }
455
+ };
456
+ }
457
+ function consumeIdentLikeToken(s, start) {
458
+ const name = consumeName(s);
459
+ if (name.toLowerCase() === "url" && s.peek() === "(") {
460
+ return consumeUrlOrFunctionToken(s, start);
461
+ }
462
+ if (s.peek() === "(") {
463
+ s.advance();
464
+ return {
465
+ type: "function-token",
466
+ raw: s.input.slice(start, s.pos),
467
+ startIndex: start,
468
+ endIndex: s.pos,
469
+ structured: { value: name }
470
+ };
471
+ }
472
+ return {
473
+ type: "ident-token",
474
+ raw: s.input.slice(start, s.pos),
475
+ startIndex: start,
476
+ endIndex: s.pos,
477
+ structured: { value: name }
478
+ };
479
+ }
480
+
481
+ // src/directive/tokenizer/numeric.ts
482
+ function consumeSign(s) {
483
+ const c = s.peek();
484
+ if (c !== "+" && c !== "-") return void 0;
485
+ s.advance();
486
+ return c;
487
+ }
488
+ function consumeDigitRun(s) {
489
+ let digits = "";
490
+ while (isDigit(s.peek())) {
491
+ digits += s.peek();
492
+ s.advance();
493
+ }
494
+ return digits;
495
+ }
496
+ function consumeFraction(s) {
497
+ if (s.peek() !== "." || !isDigit(s.peek(1))) return "";
498
+ s.advance();
499
+ return `.${consumeDigitRun(s)}`;
500
+ }
501
+ function consumeExponent(s) {
502
+ const e = s.peek();
503
+ if (e !== "e" && e !== "E") return "";
504
+ const afterE = s.peek(1);
505
+ const expSign = afterE === "+" || afterE === "-" ? afterE : "";
506
+ const digitsOffset = expSign ? 2 : 1;
507
+ if (!isDigit(s.peek(digitsOffset))) return "";
508
+ s.advance(digitsOffset);
509
+ return `${e}${expSign}${consumeDigitRun(s)}`;
510
+ }
511
+ function consumeNumber(s) {
512
+ const sign = consumeSign(s);
513
+ const integer = consumeDigitRun(s);
514
+ const fraction = consumeFraction(s);
515
+ const exponent = consumeExponent(s);
516
+ const repr = (sign ?? "") + integer + fraction + exponent;
517
+ const type = fraction || exponent ? "number" : "integer";
518
+ return { repr, value: Number(repr), type, sign };
519
+ }
520
+ function consumeNumericToken(s, start) {
521
+ const num = consumeNumber(s);
522
+ const signCharacter = num.sign ? { signCharacter: num.sign } : {};
523
+ if (isIdentSequenceStartAt(s)) {
524
+ const unit = consumeName(s);
525
+ const structured2 = {
526
+ value: num.value,
527
+ type: num.type,
528
+ unit,
529
+ ...signCharacter
530
+ };
531
+ return {
532
+ type: "dimension-token",
533
+ raw: s.input.slice(start, s.pos),
534
+ startIndex: start,
535
+ endIndex: s.pos,
536
+ structured: structured2
537
+ };
538
+ }
539
+ if (s.peek() === "%") {
540
+ s.advance();
541
+ const structured2 = { value: num.value, ...signCharacter };
542
+ return {
543
+ type: "percentage-token",
544
+ raw: s.input.slice(start, s.pos),
545
+ startIndex: start,
546
+ endIndex: s.pos,
547
+ structured: structured2
548
+ };
549
+ }
550
+ const structured = { value: num.value, type: num.type, ...signCharacter };
551
+ return {
552
+ type: "number-token",
553
+ raw: s.input.slice(start, s.pos),
554
+ startIndex: start,
555
+ endIndex: s.pos,
556
+ structured
557
+ };
558
+ }
559
+
560
+ // src/directive/tokenizer/string.ts
561
+ var NEWLINE_CHARS = /* @__PURE__ */ new Set(["\n", "\r", "\f"]);
562
+ function didConsumeLineContinuation(s) {
563
+ const c = s.peek(1);
564
+ if (c === void 0 || !NEWLINE_CHARS.has(c)) return false;
565
+ s.advance();
566
+ didConsumeNewline(s);
567
+ return true;
568
+ }
569
+ function consumeStringBackslash(s, value) {
570
+ if (s.eof(1)) {
571
+ s.advance();
572
+ return value;
573
+ }
574
+ if (didConsumeLineContinuation(s)) return value;
575
+ s.advance();
576
+ return value + consumeEscapedCodePoint(s);
577
+ }
578
+ function consumeStringToken(s, endingCodePoint) {
579
+ let value = "";
580
+ for (; ; ) {
581
+ if (s.eof()) return { value, bad: false };
582
+ const c = s.peek();
583
+ if (c === endingCodePoint) {
584
+ s.advance();
585
+ return { value, bad: false };
586
+ }
587
+ if (c !== void 0 && NEWLINE_CHARS.has(c)) {
588
+ return { value, bad: true };
589
+ }
590
+ if (c === "\\") {
591
+ value = consumeStringBackslash(s, value);
592
+ continue;
593
+ }
594
+ value += consumeLiteralCodePoint(s);
595
+ }
596
+ }
597
+
598
+ // src/directive/tokenizer.ts
599
+ function delim(s, start, char) {
600
+ return {
601
+ type: "delim-token",
602
+ raw: char,
603
+ startIndex: start,
604
+ endIndex: s.pos,
605
+ structured: { value: char }
606
+ };
607
+ }
608
+ function noStructured(s, start, raw, type) {
609
+ return { type, raw, startIndex: start, endIndex: s.pos, structured: null };
610
+ }
611
+ var SIMPLE = {
612
+ ":": "colon-token",
613
+ ";": "semicolon-token",
614
+ ",": "comma-token",
615
+ "[": "[-token",
616
+ "]": "]-token",
617
+ "(": "(-token",
618
+ ")": ")-token",
619
+ "{": "{-token",
620
+ "}": "}-token"
621
+ };
622
+ function tryConsumeWhitespace(s, start, c) {
623
+ if (!isWhitespace(c)) return void 0;
624
+ while (isWhitespace(s.peek())) s.advance();
625
+ return noStructured(s, start, s.input.slice(start, s.pos), "whitespace-token");
626
+ }
627
+ function tryConsumeQuoted(s, start, c) {
628
+ if (c !== '"' && c !== "'") return void 0;
629
+ s.advance();
630
+ const { value, bad: bad2 } = consumeStringToken(s, c);
631
+ return {
632
+ type: bad2 ? "bad-string-token" : "string-token",
633
+ raw: s.input.slice(start, s.pos),
634
+ startIndex: start,
635
+ endIndex: s.pos,
636
+ // eslint-disable-next-line unicorn/no-null -- the corpus's own format carries null, not undefined, for a bad-string-token
637
+ structured: bad2 ? null : { value }
638
+ };
639
+ }
640
+ function consumeHashToken(s, start) {
641
+ const type = isIdentSequenceStartAt(s) ? "id" : "unrestricted";
642
+ const value = consumeName(s);
643
+ return {
644
+ type: "hash-token",
645
+ raw: s.input.slice(start, s.pos),
646
+ startIndex: start,
647
+ endIndex: s.pos,
648
+ structured: { value, type }
649
+ };
650
+ }
651
+ function tryConsumeHashOrDelim(s, start, c) {
652
+ if (c !== "#") return void 0;
653
+ s.advance();
654
+ if (isIdentCodePoint(s.peek()) || isValidEscapeAt(s)) return consumeHashToken(s, start);
655
+ return delim(s, start, c);
656
+ }
657
+ function tryConsumeSimple(s, start, c) {
658
+ const type = SIMPLE[c];
659
+ if (!type) return void 0;
660
+ s.advance();
661
+ return noStructured(s, start, c, type);
662
+ }
663
+ function tryConsumePlusOrDot(s, start, c) {
664
+ if (c !== "+" && c !== ".") return void 0;
665
+ if (isNumberStartAt(s)) return consumeNumericToken(s, start);
666
+ s.advance();
667
+ return delim(s, start, c);
668
+ }
669
+ function tryConsumeHyphen(s, start, c) {
670
+ if (c !== "-") return void 0;
671
+ if (isNumberStartAt(s)) return consumeNumericToken(s, start);
672
+ if (s.peek(1) === "-" && s.peek(2) === ">") {
673
+ s.advance(3);
674
+ return noStructured(s, start, s.input.slice(start, s.pos), "CDC-token");
675
+ }
676
+ if (isIdentSequenceStartAt(s)) return consumeIdentLikeToken(s, start);
677
+ s.advance();
678
+ return delim(s, start, c);
679
+ }
680
+ function tryConsumeLessThan(s, start, c) {
681
+ if (c !== "<") return void 0;
682
+ if (s.peek(1) === "!" && s.peek(2) === "-" && s.peek(3) === "-") {
683
+ s.advance(4);
684
+ return noStructured(s, start, s.input.slice(start, s.pos), "CDO-token");
685
+ }
686
+ s.advance();
687
+ return delim(s, start, c);
688
+ }
689
+ function tryConsumeAtKeyword(s, start, c) {
690
+ if (c !== "@") return void 0;
691
+ s.advance();
692
+ if (isIdentSequenceStartAt(s)) {
693
+ const value = consumeName(s);
694
+ return {
695
+ type: "at-keyword-token",
696
+ raw: s.input.slice(start, s.pos),
697
+ startIndex: start,
698
+ endIndex: s.pos,
699
+ structured: { value }
700
+ };
701
+ }
702
+ return delim(s, start, c);
703
+ }
704
+ function tryConsumeBackslash(s, start, c) {
705
+ if (c !== "\\") return void 0;
706
+ if (isValidEscapeAt(s)) return consumeIdentLikeToken(s, start);
707
+ s.advance();
708
+ return delim(s, start, c);
709
+ }
710
+ function tryConsumeDigitOrIdentStart(s, start, c) {
711
+ if (isDigit(c)) return consumeNumericToken(s, start);
712
+ if (isIdentStartCodePoint(c)) return consumeIdentLikeToken(s, start);
713
+ return void 0;
714
+ }
715
+ var DISPATCHERS = [
716
+ tryConsumeWhitespace,
717
+ tryConsumeQuoted,
718
+ tryConsumeHashOrDelim,
719
+ tryConsumeSimple,
720
+ tryConsumePlusOrDot,
721
+ tryConsumeHyphen,
722
+ tryConsumeLessThan,
723
+ tryConsumeAtKeyword,
724
+ tryConsumeBackslash,
725
+ tryConsumeDigitOrIdentStart
726
+ ];
727
+ function nextToken(s, start) {
728
+ const comment = tryConsumeComment(s, start);
729
+ if (comment) return comment;
730
+ const c = s.peek();
731
+ for (const dispatch of DISPATCHERS) {
732
+ const token = dispatch(s, start, c);
733
+ if (token) return token;
734
+ }
735
+ s.advance();
736
+ return delim(s, start, c);
737
+ }
738
+ function tokenize(css) {
739
+ const s = new Scanner(css);
740
+ const tokens = [];
741
+ while (!s.eof()) {
742
+ tokens.push(nextToken(s, s.pos));
743
+ }
744
+ return tokens;
745
+ }
746
+
747
+ export { OPENERS, atKeywordName, createPositionFinder, isNaveAtKeyword, matchBrackets, readItem, reportDiagnostics, tokenize };