source: imaps-frontend/node_modules/webpack/lib/css/CssParser.js@ 79a0317

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

F4 Finalna Verzija

  • Property mode set to 100644
File size: 44.8 KB
RevLine 
[79a0317]1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const vm = require("vm");
9const CommentCompilationWarning = require("../CommentCompilationWarning");
10const ModuleDependencyWarning = require("../ModuleDependencyWarning");
11const { CSS_MODULE_TYPE_AUTO } = require("../ModuleTypeConstants");
12const Parser = require("../Parser");
13const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
14const WebpackError = require("../WebpackError");
15const ConstDependency = require("../dependencies/ConstDependency");
16const CssIcssExportDependency = require("../dependencies/CssIcssExportDependency");
17const CssIcssImportDependency = require("../dependencies/CssIcssImportDependency");
18const CssIcssSymbolDependency = require("../dependencies/CssIcssSymbolDependency");
19const CssImportDependency = require("../dependencies/CssImportDependency");
20const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
21const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
22const CssUrlDependency = require("../dependencies/CssUrlDependency");
23const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
24const binarySearchBounds = require("../util/binarySearchBounds");
25const { parseResource } = require("../util/identifier");
26const {
27 webpackCommentRegExp,
28 createMagicCommentContext
29} = require("../util/magicComment");
30const walkCssTokens = require("./walkCssTokens");
31
32/** @typedef {import("../Module").BuildInfo} BuildInfo */
33/** @typedef {import("../Module").BuildMeta} BuildMeta */
34/** @typedef {import("../Parser").ParserState} ParserState */
35/** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
36/** @typedef {import("./walkCssTokens").CssTokenCallbacks} CssTokenCallbacks */
37
38/** @typedef {[number, number]} Range */
39/** @typedef {{ line: number, column: number }} Position */
40/** @typedef {{ value: string, range: Range, loc: { start: Position, end: Position } }} Comment */
41
42const CC_COLON = ":".charCodeAt(0);
43const CC_SLASH = "/".charCodeAt(0);
44const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);
45const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);
46const CC_LOWER_F = "f".charCodeAt(0);
47const CC_UPPER_F = "F".charCodeAt(0);
48
49// https://www.w3.org/TR/css-syntax-3/#newline
50// We don't have `preprocessing` stage, so we need specify all of them
51const STRING_MULTILINE = /\\[\n\r\f]/g;
52// https://www.w3.org/TR/css-syntax-3/#whitespace
53const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g;
54const UNESCAPE = /\\([0-9a-fA-F]{1,6}[ \t\n\r\f]?|[\s\S])/g;
55const IMAGE_SET_FUNCTION = /^(-\w+-)?image-set$/i;
56const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(-\w+-)?keyframes$/;
57const OPTIONALLY_VENDOR_PREFIXED_ANIMATION_PROPERTY =
58 /^(-\w+-)?animation(-name)?$/i;
59const IS_MODULES = /\.module(s)?\.[^.]+$/i;
60const CSS_COMMENT = /\/\*((?!\*\/).*?)\*\//g;
61
62/**
63 * @param {string} str url string
64 * @param {boolean} isString is url wrapped in quotes
65 * @returns {string} normalized url
66 */
67const normalizeUrl = (str, isString) => {
68 // Remove extra spaces and newlines:
69 // `url("im\
70 // g.png")`
71 if (isString) {
72 str = str.replace(STRING_MULTILINE, "");
73 }
74
75 str = str
76 // Remove unnecessary spaces from `url(" img.png ")`
77 .replace(TRIM_WHITE_SPACES, "")
78 // Unescape
79 .replace(UNESCAPE, match => {
80 if (match.length > 2) {
81 return String.fromCharCode(Number.parseInt(match.slice(1).trim(), 16));
82 }
83 return match[1];
84 });
85
86 if (/^data:/i.test(str)) {
87 return str;
88 }
89
90 if (str.includes("%")) {
91 // Convert `url('%2E/img.png')` -> `url('./img.png')`
92 try {
93 str = decodeURIComponent(str);
94 } catch (_err) {
95 // Ignore
96 }
97 }
98
99 return str;
100};
101
102// eslint-disable-next-line no-useless-escape
103const regexSingleEscape = /[ -,.\/:-@[\]\^`{-~]/;
104const regexExcessiveSpaces =
105 /(^|\\+)?(\\[A-F0-9]{1,6})\u0020(?![a-fA-F0-9\u0020])/g;
106
107/**
108 * @param {string} str string
109 * @returns {string} escaped identifier
110 */
111const escapeIdentifier = str => {
112 let output = "";
113 let counter = 0;
114
115 while (counter < str.length) {
116 const character = str.charAt(counter++);
117
118 let value;
119
120 if (/[\t\n\f\r\u000B]/.test(character)) {
121 const codePoint = character.charCodeAt(0);
122
123 value = `\\${codePoint.toString(16).toUpperCase()} `;
124 } else if (character === "\\" || regexSingleEscape.test(character)) {
125 value = `\\${character}`;
126 } else {
127 value = character;
128 }
129
130 output += value;
131 }
132
133 const firstChar = str.charAt(0);
134
135 if (/^-[-\d]/.test(output)) {
136 output = `\\-${output.slice(1)}`;
137 } else if (/\d/.test(firstChar)) {
138 output = `\\3${firstChar} ${output.slice(1)}`;
139 }
140
141 // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
142 // since they’re redundant. Note that this is only possible if the escape
143 // sequence isn’t preceded by an odd number of backslashes.
144 output = output.replace(regexExcessiveSpaces, ($0, $1, $2) => {
145 if ($1 && $1.length % 2) {
146 // It’s not safe to remove the space, so don’t.
147 return $0;
148 }
149
150 // Strip the space.
151 return ($1 || "") + $2;
152 });
153
154 return output;
155};
156
157const CONTAINS_ESCAPE = /\\/;
158
159/**
160 * @param {string} str string
161 * @returns {[string, number] | undefined} hex
162 */
163const gobbleHex = str => {
164 const lower = str.toLowerCase();
165 let hex = "";
166 let spaceTerminated = false;
167
168 for (let i = 0; i < 6 && lower[i] !== undefined; i++) {
169 const code = lower.charCodeAt(i);
170 // check to see if we are dealing with a valid hex char [a-f|0-9]
171 const valid = (code >= 97 && code <= 102) || (code >= 48 && code <= 57);
172 // https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
173 spaceTerminated = code === 32;
174 if (!valid) break;
175 hex += lower[i];
176 }
177
178 if (hex.length === 0) return undefined;
179
180 const codePoint = Number.parseInt(hex, 16);
181 const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff;
182
183 // Add special case for
184 // "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
185 // https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
186 if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) {
187 return ["\uFFFD", hex.length + (spaceTerminated ? 1 : 0)];
188 }
189
190 return [
191 String.fromCodePoint(codePoint),
192 hex.length + (spaceTerminated ? 1 : 0)
193 ];
194};
195
196/**
197 * @param {string} str string
198 * @returns {string} unescaped string
199 */
200const unescapeIdentifier = str => {
201 const needToProcess = CONTAINS_ESCAPE.test(str);
202 if (!needToProcess) return str;
203 let ret = "";
204 for (let i = 0; i < str.length; i++) {
205 if (str[i] === "\\") {
206 const gobbled = gobbleHex(str.slice(i + 1, i + 7));
207 if (gobbled !== undefined) {
208 ret += gobbled[0];
209 i += gobbled[1];
210 continue;
211 }
212 // Retain a pair of \\ if double escaped `\\\\`
213 // https://github.com/postcss/postcss-selector-parser/commit/268c9a7656fb53f543dc620aa5b73a30ec3ff20e
214 if (str[i + 1] === "\\") {
215 ret += "\\";
216 i += 1;
217 continue;
218 }
219 // if \\ is at the end of the string retain it
220 // https://github.com/postcss/postcss-selector-parser/commit/01a6b346e3612ce1ab20219acc26abdc259ccefb
221 if (str.length === i + 1) {
222 ret += str[i];
223 }
224 continue;
225 }
226 ret += str[i];
227 }
228
229 return ret;
230};
231
232class LocConverter {
233 /**
234 * @param {string} input input
235 */
236 constructor(input) {
237 this._input = input;
238 this.line = 1;
239 this.column = 0;
240 this.pos = 0;
241 }
242
243 /**
244 * @param {number} pos position
245 * @returns {LocConverter} location converter
246 */
247 get(pos) {
248 if (this.pos !== pos) {
249 if (this.pos < pos) {
250 const str = this._input.slice(this.pos, pos);
251 let i = str.lastIndexOf("\n");
252 if (i === -1) {
253 this.column += str.length;
254 } else {
255 this.column = str.length - i - 1;
256 this.line++;
257 while (i > 0 && (i = str.lastIndexOf("\n", i - 1)) !== -1)
258 this.line++;
259 }
260 } else {
261 let i = this._input.lastIndexOf("\n", this.pos);
262 while (i >= pos) {
263 this.line--;
264 i = i > 0 ? this._input.lastIndexOf("\n", i - 1) : -1;
265 }
266 this.column = pos - i;
267 }
268 this.pos = pos;
269 }
270 return this;
271 }
272}
273
274const EMPTY_COMMENT_OPTIONS = {
275 options: null,
276 errors: null
277};
278
279const CSS_MODE_TOP_LEVEL = 0;
280const CSS_MODE_IN_BLOCK = 1;
281
282const eatUntilSemi = walkCssTokens.eatUntil(";");
283const eatUntilLeftCurly = walkCssTokens.eatUntil("{");
284const eatSemi = walkCssTokens.eatUntil(";");
285
286class CssParser extends Parser {
287 /**
288 * @param {object} options options
289 * @param {boolean=} options.importOption need handle `@import`
290 * @param {boolean=} options.url need handle URLs
291 * @param {("pure" | "global" | "local" | "auto")=} options.defaultMode default mode
292 * @param {boolean=} options.namedExports is named exports
293 */
294 constructor({
295 defaultMode = "pure",
296 importOption = true,
297 url = true,
298 namedExports = true
299 } = {}) {
300 super();
301 this.defaultMode = defaultMode;
302 this.import = importOption;
303 this.url = url;
304 this.namedExports = namedExports;
305 /** @type {Comment[] | undefined} */
306 this.comments = undefined;
307 this.magicCommentContext = createMagicCommentContext();
308 }
309
310 /**
311 * @param {ParserState} state parser state
312 * @param {string} message warning message
313 * @param {LocConverter} locConverter location converter
314 * @param {number} start start offset
315 * @param {number} end end offset
316 */
317 _emitWarning(state, message, locConverter, start, end) {
318 const { line: sl, column: sc } = locConverter.get(start);
319 const { line: el, column: ec } = locConverter.get(end);
320
321 state.current.addWarning(
322 new ModuleDependencyWarning(state.module, new WebpackError(message), {
323 start: { line: sl, column: sc },
324 end: { line: el, column: ec }
325 })
326 );
327 }
328
329 /**
330 * @param {string | Buffer | PreparsedAst} source the source to parse
331 * @param {ParserState} state the parser state
332 * @returns {ParserState} the parser state
333 */
334 parse(source, state) {
335 if (Buffer.isBuffer(source)) {
336 source = source.toString("utf-8");
337 } else if (typeof source === "object") {
338 throw new Error("webpackAst is unexpected for the CssParser");
339 }
340 if (source[0] === "\uFEFF") {
341 source = source.slice(1);
342 }
343
344 let mode = this.defaultMode;
345
346 const module = state.module;
347
348 if (
349 mode === "auto" &&
350 module.type === CSS_MODULE_TYPE_AUTO &&
351 IS_MODULES.test(
352 parseResource(module.matchResource || module.resource).path
353 )
354 ) {
355 mode = "local";
356 }
357
358 const isModules = mode === "global" || mode === "local";
359
360 const locConverter = new LocConverter(source);
361
362 /** @type {number} */
363 let scope = CSS_MODE_TOP_LEVEL;
364 /** @type {boolean} */
365 let allowImportAtRule = true;
366 /** @type [string, number, number][] */
367 const balanced = [];
368 let lastTokenEndForComments = 0;
369
370 /** @type {boolean} */
371 let isNextRulePrelude = isModules;
372 /** @type {number} */
373 let blockNestingLevel = 0;
374 /** @type {"local" | "global" | undefined} */
375 let modeData;
376 /** @type {boolean} */
377 let inAnimationProperty = false;
378 /** @type {[number, number, boolean] | undefined} */
379 let lastIdentifier;
380 /** @type {Set<string>} */
381 const declaredCssVariables = new Set();
382 /** @type {Map<string, { path?: string, value: string }>} */
383 const icssDefinitions = new Map();
384
385 /**
386 * @param {string} input input
387 * @param {number} pos position
388 * @returns {boolean} true, when next is nested syntax
389 */
390 const isNextNestedSyntax = (input, pos) => {
391 pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
392
393 if (input[pos] === "}") {
394 return false;
395 }
396
397 // According spec only identifier can be used as a property name
398 const isIdentifier = walkCssTokens.isIdentStartCodePoint(
399 input.charCodeAt(pos)
400 );
401
402 return !isIdentifier;
403 };
404 /**
405 * @returns {boolean} true, when in local scope
406 */
407 const isLocalMode = () =>
408 modeData === "local" || (mode === "local" && modeData === undefined);
409
410 /**
411 * @param {string} input input
412 * @param {number} pos start position
413 * @param {(input: string, pos: number) => number} eater eater
414 * @returns {[number,string]} new position and text
415 */
416 const eatText = (input, pos, eater) => {
417 let text = "";
418 for (;;) {
419 if (input.charCodeAt(pos) === CC_SLASH) {
420 const newPos = walkCssTokens.eatComments(input, pos);
421 if (pos !== newPos) {
422 pos = newPos;
423 if (pos === input.length) break;
424 } else {
425 text += "/";
426 pos++;
427 if (pos === input.length) break;
428 }
429 }
430 const newPos = eater(input, pos);
431 if (pos !== newPos) {
432 text += input.slice(pos, newPos);
433 pos = newPos;
434 } else {
435 break;
436 }
437 if (pos === input.length) break;
438 }
439 return [pos, text.trimEnd()];
440 };
441
442 /**
443 * @param {0 | 1} type import or export
444 * @param {string} input input
445 * @param {number} pos start position
446 * @returns {number} position after parse
447 */
448 const parseImportOrExport = (type, input, pos) => {
449 pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
450 let importPath;
451 if (type === 0) {
452 let cc = input.charCodeAt(pos);
453 if (cc !== CC_LEFT_PARENTHESIS) {
454 this._emitWarning(
455 state,
456 `Unexpected '${input[pos]}' at ${pos} during parsing of ':import' (expected '(')`,
457 locConverter,
458 pos,
459 pos
460 );
461 return pos;
462 }
463 pos++;
464 const stringStart = pos;
465 const str = walkCssTokens.eatString(input, pos);
466 if (!str) {
467 this._emitWarning(
468 state,
469 `Unexpected '${input[pos]}' at ${pos} during parsing of ':import' (expected string)`,
470 locConverter,
471 stringStart,
472 pos
473 );
474 return pos;
475 }
476 importPath = input.slice(str[0] + 1, str[1] - 1);
477 pos = str[1];
478 pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
479 cc = input.charCodeAt(pos);
480 if (cc !== CC_RIGHT_PARENTHESIS) {
481 this._emitWarning(
482 state,
483 `Unexpected '${input[pos]}' at ${pos} during parsing of ':import' (expected ')')`,
484 locConverter,
485 pos,
486 pos
487 );
488 return pos;
489 }
490 pos++;
491 pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
492 }
493
494 /**
495 * @param {string} name name
496 * @param {string} value value
497 * @param {number} start start of position
498 * @param {number} end end of position
499 */
500 const createDep = (name, value, start, end) => {
501 if (type === 0) {
502 icssDefinitions.set(name, {
503 path: /** @type {string} */ (importPath),
504 value
505 });
506 } else if (type === 1) {
507 const dep = new CssIcssExportDependency(name, value);
508 const { line: sl, column: sc } = locConverter.get(start);
509 const { line: el, column: ec } = locConverter.get(end);
510 dep.setLoc(sl, sc, el, ec);
511 module.addDependency(dep);
512 }
513 };
514
515 let needTerminate = false;
516 let balanced = 0;
517 /** @type {undefined | 0 | 1 | 2} */
518 let scope;
519
520 /** @type {[number, number] | undefined} */
521 let name;
522 /** @type {number | undefined} */
523 let value;
524
525 /** @type {CssTokenCallbacks} */
526 const callbacks = {
527 leftCurlyBracket: (_input, _start, end) => {
528 balanced++;
529
530 if (scope === undefined) {
531 scope = 0;
532 }
533
534 return end;
535 },
536 rightCurlyBracket: (_input, _start, end) => {
537 balanced--;
538
539 if (scope === 2) {
540 createDep(
541 input.slice(name[0], name[1]),
542 input.slice(value, end - 1).trim(),
543 name[1],
544 end - 1
545 );
546 scope = 0;
547 }
548
549 if (balanced === 0 && scope === 0) {
550 needTerminate = true;
551 }
552
553 return end;
554 },
555 identifier: (_input, start, end) => {
556 if (scope === 0) {
557 name = [start, end];
558 scope = 1;
559 }
560
561 return end;
562 },
563 colon: (_input, _start, end) => {
564 if (scope === 1) {
565 scope = 2;
566 value = walkCssTokens.eatWhitespace(input, end);
567 return value;
568 }
569
570 return end;
571 },
572 semicolon: (input, _start, end) => {
573 if (scope === 2) {
574 createDep(
575 input.slice(name[0], name[1]),
576 input.slice(value, end - 1),
577 name[1],
578 end - 1
579 );
580 scope = 0;
581 }
582
583 return end;
584 },
585 needTerminate: () => needTerminate
586 };
587
588 pos = walkCssTokens(input, pos, callbacks);
589 pos = walkCssTokens.eatWhiteLine(input, pos);
590
591 return pos;
592 };
593 const eatPropertyName = walkCssTokens.eatUntil(":{};");
594 /**
595 * @param {string} input input
596 * @param {number} pos name start position
597 * @param {number} end name end position
598 * @returns {number} position after handling
599 */
600 const processLocalDeclaration = (input, pos, end) => {
601 modeData = undefined;
602 pos = walkCssTokens.eatWhitespaceAndComments(input, pos);
603 const propertyNameStart = pos;
604 const [propertyNameEnd, propertyName] = eatText(
605 input,
606 pos,
607 eatPropertyName
608 );
609 if (input.charCodeAt(propertyNameEnd) !== CC_COLON) return end;
610 pos = propertyNameEnd + 1;
611 if (propertyName.startsWith("--") && propertyName.length >= 3) {
612 // CSS Variable
613 const { line: sl, column: sc } = locConverter.get(propertyNameStart);
614 const { line: el, column: ec } = locConverter.get(propertyNameEnd);
615 const name = unescapeIdentifier(propertyName.slice(2));
616 const dep = new CssLocalIdentifierDependency(
617 name,
618 [propertyNameStart, propertyNameEnd],
619 "--"
620 );
621 dep.setLoc(sl, sc, el, ec);
622 module.addDependency(dep);
623 declaredCssVariables.add(name);
624 } else if (
625 OPTIONALLY_VENDOR_PREFIXED_ANIMATION_PROPERTY.test(propertyName)
626 ) {
627 inAnimationProperty = true;
628 }
629 return pos;
630 };
631 /**
632 * @param {string} input input
633 */
634 const processDeclarationValueDone = input => {
635 if (inAnimationProperty && lastIdentifier) {
636 const { line: sl, column: sc } = locConverter.get(lastIdentifier[0]);
637 const { line: el, column: ec } = locConverter.get(lastIdentifier[1]);
638 const name = unescapeIdentifier(
639 lastIdentifier[2]
640 ? input.slice(lastIdentifier[0], lastIdentifier[1])
641 : input.slice(lastIdentifier[0] + 1, lastIdentifier[1] - 1)
642 );
643 const dep = new CssSelfLocalIdentifierDependency(name, [
644 lastIdentifier[0],
645 lastIdentifier[1]
646 ]);
647 dep.setLoc(sl, sc, el, ec);
648 module.addDependency(dep);
649 lastIdentifier = undefined;
650 }
651 };
652
653 /**
654 * @param {string} input input
655 * @param {number} start start
656 * @param {number} end end
657 * @returns {number} end
658 */
659 const comment = (input, start, end) => {
660 if (!this.comments) this.comments = [];
661 const { line: sl, column: sc } = locConverter.get(start);
662 const { line: el, column: ec } = locConverter.get(end);
663
664 /** @type {Comment} */
665 const comment = {
666 value: input.slice(start + 2, end - 2),
667 range: [start, end],
668 loc: {
669 start: { line: sl, column: sc },
670 end: { line: el, column: ec }
671 }
672 };
673 this.comments.push(comment);
674 return end;
675 };
676
677 walkCssTokens(source, 0, {
678 comment,
679 leftCurlyBracket: (input, start, end) => {
680 switch (scope) {
681 case CSS_MODE_TOP_LEVEL: {
682 allowImportAtRule = false;
683 scope = CSS_MODE_IN_BLOCK;
684
685 if (isModules) {
686 blockNestingLevel = 1;
687 isNextRulePrelude = isNextNestedSyntax(input, end);
688 }
689
690 break;
691 }
692 case CSS_MODE_IN_BLOCK: {
693 if (isModules) {
694 blockNestingLevel++;
695 isNextRulePrelude = isNextNestedSyntax(input, end);
696 }
697 break;
698 }
699 }
700 return end;
701 },
702 rightCurlyBracket: (input, start, end) => {
703 switch (scope) {
704 case CSS_MODE_IN_BLOCK: {
705 if (--blockNestingLevel === 0) {
706 scope = CSS_MODE_TOP_LEVEL;
707
708 if (isModules) {
709 isNextRulePrelude = true;
710 modeData = undefined;
711 }
712 } else if (isModules) {
713 if (isLocalMode()) {
714 processDeclarationValueDone(input);
715 inAnimationProperty = false;
716 }
717
718 isNextRulePrelude = isNextNestedSyntax(input, end);
719 }
720 break;
721 }
722 }
723 return end;
724 },
725 url: (input, start, end, contentStart, contentEnd) => {
726 if (!this.url) {
727 return end;
728 }
729
730 const { options, errors: commentErrors } = this.parseCommentOptions([
731 lastTokenEndForComments,
732 end
733 ]);
734 if (commentErrors) {
735 for (const e of commentErrors) {
736 const { comment } = e;
737 state.module.addWarning(
738 new CommentCompilationWarning(
739 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
740 comment.loc
741 )
742 );
743 }
744 }
745 if (options && options.webpackIgnore !== undefined) {
746 if (typeof options.webpackIgnore !== "boolean") {
747 const { line: sl, column: sc } = locConverter.get(
748 lastTokenEndForComments
749 );
750 const { line: el, column: ec } = locConverter.get(end);
751
752 state.module.addWarning(
753 new UnsupportedFeatureWarning(
754 `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
755 {
756 start: { line: sl, column: sc },
757 end: { line: el, column: ec }
758 }
759 )
760 );
761 } else if (options.webpackIgnore) {
762 return end;
763 }
764 }
765 const value = normalizeUrl(
766 input.slice(contentStart, contentEnd),
767 false
768 );
769 // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
770 if (value.length === 0) return end;
771 const dep = new CssUrlDependency(value, [start, end], "url");
772 const { line: sl, column: sc } = locConverter.get(start);
773 const { line: el, column: ec } = locConverter.get(end);
774 dep.setLoc(sl, sc, el, ec);
775 module.addDependency(dep);
776 module.addCodeGenerationDependency(dep);
777 return end;
778 },
779 string: (_input, start, end) => {
780 switch (scope) {
781 case CSS_MODE_IN_BLOCK: {
782 if (inAnimationProperty && balanced.length === 0) {
783 lastIdentifier = [start, end, false];
784 }
785 }
786 }
787 return end;
788 },
789 atKeyword: (input, start, end) => {
790 const name = input.slice(start, end).toLowerCase();
791
792 switch (name) {
793 case "@namespace": {
794 this._emitWarning(
795 state,
796 "'@namespace' is not supported in bundled CSS",
797 locConverter,
798 start,
799 end
800 );
801
802 return eatUntilSemi(input, start);
803 }
804 case "@import": {
805 if (!this.import) {
806 return eatSemi(input, end);
807 }
808
809 if (!allowImportAtRule) {
810 this._emitWarning(
811 state,
812 "Any '@import' rules must precede all other rules",
813 locConverter,
814 start,
815 end
816 );
817 return end;
818 }
819
820 const tokens = walkCssTokens.eatImportTokens(input, end, {
821 comment
822 });
823 if (!tokens[3]) return end;
824 const semi = tokens[3][1];
825 if (!tokens[0]) {
826 this._emitWarning(
827 state,
828 `Expected URL in '${input.slice(start, semi)}'`,
829 locConverter,
830 start,
831 semi
832 );
833 return end;
834 }
835
836 const urlToken = tokens[0];
837 const url = normalizeUrl(
838 input.slice(urlToken[2], urlToken[3]),
839 true
840 );
841 const newline = walkCssTokens.eatWhiteLine(input, semi);
842 const { options, errors: commentErrors } = this.parseCommentOptions(
843 [end, urlToken[1]]
844 );
845 if (commentErrors) {
846 for (const e of commentErrors) {
847 const { comment } = e;
848 state.module.addWarning(
849 new CommentCompilationWarning(
850 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
851 comment.loc
852 )
853 );
854 }
855 }
856 if (options && options.webpackIgnore !== undefined) {
857 if (typeof options.webpackIgnore !== "boolean") {
858 const { line: sl, column: sc } = locConverter.get(start);
859 const { line: el, column: ec } = locConverter.get(newline);
860
861 state.module.addWarning(
862 new UnsupportedFeatureWarning(
863 `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
864 {
865 start: { line: sl, column: sc },
866 end: { line: el, column: ec }
867 }
868 )
869 );
870 } else if (options.webpackIgnore) {
871 return newline;
872 }
873 }
874 if (url.length === 0) {
875 const { line: sl, column: sc } = locConverter.get(start);
876 const { line: el, column: ec } = locConverter.get(newline);
877 const dep = new ConstDependency("", [start, newline]);
878 module.addPresentationalDependency(dep);
879 dep.setLoc(sl, sc, el, ec);
880
881 return newline;
882 }
883
884 let layer;
885
886 if (tokens[1]) {
887 layer = input.slice(tokens[1][0] + 6, tokens[1][1] - 1).trim();
888 }
889
890 let supports;
891
892 if (tokens[2]) {
893 supports = input.slice(tokens[2][0] + 9, tokens[2][1] - 1).trim();
894 }
895
896 const last = tokens[2] || tokens[1] || tokens[0];
897 const mediaStart = walkCssTokens.eatWhitespaceAndComments(
898 input,
899 last[1]
900 );
901
902 let media;
903
904 if (mediaStart !== semi - 1) {
905 media = input.slice(mediaStart, semi - 1).trim();
906 }
907
908 const { line: sl, column: sc } = locConverter.get(start);
909 const { line: el, column: ec } = locConverter.get(newline);
910 const dep = new CssImportDependency(
911 url,
912 [start, newline],
913 layer,
914 supports && supports.length > 0 ? supports : undefined,
915 media && media.length > 0 ? media : undefined
916 );
917 dep.setLoc(sl, sc, el, ec);
918 module.addDependency(dep);
919
920 return newline;
921 }
922 default: {
923 if (isModules) {
924 if (name === "@value") {
925 const semi = eatUntilSemi(input, end);
926 const atRuleEnd = semi + 1;
927 const params = input.slice(end, semi);
928 let [alias, from] = params.split(/\s*from\s*/);
929
930 if (from) {
931 const aliases = alias
932 .replace(CSS_COMMENT, " ")
933 .trim()
934 .replace(/^\(|\)$/g, "")
935 .split(/\s*,\s*/);
936
937 from = from.replace(CSS_COMMENT, "").trim();
938
939 const isExplicitImport = from[0] === "'" || from[0] === '"';
940
941 if (isExplicitImport) {
942 from = from.slice(1, -1);
943 }
944
945 for (const alias of aliases) {
946 const [name, aliasName] = alias.split(/\s*as\s*/);
947
948 icssDefinitions.set(aliasName || name, {
949 value: name,
950 path: from
951 });
952 }
953 } else {
954 const ident = walkCssTokens.eatIdentSequence(alias, 0);
955
956 if (!ident) {
957 this._emitWarning(
958 state,
959 `Broken '@value' at-rule: ${input.slice(
960 start,
961 atRuleEnd
962 )}'`,
963 locConverter,
964 start,
965 atRuleEnd
966 );
967
968 const dep = new ConstDependency("", [start, atRuleEnd]);
969 module.addPresentationalDependency(dep);
970 return atRuleEnd;
971 }
972
973 const pos = walkCssTokens.eatWhitespaceAndComments(
974 alias,
975 ident[1]
976 );
977
978 const name = alias.slice(ident[0], ident[1]);
979 let value =
980 alias.charCodeAt(pos) === CC_COLON
981 ? alias.slice(pos + 1)
982 : alias.slice(ident[1]);
983
984 if (value && !/^\s+$/.test(value)) {
985 value = value.trim();
986 }
987
988 if (icssDefinitions.has(value)) {
989 const def = icssDefinitions.get(value);
990
991 value = def.value;
992 }
993
994 icssDefinitions.set(name, { value });
995
996 const dep = new CssIcssExportDependency(name, value);
997 const { line: sl, column: sc } = locConverter.get(start);
998 const { line: el, column: ec } = locConverter.get(end);
999 dep.setLoc(sl, sc, el, ec);
1000 module.addDependency(dep);
1001 }
1002
1003 const dep = new ConstDependency("", [start, atRuleEnd]);
1004 module.addPresentationalDependency(dep);
1005 return atRuleEnd;
1006 } else if (
1007 OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE.test(name) &&
1008 isLocalMode()
1009 ) {
1010 const ident = walkCssTokens.eatIdentSequenceOrString(
1011 input,
1012 end
1013 );
1014 if (!ident) return end;
1015 const name = unescapeIdentifier(
1016 ident[2] === true
1017 ? input.slice(ident[0], ident[1])
1018 : input.slice(ident[0] + 1, ident[1] - 1)
1019 );
1020 const { line: sl, column: sc } = locConverter.get(ident[0]);
1021 const { line: el, column: ec } = locConverter.get(ident[1]);
1022 const dep = new CssLocalIdentifierDependency(name, [
1023 ident[0],
1024 ident[1]
1025 ]);
1026 dep.setLoc(sl, sc, el, ec);
1027 module.addDependency(dep);
1028 return ident[1];
1029 } else if (name === "@property" && isLocalMode()) {
1030 const ident = walkCssTokens.eatIdentSequence(input, end);
1031 if (!ident) return end;
1032 let name = input.slice(ident[0], ident[1]);
1033 if (!name.startsWith("--") || name.length < 3) return end;
1034 name = unescapeIdentifier(name.slice(2));
1035 declaredCssVariables.add(name);
1036 const { line: sl, column: sc } = locConverter.get(ident[0]);
1037 const { line: el, column: ec } = locConverter.get(ident[1]);
1038 const dep = new CssLocalIdentifierDependency(
1039 name,
1040 [ident[0], ident[1]],
1041 "--"
1042 );
1043 dep.setLoc(sl, sc, el, ec);
1044 module.addDependency(dep);
1045 return ident[1];
1046 } else if (name === "@scope") {
1047 isNextRulePrelude = true;
1048 return end;
1049 }
1050
1051 isNextRulePrelude = false;
1052 }
1053 }
1054 }
1055
1056 return end;
1057 },
1058 semicolon: (input, start, end) => {
1059 if (isModules && scope === CSS_MODE_IN_BLOCK) {
1060 if (isLocalMode()) {
1061 processDeclarationValueDone(input);
1062 inAnimationProperty = false;
1063 }
1064
1065 isNextRulePrelude = isNextNestedSyntax(input, end);
1066 }
1067 return end;
1068 },
1069 identifier: (input, start, end) => {
1070 if (isModules) {
1071 if (icssDefinitions.has(input.slice(start, end))) {
1072 const name = input.slice(start, end);
1073 let { path, value } = icssDefinitions.get(name);
1074
1075 if (path) {
1076 if (icssDefinitions.has(path)) {
1077 const definition = icssDefinitions.get(path);
1078
1079 path = definition.value.slice(1, -1);
1080 }
1081
1082 const dep = new CssIcssImportDependency(path, value, [
1083 start,
1084 end - 1
1085 ]);
1086 const { line: sl, column: sc } = locConverter.get(start);
1087 const { line: el, column: ec } = locConverter.get(end - 1);
1088 dep.setLoc(sl, sc, el, ec);
1089 module.addDependency(dep);
1090 } else {
1091 const { line: sl, column: sc } = locConverter.get(start);
1092 const { line: el, column: ec } = locConverter.get(end);
1093 const dep = new CssIcssSymbolDependency(name, value, [
1094 start,
1095 end
1096 ]);
1097 dep.setLoc(sl, sc, el, ec);
1098 module.addDependency(dep);
1099 }
1100
1101 return end;
1102 }
1103
1104 switch (scope) {
1105 case CSS_MODE_IN_BLOCK: {
1106 if (isLocalMode()) {
1107 // Handle only top level values and not inside functions
1108 if (inAnimationProperty && balanced.length === 0) {
1109 lastIdentifier = [start, end, true];
1110 } else {
1111 return processLocalDeclaration(input, start, end);
1112 }
1113 }
1114 break;
1115 }
1116 }
1117 }
1118
1119 return end;
1120 },
1121 delim: (input, start, end) => {
1122 if (isNextRulePrelude && isLocalMode()) {
1123 const ident = walkCssTokens.skipCommentsAndEatIdentSequence(
1124 input,
1125 end
1126 );
1127 if (!ident) return end;
1128 const name = unescapeIdentifier(input.slice(ident[0], ident[1]));
1129 const dep = new CssLocalIdentifierDependency(name, [
1130 ident[0],
1131 ident[1]
1132 ]);
1133 const { line: sl, column: sc } = locConverter.get(ident[0]);
1134 const { line: el, column: ec } = locConverter.get(ident[1]);
1135 dep.setLoc(sl, sc, el, ec);
1136 module.addDependency(dep);
1137 return ident[1];
1138 }
1139
1140 return end;
1141 },
1142 hash: (input, start, end, isID) => {
1143 if (isNextRulePrelude && isLocalMode() && isID) {
1144 const valueStart = start + 1;
1145 const name = unescapeIdentifier(input.slice(valueStart, end));
1146 const dep = new CssLocalIdentifierDependency(name, [valueStart, end]);
1147 const { line: sl, column: sc } = locConverter.get(start);
1148 const { line: el, column: ec } = locConverter.get(end);
1149 dep.setLoc(sl, sc, el, ec);
1150 module.addDependency(dep);
1151 }
1152
1153 return end;
1154 },
1155 colon: (input, start, end) => {
1156 if (isModules) {
1157 const ident = walkCssTokens.skipCommentsAndEatIdentSequence(
1158 input,
1159 end
1160 );
1161 if (!ident) return end;
1162 const name = input.slice(ident[0], ident[1]).toLowerCase();
1163
1164 switch (scope) {
1165 case CSS_MODE_TOP_LEVEL: {
1166 if (name === "import") {
1167 const pos = parseImportOrExport(0, input, ident[1]);
1168 const dep = new ConstDependency("", [start, pos]);
1169 module.addPresentationalDependency(dep);
1170 return pos;
1171 } else if (name === "export") {
1172 const pos = parseImportOrExport(1, input, ident[1]);
1173 const dep = new ConstDependency("", [start, pos]);
1174 module.addPresentationalDependency(dep);
1175 return pos;
1176 }
1177 }
1178 // falls through
1179 default: {
1180 if (isNextRulePrelude) {
1181 const isFn = input.charCodeAt(ident[1]) === CC_LEFT_PARENTHESIS;
1182
1183 if (isFn && name === "local") {
1184 const end = ident[1] + 1;
1185 modeData = "local";
1186 const dep = new ConstDependency("", [start, end]);
1187 module.addPresentationalDependency(dep);
1188 balanced.push([":local", start, end]);
1189 return end;
1190 } else if (name === "local") {
1191 modeData = "local";
1192 // Eat extra whitespace
1193 end = walkCssTokens.eatWhitespace(input, ident[1]);
1194
1195 if (ident[1] === end) {
1196 this._emitWarning(
1197 state,
1198 `Missing whitespace after ':local' in '${input.slice(
1199 start,
1200 eatUntilLeftCurly(input, end) + 1
1201 )}'`,
1202 locConverter,
1203 start,
1204 end
1205 );
1206 }
1207
1208 const dep = new ConstDependency("", [start, end]);
1209 module.addPresentationalDependency(dep);
1210 return end;
1211 } else if (isFn && name === "global") {
1212 const end = ident[1] + 1;
1213 modeData = "global";
1214 const dep = new ConstDependency("", [start, end]);
1215 module.addPresentationalDependency(dep);
1216 balanced.push([":global", start, end]);
1217 return end;
1218 } else if (name === "global") {
1219 modeData = "global";
1220 // Eat extra whitespace
1221 end = walkCssTokens.eatWhitespace(input, ident[1]);
1222
1223 if (ident[1] === end) {
1224 this._emitWarning(
1225 state,
1226 `Missing whitespace after ':global' in '${input.slice(
1227 start,
1228 eatUntilLeftCurly(input, end) + 1
1229 )}'`,
1230 locConverter,
1231 start,
1232 end
1233 );
1234 }
1235
1236 const dep = new ConstDependency("", [start, end]);
1237 module.addPresentationalDependency(dep);
1238 return end;
1239 }
1240 }
1241 }
1242 }
1243 }
1244
1245 lastTokenEndForComments = end;
1246
1247 return end;
1248 },
1249 function: (input, start, end) => {
1250 const name = input
1251 .slice(start, end - 1)
1252 .replace(/\\/g, "")
1253 .toLowerCase();
1254
1255 balanced.push([name, start, end]);
1256
1257 switch (name) {
1258 case "src":
1259 case "url": {
1260 if (!this.url) {
1261 return end;
1262 }
1263
1264 const string = walkCssTokens.eatString(input, end);
1265 if (!string) return end;
1266 const { options, errors: commentErrors } = this.parseCommentOptions(
1267 [lastTokenEndForComments, end]
1268 );
1269 if (commentErrors) {
1270 for (const e of commentErrors) {
1271 const { comment } = e;
1272 state.module.addWarning(
1273 new CommentCompilationWarning(
1274 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
1275 comment.loc
1276 )
1277 );
1278 }
1279 }
1280 if (options && options.webpackIgnore !== undefined) {
1281 if (typeof options.webpackIgnore !== "boolean") {
1282 const { line: sl, column: sc } = locConverter.get(string[0]);
1283 const { line: el, column: ec } = locConverter.get(string[1]);
1284
1285 state.module.addWarning(
1286 new UnsupportedFeatureWarning(
1287 `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
1288 {
1289 start: { line: sl, column: sc },
1290 end: { line: el, column: ec }
1291 }
1292 )
1293 );
1294 } else if (options.webpackIgnore) {
1295 return end;
1296 }
1297 }
1298 const value = normalizeUrl(
1299 input.slice(string[0] + 1, string[1] - 1),
1300 true
1301 );
1302 // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
1303 if (value.length === 0) return end;
1304 const isUrl = name === "url" || name === "src";
1305 const dep = new CssUrlDependency(
1306 value,
1307 [string[0], string[1]],
1308 isUrl ? "string" : "url"
1309 );
1310 const { line: sl, column: sc } = locConverter.get(string[0]);
1311 const { line: el, column: ec } = locConverter.get(string[1]);
1312 dep.setLoc(sl, sc, el, ec);
1313 module.addDependency(dep);
1314 module.addCodeGenerationDependency(dep);
1315 return string[1];
1316 }
1317 default: {
1318 if (this.url && IMAGE_SET_FUNCTION.test(name)) {
1319 lastTokenEndForComments = end;
1320 const values = walkCssTokens.eatImageSetStrings(input, end, {
1321 comment
1322 });
1323 if (values.length === 0) return end;
1324 for (const [index, string] of values.entries()) {
1325 const value = normalizeUrl(
1326 input.slice(string[0] + 1, string[1] - 1),
1327 true
1328 );
1329 if (value.length === 0) return end;
1330 const { options, errors: commentErrors } =
1331 this.parseCommentOptions([
1332 index === 0 ? start : values[index - 1][1],
1333 string[1]
1334 ]);
1335 if (commentErrors) {
1336 for (const e of commentErrors) {
1337 const { comment } = e;
1338 state.module.addWarning(
1339 new CommentCompilationWarning(
1340 `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
1341 comment.loc
1342 )
1343 );
1344 }
1345 }
1346 if (options && options.webpackIgnore !== undefined) {
1347 if (typeof options.webpackIgnore !== "boolean") {
1348 const { line: sl, column: sc } = locConverter.get(
1349 string[0]
1350 );
1351 const { line: el, column: ec } = locConverter.get(
1352 string[1]
1353 );
1354
1355 state.module.addWarning(
1356 new UnsupportedFeatureWarning(
1357 `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
1358 {
1359 start: { line: sl, column: sc },
1360 end: { line: el, column: ec }
1361 }
1362 )
1363 );
1364 } else if (options.webpackIgnore) {
1365 continue;
1366 }
1367 }
1368 const dep = new CssUrlDependency(
1369 value,
1370 [string[0], string[1]],
1371 "url"
1372 );
1373 const { line: sl, column: sc } = locConverter.get(string[0]);
1374 const { line: el, column: ec } = locConverter.get(string[1]);
1375 dep.setLoc(sl, sc, el, ec);
1376 module.addDependency(dep);
1377 module.addCodeGenerationDependency(dep);
1378 }
1379 // Can contain `url()` inside, so let's return end to allow parse them
1380 return end;
1381 } else if (isLocalMode()) {
1382 // Don't rename animation name when we have `var()` function
1383 if (inAnimationProperty && balanced.length === 1) {
1384 lastIdentifier = undefined;
1385 }
1386
1387 if (name === "var") {
1388 const customIdent = walkCssTokens.eatIdentSequence(input, end);
1389 if (!customIdent) return end;
1390 let name = input.slice(customIdent[0], customIdent[1]);
1391 // A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo.
1392 // The <custom-property-name> production corresponds to this:
1393 // it’s defined as any <dashed-ident> (a valid identifier that starts with two dashes),
1394 // except -- itself, which is reserved for future use by CSS.
1395 if (!name.startsWith("--") || name.length < 3) return end;
1396 name = unescapeIdentifier(
1397 input.slice(customIdent[0] + 2, customIdent[1])
1398 );
1399 const afterCustomIdent = walkCssTokens.eatWhitespaceAndComments(
1400 input,
1401 customIdent[1]
1402 );
1403 if (
1404 input.charCodeAt(afterCustomIdent) === CC_LOWER_F ||
1405 input.charCodeAt(afterCustomIdent) === CC_UPPER_F
1406 ) {
1407 const fromWord = walkCssTokens.eatIdentSequence(
1408 input,
1409 afterCustomIdent
1410 );
1411 if (
1412 !fromWord ||
1413 input.slice(fromWord[0], fromWord[1]).toLowerCase() !==
1414 "from"
1415 ) {
1416 return end;
1417 }
1418 const from = walkCssTokens.eatIdentSequenceOrString(
1419 input,
1420 walkCssTokens.eatWhitespaceAndComments(input, fromWord[1])
1421 );
1422 if (!from) {
1423 return end;
1424 }
1425 const path = input.slice(from[0], from[1]);
1426 if (from[2] === true && path === "global") {
1427 const dep = new ConstDependency("", [
1428 customIdent[1],
1429 from[1]
1430 ]);
1431 module.addPresentationalDependency(dep);
1432 return end;
1433 } else if (from[2] === false) {
1434 const dep = new CssIcssImportDependency(
1435 path.slice(1, -1),
1436 name,
1437 [customIdent[0], from[1] - 1]
1438 );
1439 const { line: sl, column: sc } = locConverter.get(
1440 customIdent[0]
1441 );
1442 const { line: el, column: ec } = locConverter.get(
1443 from[1] - 1
1444 );
1445 dep.setLoc(sl, sc, el, ec);
1446 module.addDependency(dep);
1447 }
1448 } else {
1449 const { line: sl, column: sc } = locConverter.get(
1450 customIdent[0]
1451 );
1452 const { line: el, column: ec } = locConverter.get(
1453 customIdent[1]
1454 );
1455 const dep = new CssSelfLocalIdentifierDependency(
1456 name,
1457 [customIdent[0], customIdent[1]],
1458 "--",
1459 declaredCssVariables
1460 );
1461 dep.setLoc(sl, sc, el, ec);
1462 module.addDependency(dep);
1463 return end;
1464 }
1465 }
1466 }
1467 }
1468 }
1469
1470 return end;
1471 },
1472 leftParenthesis: (input, start, end) => {
1473 balanced.push(["(", start, end]);
1474
1475 return end;
1476 },
1477 rightParenthesis: (input, start, end) => {
1478 const popped = balanced.pop();
1479
1480 if (
1481 isModules &&
1482 popped &&
1483 (popped[0] === ":local" || popped[0] === ":global")
1484 ) {
1485 modeData = balanced[balanced.length - 1]
1486 ? /** @type {"local" | "global"} */
1487 (balanced[balanced.length - 1][0])
1488 : undefined;
1489 const dep = new ConstDependency("", [start, end]);
1490 module.addPresentationalDependency(dep);
1491 }
1492
1493 return end;
1494 },
1495 comma: (input, start, end) => {
1496 if (isModules) {
1497 // Reset stack for `:global .class :local .class-other` selector after
1498 modeData = undefined;
1499
1500 if (scope === CSS_MODE_IN_BLOCK && isLocalMode()) {
1501 processDeclarationValueDone(input);
1502 }
1503 }
1504
1505 lastTokenEndForComments = start;
1506
1507 return end;
1508 }
1509 });
1510
1511 /** @type {BuildInfo} */
1512 (module.buildInfo).strict = true;
1513 /** @type {BuildMeta} */
1514 (module.buildMeta).exportsType = this.namedExports
1515 ? "namespace"
1516 : "default";
1517
1518 if (!this.namedExports) {
1519 /** @type {BuildMeta} */
1520 (module.buildMeta).defaultObject = "redirect";
1521 }
1522
1523 module.addDependency(new StaticExportsDependency([], true));
1524 return state;
1525 }
1526
1527 /**
1528 * @param {Range} range range
1529 * @returns {Comment[]} comments in the range
1530 */
1531 getComments(range) {
1532 if (!this.comments) return [];
1533 const [rangeStart, rangeEnd] = range;
1534 /**
1535 * @param {Comment} comment comment
1536 * @param {number} needle needle
1537 * @returns {number} compared
1538 */
1539 const compare = (comment, needle) =>
1540 /** @type {Range} */ (comment.range)[0] - needle;
1541 const comments = /** @type {Comment[]} */ (this.comments);
1542 let idx = binarySearchBounds.ge(comments, rangeStart, compare);
1543 /** @type {Comment[]} */
1544 const commentsInRange = [];
1545 while (
1546 comments[idx] &&
1547 /** @type {Range} */ (comments[idx].range)[1] <= rangeEnd
1548 ) {
1549 commentsInRange.push(comments[idx]);
1550 idx++;
1551 }
1552
1553 return commentsInRange;
1554 }
1555
1556 /**
1557 * @param {Range} range range of the comment
1558 * @returns {{ options: Record<string, any> | null, errors: (Error & { comment: Comment })[] | null }} result
1559 */
1560 parseCommentOptions(range) {
1561 const comments = this.getComments(range);
1562 if (comments.length === 0) {
1563 return EMPTY_COMMENT_OPTIONS;
1564 }
1565 /** @type {Record<string, EXPECTED_ANY> } */
1566 const options = {};
1567 /** @type {(Error & { comment: Comment })[]} */
1568 const errors = [];
1569 for (const comment of comments) {
1570 const { value } = comment;
1571 if (value && webpackCommentRegExp.test(value)) {
1572 // try compile only if webpack options comment is present
1573 try {
1574 for (let [key, val] of Object.entries(
1575 vm.runInContext(
1576 `(function(){return {${value}};})()`,
1577 this.magicCommentContext
1578 )
1579 )) {
1580 if (typeof val === "object" && val !== null) {
1581 val =
1582 val.constructor.name === "RegExp"
1583 ? new RegExp(val)
1584 : JSON.parse(JSON.stringify(val));
1585 }
1586 options[key] = val;
1587 }
1588 } catch (err) {
1589 const newErr = new Error(String(/** @type {Error} */ (err).message));
1590 newErr.stack = String(/** @type {Error} */ (err).stack);
1591 Object.assign(newErr, { comment });
1592 errors.push(/** @type (Error & { comment: Comment }) */ (newErr));
1593 }
1594 }
1595 }
1596 return { options, errors };
1597 }
1598}
1599
1600module.exports = CssParser;
1601module.exports.escapeIdentifier = escapeIdentifier;
1602module.exports.unescapeIdentifier = unescapeIdentifier;
Note: See TracBrowser for help on using the repository browser.