source: imaps-frontend/node_modules/webpack/lib/css/walkCssTokens.js

main
Last change on this file was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 46.0 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8/**
9 * @typedef {object} CssTokenCallbacks
10 * @property {(function(string, number, number, number, number): number)=} url
11 * @property {(function(string, number, number): number)=} comment
12 * @property {(function(string, number, number): number)=} string
13 * @property {(function(string, number, number): number)=} leftParenthesis
14 * @property {(function(string, number, number): number)=} rightParenthesis
15 * @property {(function(string, number, number): number)=} function
16 * @property {(function(string, number, number): number)=} colon
17 * @property {(function(string, number, number): number)=} atKeyword
18 * @property {(function(string, number, number): number)=} delim
19 * @property {(function(string, number, number): number)=} identifier
20 * @property {(function(string, number, number, boolean): number)=} hash
21 * @property {(function(string, number, number): number)=} leftCurlyBracket
22 * @property {(function(string, number, number): number)=} rightCurlyBracket
23 * @property {(function(string, number, number): number)=} semicolon
24 * @property {(function(string, number, number): number)=} comma
25 * @property {(function(): boolean)=} needTerminate
26 */
27
28/** @typedef {function(string, number, CssTokenCallbacks): number} CharHandler */
29
30// spec: https://drafts.csswg.org/css-syntax/
31
32const CC_LINE_FEED = "\n".charCodeAt(0);
33const CC_CARRIAGE_RETURN = "\r".charCodeAt(0);
34const CC_FORM_FEED = "\f".charCodeAt(0);
35
36const CC_TAB = "\t".charCodeAt(0);
37const CC_SPACE = " ".charCodeAt(0);
38
39const CC_SOLIDUS = "/".charCodeAt(0);
40const CC_REVERSE_SOLIDUS = "\\".charCodeAt(0);
41const CC_ASTERISK = "*".charCodeAt(0);
42
43const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);
44const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);
45const CC_LEFT_CURLY = "{".charCodeAt(0);
46const CC_RIGHT_CURLY = "}".charCodeAt(0);
47const CC_LEFT_SQUARE = "[".charCodeAt(0);
48const CC_RIGHT_SQUARE = "]".charCodeAt(0);
49
50const CC_QUOTATION_MARK = '"'.charCodeAt(0);
51const CC_APOSTROPHE = "'".charCodeAt(0);
52
53const CC_FULL_STOP = ".".charCodeAt(0);
54const CC_COLON = ":".charCodeAt(0);
55const CC_SEMICOLON = ";".charCodeAt(0);
56const CC_COMMA = ",".charCodeAt(0);
57const CC_PERCENTAGE = "%".charCodeAt(0);
58const CC_AT_SIGN = "@".charCodeAt(0);
59
60const CC_LOW_LINE = "_".charCodeAt(0);
61const CC_LOWER_A = "a".charCodeAt(0);
62const CC_LOWER_F = "f".charCodeAt(0);
63const CC_LOWER_E = "e".charCodeAt(0);
64const CC_LOWER_U = "u".charCodeAt(0);
65const CC_LOWER_Z = "z".charCodeAt(0);
66const CC_UPPER_A = "A".charCodeAt(0);
67const CC_UPPER_F = "F".charCodeAt(0);
68const CC_UPPER_E = "E".charCodeAt(0);
69const CC_UPPER_U = "E".charCodeAt(0);
70const CC_UPPER_Z = "Z".charCodeAt(0);
71const CC_0 = "0".charCodeAt(0);
72const CC_9 = "9".charCodeAt(0);
73
74const CC_NUMBER_SIGN = "#".charCodeAt(0);
75const CC_PLUS_SIGN = "+".charCodeAt(0);
76const CC_HYPHEN_MINUS = "-".charCodeAt(0);
77
78const CC_LESS_THAN_SIGN = "<".charCodeAt(0);
79const CC_GREATER_THAN_SIGN = ">".charCodeAt(0);
80
81/** @type {CharHandler} */
82const consumeSpace = (input, pos, _callbacks) => {
83 // Consume as much whitespace as possible.
84 while (_isWhiteSpace(input.charCodeAt(pos))) {
85 pos++;
86 }
87
88 // Return a <whitespace-token>.
89 return pos;
90};
91
92// U+000A LINE FEED. Note that U+000D CARRIAGE RETURN and U+000C FORM FEED are not included in this definition,
93// as they are converted to U+000A LINE FEED during preprocessing.
94//
95// Replace any U+000D CARRIAGE RETURN (CR) code points, U+000C FORM FEED (FF) code points, or pairs of U+000D CARRIAGE RETURN (CR) followed by U+000A LINE FEED (LF) in input by a single U+000A LINE FEED (LF) code point.
96
97/**
98 * @param {number} cc char code
99 * @returns {boolean} true, if cc is a newline
100 */
101const _isNewline = cc =>
102 cc === CC_LINE_FEED || cc === CC_CARRIAGE_RETURN || cc === CC_FORM_FEED;
103
104/**
105 * @param {number} cc char code
106 * @param {string} input input
107 * @param {number} pos position
108 * @returns {number} position
109 */
110const consumeExtraNewline = (cc, input, pos) => {
111 if (cc === CC_CARRIAGE_RETURN && input.charCodeAt(pos) === CC_LINE_FEED) {
112 pos++;
113 }
114
115 return pos;
116};
117
118/**
119 * @param {number} cc char code
120 * @returns {boolean} true, if cc is a space (U+0009 CHARACTER TABULATION or U+0020 SPACE)
121 */
122const _isSpace = cc => cc === CC_TAB || cc === CC_SPACE;
123
124/**
125 * @param {number} cc char code
126 * @returns {boolean} true, if cc is a whitespace
127 */
128const _isWhiteSpace = cc => _isNewline(cc) || _isSpace(cc);
129
130/**
131 * ident-start code point
132 *
133 * A letter, a non-ASCII code point, or U+005F LOW LINE (_).
134 * @param {number} cc char code
135 * @returns {boolean} true, if cc is a start code point of an identifier
136 */
137const isIdentStartCodePoint = cc =>
138 (cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
139 (cc >= CC_UPPER_A && cc <= CC_UPPER_Z) ||
140 cc === CC_LOW_LINE ||
141 cc >= 0x80;
142
143/** @type {CharHandler} */
144const consumeDelimToken = (input, pos, _callbacks) =>
145 // Return a <delim-token> with its value set to the current input code point.
146 pos;
147
148/** @type {CharHandler} */
149const consumeComments = (input, pos, callbacks) => {
150 // This section describes how to consume comments from a stream of code points. It returns nothing.
151 // If the next two input code point are U+002F SOLIDUS (/) followed by a U+002A ASTERISK (*),
152 // consume them and all following code points up to and including the first U+002A ASTERISK (*)
153 // followed by a U+002F SOLIDUS (/), or up to an EOF code point.
154 // Return to the start of this step.
155 while (
156 input.charCodeAt(pos) === CC_SOLIDUS &&
157 input.charCodeAt(pos + 1) === CC_ASTERISK
158 ) {
159 const start = pos;
160 pos += 2;
161
162 for (;;) {
163 if (pos === input.length) {
164 // If the preceding paragraph ended by consuming an EOF code point, this is a parse error.
165 return pos;
166 }
167
168 if (
169 input.charCodeAt(pos) === CC_ASTERISK &&
170 input.charCodeAt(pos + 1) === CC_SOLIDUS
171 ) {
172 pos += 2;
173
174 if (callbacks.comment) {
175 pos = callbacks.comment(input, start, pos);
176 }
177
178 break;
179 }
180
181 pos++;
182 }
183 }
184
185 return pos;
186};
187
188/**
189 * @param {number} cc char code
190 * @returns {boolean} true, if cc is a hex digit
191 */
192const _isHexDigit = cc =>
193 _isDigit(cc) ||
194 (cc >= CC_UPPER_A && cc <= CC_UPPER_F) ||
195 (cc >= CC_LOWER_A && cc <= CC_LOWER_F);
196
197/**
198 * @param {string} input input
199 * @param {number} pos position
200 * @returns {number} position
201 */
202const _consumeAnEscapedCodePoint = (input, pos) => {
203 // This section describes how to consume an escaped code point.
204 // It assumes that the U+005C REVERSE SOLIDUS (\) has already been consumed and that the next input code point has already been verified to be part of a valid escape.
205 // It will return a code point.
206
207 // Consume the next input code point.
208 const cc = input.charCodeAt(pos);
209 pos++;
210
211 // EOF
212 // This is a parse error. Return U+FFFD REPLACEMENT CHARACTER (�).
213 if (pos === input.length) {
214 return pos;
215 }
216
217 // hex digit
218 // Consume as many hex digits as possible, but no more than 5.
219 // Note that this means 1-6 hex digits have been consumed in total.
220 // If the next input code point is whitespace, consume it as well.
221 // Interpret the hex digits as a hexadecimal number.
222 // If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point, return U+FFFD REPLACEMENT CHARACTER (�).
223 // Otherwise, return the code point with that value.
224 if (_isHexDigit(cc)) {
225 for (let i = 0; i < 5; i++) {
226 if (_isHexDigit(input.charCodeAt(pos))) {
227 pos++;
228 }
229 }
230
231 const cc = input.charCodeAt(pos);
232
233 if (_isWhiteSpace(cc)) {
234 pos++;
235 pos = consumeExtraNewline(cc, input, pos);
236 }
237
238 return pos;
239 }
240
241 // anything else
242 // Return the current input code point.
243 return pos;
244};
245
246/** @type {CharHandler} */
247const consumeAStringToken = (input, pos, callbacks) => {
248 // This section describes how to consume a string token from a stream of code points.
249 // It returns either a <string-token> or <bad-string-token>.
250 //
251 // This algorithm may be called with an ending code point, which denotes the code point that ends the string.
252 // If an ending code point is not specified, the current input code point is used.
253 const start = pos - 1;
254 const endingCodePoint = input.charCodeAt(pos - 1);
255
256 // Initially create a <string-token> with its value set to the empty string.
257
258 // Repeatedly consume the next input code point from the stream:
259 for (;;) {
260 // EOF
261 // This is a parse error. Return the <string-token>.
262 if (pos === input.length) {
263 if (callbacks.string !== undefined) {
264 return callbacks.string(input, start, pos);
265 }
266
267 return pos;
268 }
269
270 const cc = input.charCodeAt(pos);
271 pos++;
272
273 // ending code point
274 // Return the <string-token>.
275 if (cc === endingCodePoint) {
276 if (callbacks.string !== undefined) {
277 return callbacks.string(input, start, pos);
278 }
279
280 return pos;
281 }
282 // newline
283 // This is a parse error.
284 // Reconsume the current input code point, create a <bad-string-token>, and return it.
285 else if (_isNewline(cc)) {
286 pos--;
287 // bad string
288 return pos;
289 }
290 // U+005C REVERSE SOLIDUS (\)
291 else if (cc === CC_REVERSE_SOLIDUS) {
292 // If the next input code point is EOF, do nothing.
293 if (pos === input.length) {
294 return pos;
295 }
296 // Otherwise, if the next input code point is a newline, consume it.
297 else if (_isNewline(input.charCodeAt(pos))) {
298 const cc = input.charCodeAt(pos);
299 pos++;
300 pos = consumeExtraNewline(cc, input, pos);
301 }
302 // Otherwise, (the stream starts with a valid escape) consume an escaped code point and append the returned code point to the <string-token>’s value.
303 else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
304 pos = _consumeAnEscapedCodePoint(input, pos);
305 }
306 }
307 // anything else
308 // Append the current input code point to the <string-token>’s value.
309 else {
310 // Append
311 }
312 }
313};
314
315/**
316 * @param {number} cc char code
317 * @param {number} q char code
318 * @returns {boolean} is non-ASCII code point
319 */
320const isNonASCIICodePoint = (cc, q) =>
321 // Simplify
322 cc > 0x80;
323/**
324 * @param {number} cc char code
325 * @returns {boolean} is letter
326 */
327const isLetter = cc =>
328 (cc >= CC_LOWER_A && cc <= CC_LOWER_Z) ||
329 (cc >= CC_UPPER_A && cc <= CC_UPPER_Z);
330
331/**
332 * @param {number} cc char code
333 * @param {number} q char code
334 * @returns {boolean} is identifier start code
335 */
336const _isIdentStartCodePoint = (cc, q) =>
337 isLetter(cc) || isNonASCIICodePoint(cc, q) || cc === CC_LOW_LINE;
338
339/**
340 * @param {number} cc char code
341 * @param {number} q char code
342 * @returns {boolean} is identifier code
343 */
344const _isIdentCodePoint = (cc, q) =>
345 _isIdentStartCodePoint(cc, q) || _isDigit(cc) || cc === CC_HYPHEN_MINUS;
346/**
347 * @param {number} cc char code
348 * @returns {boolean} is digit
349 */
350const _isDigit = cc => cc >= CC_0 && cc <= CC_9;
351
352/**
353 * @param {string} input input
354 * @param {number} pos position
355 * @param {number=} f first code point
356 * @param {number=} s second code point
357 * @returns {boolean} true if two code points are a valid escape
358 */
359const _ifTwoCodePointsAreValidEscape = (input, pos, f, s) => {
360 // This section describes how to check if two code points are a valid escape.
361 // The algorithm described here can be called explicitly with two code points, or can be called with the input stream itself.
362 // In the latter case, the two code points in question are the current input code point and the next input code point, in that order.
363
364 // Note: This algorithm will not consume any additional code point.
365 const first = f || input.charCodeAt(pos - 1);
366 const second = s || input.charCodeAt(pos);
367
368 // If the first code point is not U+005C REVERSE SOLIDUS (\), return false.
369 if (first !== CC_REVERSE_SOLIDUS) return false;
370 // Otherwise, if the second code point is a newline, return false.
371 if (_isNewline(second)) return false;
372 // Otherwise, return true.
373 return true;
374};
375
376/**
377 * @param {string} input input
378 * @param {number} pos position
379 * @param {number=} f first
380 * @param {number=} s second
381 * @param {number=} t third
382 * @returns {boolean} true, if input at pos starts an identifier
383 */
384const _ifThreeCodePointsWouldStartAnIdentSequence = (input, pos, f, s, t) => {
385 // This section describes how to check if three code points would start an ident sequence.
386 // The algorithm described here can be called explicitly with three code points, or can be called with the input stream itself.
387 // In the latter case, the three code points in question are the current input code point and the next two input code points, in that order.
388
389 // Note: This algorithm will not consume any additional code points.
390
391 const first = f || input.charCodeAt(pos - 1);
392 const second = s || input.charCodeAt(pos);
393 const third = t || input.charCodeAt(pos + 1);
394
395 // Look at the first code point:
396
397 // U+002D HYPHEN-MINUS
398 if (first === CC_HYPHEN_MINUS) {
399 // If the second code point is an ident-start code point or a U+002D HYPHEN-MINUS
400 // or a U+002D HYPHEN-MINUS, or the second and third code points are a valid escape, return true.
401 if (
402 _isIdentStartCodePoint(second, pos) ||
403 second === CC_HYPHEN_MINUS ||
404 _ifTwoCodePointsAreValidEscape(input, pos, second, third)
405 ) {
406 return true;
407 }
408 return false;
409 }
410 // ident-start code point
411 else if (_isIdentStartCodePoint(first, pos - 1)) {
412 return true;
413 }
414 // U+005C REVERSE SOLIDUS (\)
415 // If the first and second code points are a valid escape, return true. Otherwise, return false.
416 else if (first === CC_REVERSE_SOLIDUS) {
417 if (_ifTwoCodePointsAreValidEscape(input, pos, first, second)) {
418 return true;
419 }
420
421 return false;
422 }
423 // anything else
424 // Return false.
425 return false;
426};
427
428/**
429 * @param {string} input input
430 * @param {number} pos position
431 * @param {number=} f first
432 * @param {number=} s second
433 * @param {number=} t third
434 * @returns {boolean} true, if input at pos starts an identifier
435 */
436const _ifThreeCodePointsWouldStartANumber = (input, pos, f, s, t) => {
437 // This section describes how to check if three code points would start a number.
438 // The algorithm described here can be called explicitly with three code points, or can be called with the input stream itself.
439 // In the latter case, the three code points in question are the current input code point and the next two input code points, in that order.
440
441 // Note: This algorithm will not consume any additional code points.
442
443 const first = f || input.charCodeAt(pos - 1);
444 const second = s || input.charCodeAt(pos);
445 const third = t || input.charCodeAt(pos);
446
447 // Look at the first code point:
448
449 // U+002B PLUS SIGN (+)
450 // U+002D HYPHEN-MINUS (-)
451 //
452 // If the second code point is a digit, return true.
453 // Otherwise, if the second code point is a U+002E FULL STOP (.) and the third code point is a digit, return true.
454 // Otherwise, return false.
455 if (first === CC_PLUS_SIGN || first === CC_HYPHEN_MINUS) {
456 if (_isDigit(second)) {
457 return true;
458 } else if (second === CC_FULL_STOP && _isDigit(third)) {
459 return true;
460 }
461
462 return false;
463 }
464 // U+002E FULL STOP (.)
465 // If the second code point is a digit, return true. Otherwise, return false.
466 else if (first === CC_FULL_STOP) {
467 if (_isDigit(second)) {
468 return true;
469 }
470
471 return false;
472 }
473 // digit
474 // Return true.
475 else if (_isDigit(first)) {
476 return true;
477 }
478
479 // anything else
480 // Return false.
481 return false;
482};
483
484/** @type {CharHandler} */
485const consumeNumberSign = (input, pos, callbacks) => {
486 // If the next input code point is an ident code point or the next two input code points are a valid escape, then:
487 // - Create a <hash-token>.
488 // - If the next 3 input code points would start an ident sequence, set the <hash-token>’s type flag to "id".
489 // - Consume an ident sequence, and set the <hash-token>’s value to the returned string.
490 // - Return the <hash-token>.
491 const start = pos - 1;
492 const first = input.charCodeAt(pos);
493 const second = input.charCodeAt(pos + 1);
494
495 if (
496 _isIdentCodePoint(first, pos - 1) ||
497 _ifTwoCodePointsAreValidEscape(input, pos, first, second)
498 ) {
499 const third = input.charCodeAt(pos + 2);
500 let isId = false;
501
502 if (
503 _ifThreeCodePointsWouldStartAnIdentSequence(
504 input,
505 pos,
506 first,
507 second,
508 third
509 )
510 ) {
511 isId = true;
512 }
513
514 pos = _consumeAnIdentSequence(input, pos, callbacks);
515
516 if (callbacks.hash !== undefined) {
517 return callbacks.hash(input, start, pos, isId);
518 }
519
520 return pos;
521 }
522
523 // Otherwise, return a <delim-token> with its value set to the current input code point.
524 return pos;
525};
526
527/** @type {CharHandler} */
528const consumeHyphenMinus = (input, pos, callbacks) => {
529 // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
530 if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
531 pos--;
532 return consumeANumericToken(input, pos, callbacks);
533 }
534 // Otherwise, if the next 2 input code points are U+002D HYPHEN-MINUS U+003E GREATER-THAN SIGN (->), consume them and return a <CDC-token>.
535 else if (
536 input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
537 input.charCodeAt(pos + 1) === CC_GREATER_THAN_SIGN
538 ) {
539 return pos + 2;
540 }
541 // Otherwise, if the input stream starts with an ident sequence, reconsume the current input code point, consume an ident-like token, and return it.
542 else if (_ifThreeCodePointsWouldStartAnIdentSequence(input, pos)) {
543 pos--;
544 return consumeAnIdentLikeToken(input, pos, callbacks);
545 }
546
547 // Otherwise, return a <delim-token> with its value set to the current input code point.
548 return pos;
549};
550
551/** @type {CharHandler} */
552const consumeFullStop = (input, pos, callbacks) => {
553 const start = pos - 1;
554
555 // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
556 if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
557 pos--;
558 return consumeANumericToken(input, pos, callbacks);
559 }
560
561 // Otherwise, return a <delim-token> with its value set to the current input code point.
562 if (callbacks.delim !== undefined) {
563 return callbacks.delim(input, start, pos);
564 }
565
566 return pos;
567};
568
569/** @type {CharHandler} */
570const consumePlusSign = (input, pos, callbacks) => {
571 // If the input stream starts with a number, reconsume the current input code point, consume a numeric token, and return it.
572 if (_ifThreeCodePointsWouldStartANumber(input, pos)) {
573 pos--;
574 return consumeANumericToken(input, pos, callbacks);
575 }
576
577 // Otherwise, return a <delim-token> with its value set to the current input code point.
578 return pos;
579};
580
581/** @type {CharHandler} */
582const _consumeANumber = (input, pos) => {
583 // This section describes how to consume a number from a stream of code points.
584 // It returns a numeric value, and a type which is either "integer" or "number".
585
586 // Execute the following steps in order:
587 // Initially set type to "integer". Let repr be the empty string.
588
589 // If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-), consume it and append it to repr.
590 if (
591 input.charCodeAt(pos) === CC_HYPHEN_MINUS ||
592 input.charCodeAt(pos) === CC_PLUS_SIGN
593 ) {
594 pos++;
595 }
596
597 // While the next input code point is a digit, consume it and append it to repr.
598 while (_isDigit(input.charCodeAt(pos))) {
599 pos++;
600 }
601
602 // If the next 2 input code points are U+002E FULL STOP (.) followed by a digit, then:
603 // 1. Consume the next input code point and append it to number part.
604 // 2. While the next input code point is a digit, consume it and append it to number part.
605 // 3. Set type to "number".
606 if (
607 input.charCodeAt(pos) === CC_FULL_STOP &&
608 _isDigit(input.charCodeAt(pos + 1))
609 ) {
610 pos++;
611
612 while (_isDigit(input.charCodeAt(pos))) {
613 pos++;
614 }
615 }
616
617 // If the next 2 or 3 input code points are U+0045 LATIN CAPITAL LETTER E (E) or U+0065 LATIN SMALL LETTER E (e), optionally followed by U+002D HYPHEN-MINUS (-) or U+002B PLUS SIGN (+), followed by a digit, then:
618 // 1. Consume the next input code point.
619 // 2. If the next input code point is "+" or "-", consume it and append it to exponent part.
620 // 3. While the next input code point is a digit, consume it and append it to exponent part.
621 // 4. Set type to "number".
622 if (
623 (input.charCodeAt(pos) === CC_LOWER_E ||
624 input.charCodeAt(pos) === CC_UPPER_E) &&
625 (((input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS ||
626 input.charCodeAt(pos + 1) === CC_PLUS_SIGN) &&
627 _isDigit(input.charCodeAt(pos + 2))) ||
628 _isDigit(input.charCodeAt(pos + 1)))
629 ) {
630 pos++;
631
632 if (
633 input.charCodeAt(pos) === CC_PLUS_SIGN ||
634 input.charCodeAt(pos) === CC_HYPHEN_MINUS
635 ) {
636 pos++;
637 }
638
639 while (_isDigit(input.charCodeAt(pos))) {
640 pos++;
641 }
642 }
643
644 // Let value be the result of interpreting number part as a base-10 number.
645
646 // If exponent part is non-empty, interpret it as a base-10 integer, then raise 10 to the power of the result, multiply it by value, and set value to that result.
647
648 // Return value and type.
649 return pos;
650};
651
652/** @type {CharHandler} */
653const consumeANumericToken = (input, pos, callbacks) => {
654 // This section describes how to consume a numeric token from a stream of code points.
655 // It returns either a <number-token>, <percentage-token>, or <dimension-token>.
656
657 // Consume a number and let number be the result.
658 pos = _consumeANumber(input, pos, callbacks);
659
660 // If the next 3 input code points would start an ident sequence, then:
661 //
662 // - Create a <dimension-token> with the same value and type flag as number, and a unit set initially to the empty string.
663 // - Consume an ident sequence. Set the <dimension-token>’s unit to the returned value.
664 // - Return the <dimension-token>.
665
666 const first = input.charCodeAt(pos);
667 const second = input.charCodeAt(pos + 1);
668 const third = input.charCodeAt(pos + 2);
669
670 if (
671 _ifThreeCodePointsWouldStartAnIdentSequence(
672 input,
673 pos,
674 first,
675 second,
676 third
677 )
678 ) {
679 return _consumeAnIdentSequence(input, pos, callbacks);
680 }
681 // Otherwise, if the next input code point is U+0025 PERCENTAGE SIGN (%), consume it.
682 // Create a <percentage-token> with the same value as number, and return it.
683 else if (first === CC_PERCENTAGE) {
684 return pos + 1;
685 }
686
687 // Otherwise, create a <number-token> with the same value and type flag as number, and return it.
688 return pos;
689};
690
691/** @type {CharHandler} */
692const consumeColon = (input, pos, callbacks) => {
693 // Return a <colon-token>.
694 if (callbacks.colon !== undefined) {
695 return callbacks.colon(input, pos - 1, pos);
696 }
697 return pos;
698};
699
700/** @type {CharHandler} */
701const consumeLeftParenthesis = (input, pos, callbacks) => {
702 // Return a <(-token>.
703 if (callbacks.leftParenthesis !== undefined) {
704 return callbacks.leftParenthesis(input, pos - 1, pos);
705 }
706 return pos;
707};
708
709/** @type {CharHandler} */
710const consumeRightParenthesis = (input, pos, callbacks) => {
711 // Return a <)-token>.
712 if (callbacks.rightParenthesis !== undefined) {
713 return callbacks.rightParenthesis(input, pos - 1, pos);
714 }
715 return pos;
716};
717
718/** @type {CharHandler} */
719const consumeLeftSquareBracket = (input, pos, callbacks) =>
720 // Return a <]-token>.
721 pos;
722
723/** @type {CharHandler} */
724const consumeRightSquareBracket = (input, pos, callbacks) =>
725 // Return a <]-token>.
726 pos;
727
728/** @type {CharHandler} */
729const consumeLeftCurlyBracket = (input, pos, callbacks) => {
730 // Return a <{-token>.
731 if (callbacks.leftCurlyBracket !== undefined) {
732 return callbacks.leftCurlyBracket(input, pos - 1, pos);
733 }
734 return pos;
735};
736
737/** @type {CharHandler} */
738const consumeRightCurlyBracket = (input, pos, callbacks) => {
739 // Return a <}-token>.
740 if (callbacks.rightCurlyBracket !== undefined) {
741 return callbacks.rightCurlyBracket(input, pos - 1, pos);
742 }
743 return pos;
744};
745
746/** @type {CharHandler} */
747const consumeSemicolon = (input, pos, callbacks) => {
748 // Return a <semicolon-token>.
749 if (callbacks.semicolon !== undefined) {
750 return callbacks.semicolon(input, pos - 1, pos);
751 }
752 return pos;
753};
754
755/** @type {CharHandler} */
756const consumeComma = (input, pos, callbacks) => {
757 // Return a <comma-token>.
758 if (callbacks.comma !== undefined) {
759 return callbacks.comma(input, pos - 1, pos);
760 }
761 return pos;
762};
763
764/** @type {CharHandler} */
765const _consumeAnIdentSequence = (input, pos) => {
766 // This section describes how to consume an ident sequence from a stream of code points.
767 // It returns a string containing the largest name that can be formed from adjacent code points in the stream, starting from the first.
768
769 // Note: This algorithm does not do the verification of the first few code points that are necessary to ensure the returned code points would constitute an <ident-token>.
770 // If that is the intended use, ensure that the stream starts with an ident sequence before calling this algorithm.
771
772 // Let result initially be an empty string.
773
774 // Repeatedly consume the next input code point from the stream:
775 for (;;) {
776 const cc = input.charCodeAt(pos);
777 pos++;
778
779 // ident code point
780 // Append the code point to result.
781 if (_isIdentCodePoint(cc, pos - 1)) {
782 // Nothing
783 }
784 // the stream starts with a valid escape
785 // Consume an escaped code point. Append the returned code point to result.
786 else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
787 pos = _consumeAnEscapedCodePoint(input, pos);
788 }
789 // anything else
790 // Reconsume the current input code point. Return result.
791 else {
792 return pos - 1;
793 }
794 }
795};
796
797/**
798 * @param {number} cc char code
799 * @returns {boolean} true, when cc is the non-printable code point, otherwise false
800 */
801const _isNonPrintableCodePoint = cc =>
802 (cc >= 0x00 && cc <= 0x08) ||
803 cc === 0x0b ||
804 (cc >= 0x0e && cc <= 0x1f) ||
805 cc === 0x7f;
806
807/**
808 * @param {string} input input
809 * @param {number} pos position
810 * @returns {number} position
811 */
812const consumeTheRemnantsOfABadUrl = (input, pos) => {
813 // This section describes how to consume the remnants of a bad url from a stream of code points,
814 // "cleaning up" after the tokenizer realizes that it’s in the middle of a <bad-url-token> rather than a <url-token>.
815 // It returns nothing; its sole use is to consume enough of the input stream to reach a recovery point where normal tokenizing can resume.
816
817 // Repeatedly consume the next input code point from the stream:
818 for (;;) {
819 // EOF
820 // Return.
821 if (pos === input.length) {
822 return pos;
823 }
824
825 const cc = input.charCodeAt(pos);
826 pos++;
827
828 // U+0029 RIGHT PARENTHESIS ())
829 // Return.
830 if (cc === CC_RIGHT_PARENTHESIS) {
831 return pos;
832 }
833 // the input stream starts with a valid escape
834 // Consume an escaped code point.
835 // This allows an escaped right parenthesis ("\)") to be encountered without ending the <bad-url-token>.
836 // This is otherwise identical to the "anything else" clause.
837 else if (_ifTwoCodePointsAreValidEscape(input, pos)) {
838 pos = _consumeAnEscapedCodePoint(input, pos);
839 }
840 // anything else
841 // Do nothing.
842 else {
843 // Do nothing.
844 }
845 }
846};
847
848/**
849 * @param {string} input input
850 * @param {number} pos position
851 * @param {number} fnStart start
852 * @param {CssTokenCallbacks} callbacks callbacks
853 * @returns {pos} pos
854 */
855const consumeAUrlToken = (input, pos, fnStart, callbacks) => {
856 // This section describes how to consume a url token from a stream of code points.
857 // It returns either a <url-token> or a <bad-url-token>.
858
859 // Note: This algorithm assumes that the initial "url(" has already been consumed.
860 // This algorithm also assumes that it’s being called to consume an "unquoted" value, like url(foo).
861 // A quoted value, like url("foo"), is parsed as a <function-token>.
862 // Consume an ident-like token automatically handles this distinction; this algorithm shouldn’t be called directly otherwise.
863
864 // Initially create a <url-token> with its value set to the empty string.
865
866 // Consume as much whitespace as possible.
867 while (_isWhiteSpace(input.charCodeAt(pos))) {
868 pos++;
869 }
870
871 const contentStart = pos;
872
873 // Repeatedly consume the next input code point from the stream:
874 for (;;) {
875 // EOF
876 // This is a parse error. Return the <url-token>.
877 if (pos === input.length) {
878 if (callbacks.url !== undefined) {
879 return callbacks.url(input, fnStart, pos, contentStart, pos - 1);
880 }
881
882 return pos;
883 }
884
885 const cc = input.charCodeAt(pos);
886 pos++;
887
888 // U+0029 RIGHT PARENTHESIS ())
889 // Return the <url-token>.
890 if (cc === CC_RIGHT_PARENTHESIS) {
891 if (callbacks.url !== undefined) {
892 return callbacks.url(input, fnStart, pos, contentStart, pos - 1);
893 }
894
895 return pos;
896 }
897 // whitespace
898 // Consume as much whitespace as possible.
899 // If the next input code point is U+0029 RIGHT PARENTHESIS ()) or EOF, consume it and return the <url-token>
900 // (if EOF was encountered, this is a parse error); otherwise, consume the remnants of a bad url, create a <bad-url-token>, and return it.
901 else if (_isWhiteSpace(cc)) {
902 const end = pos - 1;
903
904 while (_isWhiteSpace(input.charCodeAt(pos))) {
905 pos++;
906 }
907
908 if (pos === input.length) {
909 if (callbacks.url !== undefined) {
910 return callbacks.url(input, fnStart, pos, contentStart, end);
911 }
912
913 return pos;
914 }
915
916 if (input.charCodeAt(pos) === CC_RIGHT_PARENTHESIS) {
917 pos++;
918
919 if (callbacks.url !== undefined) {
920 return callbacks.url(input, fnStart, pos, contentStart, end);
921 }
922
923 return pos;
924 }
925
926 // Don't handle bad urls
927 return consumeTheRemnantsOfABadUrl(input, pos);
928 }
929 // U+0022 QUOTATION MARK (")
930 // U+0027 APOSTROPHE (')
931 // U+0028 LEFT PARENTHESIS (()
932 // non-printable code point
933 // This is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
934 else if (
935 cc === CC_QUOTATION_MARK ||
936 cc === CC_APOSTROPHE ||
937 cc === CC_LEFT_PARENTHESIS ||
938 _isNonPrintableCodePoint(cc)
939 ) {
940 // Don't handle bad urls
941 return consumeTheRemnantsOfABadUrl(input, pos);
942 }
943 // // U+005C REVERSE SOLIDUS (\)
944 // // If the stream starts with a valid escape, consume an escaped code point and append the returned code point to the <url-token>’s value.
945 // // Otherwise, this is a parse error. Consume the remnants of a bad url, create a <bad-url-token>, and return it.
946 else if (cc === CC_REVERSE_SOLIDUS) {
947 if (_ifTwoCodePointsAreValidEscape(input, pos)) {
948 pos = _consumeAnEscapedCodePoint(input, pos);
949 } else {
950 // Don't handle bad urls
951 return consumeTheRemnantsOfABadUrl(input, pos);
952 }
953 }
954 // anything else
955 // Append the current input code point to the <url-token>’s value.
956 else {
957 // Nothing
958 }
959 }
960};
961
962/** @type {CharHandler} */
963const consumeAnIdentLikeToken = (input, pos, callbacks) => {
964 const start = pos;
965 // This section describes how to consume an ident-like token from a stream of code points.
966 // It returns an <ident-token>, <function-token>, <url-token>, or <bad-url-token>.
967 pos = _consumeAnIdentSequence(input, pos, callbacks);
968
969 // If string’s value is an ASCII case-insensitive match for "url", and the next input code point is U+0028 LEFT PARENTHESIS ((), consume it.
970 // While the next two input code points are whitespace, consume the next input code point.
971 // If the next one or two input code points are U+0022 QUOTATION MARK ("), U+0027 APOSTROPHE ('), or whitespace followed by U+0022 QUOTATION MARK (") or U+0027 APOSTROPHE ('), then create a <function-token> with its value set to string and return it.
972 // Otherwise, consume a url token, and return it.
973 if (
974 input.slice(start, pos).toLowerCase() === "url" &&
975 input.charCodeAt(pos) === CC_LEFT_PARENTHESIS
976 ) {
977 pos++;
978 const end = pos;
979
980 while (
981 _isWhiteSpace(input.charCodeAt(pos)) &&
982 _isWhiteSpace(input.charCodeAt(pos + 1))
983 ) {
984 pos++;
985 }
986
987 if (
988 input.charCodeAt(pos) === CC_QUOTATION_MARK ||
989 input.charCodeAt(pos) === CC_APOSTROPHE ||
990 (_isWhiteSpace(input.charCodeAt(pos)) &&
991 (input.charCodeAt(pos + 1) === CC_QUOTATION_MARK ||
992 input.charCodeAt(pos + 1) === CC_APOSTROPHE))
993 ) {
994 if (callbacks.function !== undefined) {
995 return callbacks.function(input, start, end);
996 }
997
998 return pos;
999 }
1000
1001 return consumeAUrlToken(input, pos, start, callbacks);
1002 }
1003
1004 // Otherwise, if the next input code point is U+0028 LEFT PARENTHESIS ((), consume it.
1005 // Create a <function-token> with its value set to string and return it.
1006 if (input.charCodeAt(pos) === CC_LEFT_PARENTHESIS) {
1007 pos++;
1008
1009 if (callbacks.function !== undefined) {
1010 return callbacks.function(input, start, pos);
1011 }
1012
1013 return pos;
1014 }
1015
1016 // Otherwise, create an <ident-token> with its value set to string and return it.
1017 if (callbacks.identifier !== undefined) {
1018 return callbacks.identifier(input, start, pos);
1019 }
1020
1021 return pos;
1022};
1023
1024/** @type {CharHandler} */
1025const consumeLessThan = (input, pos, _callbacks) => {
1026 // If the next 3 input code points are U+0021 EXCLAMATION MARK U+002D HYPHEN-MINUS U+002D HYPHEN-MINUS (!--), consume them and return a <CDO-token>.
1027 if (input.slice(pos, pos + 3) === "!--") {
1028 return pos + 3;
1029 }
1030
1031 // Otherwise, return a <delim-token> with its value set to the current input code point.
1032 return pos;
1033};
1034
1035/** @type {CharHandler} */
1036const consumeCommercialAt = (input, pos, callbacks) => {
1037 const start = pos - 1;
1038
1039 // If the next 3 input code points would start an ident sequence, consume an ident sequence, create an <at-keyword-token> with its value set to the returned value, and return it.
1040 if (
1041 _ifThreeCodePointsWouldStartAnIdentSequence(
1042 input,
1043 pos,
1044 input.charCodeAt(pos),
1045 input.charCodeAt(pos + 1),
1046 input.charCodeAt(pos + 2)
1047 )
1048 ) {
1049 pos = _consumeAnIdentSequence(input, pos, callbacks);
1050
1051 if (callbacks.atKeyword !== undefined) {
1052 pos = callbacks.atKeyword(input, start, pos);
1053 }
1054
1055 return pos;
1056 }
1057
1058 // Otherwise, return a <delim-token> with its value set to the current input code point.
1059 return pos;
1060};
1061
1062/** @type {CharHandler} */
1063const consumeReverseSolidus = (input, pos, callbacks) => {
1064 // If the input stream starts with a valid escape, reconsume the current input code point, consume an ident-like token, and return it.
1065 if (_ifTwoCodePointsAreValidEscape(input, pos)) {
1066 pos--;
1067 return consumeAnIdentLikeToken(input, pos, callbacks);
1068 }
1069
1070 // Otherwise, this is a parse error. Return a <delim-token> with its value set to the current input code point.
1071 return pos;
1072};
1073
1074/** @type {CharHandler} */
1075const consumeAToken = (input, pos, callbacks) => {
1076 const cc = input.charCodeAt(pos - 1);
1077
1078 // https://drafts.csswg.org/css-syntax/#consume-token
1079 switch (cc) {
1080 // whitespace
1081 case CC_LINE_FEED:
1082 case CC_CARRIAGE_RETURN:
1083 case CC_FORM_FEED:
1084 case CC_TAB:
1085 case CC_SPACE:
1086 return consumeSpace(input, pos, callbacks);
1087 // U+0022 QUOTATION MARK (")
1088 case CC_QUOTATION_MARK:
1089 return consumeAStringToken(input, pos, callbacks);
1090 // U+0023 NUMBER SIGN (#)
1091 case CC_NUMBER_SIGN:
1092 return consumeNumberSign(input, pos, callbacks);
1093 // U+0027 APOSTROPHE (')
1094 case CC_APOSTROPHE:
1095 return consumeAStringToken(input, pos, callbacks);
1096 // U+0028 LEFT PARENTHESIS (()
1097 case CC_LEFT_PARENTHESIS:
1098 return consumeLeftParenthesis(input, pos, callbacks);
1099 // U+0029 RIGHT PARENTHESIS ())
1100 case CC_RIGHT_PARENTHESIS:
1101 return consumeRightParenthesis(input, pos, callbacks);
1102 // U+002B PLUS SIGN (+)
1103 case CC_PLUS_SIGN:
1104 return consumePlusSign(input, pos, callbacks);
1105 // U+002C COMMA (,)
1106 case CC_COMMA:
1107 return consumeComma(input, pos, callbacks);
1108 // U+002D HYPHEN-MINUS (-)
1109 case CC_HYPHEN_MINUS:
1110 return consumeHyphenMinus(input, pos, callbacks);
1111 // U+002E FULL STOP (.)
1112 case CC_FULL_STOP:
1113 return consumeFullStop(input, pos, callbacks);
1114 // U+003A COLON (:)
1115 case CC_COLON:
1116 return consumeColon(input, pos, callbacks);
1117 // U+003B SEMICOLON (;)
1118 case CC_SEMICOLON:
1119 return consumeSemicolon(input, pos, callbacks);
1120 // U+003C LESS-THAN SIGN (<)
1121 case CC_LESS_THAN_SIGN:
1122 return consumeLessThan(input, pos, callbacks);
1123 // U+0040 COMMERCIAL AT (@)
1124 case CC_AT_SIGN:
1125 return consumeCommercialAt(input, pos, callbacks);
1126 // U+005B LEFT SQUARE BRACKET ([)
1127 case CC_LEFT_SQUARE:
1128 return consumeLeftSquareBracket(input, pos, callbacks);
1129 // U+005C REVERSE SOLIDUS (\)
1130 case CC_REVERSE_SOLIDUS:
1131 return consumeReverseSolidus(input, pos, callbacks);
1132 // U+005D RIGHT SQUARE BRACKET (])
1133 case CC_RIGHT_SQUARE:
1134 return consumeRightSquareBracket(input, pos, callbacks);
1135 // U+007B LEFT CURLY BRACKET ({)
1136 case CC_LEFT_CURLY:
1137 return consumeLeftCurlyBracket(input, pos, callbacks);
1138 // U+007D RIGHT CURLY BRACKET (})
1139 case CC_RIGHT_CURLY:
1140 return consumeRightCurlyBracket(input, pos, callbacks);
1141 default:
1142 // digit
1143 // Reconsume the current input code point, consume a numeric token, and return it.
1144 if (_isDigit(cc)) {
1145 pos--;
1146 return consumeANumericToken(input, pos, callbacks);
1147 } else if (cc === CC_LOWER_U || cc === CC_UPPER_U) {
1148 // If unicode ranges allowed is true and the input stream would start a unicode-range,
1149 // reconsume the current input code point, consume a unicode-range token, and return it.
1150 // Skip now
1151 // if (_ifThreeCodePointsWouldStartAUnicodeRange(input, pos)) {
1152 // pos--;
1153 // return consumeAUnicodeRangeToken(input, pos, callbacks);
1154 // }
1155
1156 // Otherwise, reconsume the current input code point, consume an ident-like token, and return it.
1157 pos--;
1158 return consumeAnIdentLikeToken(input, pos, callbacks);
1159 }
1160 // ident-start code point
1161 // Reconsume the current input code point, consume an ident-like token, and return it.
1162 else if (isIdentStartCodePoint(cc)) {
1163 pos--;
1164 return consumeAnIdentLikeToken(input, pos, callbacks);
1165 }
1166
1167 // EOF, but we don't have it
1168
1169 // anything else
1170 // Return a <delim-token> with its value set to the current input code point.
1171 return consumeDelimToken(input, pos, callbacks);
1172 }
1173};
1174
1175/**
1176 * @param {string} input input css
1177 * @param {number=} pos pos
1178 * @param {CssTokenCallbacks=} callbacks callbacks
1179 * @returns {number} pos
1180 */
1181module.exports = (input, pos = 0, callbacks = {}) => {
1182 // This section describes how to consume a token from a stream of code points. It will return a single token of any type.
1183 while (pos < input.length) {
1184 // Consume comments.
1185 pos = consumeComments(input, pos, callbacks);
1186
1187 // Consume the next input code point.
1188 pos++;
1189 pos = consumeAToken(input, pos, callbacks);
1190
1191 if (callbacks.needTerminate && callbacks.needTerminate()) {
1192 break;
1193 }
1194 }
1195
1196 return pos;
1197};
1198
1199module.exports.isIdentStartCodePoint = isIdentStartCodePoint;
1200
1201/**
1202 * @param {string} input input
1203 * @param {number} pos position
1204 * @returns {number} position after comments
1205 */
1206module.exports.eatComments = (input, pos) => {
1207 for (;;) {
1208 const originalPos = pos;
1209 pos = consumeComments(input, pos, {});
1210 if (originalPos === pos) {
1211 break;
1212 }
1213 }
1214
1215 return pos;
1216};
1217
1218/**
1219 * @param {string} input input
1220 * @param {number} pos position
1221 * @returns {number} position after whitespace
1222 */
1223module.exports.eatWhitespace = (input, pos) => {
1224 while (_isWhiteSpace(input.charCodeAt(pos))) {
1225 pos++;
1226 }
1227
1228 return pos;
1229};
1230
1231/**
1232 * @param {string} input input
1233 * @param {number} pos position
1234 * @returns {number} position after whitespace and comments
1235 */
1236module.exports.eatWhitespaceAndComments = (input, pos) => {
1237 for (;;) {
1238 const originalPos = pos;
1239 pos = consumeComments(input, pos, {});
1240 while (_isWhiteSpace(input.charCodeAt(pos))) {
1241 pos++;
1242 }
1243 if (originalPos === pos) {
1244 break;
1245 }
1246 }
1247
1248 return pos;
1249};
1250
1251/**
1252 * @param {string} input input
1253 * @param {number} pos position
1254 * @returns {number} position after whitespace and comments
1255 */
1256module.exports.eatComments = (input, pos) => {
1257 for (;;) {
1258 const originalPos = pos;
1259 pos = consumeComments(input, pos, {});
1260 if (originalPos === pos) {
1261 break;
1262 }
1263 }
1264
1265 return pos;
1266};
1267
1268/**
1269 * @param {string} input input
1270 * @param {number} pos position
1271 * @returns {number} position after whitespace
1272 */
1273module.exports.eatWhiteLine = (input, pos) => {
1274 for (;;) {
1275 const cc = input.charCodeAt(pos);
1276 if (_isSpace(cc)) {
1277 pos++;
1278 continue;
1279 }
1280 if (_isNewline(cc)) pos++;
1281 pos = consumeExtraNewline(cc, input, pos);
1282 break;
1283 }
1284
1285 return pos;
1286};
1287
1288/**
1289 * @param {string} input input
1290 * @param {number} pos position
1291 * @returns {[number, number] | undefined} positions of ident sequence
1292 */
1293module.exports.skipCommentsAndEatIdentSequence = (input, pos) => {
1294 pos = module.exports.eatComments(input, pos);
1295
1296 const start = pos;
1297
1298 if (
1299 _ifThreeCodePointsWouldStartAnIdentSequence(
1300 input,
1301 pos,
1302 input.charCodeAt(pos),
1303 input.charCodeAt(pos + 1),
1304 input.charCodeAt(pos + 2)
1305 )
1306 ) {
1307 return [start, _consumeAnIdentSequence(input, pos, {})];
1308 }
1309
1310 return undefined;
1311};
1312
1313/**
1314 * @param {string} input input
1315 * @param {number} pos position
1316 * @returns {[number, number] | undefined} positions of ident sequence
1317 */
1318module.exports.eatString = (input, pos) => {
1319 pos = module.exports.eatWhitespaceAndComments(input, pos);
1320
1321 const start = pos;
1322
1323 if (
1324 input.charCodeAt(pos) === CC_QUOTATION_MARK ||
1325 input.charCodeAt(pos) === CC_APOSTROPHE
1326 ) {
1327 return [start, consumeAStringToken(input, pos + 1, {})];
1328 }
1329
1330 return undefined;
1331};
1332
1333/**
1334 * @param {string} input input
1335 * @param {number} pos position
1336 * @param {CssTokenCallbacks} cbs callbacks
1337 * @returns {[number, number][]} positions of ident sequence
1338 */
1339module.exports.eatImageSetStrings = (input, pos, cbs) => {
1340 /** @type {[number, number][]} */
1341 const result = [];
1342
1343 let isFirst = true;
1344 let needStop = false;
1345 // We already in `func(` token
1346 let balanced = 1;
1347
1348 /** @type {CssTokenCallbacks} */
1349 const callbacks = {
1350 ...cbs,
1351 string: (_input, start, end) => {
1352 if (isFirst && balanced === 1) {
1353 result.push([start, end]);
1354 isFirst = false;
1355 }
1356
1357 return end;
1358 },
1359 comma: (_input, _start, end) => {
1360 if (balanced === 1) {
1361 isFirst = true;
1362 }
1363
1364 return end;
1365 },
1366 leftParenthesis: (input, start, end) => {
1367 balanced++;
1368
1369 return end;
1370 },
1371 function: (_input, start, end) => {
1372 balanced++;
1373
1374 return end;
1375 },
1376 rightParenthesis: (_input, _start, end) => {
1377 balanced--;
1378
1379 if (balanced === 0) {
1380 needStop = true;
1381 }
1382
1383 return end;
1384 }
1385 };
1386
1387 while (pos < input.length) {
1388 // Consume comments.
1389 pos = consumeComments(input, pos, callbacks);
1390
1391 // Consume the next input code point.
1392 pos++;
1393 pos = consumeAToken(input, pos, callbacks);
1394
1395 if (needStop) {
1396 break;
1397 }
1398 }
1399
1400 return result;
1401};
1402
1403/**
1404 * @param {string} input input
1405 * @param {number} pos position
1406 * @param {CssTokenCallbacks} cbs callbacks
1407 * @returns {[[number, number, number, number] | undefined, [number, number] | undefined, [number, number] | undefined, [number, number] | undefined]} positions of top level tokens
1408 */
1409module.exports.eatImportTokens = (input, pos, cbs) => {
1410 const result =
1411 /** @type {[[number, number, number, number] | undefined, [number, number] | undefined, [number, number] | undefined, [number, number] | undefined]} */
1412 (new Array(4));
1413
1414 /** @type {0 | 1 | 2 | undefined} */
1415 let scope;
1416 let needStop = false;
1417 let balanced = 0;
1418
1419 /** @type {CssTokenCallbacks} */
1420 const callbacks = {
1421 ...cbs,
1422 url: (_input, start, end, contentStart, contentEnd) => {
1423 if (
1424 result[0] === undefined &&
1425 balanced === 0 &&
1426 result[1] === undefined &&
1427 result[2] === undefined &&
1428 result[3] === undefined
1429 ) {
1430 result[0] = [start, end, contentStart, contentEnd];
1431 scope = undefined;
1432 }
1433
1434 return end;
1435 },
1436 string: (_input, start, end) => {
1437 if (
1438 balanced === 0 &&
1439 result[0] === undefined &&
1440 result[1] === undefined &&
1441 result[2] === undefined &&
1442 result[3] === undefined
1443 ) {
1444 result[0] = [start, end, start + 1, end - 1];
1445 scope = undefined;
1446 } else if (result[0] !== undefined && scope === 0) {
1447 result[0][2] = start + 1;
1448 result[0][3] = end - 1;
1449 }
1450
1451 return end;
1452 },
1453 leftParenthesis: (_input, _start, end) => {
1454 balanced++;
1455
1456 return end;
1457 },
1458 rightParenthesis: (_input, _start, end) => {
1459 balanced--;
1460
1461 if (balanced === 0 && scope !== undefined) {
1462 /** @type {[number, number]} */
1463 (result[scope])[1] = end;
1464 scope = undefined;
1465 }
1466
1467 return end;
1468 },
1469 function: (input, start, end) => {
1470 if (balanced === 0) {
1471 const name = input
1472 .slice(start, end - 1)
1473 .replace(/\\/g, "")
1474 .toLowerCase();
1475
1476 if (
1477 name === "url" &&
1478 result[0] === undefined &&
1479 result[1] === undefined &&
1480 result[2] === undefined &&
1481 result[3] === undefined
1482 ) {
1483 scope = 0;
1484 result[scope] = [start, end + 1, end + 1, end + 1];
1485 } else if (
1486 name === "layer" &&
1487 result[1] === undefined &&
1488 result[2] === undefined
1489 ) {
1490 scope = 1;
1491 result[scope] = [start, end];
1492 } else if (name === "supports" && result[2] === undefined) {
1493 scope = 2;
1494 result[scope] = [start, end];
1495 } else {
1496 scope = undefined;
1497 }
1498 }
1499
1500 balanced++;
1501
1502 return end;
1503 },
1504 identifier: (input, start, end) => {
1505 if (
1506 balanced === 0 &&
1507 result[1] === undefined &&
1508 result[2] === undefined
1509 ) {
1510 const name = input.slice(start, end).replace(/\\/g, "").toLowerCase();
1511
1512 if (name === "layer") {
1513 result[1] = [start, end];
1514 scope = undefined;
1515 }
1516 }
1517
1518 return end;
1519 },
1520 semicolon: (_input, start, end) => {
1521 if (balanced === 0) {
1522 needStop = true;
1523 result[3] = [start, end];
1524 }
1525
1526 return end;
1527 }
1528 };
1529
1530 while (pos < input.length) {
1531 // Consume comments.
1532 pos = consumeComments(input, pos, callbacks);
1533
1534 // Consume the next input code point.
1535 pos++;
1536 pos = consumeAToken(input, pos, callbacks);
1537
1538 if (needStop) {
1539 break;
1540 }
1541 }
1542
1543 return result;
1544};
1545
1546/**
1547 * @param {string} input input
1548 * @param {number} pos position
1549 * @returns {[number, number] | undefined} positions of ident sequence
1550 */
1551module.exports.eatIdentSequence = (input, pos) => {
1552 pos = module.exports.eatWhitespaceAndComments(input, pos);
1553
1554 const start = pos;
1555
1556 if (
1557 _ifThreeCodePointsWouldStartAnIdentSequence(
1558 input,
1559 pos,
1560 input.charCodeAt(pos),
1561 input.charCodeAt(pos + 1),
1562 input.charCodeAt(pos + 2)
1563 )
1564 ) {
1565 return [start, _consumeAnIdentSequence(input, pos, {})];
1566 }
1567
1568 return undefined;
1569};
1570
1571/**
1572 * @param {string} input input
1573 * @param {number} pos position
1574 * @returns {[number, number, boolean] | undefined} positions of ident sequence or string
1575 */
1576module.exports.eatIdentSequenceOrString = (input, pos) => {
1577 pos = module.exports.eatWhitespaceAndComments(input, pos);
1578
1579 const start = pos;
1580
1581 if (
1582 input.charCodeAt(pos) === CC_QUOTATION_MARK ||
1583 input.charCodeAt(pos) === CC_APOSTROPHE
1584 ) {
1585 return [start, consumeAStringToken(input, pos + 1, {}), false];
1586 } else if (
1587 _ifThreeCodePointsWouldStartAnIdentSequence(
1588 input,
1589 pos,
1590 input.charCodeAt(pos),
1591 input.charCodeAt(pos + 1),
1592 input.charCodeAt(pos + 2)
1593 )
1594 ) {
1595 return [start, _consumeAnIdentSequence(input, pos, {}), true];
1596 }
1597
1598 return undefined;
1599};
1600
1601/**
1602 * @param {string} chars characters
1603 * @returns {(input: string, pos: number) => number} function to eat characters
1604 */
1605module.exports.eatUntil = chars => {
1606 const charCodes = Array.from({ length: chars.length }, (_, i) =>
1607 chars.charCodeAt(i)
1608 );
1609 const arr = Array.from(
1610 { length: charCodes.reduce((a, b) => Math.max(a, b), 0) + 1 },
1611 () => false
1612 );
1613 for (const cc of charCodes) {
1614 arr[cc] = true;
1615 }
1616
1617 return (input, pos) => {
1618 for (;;) {
1619 const cc = input.charCodeAt(pos);
1620 if (cc < arr.length && arr[cc]) {
1621 return pos;
1622 }
1623 pos++;
1624 if (pos === input.length) return pos;
1625 }
1626 };
1627};
Note: See TracBrowser for help on using the repository browser.