| 1 | /*
|
|---|
| 2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
|---|
| 3 | Author Tobias Koppers @sokra
|
|---|
| 4 | */
|
|---|
| 5 |
|
|---|
| 6 | "use strict";
|
|---|
| 7 |
|
|---|
| 8 | const path = require("path");
|
|---|
| 9 | const vm = require("vm");
|
|---|
| 10 | const { CSS_MODULE_TYPE_AUTO } = require("../ModuleTypeConstants");
|
|---|
| 11 | const Parser = require("../Parser");
|
|---|
| 12 | const ConstDependency = require("../dependencies/ConstDependency");
|
|---|
| 13 | const CssIcssExportDependency = require("../dependencies/CssIcssExportDependency");
|
|---|
| 14 | const CssIcssImportDependency = require("../dependencies/CssIcssImportDependency");
|
|---|
| 15 | const CssIcssSymbolDependency = require("../dependencies/CssIcssSymbolDependency");
|
|---|
| 16 | const CssImportDependency = require("../dependencies/CssImportDependency");
|
|---|
| 17 | const CssUrlDependency = require("../dependencies/CssUrlDependency");
|
|---|
| 18 | const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
|
|---|
| 19 | const CommentCompilationWarning = require("../errors/CommentCompilationWarning");
|
|---|
| 20 | const ModuleDependencyWarning = require("../errors/ModuleDependencyWarning");
|
|---|
| 21 | const UnsupportedFeatureWarning = require("../errors/UnsupportedFeatureWarning");
|
|---|
| 22 | const WebpackError = require("../errors/WebpackError");
|
|---|
| 23 | const LocConverter = require("../util/LocConverter");
|
|---|
| 24 | const binarySearchBounds = require("../util/binarySearchBounds");
|
|---|
| 25 | const { parseResource } = require("../util/identifier");
|
|---|
| 26 | const {
|
|---|
| 27 | createMagicCommentContext,
|
|---|
| 28 | webpackCommentRegExp
|
|---|
| 29 | } = require("../util/magicComment");
|
|---|
| 30 | const topologicalSort = require("../util/topologicalSort");
|
|---|
| 31 | const walkCssTokens = require("./walkCssTokens");
|
|---|
| 32 |
|
|---|
| 33 | /** @typedef {import("../Module").BuildInfo} BuildInfo */
|
|---|
| 34 | /** @typedef {import("../Module").BuildMeta} BuildMeta */
|
|---|
| 35 | /** @typedef {import("../Parser").ParserState} ParserState */
|
|---|
| 36 | /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
|
|---|
| 37 | /** @typedef {import("./walkCssTokens").CssTokenCallbacks} CssTokenCallbacks */
|
|---|
| 38 | /** @typedef {import("../../declarations/WebpackOptions").CssAutoOrModuleParserOptions} CssAutoOrModuleParserOptions */
|
|---|
| 39 | /** @typedef {import("../../declarations/WebpackOptions").CssModuleParserOptions} CssModuleParserOptions */
|
|---|
| 40 | /** @typedef {import("./CssModule")} CssModule */
|
|---|
| 41 |
|
|---|
| 42 | /** @typedef {[number, number]} Range */
|
|---|
| 43 | /** @typedef {{ line: number, column: number }} Position */
|
|---|
| 44 | /** @typedef {{ value: string, range: Range, loc: { start: Position, end: Position } }} Comment */
|
|---|
| 45 |
|
|---|
| 46 | const CC_COLON = ":".charCodeAt(0);
|
|---|
| 47 | const CC_SEMICOLON = ";".charCodeAt(0);
|
|---|
| 48 | const CC_COMMA = ",".charCodeAt(0);
|
|---|
| 49 | const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);
|
|---|
| 50 | const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);
|
|---|
| 51 | const CC_LOWER_F = "f".charCodeAt(0);
|
|---|
| 52 | const CC_UPPER_F = "F".charCodeAt(0);
|
|---|
| 53 | const CC_RIGHT_CURLY = "}".charCodeAt(0);
|
|---|
| 54 | const CC_HYPHEN_MINUS = "-".charCodeAt(0);
|
|---|
| 55 | const CC_TILDE = "~".charCodeAt(0);
|
|---|
| 56 | const CC_EQUAL = "=".charCodeAt(0);
|
|---|
| 57 | const CC_FULL_STOP = ".".charCodeAt(0);
|
|---|
| 58 | const CC_EXCLAMATION = "!".charCodeAt(0);
|
|---|
| 59 | const CC_AMPERSAND = "&".charCodeAt(0);
|
|---|
| 60 |
|
|---|
| 61 | // https://www.w3.org/TR/css-syntax-3/#newline
|
|---|
| 62 | // We don't have `preprocessing` stage, so we need specify all of them
|
|---|
| 63 | const STRING_MULTILINE = /\\[\n\r\f]/g;
|
|---|
| 64 | // https://www.w3.org/TR/css-syntax-3/#whitespace
|
|---|
| 65 | const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g;
|
|---|
| 66 | const UNESCAPE = /\\([0-9a-f]{1,6}[ \t\n\r\f]?|[\s\S])/gi;
|
|---|
| 67 | const IMAGE_SET_FUNCTION = /^(?:-\w+-)?image-set$/i;
|
|---|
| 68 | const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(?:-\w+-)?keyframes$/;
|
|---|
| 69 | const COMPOSES_PROPERTY = /^(?:composes|compose-with)$/i;
|
|---|
| 70 | const IS_MODULES = /\.modules?\.[^.]+$/i;
|
|---|
| 71 | const CSS_COMMENT = /\/\*((?!\*\/)[\s\S]*?)\*\//g;
|
|---|
| 72 |
|
|---|
| 73 | /**
|
|---|
| 74 | * Returns matches.
|
|---|
| 75 | * @param {RegExp} regexp a regexp
|
|---|
| 76 | * @param {string} str a string
|
|---|
| 77 | * @returns {RegExpExecArray[]} matches
|
|---|
| 78 | */
|
|---|
| 79 | const matchAll = (regexp, str) => {
|
|---|
| 80 | /** @type {RegExpExecArray[]} */
|
|---|
| 81 | const result = [];
|
|---|
| 82 |
|
|---|
| 83 | /** @type {null | RegExpExecArray} */
|
|---|
| 84 | let match;
|
|---|
| 85 |
|
|---|
| 86 | // Use a while loop with exec() to find all matches
|
|---|
| 87 | while ((match = regexp.exec(str)) !== null) {
|
|---|
| 88 | result.push(match);
|
|---|
| 89 | }
|
|---|
| 90 | // Return an array to be easily iterable (note: a true spec-compliant polyfill
|
|---|
| 91 | // returns an iterator object, but an array spread often suffices for basic use)
|
|---|
| 92 | return result;
|
|---|
| 93 | };
|
|---|
| 94 |
|
|---|
| 95 | /**
|
|---|
| 96 | * Returns normalized url.
|
|---|
| 97 | * @param {string} str url string
|
|---|
| 98 | * @param {boolean} isString is url wrapped in quotes
|
|---|
| 99 | * @returns {string} normalized url
|
|---|
| 100 | */
|
|---|
| 101 | const normalizeUrl = (str, isString) => {
|
|---|
| 102 | // Remove extra spaces and newlines:
|
|---|
| 103 | // `url("im\
|
|---|
| 104 | // g.png")`
|
|---|
| 105 | if (isString) {
|
|---|
| 106 | str = str.replace(STRING_MULTILINE, "");
|
|---|
| 107 | }
|
|---|
| 108 |
|
|---|
| 109 | str = str
|
|---|
| 110 | // Remove unnecessary spaces from `url(" img.png ")`
|
|---|
| 111 | .replace(TRIM_WHITE_SPACES, "")
|
|---|
| 112 | // Unescape
|
|---|
| 113 | .replace(UNESCAPE, (match) => {
|
|---|
| 114 | if (match.length > 2) {
|
|---|
| 115 | return String.fromCharCode(Number.parseInt(match.slice(1).trim(), 16));
|
|---|
| 116 | }
|
|---|
| 117 | return match[1];
|
|---|
| 118 | });
|
|---|
| 119 |
|
|---|
| 120 | if (/^data:/i.test(str)) {
|
|---|
| 121 | return str;
|
|---|
| 122 | }
|
|---|
| 123 |
|
|---|
| 124 | if (str.includes("%")) {
|
|---|
| 125 | // Convert `url('%2E/img.png')` -> `url('./img.png')`
|
|---|
| 126 | try {
|
|---|
| 127 | str = decodeURIComponent(str);
|
|---|
| 128 | } catch (_err) {
|
|---|
| 129 | // Ignore
|
|---|
| 130 | }
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | return str;
|
|---|
| 134 | };
|
|---|
| 135 |
|
|---|
| 136 | const { escapeIdentifier, unescapeIdentifier } = walkCssTokens;
|
|---|
| 137 |
|
|---|
| 138 | /**
|
|---|
| 139 | * A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo.
|
|---|
| 140 | * The <custom-property-name> production corresponds to this:
|
|---|
| 141 | * it’s defined as any <dashed-ident> (a valid identifier that starts with two dashes),
|
|---|
| 142 | * except -- itself, which is reserved for future use by CSS.
|
|---|
| 143 | * @param {string} identifier identifier
|
|---|
| 144 | * @returns {boolean} true when identifier is dashed, otherwise false
|
|---|
| 145 | */
|
|---|
| 146 | const isDashedIdentifier = (identifier) =>
|
|---|
| 147 | identifier.startsWith("--") && identifier.length >= 3;
|
|---|
| 148 |
|
|---|
| 149 | /** @type {Record<string, number>} */
|
|---|
| 150 | const PREDEFINED_COUNTER_STYLES = {
|
|---|
| 151 | decimal: 1,
|
|---|
| 152 | "decimal-leading-zero": 1,
|
|---|
| 153 | "arabic-indic": 1,
|
|---|
| 154 | armenian: 1,
|
|---|
| 155 | "upper-armenian": 1,
|
|---|
| 156 | "lower-armenian": 1,
|
|---|
| 157 | bengali: 1,
|
|---|
| 158 | cambodian: 1,
|
|---|
| 159 | khmer: 1,
|
|---|
| 160 | "cjk-decimal": 1,
|
|---|
| 161 | devanagari: 1,
|
|---|
| 162 | georgian: 1,
|
|---|
| 163 | gujarati: 1,
|
|---|
| 164 | /* cspell:disable-next-line */
|
|---|
| 165 | gurmukhi: 1,
|
|---|
| 166 | hebrew: 1,
|
|---|
| 167 | kannada: 1,
|
|---|
| 168 | lao: 1,
|
|---|
| 169 | malayalam: 1,
|
|---|
| 170 | mongolian: 1,
|
|---|
| 171 | myanmar: 1,
|
|---|
| 172 | oriya: 1,
|
|---|
| 173 | persian: 1,
|
|---|
| 174 | "lower-roman": 1,
|
|---|
| 175 | "upper-roman": 1,
|
|---|
| 176 | tamil: 1,
|
|---|
| 177 | telugu: 1,
|
|---|
| 178 | thai: 1,
|
|---|
| 179 | tibetan: 1,
|
|---|
| 180 |
|
|---|
| 181 | "lower-alpha": 1,
|
|---|
| 182 | "lower-latin": 1,
|
|---|
| 183 | "upper-alpha": 1,
|
|---|
| 184 | "upper-latin": 1,
|
|---|
| 185 | "lower-greek": 1,
|
|---|
| 186 | hiragana: 1,
|
|---|
| 187 | /* cspell:disable-next-line */
|
|---|
| 188 | "hiragana-iroha": 1,
|
|---|
| 189 | katakana: 1,
|
|---|
| 190 | /* cspell:disable-next-line */
|
|---|
| 191 | "katakana-iroha": 1,
|
|---|
| 192 |
|
|---|
| 193 | disc: 1,
|
|---|
| 194 | circle: 1,
|
|---|
| 195 | square: 1,
|
|---|
| 196 | "disclosure-open": 1,
|
|---|
| 197 | "disclosure-closed": 1,
|
|---|
| 198 |
|
|---|
| 199 | "cjk-earthly-branch": 1,
|
|---|
| 200 | "cjk-heavenly-stem": 1,
|
|---|
| 201 |
|
|---|
| 202 | "japanese-informal": 1,
|
|---|
| 203 | "japanese-formal": 1,
|
|---|
| 204 |
|
|---|
| 205 | "korean-hangul-formal": 1,
|
|---|
| 206 | /* cspell:disable-next-line */
|
|---|
| 207 | "korean-hanja-informal": 1,
|
|---|
| 208 | /* cspell:disable-next-line */
|
|---|
| 209 | "korean-hanja-formal": 1,
|
|---|
| 210 |
|
|---|
| 211 | "simp-chinese-informal": 1,
|
|---|
| 212 | "simp-chinese-formal": 1,
|
|---|
| 213 | "trad-chinese-informal": 1,
|
|---|
| 214 | "trad-chinese-formal": 1,
|
|---|
| 215 | "cjk-ideographic": 1,
|
|---|
| 216 |
|
|---|
| 217 | "ethiopic-numeric": 1
|
|---|
| 218 | };
|
|---|
| 219 |
|
|---|
| 220 | /** @type {Record<string, number>} */
|
|---|
| 221 | const GLOBAL_VALUES = {
|
|---|
| 222 | // Global values
|
|---|
| 223 | initial: Infinity,
|
|---|
| 224 | inherit: Infinity,
|
|---|
| 225 | unset: Infinity,
|
|---|
| 226 | revert: Infinity,
|
|---|
| 227 | "revert-layer": Infinity
|
|---|
| 228 | };
|
|---|
| 229 |
|
|---|
| 230 | /** @type {Record<string, number>} */
|
|---|
| 231 | const GRID_AREA_OR_COLUMN_OR_ROW = {
|
|---|
| 232 | auto: Infinity,
|
|---|
| 233 | span: Infinity,
|
|---|
| 234 | ...GLOBAL_VALUES
|
|---|
| 235 | };
|
|---|
| 236 |
|
|---|
| 237 | /** @type {Record<string, number>} */
|
|---|
| 238 | const GRID_AUTO_COLUMNS_OR_ROW = {
|
|---|
| 239 | "min-content": Infinity,
|
|---|
| 240 | "max-content": Infinity,
|
|---|
| 241 | auto: Infinity,
|
|---|
| 242 | ...GLOBAL_VALUES
|
|---|
| 243 | };
|
|---|
| 244 |
|
|---|
| 245 | /** @type {Record<string, number>} */
|
|---|
| 246 | const GRID_AUTO_FLOW = {
|
|---|
| 247 | row: 1,
|
|---|
| 248 | column: 1,
|
|---|
| 249 | dense: 1,
|
|---|
| 250 | ...GLOBAL_VALUES
|
|---|
| 251 | };
|
|---|
| 252 |
|
|---|
| 253 | /** @type {Record<string, number>} */
|
|---|
| 254 | const GRID_TEMPLATE_AREAS = {
|
|---|
| 255 | // Special
|
|---|
| 256 | none: 1,
|
|---|
| 257 | ...GLOBAL_VALUES
|
|---|
| 258 | };
|
|---|
| 259 |
|
|---|
| 260 | /** @type {Record<string, number>} */
|
|---|
| 261 | const GRID_TEMPLATE_COLUMNS_OR_ROWS = {
|
|---|
| 262 | none: 1,
|
|---|
| 263 | subgrid: 1,
|
|---|
| 264 | masonry: 1,
|
|---|
| 265 | "max-content": Infinity,
|
|---|
| 266 | "min-content": Infinity,
|
|---|
| 267 | auto: Infinity,
|
|---|
| 268 | ...GLOBAL_VALUES
|
|---|
| 269 | };
|
|---|
| 270 |
|
|---|
| 271 | /** @type {Record<string, number>} */
|
|---|
| 272 | const GRID_TEMPLATE = {
|
|---|
| 273 | ...GRID_TEMPLATE_AREAS,
|
|---|
| 274 | ...GRID_TEMPLATE_COLUMNS_OR_ROWS
|
|---|
| 275 | };
|
|---|
| 276 |
|
|---|
| 277 | /** @type {Record<string, number>} */
|
|---|
| 278 | const GRID = {
|
|---|
| 279 | "auto-flow": 1,
|
|---|
| 280 | dense: 1,
|
|---|
| 281 | ...GRID_AUTO_COLUMNS_OR_ROW,
|
|---|
| 282 | ...GRID_AUTO_FLOW,
|
|---|
| 283 | ...GRID_TEMPLATE_AREAS,
|
|---|
| 284 | ...GRID_TEMPLATE_COLUMNS_OR_ROWS
|
|---|
| 285 | };
|
|---|
| 286 |
|
|---|
| 287 | /**
|
|---|
| 288 | * Gets known properties.
|
|---|
| 289 | * @param {{ animation?: boolean, container?: boolean, customIdents?: boolean, grid?: boolean }=} options options
|
|---|
| 290 | * @returns {Map<string, Record<string, number>>} list of known properties
|
|---|
| 291 | */
|
|---|
| 292 | const getKnownProperties = (options = {}) => {
|
|---|
| 293 | /** @type {Map<string, Record<string, number>>} */
|
|---|
| 294 | const knownProperties = new Map();
|
|---|
| 295 |
|
|---|
| 296 | if (options.animation) {
|
|---|
| 297 | knownProperties.set("animation", {
|
|---|
| 298 | // animation-direction
|
|---|
| 299 | normal: 1,
|
|---|
| 300 | reverse: 1,
|
|---|
| 301 | alternate: 1,
|
|---|
| 302 | "alternate-reverse": 1,
|
|---|
| 303 | // animation-fill-mode
|
|---|
| 304 | forwards: 1,
|
|---|
| 305 | backwards: 1,
|
|---|
| 306 | both: 1,
|
|---|
| 307 | // animation-iteration-count
|
|---|
| 308 | infinite: 1,
|
|---|
| 309 | // animation-play-state
|
|---|
| 310 | paused: 1,
|
|---|
| 311 | running: 1,
|
|---|
| 312 | // animation-timing-function
|
|---|
| 313 | ease: 1,
|
|---|
| 314 | "ease-in": 1,
|
|---|
| 315 | "ease-out": 1,
|
|---|
| 316 | "ease-in-out": 1,
|
|---|
| 317 | linear: 1,
|
|---|
| 318 | "step-end": 1,
|
|---|
| 319 | "step-start": 1,
|
|---|
| 320 | // Special
|
|---|
| 321 | none: Infinity, // No matter how many times you write none, it will never be an animation name
|
|---|
| 322 | ...GLOBAL_VALUES
|
|---|
| 323 | });
|
|---|
| 324 | knownProperties.set("animation-name", {
|
|---|
| 325 | // Special
|
|---|
| 326 | none: Infinity, // No matter how many times you write none, it will never be an animation name
|
|---|
| 327 | ...GLOBAL_VALUES
|
|---|
| 328 | });
|
|---|
| 329 | }
|
|---|
| 330 |
|
|---|
| 331 | if (options.container) {
|
|---|
| 332 | knownProperties.set("container", {
|
|---|
| 333 | // container-type
|
|---|
| 334 | normal: 1,
|
|---|
| 335 | size: 1,
|
|---|
| 336 | "inline-size": 1,
|
|---|
| 337 | "scroll-state": 1,
|
|---|
| 338 | // Special
|
|---|
| 339 | none: Infinity,
|
|---|
| 340 | ...GLOBAL_VALUES
|
|---|
| 341 | });
|
|---|
| 342 | knownProperties.set("container-name", {
|
|---|
| 343 | // Special
|
|---|
| 344 | none: Infinity,
|
|---|
| 345 | ...GLOBAL_VALUES
|
|---|
| 346 | });
|
|---|
| 347 | }
|
|---|
| 348 |
|
|---|
| 349 | if (options.customIdents) {
|
|---|
| 350 | knownProperties.set("list-style", {
|
|---|
| 351 | // list-style-position
|
|---|
| 352 | inside: 1,
|
|---|
| 353 | outside: 1,
|
|---|
| 354 | // list-style-type
|
|---|
| 355 | ...PREDEFINED_COUNTER_STYLES,
|
|---|
| 356 | // Special
|
|---|
| 357 | none: Infinity,
|
|---|
| 358 | ...GLOBAL_VALUES
|
|---|
| 359 | });
|
|---|
| 360 | knownProperties.set("list-style-type", {
|
|---|
| 361 | // list-style-type
|
|---|
| 362 | ...PREDEFINED_COUNTER_STYLES,
|
|---|
| 363 | // Special
|
|---|
| 364 | none: Infinity,
|
|---|
| 365 | ...GLOBAL_VALUES
|
|---|
| 366 | });
|
|---|
| 367 | knownProperties.set("system", {
|
|---|
| 368 | cyclic: 1,
|
|---|
| 369 | numeric: 1,
|
|---|
| 370 | alphabetic: 1,
|
|---|
| 371 | symbolic: 1,
|
|---|
| 372 | additive: 1,
|
|---|
| 373 | fixed: 1,
|
|---|
| 374 | extends: 1,
|
|---|
| 375 | ...PREDEFINED_COUNTER_STYLES
|
|---|
| 376 | });
|
|---|
| 377 | knownProperties.set("fallback", {
|
|---|
| 378 | ...PREDEFINED_COUNTER_STYLES
|
|---|
| 379 | });
|
|---|
| 380 | knownProperties.set("speak-as", {
|
|---|
| 381 | auto: 1,
|
|---|
| 382 | bullets: 1,
|
|---|
| 383 | numbers: 1,
|
|---|
| 384 | words: 1,
|
|---|
| 385 | "spell-out": 1,
|
|---|
| 386 | ...PREDEFINED_COUNTER_STYLES
|
|---|
| 387 | });
|
|---|
| 388 | }
|
|---|
| 389 |
|
|---|
| 390 | if (options.grid) {
|
|---|
| 391 | knownProperties.set("grid", GRID);
|
|---|
| 392 | knownProperties.set("grid-area", GRID_AREA_OR_COLUMN_OR_ROW);
|
|---|
| 393 | knownProperties.set("grid-column", GRID_AREA_OR_COLUMN_OR_ROW);
|
|---|
| 394 | knownProperties.set("grid-column-end", GRID_AREA_OR_COLUMN_OR_ROW);
|
|---|
| 395 | knownProperties.set("grid-column-start", GRID_AREA_OR_COLUMN_OR_ROW);
|
|---|
| 396 | knownProperties.set("grid-row", GRID_AREA_OR_COLUMN_OR_ROW);
|
|---|
| 397 | knownProperties.set("grid-row-end", GRID_AREA_OR_COLUMN_OR_ROW);
|
|---|
| 398 | knownProperties.set("grid-row-start", GRID_AREA_OR_COLUMN_OR_ROW);
|
|---|
| 399 | knownProperties.set("grid-template", GRID_TEMPLATE);
|
|---|
| 400 | knownProperties.set("grid-template-areas", GRID_TEMPLATE_AREAS);
|
|---|
| 401 | knownProperties.set("grid-template-columns", GRID_TEMPLATE_COLUMNS_OR_ROWS);
|
|---|
| 402 | knownProperties.set("grid-template-rows", GRID_TEMPLATE_COLUMNS_OR_ROWS);
|
|---|
| 403 | }
|
|---|
| 404 |
|
|---|
| 405 | return knownProperties;
|
|---|
| 406 | };
|
|---|
| 407 |
|
|---|
| 408 | const EMPTY_COMMENT_OPTIONS = {
|
|---|
| 409 | options: null,
|
|---|
| 410 | errors: null
|
|---|
| 411 | };
|
|---|
| 412 |
|
|---|
| 413 | const CSS_MODE_TOP_LEVEL = 0;
|
|---|
| 414 | const CSS_MODE_IN_BLOCK = 1;
|
|---|
| 415 |
|
|---|
| 416 | const LOCAL_MODE = 0;
|
|---|
| 417 | const GLOBAL_MODE = 1;
|
|---|
| 418 |
|
|---|
| 419 | const eatUntilSemi = walkCssTokens.eatUntil(";");
|
|---|
| 420 | const eatUntilLeftCurly = walkCssTokens.eatUntil("{");
|
|---|
| 421 |
|
|---|
| 422 | /**
|
|---|
| 423 | * Defines the css parser own options type used by this module.
|
|---|
| 424 | * @typedef {object} CssParserOwnOptions
|
|---|
| 425 | * @property {("pure" | "global" | "local" | "auto")=} defaultMode default mode
|
|---|
| 426 | */
|
|---|
| 427 |
|
|---|
| 428 | /** @typedef {CssAutoOrModuleParserOptions & CssParserOwnOptions} CssParserOptions */
|
|---|
| 429 |
|
|---|
| 430 | class CssParser extends Parser {
|
|---|
| 431 | /**
|
|---|
| 432 | * Creates an instance of CssParser.
|
|---|
| 433 | * @param {CssParserOptions=} options options
|
|---|
| 434 | */
|
|---|
| 435 | constructor(options = {}) {
|
|---|
| 436 | super();
|
|---|
| 437 | this.defaultMode =
|
|---|
| 438 | typeof options.defaultMode !== "undefined" ? options.defaultMode : "pure";
|
|---|
| 439 | this.options = {
|
|---|
| 440 | url: true,
|
|---|
| 441 | import: true,
|
|---|
| 442 | namedExports: true,
|
|---|
| 443 | animation: true,
|
|---|
| 444 | container: true,
|
|---|
| 445 | customIdents: true,
|
|---|
| 446 | dashedIdents: true,
|
|---|
| 447 | function: true,
|
|---|
| 448 | grid: true,
|
|---|
| 449 | ...options
|
|---|
| 450 | };
|
|---|
| 451 | /** @type {Comment[] | undefined} */
|
|---|
| 452 | this.comments = undefined;
|
|---|
| 453 | this.magicCommentContext = createMagicCommentContext();
|
|---|
| 454 | }
|
|---|
| 455 |
|
|---|
| 456 | /**
|
|---|
| 457 | * Processes the provided state.
|
|---|
| 458 | * @param {ParserState} state parser state
|
|---|
| 459 | * @param {string} message warning message
|
|---|
| 460 | * @param {LocConverter} locConverter location converter
|
|---|
| 461 | * @param {number} start start offset
|
|---|
| 462 | * @param {number} end end offset
|
|---|
| 463 | */
|
|---|
| 464 | _emitWarning(state, message, locConverter, start, end) {
|
|---|
| 465 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 466 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 467 |
|
|---|
| 468 | state.current.addWarning(
|
|---|
| 469 | new ModuleDependencyWarning(state.module, new WebpackError(message), {
|
|---|
| 470 | start: { line: sl, column: sc },
|
|---|
| 471 | end: { line: el, column: ec }
|
|---|
| 472 | })
|
|---|
| 473 | );
|
|---|
| 474 | }
|
|---|
| 475 |
|
|---|
| 476 | /**
|
|---|
| 477 | * Emits a build error for the provided range.
|
|---|
| 478 | * @param {ParserState} state parser state
|
|---|
| 479 | * @param {string} message error message
|
|---|
| 480 | * @param {LocConverter} locConverter location converter
|
|---|
| 481 | * @param {number} start start offset
|
|---|
| 482 | * @param {number} end end offset
|
|---|
| 483 | */
|
|---|
| 484 | _emitError(state, message, locConverter, start, end) {
|
|---|
| 485 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 486 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 487 |
|
|---|
| 488 | const err = new WebpackError(message);
|
|---|
| 489 | err.module = state.module;
|
|---|
| 490 | err.loc = {
|
|---|
| 491 | start: { line: sl, column: sc },
|
|---|
| 492 | end: { line: el, column: ec }
|
|---|
| 493 | };
|
|---|
| 494 | state.module.addError(err);
|
|---|
| 495 | }
|
|---|
| 496 |
|
|---|
| 497 | /**
|
|---|
| 498 | * Parses the provided source and updates the parser state.
|
|---|
| 499 | * @param {string | Buffer | PreparsedAst} source the source to parse
|
|---|
| 500 | * @param {ParserState} state the parser state
|
|---|
| 501 | * @returns {ParserState} the parser state
|
|---|
| 502 | */
|
|---|
| 503 | parse(source, state) {
|
|---|
| 504 | if (Buffer.isBuffer(source)) {
|
|---|
| 505 | source = source.toString("utf8");
|
|---|
| 506 | } else if (typeof source === "object") {
|
|---|
| 507 | throw new Error("webpackAst is unexpected for the CssParser");
|
|---|
| 508 | }
|
|---|
| 509 | if (source[0] === "\uFEFF") {
|
|---|
| 510 | source = source.slice(1);
|
|---|
| 511 | }
|
|---|
| 512 |
|
|---|
| 513 | const unescapeIdentifierCached = unescapeIdentifier.bindCache(
|
|---|
| 514 | state.compilation.compiler.root
|
|---|
| 515 | );
|
|---|
| 516 |
|
|---|
| 517 | let mode = this.defaultMode;
|
|---|
| 518 |
|
|---|
| 519 | const module = state.module;
|
|---|
| 520 |
|
|---|
| 521 | if (
|
|---|
| 522 | mode === "auto" &&
|
|---|
| 523 | module.type === CSS_MODULE_TYPE_AUTO &&
|
|---|
| 524 | IS_MODULES.test(
|
|---|
| 525 | parseResource(/** @type {string} */ (module.getResource())).path
|
|---|
| 526 | )
|
|---|
| 527 | ) {
|
|---|
| 528 | mode = "local";
|
|---|
| 529 | }
|
|---|
| 530 |
|
|---|
| 531 | const isModules = mode === "global" || mode === "local";
|
|---|
| 532 |
|
|---|
| 533 | const parsedModuleResource = parseResource(
|
|---|
| 534 | /** @type {string} */ (module.getResource())
|
|---|
| 535 | );
|
|---|
| 536 |
|
|---|
| 537 | /**
|
|---|
| 538 | * Check whether a request points back to the current module
|
|---|
| 539 | * (e.g. `composes: foo from "./self.module.css"` inside `self.module.css`).
|
|---|
| 540 | * Only relative requests are checked — aliases / package / absolute requests
|
|---|
| 541 | * fall through to the normal import path. Requests with a `?query` or
|
|---|
| 542 | * `#fragment` are only treated as self when the parent module's resource
|
|---|
| 543 | * has the same query/fragment, since `NormalModuleFactory` keys modules
|
|---|
| 544 | * on the full resource string.
|
|---|
| 545 | * @param {string} request request string from `from "<request>"`
|
|---|
| 546 | * @returns {boolean} true if request resolves to the current module
|
|---|
| 547 | */
|
|---|
| 548 | const isSelfReferenceRequest = (request) => {
|
|---|
| 549 | if (!/^\.{1,2}\//.test(request)) return false;
|
|---|
| 550 | if (!module.context) return false;
|
|---|
| 551 | const parsedRequest = parseResource(request);
|
|---|
| 552 | if (parsedRequest.query !== parsedModuleResource.query) return false;
|
|---|
| 553 | if (parsedRequest.fragment !== parsedModuleResource.fragment) {
|
|---|
| 554 | return false;
|
|---|
| 555 | }
|
|---|
| 556 | try {
|
|---|
| 557 | return (
|
|---|
| 558 | path.resolve(module.context, parsedRequest.path) ===
|
|---|
| 559 | parsedModuleResource.path
|
|---|
| 560 | );
|
|---|
| 561 | } catch (_err) {
|
|---|
| 562 | return false;
|
|---|
| 563 | }
|
|---|
| 564 | };
|
|---|
| 565 |
|
|---|
| 566 | const knownProperties = getKnownProperties({
|
|---|
| 567 | animation: this.options.animation,
|
|---|
| 568 | container: this.options.container,
|
|---|
| 569 | customIdents: this.options.customIdents,
|
|---|
| 570 | grid: this.options.grid
|
|---|
| 571 | });
|
|---|
| 572 |
|
|---|
| 573 | /** @type {BuildMeta} */
|
|---|
| 574 | (module.buildMeta).isCssModule = isModules;
|
|---|
| 575 | if (/** @type {CssModule} */ (module).exportType === "style") {
|
|---|
| 576 | /** @type {BuildMeta} */
|
|---|
| 577 | (module.buildMeta).needIdInConcatenation = true;
|
|---|
| 578 | }
|
|---|
| 579 |
|
|---|
| 580 | const locConverter = new LocConverter(source);
|
|---|
| 581 |
|
|---|
| 582 | /** @type {number} */
|
|---|
| 583 | let scope = CSS_MODE_TOP_LEVEL;
|
|---|
| 584 | /** @type {boolean} */
|
|---|
| 585 | let allowImportAtRule = true;
|
|---|
| 586 | /** @type {[string, number, number, boolean?][]} */
|
|---|
| 587 | const balanced = [];
|
|---|
| 588 | let lastTokenEndForComments = 0;
|
|---|
| 589 |
|
|---|
| 590 | /** @type {boolean} */
|
|---|
| 591 | let isNextRulePrelude = isModules;
|
|---|
| 592 | /** @type {number} */
|
|---|
| 593 | let blockNestingLevel = 0;
|
|---|
| 594 | /** @type {0 | 1 | undefined} */
|
|---|
| 595 | let modeData;
|
|---|
| 596 | /** @type {number} */
|
|---|
| 597 | let counter = 0;
|
|---|
| 598 |
|
|---|
| 599 | /** @type {string[]} */
|
|---|
| 600 | let lastLocalIdentifiers = [];
|
|---|
| 601 |
|
|---|
| 602 | const pureMode = isModules && Boolean(this.options.pure);
|
|---|
| 603 | /** @type {boolean} */
|
|---|
| 604 | let currentSelectorHasLocal = false;
|
|---|
| 605 | /** Whether any comma-separated selector in the current rule's prelude was impure. */
|
|---|
| 606 | let currentRuleHasImpureSelector = false;
|
|---|
| 607 | /** Offset just after the previous `}` (or 0) — used as the prelude start. */
|
|---|
| 608 | let currentRulePreludeStart = 0;
|
|---|
| 609 | /** Pure-mode flags (only meaningful when `pureMode` is true). */
|
|---|
| 610 | let pureNoCheck = false;
|
|---|
| 611 | let pureIgnorePending = false;
|
|---|
| 612 | let nextBlockChildrenSkip = false;
|
|---|
| 613 | let nextBlockTreatAsLeaf = false;
|
|---|
| 614 | let seenTopLevelRule = false;
|
|---|
| 615 | // True after an at-rule keyword and before the next `{` or `;`. Used so
|
|---|
| 616 | // identifiers inside the at-rule prelude (e.g. `min-width` inside
|
|---|
| 617 | // `@media (min-width: 768px)`) don't get counted as declarations.
|
|---|
| 618 | let inAtRulePrelude = false;
|
|---|
| 619 | /**
|
|---|
| 620 | * One entry per open block. `skipOwn` skips this rule's own check (set
|
|---|
| 621 | * when the parent passed down `skipChildren`, e.g. `from`/`to` inside
|
|---|
| 622 | * `@keyframes`). `skipChildren` is propagated to descendants. `ignored`
|
|---|
| 623 | * is per-rule only (PCSL semantics for `cssmodules-pure-ignore`).
|
|---|
| 624 | * `ancestorHadLocal` lets nested rules inherit purity from a
|
|---|
| 625 | * local-bearing ancestor.
|
|---|
| 626 | * @type {{
|
|---|
| 627 | * ignored: boolean,
|
|---|
| 628 | * skipOwn: boolean,
|
|---|
| 629 | * skipChildren: boolean,
|
|---|
| 630 | * treatAsLeaf: boolean,
|
|---|
| 631 | * ancestorHadLocal: boolean,
|
|---|
| 632 | * impure: boolean,
|
|---|
| 633 | * hasDirectDecl: boolean,
|
|---|
| 634 | * hasNestedBlock: boolean,
|
|---|
| 635 | * isRulePrelude: boolean,
|
|---|
| 636 | * preludeStart: number,
|
|---|
| 637 | * preludeEnd: number,
|
|---|
| 638 | * }[]}
|
|---|
| 639 | */
|
|---|
| 640 | const pureBlockStack = [];
|
|---|
| 641 |
|
|---|
| 642 | const PURE_IGNORE_RE = /^\s*cssmodules-pure-ignore(?:\s|$)/;
|
|---|
| 643 | const PURE_NO_CHECK_RE = /^\s*cssmodules-pure-no-check(?:\s|$)/;
|
|---|
| 644 |
|
|---|
| 645 | /**
|
|---|
| 646 | * @returns {(typeof pureBlockStack)[number] | undefined} top of stack
|
|---|
| 647 | */
|
|---|
| 648 | const pureTop = () => pureBlockStack[pureBlockStack.length - 1];
|
|---|
| 649 |
|
|---|
| 650 | /**
|
|---|
| 651 | * Was the parent rule pure overall (its own selectors pure or any
|
|---|
| 652 | * ancestor pure)? Used both for ancestor-inheritance and `&`-resolution.
|
|---|
| 653 | * @returns {boolean} true if any ancestor (self inclusive) provided a local
|
|---|
| 654 | */
|
|---|
| 655 | const parentEffectivePure = () => {
|
|---|
| 656 | const top = pureTop();
|
|---|
| 657 | return top ? top.ancestorHadLocal : false;
|
|---|
| 658 | };
|
|---|
| 659 |
|
|---|
| 660 | /**
|
|---|
| 661 | * Marks the just-finished comma-separated selector (or whole prelude
|
|---|
| 662 | * at `{`) as impure if it lacks a local and no ancestor compensates.
|
|---|
| 663 | */
|
|---|
| 664 | const finalizeSelector = () => {
|
|---|
| 665 | if (!currentSelectorHasLocal && !parentEffectivePure()) {
|
|---|
| 666 | currentRuleHasImpureSelector = true;
|
|---|
| 667 | }
|
|---|
| 668 | currentSelectorHasLocal = false;
|
|---|
| 669 | };
|
|---|
| 670 |
|
|---|
| 671 | /**
|
|---|
| 672 | * Reports a pure-mode violation covering the entire rule prelude.
|
|---|
| 673 | * @param {number} start prelude start offset
|
|---|
| 674 | * @param {number} end prelude end offset (`{` position)
|
|---|
| 675 | */
|
|---|
| 676 | const reportPureRule = (start, end) => {
|
|---|
| 677 | const slice = source.slice(start, end);
|
|---|
| 678 | const lead = /** @type {RegExpExecArray} */ (
|
|---|
| 679 | /^(?:\s|\/\*[\s\S]*?\*\/)*/.exec(slice)
|
|---|
| 680 | )[0].length;
|
|---|
| 681 | const trail = /** @type {RegExpExecArray} */ (/\s*$/.exec(slice))[0]
|
|---|
| 682 | .length;
|
|---|
| 683 | const from = start + lead;
|
|---|
| 684 | const to = end - trail;
|
|---|
| 685 | if (to <= from) return;
|
|---|
| 686 | this._emitError(
|
|---|
| 687 | state,
|
|---|
| 688 | `Selector "${source.slice(from, to)}" is not pure (pure selectors must contain at least one local class or id)`,
|
|---|
| 689 | locConverter,
|
|---|
| 690 | from,
|
|---|
| 691 | to
|
|---|
| 692 | );
|
|---|
| 693 | };
|
|---|
| 694 |
|
|---|
| 695 | /** @typedef {{ value?: string, importName?: string, localName?: string, request?: string }} IcssDefinition */
|
|---|
| 696 | /** @type {Map<string, IcssDefinition>} */
|
|---|
| 697 | const icssDefinitions = new Map();
|
|---|
| 698 |
|
|---|
| 699 | // Tracks `composes: <name> from "<file>"` declarations to enforce a
|
|---|
| 700 | // predictable file load order across rules (port of
|
|---|
| 701 | // postcss-modules-extract-imports#138). Each rule's composes order
|
|---|
| 702 | // is a partial ordering: if `.x` composes `b from "./b"` before
|
|---|
| 703 | // `c from "./c"`, then `b.css` must load before `c.css` so `c` can
|
|---|
| 704 | // override `b` in the cascade. Edges are added inline as the rule
|
|---|
| 705 | // is parsed; at end-of-parse the first composes-import dep of each
|
|---|
| 706 | // file is tagged with `sourceOrder` according to a topological
|
|---|
| 707 | // sort (`NormalModule#build` reorders by `sourceOrder` for us).
|
|---|
| 708 | /** @type {Map<string, Set<string>>} */
|
|---|
| 709 | const composesGraph = new Map();
|
|---|
| 710 | /** @type {Map<string, CssIcssImportDependency>} */
|
|---|
| 711 | const composesFirstFileImport = new Map();
|
|---|
| 712 | /** @type {string | undefined} */
|
|---|
| 713 | let currentRulePrevComposesFile;
|
|---|
| 714 | /** @type {Set<string>} */
|
|---|
| 715 | const currentRuleComposesFiles = new Set();
|
|---|
| 716 |
|
|---|
| 717 | /**
|
|---|
| 718 | * Checks whether this css parser is next nested syntax.
|
|---|
| 719 | * @param {string} input input
|
|---|
| 720 | * @param {number} pos position
|
|---|
| 721 | * @returns {boolean} true, when next is nested syntax
|
|---|
| 722 | */
|
|---|
| 723 | const isNextNestedSyntax = (input, pos) => {
|
|---|
| 724 | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
|
|---|
| 725 |
|
|---|
| 726 | if (
|
|---|
| 727 | input.charCodeAt(pos) === CC_RIGHT_CURLY ||
|
|---|
| 728 | (input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
|
|---|
| 729 | input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS)
|
|---|
| 730 | ) {
|
|---|
| 731 | return false;
|
|---|
| 732 | }
|
|---|
| 733 |
|
|---|
| 734 | const identifier = walkCssTokens.eatIdentSequence(input, pos);
|
|---|
| 735 |
|
|---|
| 736 | if (!identifier) {
|
|---|
| 737 | return true;
|
|---|
| 738 | }
|
|---|
| 739 |
|
|---|
| 740 | const leftCurly = eatUntilLeftCurly(input, pos);
|
|---|
| 741 | const content = input.slice(identifier[0], leftCurly);
|
|---|
| 742 |
|
|---|
| 743 | if (content.includes(";") || content.includes("}")) {
|
|---|
| 744 | return false;
|
|---|
| 745 | }
|
|---|
| 746 |
|
|---|
| 747 | return true;
|
|---|
| 748 | };
|
|---|
| 749 | /**
|
|---|
| 750 | * Checks whether this css parser is local mode.
|
|---|
| 751 | * @returns {boolean} true, when in local scope
|
|---|
| 752 | */
|
|---|
| 753 | const isLocalMode = () =>
|
|---|
| 754 | modeData === LOCAL_MODE || (mode === "local" && modeData === undefined);
|
|---|
| 755 |
|
|---|
| 756 | /**
|
|---|
| 757 | * Returns end.
|
|---|
| 758 | * @param {string} input input
|
|---|
| 759 | * @param {number} start start
|
|---|
| 760 | * @param {number} end end
|
|---|
| 761 | * @returns {number} end
|
|---|
| 762 | */
|
|---|
| 763 | const comment = (input, start, end) => {
|
|---|
| 764 | if (!this.comments) this.comments = [];
|
|---|
| 765 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 766 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 767 |
|
|---|
| 768 | const value = input.slice(start + 2, end - 2);
|
|---|
| 769 |
|
|---|
| 770 | /** @type {Comment} */
|
|---|
| 771 | const comment = {
|
|---|
| 772 | value,
|
|---|
| 773 | range: [start, end],
|
|---|
| 774 | loc: {
|
|---|
| 775 | start: { line: sl, column: sc },
|
|---|
| 776 | end: { line: el, column: ec }
|
|---|
| 777 | }
|
|---|
| 778 | };
|
|---|
| 779 | this.comments.push(comment);
|
|---|
| 780 |
|
|---|
| 781 | if (pureMode) {
|
|---|
| 782 | if (PURE_IGNORE_RE.test(value)) {
|
|---|
| 783 | pureIgnorePending = true;
|
|---|
| 784 | } else if (
|
|---|
| 785 | PURE_NO_CHECK_RE.test(value) &&
|
|---|
| 786 | scope === CSS_MODE_TOP_LEVEL &&
|
|---|
| 787 | !seenTopLevelRule
|
|---|
| 788 | ) {
|
|---|
| 789 | pureNoCheck = true;
|
|---|
| 790 | }
|
|---|
| 791 | }
|
|---|
| 792 |
|
|---|
| 793 | return end;
|
|---|
| 794 | };
|
|---|
| 795 |
|
|---|
| 796 | // Vanilla CSS stuff
|
|---|
| 797 |
|
|---|
| 798 | /**
|
|---|
| 799 | * Processes the provided input.
|
|---|
| 800 | * @param {string} input input
|
|---|
| 801 | * @param {number} start name start position
|
|---|
| 802 | * @param {number} end name end position
|
|---|
| 803 | * @returns {number} position after handling
|
|---|
| 804 | */
|
|---|
| 805 | const processAtImport = (input, start, end) => {
|
|---|
| 806 | const tokens = walkCssTokens.eatImportTokens(input, end, {
|
|---|
| 807 | comment
|
|---|
| 808 | });
|
|---|
| 809 | if (!tokens[3]) return end;
|
|---|
| 810 | const semi = tokens[3][1];
|
|---|
| 811 | if (!tokens[0] || (tokens[0][4] && !isModules)) {
|
|---|
| 812 | this._emitWarning(
|
|---|
| 813 | state,
|
|---|
| 814 | `Expected URL in '${input.slice(start, semi)}'`,
|
|---|
| 815 | locConverter,
|
|---|
| 816 | start,
|
|---|
| 817 | semi
|
|---|
| 818 | );
|
|---|
| 819 | return end;
|
|---|
| 820 | }
|
|---|
| 821 |
|
|---|
| 822 | const urlToken = tokens[0];
|
|---|
| 823 | /** @type {string} */
|
|---|
| 824 | let url;
|
|---|
| 825 | if (urlToken[4]) {
|
|---|
| 826 | // URL given as identifier — resolve via CSS Modules @value.
|
|---|
| 827 | const name = input.slice(urlToken[2], urlToken[3]);
|
|---|
| 828 | const def = icssDefinitions.get(name);
|
|---|
| 829 | if (!def) {
|
|---|
| 830 | this._emitWarning(
|
|---|
| 831 | state,
|
|---|
| 832 | `Unknown '@value' identifier '${name}' in '${input.slice(start, semi)}'`,
|
|---|
| 833 | locConverter,
|
|---|
| 834 | start,
|
|---|
| 835 | semi
|
|---|
| 836 | );
|
|---|
| 837 | // Consume the whole at-rule so the unresolved identifier
|
|---|
| 838 | // doesn't get re-tokenized and accidentally substituted
|
|---|
| 839 | // into a malformed `@import` in the output.
|
|---|
| 840 | const dep = new ConstDependency("", [start, semi]);
|
|---|
| 841 | module.addPresentationalDependency(dep);
|
|---|
| 842 | return semi;
|
|---|
| 843 | }
|
|---|
| 844 | if (def.value === undefined) {
|
|---|
| 845 | this._emitWarning(
|
|---|
| 846 | state,
|
|---|
| 847 | `'@value' identifier '${name}' was imported from another module and cannot be used as the URL of '@import' — only locally defined values are supported here`,
|
|---|
| 848 | locConverter,
|
|---|
| 849 | start,
|
|---|
| 850 | semi
|
|---|
| 851 | );
|
|---|
| 852 | const dep = new ConstDependency("", [start, semi]);
|
|---|
| 853 | module.addPresentationalDependency(dep);
|
|---|
| 854 | return semi;
|
|---|
| 855 | }
|
|---|
| 856 | const raw = def.value.trim();
|
|---|
| 857 | url =
|
|---|
| 858 | (raw.startsWith('"') && raw.endsWith('"')) ||
|
|---|
| 859 | (raw.startsWith("'") && raw.endsWith("'"))
|
|---|
| 860 | ? normalizeUrl(raw.slice(1, -1), true)
|
|---|
| 861 | : normalizeUrl(raw, false);
|
|---|
| 862 | } else {
|
|---|
| 863 | url = normalizeUrl(input.slice(urlToken[2], urlToken[3]), true);
|
|---|
| 864 | }
|
|---|
| 865 | const newline = walkCssTokens.eatWhiteLine(input, semi);
|
|---|
| 866 | const { options, errors: commentErrors } = this.parseCommentOptions([
|
|---|
| 867 | end,
|
|---|
| 868 | urlToken[1]
|
|---|
| 869 | ]);
|
|---|
| 870 | if (commentErrors) {
|
|---|
| 871 | for (const e of commentErrors) {
|
|---|
| 872 | const { comment } = e;
|
|---|
| 873 | state.module.addWarning(
|
|---|
| 874 | new CommentCompilationWarning(
|
|---|
| 875 | `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
|
|---|
| 876 | comment.loc
|
|---|
| 877 | )
|
|---|
| 878 | );
|
|---|
| 879 | }
|
|---|
| 880 | }
|
|---|
| 881 | if (options && options.webpackIgnore !== undefined) {
|
|---|
| 882 | if (typeof options.webpackIgnore !== "boolean") {
|
|---|
| 883 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 884 | const { line: el, column: ec } = locConverter.get(newline);
|
|---|
| 885 |
|
|---|
| 886 | state.module.addWarning(
|
|---|
| 887 | new UnsupportedFeatureWarning(
|
|---|
| 888 | `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
|
|---|
| 889 | {
|
|---|
| 890 | start: { line: sl, column: sc },
|
|---|
| 891 | end: { line: el, column: ec }
|
|---|
| 892 | }
|
|---|
| 893 | )
|
|---|
| 894 | );
|
|---|
| 895 | } else if (options.webpackIgnore) {
|
|---|
| 896 | return newline;
|
|---|
| 897 | }
|
|---|
| 898 | }
|
|---|
| 899 | if (url.length === 0) {
|
|---|
| 900 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 901 | const { line: el, column: ec } = locConverter.get(newline);
|
|---|
| 902 | const dep = new ConstDependency("", [start, newline]);
|
|---|
| 903 | module.addPresentationalDependency(dep);
|
|---|
| 904 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 905 |
|
|---|
| 906 | return newline;
|
|---|
| 907 | }
|
|---|
| 908 |
|
|---|
| 909 | /** @type {undefined | string} */
|
|---|
| 910 | let layer;
|
|---|
| 911 |
|
|---|
| 912 | if (tokens[1]) {
|
|---|
| 913 | layer = input.slice(tokens[1][0] + 6, tokens[1][1] - 1).trim();
|
|---|
| 914 | }
|
|---|
| 915 |
|
|---|
| 916 | /** @type {undefined | string} */
|
|---|
| 917 | let supports;
|
|---|
| 918 |
|
|---|
| 919 | if (tokens[2]) {
|
|---|
| 920 | supports = input.slice(tokens[2][0] + 9, tokens[2][1] - 1).trim();
|
|---|
| 921 | }
|
|---|
| 922 |
|
|---|
| 923 | const last = tokens[2] || tokens[1] || tokens[0];
|
|---|
| 924 | const mediaStart = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 925 | input,
|
|---|
| 926 | last[1]
|
|---|
| 927 | )[0];
|
|---|
| 928 |
|
|---|
| 929 | /** @type {undefined | string} */
|
|---|
| 930 | let media;
|
|---|
| 931 |
|
|---|
| 932 | if (mediaStart !== semi - 1) {
|
|---|
| 933 | media = input.slice(mediaStart, semi - 1).trim();
|
|---|
| 934 | }
|
|---|
| 935 |
|
|---|
| 936 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 937 | const { line: el, column: ec } = locConverter.get(newline);
|
|---|
| 938 | const dep = new CssImportDependency(
|
|---|
| 939 | url,
|
|---|
| 940 | [start, newline],
|
|---|
| 941 | mode === "local" || mode === "global" ? mode : undefined,
|
|---|
| 942 | layer,
|
|---|
| 943 | supports && supports.length > 0 ? supports : undefined,
|
|---|
| 944 | media && media.length > 0 ? media : undefined
|
|---|
| 945 | );
|
|---|
| 946 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 947 | module.addDependency(dep);
|
|---|
| 948 | // `text` and `css-style-sheet` parents inline the imported
|
|---|
| 949 | // module's rendered CSS at build time, which means we read the
|
|---|
| 950 | // imported module's `codeGenerationResults` (and through it the
|
|---|
| 951 | // results of any assets the import references). Registering this
|
|---|
| 952 | // as a code-generation dependency tells the compilation scheduler
|
|---|
| 953 | // to generate the imported subtree before us.
|
|---|
| 954 | const exportType = /** @type {import("./CssModule")} */ (module)
|
|---|
| 955 | .exportType;
|
|---|
| 956 | if (exportType === "text" || exportType === "css-style-sheet") {
|
|---|
| 957 | module.addCodeGenerationDependency(dep);
|
|---|
| 958 | }
|
|---|
| 959 |
|
|---|
| 960 | return newline;
|
|---|
| 961 | };
|
|---|
| 962 |
|
|---|
| 963 | /**
|
|---|
| 964 | * Process url function.
|
|---|
| 965 | * @param {string} input input
|
|---|
| 966 | * @param {number} end end position
|
|---|
| 967 | * @param {string} name the name of function
|
|---|
| 968 | * @returns {number} position after handling
|
|---|
| 969 | */
|
|---|
| 970 | const processURLFunction = (input, end, name) => {
|
|---|
| 971 | const string = walkCssTokens.eatString(input, end);
|
|---|
| 972 | if (!string) return end;
|
|---|
| 973 | const { options, errors: commentErrors } = this.parseCommentOptions([
|
|---|
| 974 | lastTokenEndForComments,
|
|---|
| 975 | end
|
|---|
| 976 | ]);
|
|---|
| 977 | if (commentErrors) {
|
|---|
| 978 | for (const e of commentErrors) {
|
|---|
| 979 | const { comment } = e;
|
|---|
| 980 | state.module.addWarning(
|
|---|
| 981 | new CommentCompilationWarning(
|
|---|
| 982 | `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
|
|---|
| 983 | comment.loc
|
|---|
| 984 | )
|
|---|
| 985 | );
|
|---|
| 986 | }
|
|---|
| 987 | }
|
|---|
| 988 | if (options && options.webpackIgnore !== undefined) {
|
|---|
| 989 | if (typeof options.webpackIgnore !== "boolean") {
|
|---|
| 990 | const { line: sl, column: sc } = locConverter.get(string[0]);
|
|---|
| 991 | const { line: el, column: ec } = locConverter.get(string[1]);
|
|---|
| 992 |
|
|---|
| 993 | state.module.addWarning(
|
|---|
| 994 | new UnsupportedFeatureWarning(
|
|---|
| 995 | `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
|
|---|
| 996 | {
|
|---|
| 997 | start: { line: sl, column: sc },
|
|---|
| 998 | end: { line: el, column: ec }
|
|---|
| 999 | }
|
|---|
| 1000 | )
|
|---|
| 1001 | );
|
|---|
| 1002 | } else if (options.webpackIgnore) {
|
|---|
| 1003 | return end;
|
|---|
| 1004 | }
|
|---|
| 1005 | }
|
|---|
| 1006 | const value = normalizeUrl(
|
|---|
| 1007 | input.slice(string[0] + 1, string[1] - 1),
|
|---|
| 1008 | true
|
|---|
| 1009 | );
|
|---|
| 1010 | // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
|
|---|
| 1011 | if (value.length === 0) return end;
|
|---|
| 1012 | const isUrl = name === "url" || name === "src";
|
|---|
| 1013 | const dep = new CssUrlDependency(
|
|---|
| 1014 | value,
|
|---|
| 1015 | [string[0], string[1]],
|
|---|
| 1016 | isUrl ? "string" : "url"
|
|---|
| 1017 | );
|
|---|
| 1018 | const { line: sl, column: sc } = locConverter.get(string[0]);
|
|---|
| 1019 | const { line: el, column: ec } = locConverter.get(string[1]);
|
|---|
| 1020 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1021 | module.addDependency(dep);
|
|---|
| 1022 | module.addCodeGenerationDependency(dep);
|
|---|
| 1023 | return string[1];
|
|---|
| 1024 | };
|
|---|
| 1025 |
|
|---|
| 1026 | /**
|
|---|
| 1027 | * Process old url function.
|
|---|
| 1028 | * @param {string} input input
|
|---|
| 1029 | * @param {number} start start position
|
|---|
| 1030 | * @param {number} end end position
|
|---|
| 1031 | * @param {number} contentStart start position
|
|---|
| 1032 | * @param {number} contentEnd end position
|
|---|
| 1033 | * @returns {number} position after handling
|
|---|
| 1034 | */
|
|---|
| 1035 | const processOldURLFunction = (
|
|---|
| 1036 | input,
|
|---|
| 1037 | start,
|
|---|
| 1038 | end,
|
|---|
| 1039 | contentStart,
|
|---|
| 1040 | contentEnd
|
|---|
| 1041 | ) => {
|
|---|
| 1042 | const { options, errors: commentErrors } = this.parseCommentOptions([
|
|---|
| 1043 | lastTokenEndForComments,
|
|---|
| 1044 | end
|
|---|
| 1045 | ]);
|
|---|
| 1046 | if (commentErrors) {
|
|---|
| 1047 | for (const e of commentErrors) {
|
|---|
| 1048 | const { comment } = e;
|
|---|
| 1049 | state.module.addWarning(
|
|---|
| 1050 | new CommentCompilationWarning(
|
|---|
| 1051 | `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
|
|---|
| 1052 | comment.loc
|
|---|
| 1053 | )
|
|---|
| 1054 | );
|
|---|
| 1055 | }
|
|---|
| 1056 | }
|
|---|
| 1057 | if (options && options.webpackIgnore !== undefined) {
|
|---|
| 1058 | if (typeof options.webpackIgnore !== "boolean") {
|
|---|
| 1059 | const { line: sl, column: sc } = locConverter.get(
|
|---|
| 1060 | lastTokenEndForComments
|
|---|
| 1061 | );
|
|---|
| 1062 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1063 |
|
|---|
| 1064 | state.module.addWarning(
|
|---|
| 1065 | new UnsupportedFeatureWarning(
|
|---|
| 1066 | `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
|
|---|
| 1067 | {
|
|---|
| 1068 | start: { line: sl, column: sc },
|
|---|
| 1069 | end: { line: el, column: ec }
|
|---|
| 1070 | }
|
|---|
| 1071 | )
|
|---|
| 1072 | );
|
|---|
| 1073 | } else if (options.webpackIgnore) {
|
|---|
| 1074 | return end;
|
|---|
| 1075 | }
|
|---|
| 1076 | }
|
|---|
| 1077 | let value = normalizeUrl(input.slice(contentStart, contentEnd), false);
|
|---|
| 1078 | // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
|
|---|
| 1079 | if (value.length === 0) return end;
|
|---|
| 1080 | if (isModules) {
|
|---|
| 1081 | const def = icssDefinitions.get(value);
|
|---|
| 1082 | if (def) {
|
|---|
| 1083 | if (def.value !== undefined) {
|
|---|
| 1084 | const raw = def.value.trim();
|
|---|
| 1085 | value =
|
|---|
| 1086 | (raw.startsWith('"') && raw.endsWith('"')) ||
|
|---|
| 1087 | (raw.startsWith("'") && raw.endsWith("'"))
|
|---|
| 1088 | ? normalizeUrl(raw.slice(1, -1), true)
|
|---|
| 1089 | : normalizeUrl(raw, false);
|
|---|
| 1090 | if (value.length === 0) return end;
|
|---|
| 1091 | } else {
|
|---|
| 1092 | this._emitWarning(
|
|---|
| 1093 | state,
|
|---|
| 1094 | `'@value' identifier '${value}' was imported from another module and cannot be used inside 'url()' — only locally defined values are supported here`,
|
|---|
| 1095 | locConverter,
|
|---|
| 1096 | start,
|
|---|
| 1097 | end
|
|---|
| 1098 | );
|
|---|
| 1099 | return end;
|
|---|
| 1100 | }
|
|---|
| 1101 | }
|
|---|
| 1102 | }
|
|---|
| 1103 | const dep = new CssUrlDependency(value, [start, end], "url");
|
|---|
| 1104 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1105 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1106 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1107 | module.addDependency(dep);
|
|---|
| 1108 | module.addCodeGenerationDependency(dep);
|
|---|
| 1109 | return end;
|
|---|
| 1110 | };
|
|---|
| 1111 |
|
|---|
| 1112 | /**
|
|---|
| 1113 | * Process image set function.
|
|---|
| 1114 | * @param {string} input input
|
|---|
| 1115 | * @param {number} start start position
|
|---|
| 1116 | * @param {number} end end position
|
|---|
| 1117 | * @returns {number} position after handling
|
|---|
| 1118 | */
|
|---|
| 1119 | const processImageSetFunction = (input, start, end) => {
|
|---|
| 1120 | lastTokenEndForComments = end;
|
|---|
| 1121 | const values = walkCssTokens.eatImageSetStrings(input, end, {
|
|---|
| 1122 | comment
|
|---|
| 1123 | });
|
|---|
| 1124 | if (values.length === 0) return end;
|
|---|
| 1125 | for (const [index, string] of values.entries()) {
|
|---|
| 1126 | const value = normalizeUrl(
|
|---|
| 1127 | input.slice(string[0] + 1, string[1] - 1),
|
|---|
| 1128 | true
|
|---|
| 1129 | );
|
|---|
| 1130 | if (value.length === 0) return end;
|
|---|
| 1131 | const { options, errors: commentErrors } = this.parseCommentOptions([
|
|---|
| 1132 | index === 0 ? start : values[index - 1][1],
|
|---|
| 1133 | string[1]
|
|---|
| 1134 | ]);
|
|---|
| 1135 | if (commentErrors) {
|
|---|
| 1136 | for (const e of commentErrors) {
|
|---|
| 1137 | const { comment } = e;
|
|---|
| 1138 | state.module.addWarning(
|
|---|
| 1139 | new CommentCompilationWarning(
|
|---|
| 1140 | `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
|
|---|
| 1141 | comment.loc
|
|---|
| 1142 | )
|
|---|
| 1143 | );
|
|---|
| 1144 | }
|
|---|
| 1145 | }
|
|---|
| 1146 | if (options && options.webpackIgnore !== undefined) {
|
|---|
| 1147 | if (typeof options.webpackIgnore !== "boolean") {
|
|---|
| 1148 | const { line: sl, column: sc } = locConverter.get(string[0]);
|
|---|
| 1149 | const { line: el, column: ec } = locConverter.get(string[1]);
|
|---|
| 1150 |
|
|---|
| 1151 | state.module.addWarning(
|
|---|
| 1152 | new UnsupportedFeatureWarning(
|
|---|
| 1153 | `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
|
|---|
| 1154 | {
|
|---|
| 1155 | start: { line: sl, column: sc },
|
|---|
| 1156 | end: { line: el, column: ec }
|
|---|
| 1157 | }
|
|---|
| 1158 | )
|
|---|
| 1159 | );
|
|---|
| 1160 | } else if (options.webpackIgnore) {
|
|---|
| 1161 | continue;
|
|---|
| 1162 | }
|
|---|
| 1163 | }
|
|---|
| 1164 | const dep = new CssUrlDependency(value, [string[0], string[1]], "url");
|
|---|
| 1165 | const { line: sl, column: sc } = locConverter.get(string[0]);
|
|---|
| 1166 | const { line: el, column: ec } = locConverter.get(string[1]);
|
|---|
| 1167 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1168 | module.addDependency(dep);
|
|---|
| 1169 | module.addCodeGenerationDependency(dep);
|
|---|
| 1170 | }
|
|---|
| 1171 | // Can contain `url()` inside, so let's return end to allow parse them
|
|---|
| 1172 | return end;
|
|---|
| 1173 | };
|
|---|
| 1174 |
|
|---|
| 1175 | // CSS modules stuff
|
|---|
| 1176 |
|
|---|
| 1177 | /**
|
|---|
| 1178 | * Returns resolved reexport (localName and importName).
|
|---|
| 1179 | * @param {string} value value to resolve
|
|---|
| 1180 | * @param {string=} localName override local name
|
|---|
| 1181 | * @param {boolean=} isCustomProperty true when it is custom property, otherwise false
|
|---|
| 1182 | * @returns {string | [string, string] | [string, string, string]} resolved reexport (`localName`, `importName` and optional `request` of the active `@value` import)
|
|---|
| 1183 | */
|
|---|
| 1184 | const getReexport = (value, localName, isCustomProperty) => {
|
|---|
| 1185 | const reexport = icssDefinitions.get(
|
|---|
| 1186 | isCustomProperty ? `--${value}` : value
|
|---|
| 1187 | );
|
|---|
| 1188 |
|
|---|
| 1189 | if (reexport) {
|
|---|
| 1190 | if (reexport.importName) {
|
|---|
| 1191 | const resolvedLocalName =
|
|---|
| 1192 | reexport.localName || (isCustomProperty ? `--${value}` : value);
|
|---|
| 1193 | return reexport.request
|
|---|
| 1194 | ? [resolvedLocalName, reexport.importName, reexport.request]
|
|---|
| 1195 | : [resolvedLocalName, reexport.importName];
|
|---|
| 1196 | }
|
|---|
| 1197 |
|
|---|
| 1198 | if (isCustomProperty) {
|
|---|
| 1199 | return /** @type {string} */ (reexport.value).slice(2);
|
|---|
| 1200 | }
|
|---|
| 1201 |
|
|---|
| 1202 | return /** @type {string} */ (reexport.value);
|
|---|
| 1203 | }
|
|---|
| 1204 |
|
|---|
| 1205 | if (localName) {
|
|---|
| 1206 | return [localName, value];
|
|---|
| 1207 | }
|
|---|
| 1208 |
|
|---|
| 1209 | return value;
|
|---|
| 1210 | };
|
|---|
| 1211 |
|
|---|
| 1212 | /**
|
|---|
| 1213 | * Process import or export.
|
|---|
| 1214 | * @param {0 | 1} type import or export
|
|---|
| 1215 | * @param {string} input input
|
|---|
| 1216 | * @param {number} pos start position
|
|---|
| 1217 | * @returns {number} position after parse
|
|---|
| 1218 | */
|
|---|
| 1219 | const processImportOrExport = (type, input, pos) => {
|
|---|
| 1220 | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
|
|---|
| 1221 | /** @type {string | undefined} */
|
|---|
| 1222 | let request;
|
|---|
| 1223 | if (type === 0) {
|
|---|
| 1224 | let cc = input.charCodeAt(pos);
|
|---|
| 1225 | if (cc !== CC_LEFT_PARENTHESIS) {
|
|---|
| 1226 | this._emitWarning(
|
|---|
| 1227 | state,
|
|---|
| 1228 | `Unexpected '${input[pos]}' at ${pos} during parsing of ':import' (expected '(')`,
|
|---|
| 1229 | locConverter,
|
|---|
| 1230 | pos,
|
|---|
| 1231 | pos
|
|---|
| 1232 | );
|
|---|
| 1233 | return pos;
|
|---|
| 1234 | }
|
|---|
| 1235 | pos++;
|
|---|
| 1236 | const stringStart = pos;
|
|---|
| 1237 | const str = walkCssTokens.eatString(input, pos);
|
|---|
| 1238 | if (!str) {
|
|---|
| 1239 | this._emitWarning(
|
|---|
| 1240 | state,
|
|---|
| 1241 | `Unexpected '${input[pos]}' at ${pos} during parsing of '${type === 0 ? ":import" : ":export"}' (expected string)`,
|
|---|
| 1242 | locConverter,
|
|---|
| 1243 | stringStart,
|
|---|
| 1244 | pos
|
|---|
| 1245 | );
|
|---|
| 1246 | return pos;
|
|---|
| 1247 | }
|
|---|
| 1248 | request = input.slice(str[0] + 1, str[1] - 1);
|
|---|
| 1249 | pos = str[1];
|
|---|
| 1250 | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
|
|---|
| 1251 | cc = input.charCodeAt(pos);
|
|---|
| 1252 | if (cc !== CC_RIGHT_PARENTHESIS) {
|
|---|
| 1253 | this._emitWarning(
|
|---|
| 1254 | state,
|
|---|
| 1255 | `Unexpected '${input[pos]}' at ${pos} during parsing of ':import' (expected ')')`,
|
|---|
| 1256 | locConverter,
|
|---|
| 1257 | pos,
|
|---|
| 1258 | pos
|
|---|
| 1259 | );
|
|---|
| 1260 | return pos;
|
|---|
| 1261 | }
|
|---|
| 1262 | pos++;
|
|---|
| 1263 | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
|
|---|
| 1264 | }
|
|---|
| 1265 |
|
|---|
| 1266 | /**
|
|---|
| 1267 | * Creates a dep from the provided name.
|
|---|
| 1268 | * @param {string} name name
|
|---|
| 1269 | * @param {string} value value
|
|---|
| 1270 | * @param {number} start start of position
|
|---|
| 1271 | * @param {number} end end of position
|
|---|
| 1272 | */
|
|---|
| 1273 | const createDep = (name, value, start, end) => {
|
|---|
| 1274 | if (type === 0) {
|
|---|
| 1275 | const dep = new CssIcssImportDependency(
|
|---|
| 1276 | /** @type {string} */
|
|---|
| 1277 | (request),
|
|---|
| 1278 | [0, 0],
|
|---|
| 1279 | /** @type {"local" | "global"} */
|
|---|
| 1280 | (mode),
|
|---|
| 1281 | value,
|
|---|
| 1282 | name
|
|---|
| 1283 | );
|
|---|
| 1284 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1285 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1286 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1287 | module.addDependency(dep);
|
|---|
| 1288 |
|
|---|
| 1289 | icssDefinitions.set(name, {
|
|---|
| 1290 | importName: value,
|
|---|
| 1291 | request: /** @type {string} */ (request)
|
|---|
| 1292 | });
|
|---|
| 1293 | } else if (type === 1) {
|
|---|
| 1294 | const dep = new CssIcssExportDependency(name, getReexport(value));
|
|---|
| 1295 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1296 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1297 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1298 | module.addDependency(dep);
|
|---|
| 1299 | }
|
|---|
| 1300 | };
|
|---|
| 1301 |
|
|---|
| 1302 | let needTerminate = false;
|
|---|
| 1303 | let balanced = 0;
|
|---|
| 1304 | /** @type {undefined | 0 | 1 | 2} */
|
|---|
| 1305 | let scope;
|
|---|
| 1306 |
|
|---|
| 1307 | /** @typedef {[number, number]} Name */
|
|---|
| 1308 |
|
|---|
| 1309 | /** @type {Name | undefined} */
|
|---|
| 1310 | let name;
|
|---|
| 1311 | /** @type {number | undefined} */
|
|---|
| 1312 | let value;
|
|---|
| 1313 |
|
|---|
| 1314 | /** @type {CssTokenCallbacks} */
|
|---|
| 1315 | const callbacks = {
|
|---|
| 1316 | leftCurlyBracket: (_input, _start, end) => {
|
|---|
| 1317 | balanced++;
|
|---|
| 1318 |
|
|---|
| 1319 | if (scope === undefined) {
|
|---|
| 1320 | scope = 0;
|
|---|
| 1321 | }
|
|---|
| 1322 |
|
|---|
| 1323 | return end;
|
|---|
| 1324 | },
|
|---|
| 1325 | rightCurlyBracket: (_input, _start, end) => {
|
|---|
| 1326 | balanced--;
|
|---|
| 1327 |
|
|---|
| 1328 | if (scope === 2) {
|
|---|
| 1329 | const [nameStart, nameEnd] = /** @type {Name} */ (name);
|
|---|
| 1330 | createDep(
|
|---|
| 1331 | input.slice(nameStart, nameEnd),
|
|---|
| 1332 | input.slice(value, end - 1).trim(),
|
|---|
| 1333 | nameEnd,
|
|---|
| 1334 | end - 1
|
|---|
| 1335 | );
|
|---|
| 1336 | scope = 0;
|
|---|
| 1337 | }
|
|---|
| 1338 |
|
|---|
| 1339 | if (balanced === 0 && scope === 0) {
|
|---|
| 1340 | needTerminate = true;
|
|---|
| 1341 | }
|
|---|
| 1342 |
|
|---|
| 1343 | return end;
|
|---|
| 1344 | },
|
|---|
| 1345 | identifier: (_input, start, end) => {
|
|---|
| 1346 | if (scope === 0) {
|
|---|
| 1347 | name = [start, end];
|
|---|
| 1348 | scope = 1;
|
|---|
| 1349 | }
|
|---|
| 1350 |
|
|---|
| 1351 | return end;
|
|---|
| 1352 | },
|
|---|
| 1353 | colon: (_input, _start, end) => {
|
|---|
| 1354 | if (scope === 1) {
|
|---|
| 1355 | scope = 2;
|
|---|
| 1356 | value = walkCssTokens.eatWhitespace(input, end);
|
|---|
| 1357 | return value;
|
|---|
| 1358 | }
|
|---|
| 1359 |
|
|---|
| 1360 | return end;
|
|---|
| 1361 | },
|
|---|
| 1362 | semicolon: (input, _start, end) => {
|
|---|
| 1363 | if (scope === 2) {
|
|---|
| 1364 | const [nameStart, nameEnd] = /** @type {Name} */ (name);
|
|---|
| 1365 | createDep(
|
|---|
| 1366 | input.slice(nameStart, nameEnd),
|
|---|
| 1367 | input.slice(value, end - 1),
|
|---|
| 1368 | nameEnd,
|
|---|
| 1369 | end - 1
|
|---|
| 1370 | );
|
|---|
| 1371 | scope = 0;
|
|---|
| 1372 | }
|
|---|
| 1373 |
|
|---|
| 1374 | return end;
|
|---|
| 1375 | },
|
|---|
| 1376 | needTerminate: () => needTerminate
|
|---|
| 1377 | };
|
|---|
| 1378 |
|
|---|
| 1379 | pos = walkCssTokens(input, pos, callbacks);
|
|---|
| 1380 | pos = walkCssTokens.eatWhiteLine(input, pos);
|
|---|
| 1381 |
|
|---|
| 1382 | return pos;
|
|---|
| 1383 | };
|
|---|
| 1384 |
|
|---|
| 1385 | /** @typedef {{ from: string, items: ({ localName: string, importName: string })[] }} ValueAtRuleImport */
|
|---|
| 1386 | /** @typedef {{ localName: string, value: string }} ValueAtRuleValue */
|
|---|
| 1387 | /**
|
|---|
| 1388 | * Parses value at rule params.
|
|---|
| 1389 | * @param {string} str value at-rule params
|
|---|
| 1390 | * @returns {ValueAtRuleImport | ValueAtRuleValue} parsed result
|
|---|
| 1391 | */
|
|---|
| 1392 | const parseValueAtRuleParams = (str) => {
|
|---|
| 1393 | if (/from(\/\*|\s)(?:[\s\S]+)$/i.test(str)) {
|
|---|
| 1394 | str = str.replace(CSS_COMMENT, " ").trim().replace(/;$/, "");
|
|---|
| 1395 | const fromIdx = str.lastIndexOf("from");
|
|---|
| 1396 | const path = str
|
|---|
| 1397 | .slice(fromIdx + 5)
|
|---|
| 1398 | .trim()
|
|---|
| 1399 | .replace(/['"]/g, "");
|
|---|
| 1400 | let content = str.slice(0, fromIdx).trim();
|
|---|
| 1401 |
|
|---|
| 1402 | if (content.startsWith("(") && content.endsWith(")")) {
|
|---|
| 1403 | content = content.slice(1, -1);
|
|---|
| 1404 | }
|
|---|
| 1405 |
|
|---|
| 1406 | return {
|
|---|
| 1407 | from: path,
|
|---|
| 1408 | items: content.split(",").map((item) => {
|
|---|
| 1409 | item = item.trim();
|
|---|
| 1410 |
|
|---|
| 1411 | if (item.includes(":")) {
|
|---|
| 1412 | const [local, remote] = item.split(":");
|
|---|
| 1413 |
|
|---|
| 1414 | return { localName: local.trim(), importName: remote.trim() };
|
|---|
| 1415 | }
|
|---|
| 1416 |
|
|---|
| 1417 | const asParts = item.split(/\s+as\s+/);
|
|---|
| 1418 |
|
|---|
| 1419 | if (asParts.length === 2) {
|
|---|
| 1420 | return {
|
|---|
| 1421 | localName: asParts[1].trim(),
|
|---|
| 1422 | importName: asParts[0].trim()
|
|---|
| 1423 | };
|
|---|
| 1424 | }
|
|---|
| 1425 |
|
|---|
| 1426 | return { localName: item, importName: item };
|
|---|
| 1427 | })
|
|---|
| 1428 | };
|
|---|
| 1429 | }
|
|---|
| 1430 |
|
|---|
| 1431 | /** @type {string} */
|
|---|
| 1432 | let localName;
|
|---|
| 1433 | /** @type {string} */
|
|---|
| 1434 | let value;
|
|---|
| 1435 |
|
|---|
| 1436 | const idx = str.indexOf(":");
|
|---|
| 1437 |
|
|---|
| 1438 | if (idx !== -1) {
|
|---|
| 1439 | localName = str.slice(0, idx).replace(CSS_COMMENT, "").trim();
|
|---|
| 1440 | value = str.slice(idx + 1);
|
|---|
| 1441 | } else {
|
|---|
| 1442 | const mask = str.replace(CSS_COMMENT, (m) => " ".repeat(m.length));
|
|---|
| 1443 | const idx = mask.search(/\S\s/) + 1;
|
|---|
| 1444 |
|
|---|
| 1445 | localName = str.slice(0, idx).replace(CSS_COMMENT, "").trim();
|
|---|
| 1446 | value = str.slice(idx + (str[idx] === " " ? 1 : 0));
|
|---|
| 1447 | }
|
|---|
| 1448 |
|
|---|
| 1449 | if (value.length > 0 && !/^\s+$/.test(value.replace(CSS_COMMENT, ""))) {
|
|---|
| 1450 | value = value.trim();
|
|---|
| 1451 | }
|
|---|
| 1452 |
|
|---|
| 1453 | return { localName, value };
|
|---|
| 1454 | };
|
|---|
| 1455 |
|
|---|
| 1456 | /**
|
|---|
| 1457 | * Processes the provided input.
|
|---|
| 1458 | * @param {string} input input
|
|---|
| 1459 | * @param {number} start name start position
|
|---|
| 1460 | * @param {number} end name end position
|
|---|
| 1461 | * @returns {number} position after handling
|
|---|
| 1462 | */
|
|---|
| 1463 | const processAtValue = (input, start, end) => {
|
|---|
| 1464 | const semi = eatUntilSemi(input, end);
|
|---|
| 1465 | const atRuleEnd = semi + 1;
|
|---|
| 1466 | const params = input.slice(end, semi);
|
|---|
| 1467 | const parsed = parseValueAtRuleParams(params);
|
|---|
| 1468 |
|
|---|
| 1469 | if (
|
|---|
| 1470 | typeof (/** @type {ValueAtRuleImport} */ (parsed).from) !== "undefined"
|
|---|
| 1471 | ) {
|
|---|
| 1472 | if (/** @type {ValueAtRuleImport} */ (parsed).from.length === 0) {
|
|---|
| 1473 | this._emitWarning(
|
|---|
| 1474 | state,
|
|---|
| 1475 | `Broken '@value' at-rule: ${input.slice(start, atRuleEnd)}'`,
|
|---|
| 1476 | locConverter,
|
|---|
| 1477 | start,
|
|---|
| 1478 | atRuleEnd
|
|---|
| 1479 | );
|
|---|
| 1480 |
|
|---|
| 1481 | const dep = new ConstDependency("", [start, atRuleEnd]);
|
|---|
| 1482 | module.addPresentationalDependency(dep);
|
|---|
| 1483 | return atRuleEnd;
|
|---|
| 1484 | }
|
|---|
| 1485 |
|
|---|
| 1486 | let { from, items } = /** @type {ValueAtRuleImport} */ (parsed);
|
|---|
| 1487 |
|
|---|
| 1488 | for (const { importName, localName } of items) {
|
|---|
| 1489 | {
|
|---|
| 1490 | const reexport = icssDefinitions.get(from);
|
|---|
| 1491 |
|
|---|
| 1492 | if (reexport && reexport.value) {
|
|---|
| 1493 | from = reexport.value.slice(1, -1);
|
|---|
| 1494 | }
|
|---|
| 1495 |
|
|---|
| 1496 | const dep = new CssIcssImportDependency(
|
|---|
| 1497 | from,
|
|---|
| 1498 | [0, 0],
|
|---|
| 1499 | /** @type {"local" | "global"} */
|
|---|
| 1500 | (mode),
|
|---|
| 1501 | importName,
|
|---|
| 1502 | localName
|
|---|
| 1503 | );
|
|---|
| 1504 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1505 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1506 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1507 | module.addDependency(dep);
|
|---|
| 1508 |
|
|---|
| 1509 | icssDefinitions.set(localName, { importName, request: from });
|
|---|
| 1510 | }
|
|---|
| 1511 |
|
|---|
| 1512 | {
|
|---|
| 1513 | const dep = new CssIcssExportDependency(
|
|---|
| 1514 | localName,
|
|---|
| 1515 | getReexport(localName),
|
|---|
| 1516 | undefined,
|
|---|
| 1517 | false,
|
|---|
| 1518 | CssIcssExportDependency.EXPORT_MODE.REPLACE
|
|---|
| 1519 | );
|
|---|
| 1520 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1521 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1522 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1523 | module.addDependency(dep);
|
|---|
| 1524 | }
|
|---|
| 1525 | }
|
|---|
| 1526 | } else {
|
|---|
| 1527 | if (/** @type {ValueAtRuleValue} */ (parsed).localName.length === 0) {
|
|---|
| 1528 | this._emitWarning(
|
|---|
| 1529 | state,
|
|---|
| 1530 | `Broken '@value' at-rule: ${input.slice(start, atRuleEnd)}'`,
|
|---|
| 1531 | locConverter,
|
|---|
| 1532 | start,
|
|---|
| 1533 | atRuleEnd
|
|---|
| 1534 | );
|
|---|
| 1535 |
|
|---|
| 1536 | const dep = new ConstDependency("", [start, atRuleEnd]);
|
|---|
| 1537 | module.addPresentationalDependency(dep);
|
|---|
| 1538 | return atRuleEnd;
|
|---|
| 1539 | }
|
|---|
| 1540 |
|
|---|
| 1541 | const { localName, value } = /** @type {ValueAtRuleValue} */ (parsed);
|
|---|
| 1542 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1543 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1544 |
|
|---|
| 1545 | if (icssDefinitions.has(value)) {
|
|---|
| 1546 | const def =
|
|---|
| 1547 | /** @type {IcssDefinition} */
|
|---|
| 1548 | (icssDefinitions.get(value));
|
|---|
| 1549 |
|
|---|
| 1550 | def.localName = value;
|
|---|
| 1551 |
|
|---|
| 1552 | icssDefinitions.set(localName, def);
|
|---|
| 1553 |
|
|---|
| 1554 | const dep = new CssIcssExportDependency(
|
|---|
| 1555 | localName,
|
|---|
| 1556 | getReexport(value)
|
|---|
| 1557 | );
|
|---|
| 1558 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1559 | module.addDependency(dep);
|
|---|
| 1560 | } else {
|
|---|
| 1561 | icssDefinitions.set(localName, { value });
|
|---|
| 1562 |
|
|---|
| 1563 | const dep = new CssIcssExportDependency(localName, value);
|
|---|
| 1564 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1565 | module.addDependency(dep);
|
|---|
| 1566 | }
|
|---|
| 1567 | }
|
|---|
| 1568 |
|
|---|
| 1569 | const dep = new ConstDependency("", [start, atRuleEnd]);
|
|---|
| 1570 | module.addPresentationalDependency(dep);
|
|---|
| 1571 | return atRuleEnd;
|
|---|
| 1572 | };
|
|---|
| 1573 |
|
|---|
| 1574 | /**
|
|---|
| 1575 | * Process icss symbol.
|
|---|
| 1576 | * @param {string} name ICSS symbol name
|
|---|
| 1577 | * @param {number} start start position
|
|---|
| 1578 | * @param {number} end end position
|
|---|
| 1579 | * @returns {number} position after handling
|
|---|
| 1580 | */
|
|---|
| 1581 | const processICSSSymbol = (name, start, end) => {
|
|---|
| 1582 | const def =
|
|---|
| 1583 | /** @type {IcssDefinition} */
|
|---|
| 1584 | (icssDefinitions.get(name));
|
|---|
| 1585 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1586 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1587 | const dep = new CssIcssSymbolDependency(
|
|---|
| 1588 | def.localName || name,
|
|---|
| 1589 | [start, end],
|
|---|
| 1590 | def.value,
|
|---|
| 1591 | def.importName,
|
|---|
| 1592 | def.request
|
|---|
| 1593 | );
|
|---|
| 1594 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1595 | module.addDependency(dep);
|
|---|
| 1596 | return end;
|
|---|
| 1597 | };
|
|---|
| 1598 |
|
|---|
| 1599 | /**
|
|---|
| 1600 | * Process local or global function.
|
|---|
| 1601 | * @param {string} input input
|
|---|
| 1602 | * @param {1 | 2} type type of function
|
|---|
| 1603 | * @param {number} start start position
|
|---|
| 1604 | * @param {number} end end position
|
|---|
| 1605 | * @returns {number} position after handling
|
|---|
| 1606 | */
|
|---|
| 1607 | const processLocalOrGlobalFunction = (input, type, start, end) => {
|
|---|
| 1608 | // Replace `local(`/` or `global(` (handle legacy `:local(` or `:global(` too)
|
|---|
| 1609 | {
|
|---|
| 1610 | const isColon = input.charCodeAt(start - 1) === CC_COLON;
|
|---|
| 1611 | const dep = new ConstDependency("", [isColon ? start - 1 : start, end]);
|
|---|
| 1612 | module.addPresentationalDependency(dep);
|
|---|
| 1613 | }
|
|---|
| 1614 |
|
|---|
| 1615 | end = walkCssTokens.consumeUntil(
|
|---|
| 1616 | input,
|
|---|
| 1617 | start,
|
|---|
| 1618 | {
|
|---|
| 1619 | identifier(input, start, end) {
|
|---|
| 1620 | if (type === 1) {
|
|---|
| 1621 | let identifier = unescapeIdentifierCached(
|
|---|
| 1622 | input.slice(start, end)
|
|---|
| 1623 | );
|
|---|
| 1624 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1625 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1626 | const isDashedIdent = isDashedIdentifier(identifier);
|
|---|
| 1627 |
|
|---|
| 1628 | if (isDashedIdent) {
|
|---|
| 1629 | identifier = identifier.slice(2);
|
|---|
| 1630 | }
|
|---|
| 1631 |
|
|---|
| 1632 | const dep = new CssIcssExportDependency(
|
|---|
| 1633 | identifier,
|
|---|
| 1634 | getReexport(identifier),
|
|---|
| 1635 | [start, end],
|
|---|
| 1636 | true,
|
|---|
| 1637 | CssIcssExportDependency.EXPORT_MODE.ONCE,
|
|---|
| 1638 | isDashedIdent
|
|---|
| 1639 | ? CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
|
|---|
| 1640 | : CssIcssExportDependency.EXPORT_TYPE.NORMAL
|
|---|
| 1641 | );
|
|---|
| 1642 |
|
|---|
| 1643 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1644 | module.addDependency(dep);
|
|---|
| 1645 | }
|
|---|
| 1646 |
|
|---|
| 1647 | return end;
|
|---|
| 1648 | }
|
|---|
| 1649 | },
|
|---|
| 1650 | {},
|
|---|
| 1651 | { onlyTopLevel: true, functionValue: true }
|
|---|
| 1652 | );
|
|---|
| 1653 |
|
|---|
| 1654 | {
|
|---|
| 1655 | // Replace the last `)`
|
|---|
| 1656 | const dep = new ConstDependency("", [end, end + 1]);
|
|---|
| 1657 | module.addPresentationalDependency(dep);
|
|---|
| 1658 | }
|
|---|
| 1659 |
|
|---|
| 1660 | return end;
|
|---|
| 1661 | };
|
|---|
| 1662 |
|
|---|
| 1663 | /**
|
|---|
| 1664 | * Process local at rule.
|
|---|
| 1665 | * @param {string} input input
|
|---|
| 1666 | * @param {number} end name end position
|
|---|
| 1667 | * @param {{ string?: boolean, identifier?: boolean | RegExp }} options types which allowed to handle
|
|---|
| 1668 | * @returns {number} position after handling
|
|---|
| 1669 | */
|
|---|
| 1670 | const processLocalAtRule = (input, end, options) => {
|
|---|
| 1671 | let found = false;
|
|---|
| 1672 |
|
|---|
| 1673 | return walkCssTokens.consumeUntil(
|
|---|
| 1674 | input,
|
|---|
| 1675 | end,
|
|---|
| 1676 | {
|
|---|
| 1677 | string(_input, start, end) {
|
|---|
| 1678 | if (!found && options.string) {
|
|---|
| 1679 | const value = unescapeIdentifierCached(
|
|---|
| 1680 | input.slice(start + 1, end - 1)
|
|---|
| 1681 | );
|
|---|
| 1682 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1683 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1684 | const dep = new CssIcssExportDependency(
|
|---|
| 1685 | value,
|
|---|
| 1686 | value,
|
|---|
| 1687 | [start, end],
|
|---|
| 1688 | true,
|
|---|
| 1689 | CssIcssExportDependency.EXPORT_MODE.ONCE
|
|---|
| 1690 | );
|
|---|
| 1691 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1692 | module.addDependency(dep);
|
|---|
| 1693 | found = true;
|
|---|
| 1694 | if (pureMode) currentSelectorHasLocal = true;
|
|---|
| 1695 | }
|
|---|
| 1696 | return end;
|
|---|
| 1697 | },
|
|---|
| 1698 | identifier(input, start, end) {
|
|---|
| 1699 | if (!found) {
|
|---|
| 1700 | const value = input.slice(start, end);
|
|---|
| 1701 |
|
|---|
| 1702 | if (options.identifier) {
|
|---|
| 1703 | const identifier = unescapeIdentifierCached(value);
|
|---|
| 1704 |
|
|---|
| 1705 | if (
|
|---|
| 1706 | options.identifier instanceof RegExp &&
|
|---|
| 1707 | options.identifier.test(identifier)
|
|---|
| 1708 | ) {
|
|---|
| 1709 | return end;
|
|---|
| 1710 | }
|
|---|
| 1711 |
|
|---|
| 1712 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 1713 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 1714 |
|
|---|
| 1715 | const dep = new CssIcssExportDependency(
|
|---|
| 1716 | identifier,
|
|---|
| 1717 | getReexport(identifier),
|
|---|
| 1718 | [start, end],
|
|---|
| 1719 | true,
|
|---|
| 1720 | CssIcssExportDependency.EXPORT_MODE.ONCE,
|
|---|
| 1721 | CssIcssExportDependency.EXPORT_TYPE.NORMAL
|
|---|
| 1722 | );
|
|---|
| 1723 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1724 | module.addDependency(dep);
|
|---|
| 1725 | found = true;
|
|---|
| 1726 | if (pureMode) currentSelectorHasLocal = true;
|
|---|
| 1727 | }
|
|---|
| 1728 | }
|
|---|
| 1729 | return end;
|
|---|
| 1730 | }
|
|---|
| 1731 | },
|
|---|
| 1732 | {
|
|---|
| 1733 | function: (input, start, end) => {
|
|---|
| 1734 | // No need to handle `:` (COLON), because it's always a function
|
|---|
| 1735 | const name = input
|
|---|
| 1736 | .slice(start, end - 1)
|
|---|
| 1737 | .replace(/\\/g, "")
|
|---|
| 1738 | .toLowerCase();
|
|---|
| 1739 |
|
|---|
| 1740 | const type =
|
|---|
| 1741 | name === "local" ? 1 : name === "global" ? 2 : undefined;
|
|---|
| 1742 |
|
|---|
| 1743 | if (!found && type) {
|
|---|
| 1744 | found = true;
|
|---|
| 1745 | if (type === 1 && pureMode) currentSelectorHasLocal = true;
|
|---|
| 1746 | return processLocalOrGlobalFunction(input, type, start, end);
|
|---|
| 1747 | }
|
|---|
| 1748 |
|
|---|
| 1749 | if (
|
|---|
| 1750 | this.options.dashedIdents &&
|
|---|
| 1751 | isLocalMode() &&
|
|---|
| 1752 | (name === "var" || name === "style")
|
|---|
| 1753 | ) {
|
|---|
| 1754 | return processDashedIdent(input, end, end);
|
|---|
| 1755 | }
|
|---|
| 1756 |
|
|---|
| 1757 | return end;
|
|---|
| 1758 | }
|
|---|
| 1759 | },
|
|---|
| 1760 | { onlyTopLevel: true, atRulePrelude: true }
|
|---|
| 1761 | );
|
|---|
| 1762 | };
|
|---|
| 1763 | /**
|
|---|
| 1764 | * Process dashed ident.
|
|---|
| 1765 | * @param {string} input input
|
|---|
| 1766 | * @param {number} start start position
|
|---|
| 1767 | * @param {number} end end position
|
|---|
| 1768 | * @returns {number} position after handling
|
|---|
| 1769 | */
|
|---|
| 1770 | const processDashedIdent = (input, start, end) => {
|
|---|
| 1771 | const customIdent = walkCssTokens.eatIdentSequence(input, start);
|
|---|
| 1772 | if (!customIdent) return end;
|
|---|
| 1773 | const identifier = unescapeIdentifierCached(
|
|---|
| 1774 | input.slice(customIdent[0] + 2, customIdent[1])
|
|---|
| 1775 | );
|
|---|
| 1776 | const afterCustomIdent = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 1777 | input,
|
|---|
| 1778 | customIdent[1]
|
|---|
| 1779 | )[0];
|
|---|
| 1780 | if (
|
|---|
| 1781 | input.charCodeAt(afterCustomIdent) === CC_LOWER_F ||
|
|---|
| 1782 | input.charCodeAt(afterCustomIdent) === CC_UPPER_F
|
|---|
| 1783 | ) {
|
|---|
| 1784 | const fromWord = walkCssTokens.eatIdentSequence(
|
|---|
| 1785 | input,
|
|---|
| 1786 | afterCustomIdent
|
|---|
| 1787 | );
|
|---|
| 1788 | if (
|
|---|
| 1789 | !fromWord ||
|
|---|
| 1790 | input.slice(fromWord[0], fromWord[1]).toLowerCase() !== "from"
|
|---|
| 1791 | ) {
|
|---|
| 1792 | return end;
|
|---|
| 1793 | }
|
|---|
| 1794 | const from = walkCssTokens.eatIdentSequenceOrString(
|
|---|
| 1795 | input,
|
|---|
| 1796 | walkCssTokens.eatWhitespaceAndComments(input, fromWord[1])[0]
|
|---|
| 1797 | );
|
|---|
| 1798 | if (!from) {
|
|---|
| 1799 | return end;
|
|---|
| 1800 | }
|
|---|
| 1801 | const path = input.slice(from[0], from[1]);
|
|---|
| 1802 | if (from[2] === true && path === "global") {
|
|---|
| 1803 | const dep = new ConstDependency("", [customIdent[1], from[1]]);
|
|---|
| 1804 | module.addPresentationalDependency(dep);
|
|---|
| 1805 | return end;
|
|---|
| 1806 | } else if (from[2] === false) {
|
|---|
| 1807 | const { line: sl, column: sc } = locConverter.get(customIdent[0]);
|
|---|
| 1808 | const { line: el, column: ec } = locConverter.get(from[1] - 1);
|
|---|
| 1809 | const localName = `__ICSS_IMPORT_${counter++}__`;
|
|---|
| 1810 |
|
|---|
| 1811 | {
|
|---|
| 1812 | const dep = new CssIcssImportDependency(
|
|---|
| 1813 | path.slice(1, -1),
|
|---|
| 1814 | [customIdent[0], from[1] - 1],
|
|---|
| 1815 | /** @type {"local" | "global"} */
|
|---|
| 1816 | (mode),
|
|---|
| 1817 | identifier,
|
|---|
| 1818 | localName
|
|---|
| 1819 | );
|
|---|
| 1820 |
|
|---|
| 1821 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1822 | module.addDependency(dep);
|
|---|
| 1823 | }
|
|---|
| 1824 |
|
|---|
| 1825 | {
|
|---|
| 1826 | const dep = new CssIcssExportDependency(
|
|---|
| 1827 | identifier,
|
|---|
| 1828 | getReexport(identifier, localName, true),
|
|---|
| 1829 | [customIdent[0], from[1] - 1],
|
|---|
| 1830 | true,
|
|---|
| 1831 | CssIcssExportDependency.EXPORT_MODE.ONCE,
|
|---|
| 1832 | CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
|
|---|
| 1833 | );
|
|---|
| 1834 |
|
|---|
| 1835 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1836 | module.addDependency(dep);
|
|---|
| 1837 | }
|
|---|
| 1838 |
|
|---|
| 1839 | {
|
|---|
| 1840 | const dep = new ConstDependency("", [fromWord[0], from[1]]);
|
|---|
| 1841 | module.addPresentationalDependency(dep);
|
|---|
| 1842 | return end;
|
|---|
| 1843 | }
|
|---|
| 1844 | }
|
|---|
| 1845 | } else {
|
|---|
| 1846 | const { line: sl, column: sc } = locConverter.get(customIdent[0]);
|
|---|
| 1847 | const { line: el, column: ec } = locConverter.get(customIdent[1]);
|
|---|
| 1848 | const dep = new CssIcssExportDependency(
|
|---|
| 1849 | identifier,
|
|---|
| 1850 | getReexport(identifier, undefined, true),
|
|---|
| 1851 | [customIdent[0], customIdent[1]],
|
|---|
| 1852 | true,
|
|---|
| 1853 | CssIcssExportDependency.EXPORT_MODE.ONCE,
|
|---|
| 1854 | CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
|
|---|
| 1855 | );
|
|---|
| 1856 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 1857 | module.addDependency(dep);
|
|---|
| 1858 | return end;
|
|---|
| 1859 | }
|
|---|
| 1860 |
|
|---|
| 1861 | return end;
|
|---|
| 1862 | };
|
|---|
| 1863 | /**
|
|---|
| 1864 | * Process local declaration.
|
|---|
| 1865 | * @param {string} input input
|
|---|
| 1866 | * @param {number} pos name start position
|
|---|
| 1867 | * @param {number} end name end position
|
|---|
| 1868 | * @returns {number} position after handling
|
|---|
| 1869 | */
|
|---|
| 1870 | const processLocalDeclaration = (input, pos, end) => {
|
|---|
| 1871 | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
|
|---|
| 1872 | const identifier = walkCssTokens.eatIdentSequence(input, pos);
|
|---|
| 1873 |
|
|---|
| 1874 | if (!identifier) {
|
|---|
| 1875 | return end;
|
|---|
| 1876 | }
|
|---|
| 1877 |
|
|---|
| 1878 | const propertyNameStart = identifier[0];
|
|---|
| 1879 |
|
|---|
| 1880 | pos = walkCssTokens.eatWhitespaceAndComments(input, identifier[1])[0];
|
|---|
| 1881 |
|
|---|
| 1882 | if (input.charCodeAt(pos) !== CC_COLON) {
|
|---|
| 1883 | return end;
|
|---|
| 1884 | }
|
|---|
| 1885 |
|
|---|
| 1886 | pos += 1;
|
|---|
| 1887 |
|
|---|
| 1888 | // Remove prefix and lowercase
|
|---|
| 1889 | const propertyName = input
|
|---|
| 1890 | .slice(identifier[0], identifier[1])
|
|---|
| 1891 | .replace(/^(-\w+-)/, "")
|
|---|
| 1892 | .toLowerCase();
|
|---|
| 1893 |
|
|---|
| 1894 | if (isLocalMode() && knownProperties.has(propertyName)) {
|
|---|
| 1895 | /** @type {[number, number, boolean?][]} */
|
|---|
| 1896 | const values = [];
|
|---|
| 1897 | /** @type {Record<string, number>} */
|
|---|
| 1898 | let parsedKeywords = Object.create(null);
|
|---|
| 1899 |
|
|---|
| 1900 | const isGridProperty = Boolean(propertyName.startsWith("grid"));
|
|---|
| 1901 | const isGridTemplate = isGridProperty
|
|---|
| 1902 | ? Boolean(
|
|---|
| 1903 | propertyName === "grid" ||
|
|---|
| 1904 | propertyName === "grid-template" ||
|
|---|
| 1905 | propertyName === "grid-template-columns" ||
|
|---|
| 1906 | propertyName === "grid-template-rows"
|
|---|
| 1907 | )
|
|---|
| 1908 | : false;
|
|---|
| 1909 |
|
|---|
| 1910 | let afterExclamation = false;
|
|---|
| 1911 |
|
|---|
| 1912 | const end = walkCssTokens.consumeUntil(
|
|---|
| 1913 | input,
|
|---|
| 1914 | pos,
|
|---|
| 1915 | {
|
|---|
| 1916 | delim(input, start, end) {
|
|---|
| 1917 | afterExclamation = input.charCodeAt(start) === CC_EXCLAMATION;
|
|---|
| 1918 | return end;
|
|---|
| 1919 | },
|
|---|
| 1920 | leftSquareBracket(input, start, end) {
|
|---|
| 1921 | let i = end;
|
|---|
| 1922 |
|
|---|
| 1923 | while (true) {
|
|---|
| 1924 | i = walkCssTokens.eatWhitespaceAndComments(input, i)[0];
|
|---|
| 1925 | const name = walkCssTokens.eatIdentSequence(input, i);
|
|---|
| 1926 |
|
|---|
| 1927 | if (!name) {
|
|---|
| 1928 | break;
|
|---|
| 1929 | }
|
|---|
| 1930 |
|
|---|
| 1931 | values.push(name);
|
|---|
| 1932 | i = name[1];
|
|---|
| 1933 | }
|
|---|
| 1934 |
|
|---|
| 1935 | return end;
|
|---|
| 1936 | },
|
|---|
| 1937 | string(_input, start, end) {
|
|---|
| 1938 | if (
|
|---|
| 1939 | propertyName === "animation" ||
|
|---|
| 1940 | propertyName === "animation-name"
|
|---|
| 1941 | ) {
|
|---|
| 1942 | values.push([start, end, true]);
|
|---|
| 1943 | }
|
|---|
| 1944 |
|
|---|
| 1945 | if (
|
|---|
| 1946 | propertyName === "grid" ||
|
|---|
| 1947 | propertyName === "grid-template" ||
|
|---|
| 1948 | propertyName === "grid-template-areas"
|
|---|
| 1949 | ) {
|
|---|
| 1950 | const areas = unescapeIdentifierCached(
|
|---|
| 1951 | input.slice(start + 1, end - 1)
|
|---|
| 1952 | );
|
|---|
| 1953 | const matches = matchAll(/\b\w+\b/g, areas);
|
|---|
| 1954 |
|
|---|
| 1955 | for (const match of matches) {
|
|---|
| 1956 | const areaStart = start + 1 + match.index;
|
|---|
| 1957 | values.push([areaStart, areaStart + match[0].length, false]);
|
|---|
| 1958 | }
|
|---|
| 1959 | }
|
|---|
| 1960 |
|
|---|
| 1961 | return end;
|
|---|
| 1962 | },
|
|---|
| 1963 | identifier(input, start, end) {
|
|---|
| 1964 | if (isGridTemplate) {
|
|---|
| 1965 | return end;
|
|---|
| 1966 | }
|
|---|
| 1967 |
|
|---|
| 1968 | if (afterExclamation) {
|
|---|
| 1969 | afterExclamation = false;
|
|---|
| 1970 | return end;
|
|---|
| 1971 | }
|
|---|
| 1972 |
|
|---|
| 1973 | const identifier = input.slice(start, end);
|
|---|
| 1974 | const keyword = identifier.toLowerCase();
|
|---|
| 1975 |
|
|---|
| 1976 | parsedKeywords[keyword] =
|
|---|
| 1977 | typeof parsedKeywords[keyword] !== "undefined"
|
|---|
| 1978 | ? parsedKeywords[keyword] + 1
|
|---|
| 1979 | : 0;
|
|---|
| 1980 | const keywords =
|
|---|
| 1981 | /** @type {Record<string, number>} */
|
|---|
| 1982 | (knownProperties.get(propertyName));
|
|---|
| 1983 |
|
|---|
| 1984 | if (
|
|---|
| 1985 | keywords[keyword] &&
|
|---|
| 1986 | parsedKeywords[keyword] < keywords[keyword]
|
|---|
| 1987 | ) {
|
|---|
| 1988 | return end;
|
|---|
| 1989 | }
|
|---|
| 1990 |
|
|---|
| 1991 | values.push([start, end]);
|
|---|
| 1992 | return end;
|
|---|
| 1993 | },
|
|---|
| 1994 | comma(_input, _start, end) {
|
|---|
| 1995 | parsedKeywords = {};
|
|---|
| 1996 |
|
|---|
| 1997 | return end;
|
|---|
| 1998 | }
|
|---|
| 1999 | },
|
|---|
| 2000 | {
|
|---|
| 2001 | function: (input, start, end) => {
|
|---|
| 2002 | const name = input
|
|---|
| 2003 | .slice(start, end - 1)
|
|---|
| 2004 | .replace(/\\/g, "")
|
|---|
| 2005 | .toLowerCase();
|
|---|
| 2006 |
|
|---|
| 2007 | const type =
|
|---|
| 2008 | name === "local" ? 1 : name === "global" ? 2 : undefined;
|
|---|
| 2009 |
|
|---|
| 2010 | if (type) {
|
|---|
| 2011 | return processLocalOrGlobalFunction(input, type, start, end);
|
|---|
| 2012 | }
|
|---|
| 2013 |
|
|---|
| 2014 | if (
|
|---|
| 2015 | this.options.dashedIdents &&
|
|---|
| 2016 | isLocalMode() &&
|
|---|
| 2017 | name === "var"
|
|---|
| 2018 | ) {
|
|---|
| 2019 | return processDashedIdent(input, end, end);
|
|---|
| 2020 | }
|
|---|
| 2021 |
|
|---|
| 2022 | if (this.options.url) {
|
|---|
| 2023 | if (name === "src" || name === "url") {
|
|---|
| 2024 | return processURLFunction(input, end, name);
|
|---|
| 2025 | } else if (IMAGE_SET_FUNCTION.test(name)) {
|
|---|
| 2026 | return processImageSetFunction(input, start, end);
|
|---|
| 2027 | }
|
|---|
| 2028 | }
|
|---|
| 2029 |
|
|---|
| 2030 | return end;
|
|---|
| 2031 | }
|
|---|
| 2032 | },
|
|---|
| 2033 | {
|
|---|
| 2034 | onlyTopLevel: !isGridTemplate,
|
|---|
| 2035 | declarationValue: true
|
|---|
| 2036 | }
|
|---|
| 2037 | );
|
|---|
| 2038 |
|
|---|
| 2039 | if (values.length > 0) {
|
|---|
| 2040 | for (const value of values) {
|
|---|
| 2041 | const { line: sl, column: sc } = locConverter.get(value[0]);
|
|---|
| 2042 | const { line: el, column: ec } = locConverter.get(value[1]);
|
|---|
| 2043 | const [start, end, isString] = value;
|
|---|
| 2044 | const name = unescapeIdentifierCached(
|
|---|
| 2045 | isString
|
|---|
| 2046 | ? input.slice(start + 1, end - 1)
|
|---|
| 2047 | : input.slice(start, end)
|
|---|
| 2048 | );
|
|---|
| 2049 | const dep = new CssIcssExportDependency(
|
|---|
| 2050 | name,
|
|---|
| 2051 | getReexport(name),
|
|---|
| 2052 | [start, end],
|
|---|
| 2053 | true,
|
|---|
| 2054 | CssIcssExportDependency.EXPORT_MODE.ONCE,
|
|---|
| 2055 | isGridProperty
|
|---|
| 2056 | ? CssIcssExportDependency.EXPORT_TYPE.GRID_CUSTOM_IDENTIFIER
|
|---|
| 2057 | : CssIcssExportDependency.EXPORT_TYPE.NORMAL
|
|---|
| 2058 | );
|
|---|
| 2059 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2060 | module.addDependency(dep);
|
|---|
| 2061 | }
|
|---|
| 2062 | }
|
|---|
| 2063 |
|
|---|
| 2064 | return end;
|
|---|
| 2065 | } else if (COMPOSES_PROPERTY.test(propertyName)) {
|
|---|
| 2066 | if (lastLocalIdentifiers.length > 1) {
|
|---|
| 2067 | const end = eatUntilSemi(input, pos);
|
|---|
| 2068 | this._emitWarning(
|
|---|
| 2069 | state,
|
|---|
| 2070 | `Composition is only allowed when selector is single local class name not in "${lastLocalIdentifiers.join('", "')}"`,
|
|---|
| 2071 | locConverter,
|
|---|
| 2072 | pos,
|
|---|
| 2073 | end
|
|---|
| 2074 | );
|
|---|
| 2075 |
|
|---|
| 2076 | return end;
|
|---|
| 2077 | }
|
|---|
| 2078 |
|
|---|
| 2079 | if (lastLocalIdentifiers.length !== 1) return pos;
|
|---|
| 2080 |
|
|---|
| 2081 | const lastLocalIdentifier = lastLocalIdentifiers[0];
|
|---|
| 2082 | let end = pos;
|
|---|
| 2083 |
|
|---|
| 2084 | /** @type {Set<[number, number, boolean]>} */
|
|---|
| 2085 | const classNames = new Set();
|
|---|
| 2086 |
|
|---|
| 2087 | while (true) {
|
|---|
| 2088 | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
|
|---|
| 2089 |
|
|---|
| 2090 | let className = walkCssTokens.eatIdentSequence(input, pos);
|
|---|
| 2091 |
|
|---|
| 2092 | const ifFunction =
|
|---|
| 2093 | className && input.charCodeAt(className[1]) === CC_LEFT_PARENTHESIS;
|
|---|
| 2094 | let isGlobalFunction = false;
|
|---|
| 2095 |
|
|---|
| 2096 | if (className && ifFunction) {
|
|---|
| 2097 | const name = input
|
|---|
| 2098 | .slice(className[0], className[1])
|
|---|
| 2099 | .replace(/\\/g, "")
|
|---|
| 2100 | .toLowerCase();
|
|---|
| 2101 |
|
|---|
| 2102 | isGlobalFunction = name === "global";
|
|---|
| 2103 | pos = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 2104 | input,
|
|---|
| 2105 | className[1] + 1
|
|---|
| 2106 | )[0];
|
|---|
| 2107 | className = walkCssTokens.eatIdentSequence(input, pos);
|
|---|
| 2108 | if (className) {
|
|---|
| 2109 | pos = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 2110 | input,
|
|---|
| 2111 | className[1]
|
|---|
| 2112 | )[0];
|
|---|
| 2113 | pos += 1;
|
|---|
| 2114 | }
|
|---|
| 2115 | } else if (className) {
|
|---|
| 2116 | pos = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 2117 | input,
|
|---|
| 2118 | className[1]
|
|---|
| 2119 | )[0];
|
|---|
| 2120 | pos = className[1];
|
|---|
| 2121 | }
|
|---|
| 2122 |
|
|---|
| 2123 | // True when we have multiple values
|
|---|
| 2124 | const isComma = input.charCodeAt(pos) === CC_COMMA;
|
|---|
| 2125 | const isSemicolon = input.charCodeAt(pos) === CC_SEMICOLON;
|
|---|
| 2126 | const isRightCurly = input.charCodeAt(pos) === CC_RIGHT_CURLY;
|
|---|
| 2127 |
|
|---|
| 2128 | if (isComma || isSemicolon || isRightCurly) {
|
|---|
| 2129 | if (className) {
|
|---|
| 2130 | classNames.add([className[0], className[1], isGlobalFunction]);
|
|---|
| 2131 | }
|
|---|
| 2132 |
|
|---|
| 2133 | for (const entry of classNames) {
|
|---|
| 2134 | const [start, end, isGlobal] = entry;
|
|---|
| 2135 | const identifier = unescapeIdentifierCached(
|
|---|
| 2136 | input.slice(start, end)
|
|---|
| 2137 | );
|
|---|
| 2138 | const dep = new CssIcssExportDependency(
|
|---|
| 2139 | lastLocalIdentifier,
|
|---|
| 2140 | getReexport(identifier),
|
|---|
| 2141 | [start, end],
|
|---|
| 2142 | !isGlobal,
|
|---|
| 2143 | isGlobal
|
|---|
| 2144 | ? CssIcssExportDependency.EXPORT_MODE.APPEND
|
|---|
| 2145 | : CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE,
|
|---|
| 2146 | CssIcssExportDependency.EXPORT_TYPE.COMPOSES
|
|---|
| 2147 | );
|
|---|
| 2148 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 2149 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 2150 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2151 | module.addDependency(dep);
|
|---|
| 2152 | }
|
|---|
| 2153 |
|
|---|
| 2154 | classNames.clear();
|
|---|
| 2155 |
|
|---|
| 2156 | if (isSemicolon || isRightCurly) {
|
|---|
| 2157 | end = isSemicolon
|
|---|
| 2158 | ? walkCssTokens.eatWhitespace(input, pos + 1)
|
|---|
| 2159 | : pos;
|
|---|
| 2160 | break;
|
|---|
| 2161 | }
|
|---|
| 2162 |
|
|---|
| 2163 | pos += 1;
|
|---|
| 2164 | } else if (
|
|---|
| 2165 | classNames.size > 0 &&
|
|---|
| 2166 | className &&
|
|---|
| 2167 | input.slice(className[0], className[1]).toLowerCase() === "from"
|
|---|
| 2168 | ) {
|
|---|
| 2169 | let from = walkCssTokens.eatString(input, pos);
|
|---|
| 2170 |
|
|---|
| 2171 | if (from) {
|
|---|
| 2172 | const request = input.slice(from[0] + 1, from[1] - 1);
|
|---|
| 2173 | const selfReference = isSelfReferenceRequest(request);
|
|---|
| 2174 |
|
|---|
| 2175 | if (!selfReference && !currentRuleComposesFiles.has(request)) {
|
|---|
| 2176 | currentRuleComposesFiles.add(request);
|
|---|
| 2177 | if (
|
|---|
| 2178 | currentRulePrevComposesFile !== undefined &&
|
|---|
| 2179 | currentRulePrevComposesFile !== request
|
|---|
| 2180 | ) {
|
|---|
| 2181 | let successors = composesGraph.get(
|
|---|
| 2182 | currentRulePrevComposesFile
|
|---|
| 2183 | );
|
|---|
| 2184 | if (!successors) {
|
|---|
| 2185 | successors = new Set();
|
|---|
| 2186 | composesGraph.set(currentRulePrevComposesFile, successors);
|
|---|
| 2187 | }
|
|---|
| 2188 | successors.add(request);
|
|---|
| 2189 | }
|
|---|
| 2190 | currentRulePrevComposesFile = request;
|
|---|
| 2191 | }
|
|---|
| 2192 |
|
|---|
| 2193 | for (const entry of classNames) {
|
|---|
| 2194 | const [start, end] = entry;
|
|---|
| 2195 | const identifier = unescapeIdentifierCached(
|
|---|
| 2196 | input.slice(start, end)
|
|---|
| 2197 | );
|
|---|
| 2198 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 2199 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 2200 |
|
|---|
| 2201 | if (selfReference) {
|
|---|
| 2202 | // `composes: foo from "./self.module.css"` from inside
|
|---|
| 2203 | // `self.module.css` — collapse to a self-reference, like
|
|---|
| 2204 | // `composes: foo` without `from`. When the composed name
|
|---|
| 2205 | // equals the local class name, it's a true no-op.
|
|---|
| 2206 | if (identifier === lastLocalIdentifier) continue;
|
|---|
| 2207 | const dep = new CssIcssExportDependency(
|
|---|
| 2208 | lastLocalIdentifier,
|
|---|
| 2209 | getReexport(identifier),
|
|---|
| 2210 | [start, end],
|
|---|
| 2211 | true,
|
|---|
| 2212 | CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE,
|
|---|
| 2213 | CssIcssExportDependency.EXPORT_TYPE.COMPOSES
|
|---|
| 2214 | );
|
|---|
| 2215 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2216 | module.addDependency(dep);
|
|---|
| 2217 | continue;
|
|---|
| 2218 | }
|
|---|
| 2219 |
|
|---|
| 2220 | const localName = `__ICSS_IMPORT_${counter++}__`;
|
|---|
| 2221 |
|
|---|
| 2222 | {
|
|---|
| 2223 | const dep = new CssIcssImportDependency(
|
|---|
| 2224 | request,
|
|---|
| 2225 | [start, end],
|
|---|
| 2226 | /** @type {"local" | "global"} */
|
|---|
| 2227 | (mode),
|
|---|
| 2228 | identifier,
|
|---|
| 2229 | localName
|
|---|
| 2230 | );
|
|---|
| 2231 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2232 | module.addDependency(dep);
|
|---|
| 2233 | if (!composesFirstFileImport.has(request)) {
|
|---|
| 2234 | composesFirstFileImport.set(request, dep);
|
|---|
| 2235 | }
|
|---|
| 2236 | }
|
|---|
| 2237 |
|
|---|
| 2238 | {
|
|---|
| 2239 | const dep = new CssIcssExportDependency(
|
|---|
| 2240 | lastLocalIdentifier,
|
|---|
| 2241 | getReexport(identifier, localName),
|
|---|
| 2242 | [start, end],
|
|---|
| 2243 | true,
|
|---|
| 2244 | CssIcssExportDependency.EXPORT_MODE.APPEND,
|
|---|
| 2245 | CssIcssExportDependency.EXPORT_TYPE.COMPOSES
|
|---|
| 2246 | );
|
|---|
| 2247 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2248 | module.addDependency(dep);
|
|---|
| 2249 | }
|
|---|
| 2250 | }
|
|---|
| 2251 |
|
|---|
| 2252 | classNames.clear();
|
|---|
| 2253 | pos = from[1];
|
|---|
| 2254 | } else {
|
|---|
| 2255 | from = walkCssTokens.eatIdentSequence(input, pos);
|
|---|
| 2256 |
|
|---|
| 2257 | if (from && input.slice(from[0], from[1]) === "global") {
|
|---|
| 2258 | for (const entry of classNames) {
|
|---|
| 2259 | const [start, end] = entry;
|
|---|
| 2260 | const identifier = unescapeIdentifierCached(
|
|---|
| 2261 | input.slice(start, end)
|
|---|
| 2262 | );
|
|---|
| 2263 | const dep = new CssIcssExportDependency(
|
|---|
| 2264 | /** @type {string} */
|
|---|
| 2265 | (lastLocalIdentifier),
|
|---|
| 2266 | getReexport(identifier),
|
|---|
| 2267 | [start, end],
|
|---|
| 2268 | false,
|
|---|
| 2269 | CssIcssExportDependency.EXPORT_MODE.APPEND,
|
|---|
| 2270 | CssIcssExportDependency.EXPORT_TYPE.COMPOSES
|
|---|
| 2271 | );
|
|---|
| 2272 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 2273 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 2274 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2275 | module.addDependency(dep);
|
|---|
| 2276 | }
|
|---|
| 2277 |
|
|---|
| 2278 | classNames.clear();
|
|---|
| 2279 | pos = from[1];
|
|---|
| 2280 | } else {
|
|---|
| 2281 | const end = eatUntilSemi(input, pos);
|
|---|
| 2282 | this._emitWarning(
|
|---|
| 2283 | state,
|
|---|
| 2284 | "Incorrect composition, expected global keyword or string value",
|
|---|
| 2285 | locConverter,
|
|---|
| 2286 | pos,
|
|---|
| 2287 | end
|
|---|
| 2288 | );
|
|---|
| 2289 | return end;
|
|---|
| 2290 | }
|
|---|
| 2291 | }
|
|---|
| 2292 | } else if (className) {
|
|---|
| 2293 | classNames.add([className[0], className[1], isGlobalFunction]);
|
|---|
| 2294 | } else {
|
|---|
| 2295 | const end = eatUntilSemi(input, pos);
|
|---|
| 2296 | this._emitWarning(
|
|---|
| 2297 | state,
|
|---|
| 2298 | "Incorrect composition, expected class named",
|
|---|
| 2299 | locConverter,
|
|---|
| 2300 | pos,
|
|---|
| 2301 | end
|
|---|
| 2302 | );
|
|---|
| 2303 | return end;
|
|---|
| 2304 | }
|
|---|
| 2305 | }
|
|---|
| 2306 |
|
|---|
| 2307 | // Remove `composes` from source code
|
|---|
| 2308 | const dep = new ConstDependency("", [propertyNameStart, end]);
|
|---|
| 2309 | module.addPresentationalDependency(dep);
|
|---|
| 2310 | }
|
|---|
| 2311 |
|
|---|
| 2312 | return pos;
|
|---|
| 2313 | };
|
|---|
| 2314 |
|
|---|
| 2315 | /**
|
|---|
| 2316 | * Process id selector.
|
|---|
| 2317 | * @param {string} input input
|
|---|
| 2318 | * @param {number} start start position
|
|---|
| 2319 | * @param {number} end end position
|
|---|
| 2320 | * @returns {number} position after handling
|
|---|
| 2321 | */
|
|---|
| 2322 | const processIdSelector = (input, start, end) => {
|
|---|
| 2323 | const valueStart = start + 1;
|
|---|
| 2324 | const name = unescapeIdentifierCached(input.slice(valueStart, end));
|
|---|
| 2325 | const dep = new CssIcssExportDependency(
|
|---|
| 2326 | name,
|
|---|
| 2327 | getReexport(name),
|
|---|
| 2328 | [valueStart, end],
|
|---|
| 2329 | true,
|
|---|
| 2330 | CssIcssExportDependency.EXPORT_MODE.ONCE
|
|---|
| 2331 | );
|
|---|
| 2332 | const { line: sl, column: sc } = locConverter.get(start);
|
|---|
| 2333 | const { line: el, column: ec } = locConverter.get(end);
|
|---|
| 2334 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2335 | module.addDependency(dep);
|
|---|
| 2336 | if (pureMode) currentSelectorHasLocal = true;
|
|---|
| 2337 | return end;
|
|---|
| 2338 | };
|
|---|
| 2339 |
|
|---|
| 2340 | /**
|
|---|
| 2341 | * Process class selector.
|
|---|
| 2342 | * @param {string} input input
|
|---|
| 2343 | * @param {number} start start position
|
|---|
| 2344 | * @param {number} end end position
|
|---|
| 2345 | * @returns {number} position after handling
|
|---|
| 2346 | */
|
|---|
| 2347 | const processClassSelector = (input, start, end) => {
|
|---|
| 2348 | const ident = walkCssTokens.skipCommentsAndEatIdentSequence(input, end);
|
|---|
| 2349 | if (!ident) return end;
|
|---|
| 2350 | const name = unescapeIdentifierCached(input.slice(ident[0], ident[1]));
|
|---|
| 2351 | lastLocalIdentifiers.push(name);
|
|---|
| 2352 | const dep = new CssIcssExportDependency(
|
|---|
| 2353 | name,
|
|---|
| 2354 | getReexport(name),
|
|---|
| 2355 | [ident[0], ident[1]],
|
|---|
| 2356 | true,
|
|---|
| 2357 | CssIcssExportDependency.EXPORT_MODE.ONCE
|
|---|
| 2358 | );
|
|---|
| 2359 | const { line: sl, column: sc } = locConverter.get(ident[0]);
|
|---|
| 2360 | const { line: el, column: ec } = locConverter.get(ident[1]);
|
|---|
| 2361 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2362 | module.addDependency(dep);
|
|---|
| 2363 | if (pureMode) currentSelectorHasLocal = true;
|
|---|
| 2364 | return ident[1];
|
|---|
| 2365 | };
|
|---|
| 2366 |
|
|---|
| 2367 | /**
|
|---|
| 2368 | * Process attribute selector.
|
|---|
| 2369 | * @param {string} input input
|
|---|
| 2370 | * @param {number} start start position
|
|---|
| 2371 | * @param {number} end end position
|
|---|
| 2372 | * @returns {number} position after handling
|
|---|
| 2373 | */
|
|---|
| 2374 | const processAttributeSelector = (input, start, end) => {
|
|---|
| 2375 | end = walkCssTokens.eatWhitespaceAndComments(input, end)[0];
|
|---|
| 2376 | const identifier = walkCssTokens.eatIdentSequence(input, end);
|
|---|
| 2377 | if (!identifier) return end;
|
|---|
| 2378 | const name = unescapeIdentifierCached(
|
|---|
| 2379 | input.slice(identifier[0], identifier[1])
|
|---|
| 2380 | );
|
|---|
| 2381 | if (name.toLowerCase() !== "class") {
|
|---|
| 2382 | return end;
|
|---|
| 2383 | }
|
|---|
| 2384 | end = walkCssTokens.eatWhitespaceAndComments(input, identifier[1])[0];
|
|---|
| 2385 |
|
|---|
| 2386 | const isTilde = input.charCodeAt(end) === CC_TILDE;
|
|---|
| 2387 |
|
|---|
| 2388 | if (
|
|---|
| 2389 | input.charCodeAt(end) !== CC_EQUAL &&
|
|---|
| 2390 | input.charCodeAt(end) !== CC_TILDE
|
|---|
| 2391 | ) {
|
|---|
| 2392 | return end;
|
|---|
| 2393 | }
|
|---|
| 2394 |
|
|---|
| 2395 | end += 1;
|
|---|
| 2396 |
|
|---|
| 2397 | if (isTilde) {
|
|---|
| 2398 | if (input.charCodeAt(end) !== CC_EQUAL) {
|
|---|
| 2399 | return end;
|
|---|
| 2400 | }
|
|---|
| 2401 |
|
|---|
| 2402 | end += 1;
|
|---|
| 2403 | }
|
|---|
| 2404 |
|
|---|
| 2405 | end = walkCssTokens.eatWhitespaceAndComments(input, end)[0];
|
|---|
| 2406 | const value = walkCssTokens.eatIdentSequenceOrString(input, end);
|
|---|
| 2407 |
|
|---|
| 2408 | if (!value) {
|
|---|
| 2409 | return end;
|
|---|
| 2410 | }
|
|---|
| 2411 |
|
|---|
| 2412 | const classNameStart = value[2] ? value[0] : value[0] + 1;
|
|---|
| 2413 | const classNameEnd = value[2] ? value[1] : value[1] - 1;
|
|---|
| 2414 | const className = unescapeIdentifierCached(
|
|---|
| 2415 | input.slice(classNameStart, classNameEnd)
|
|---|
| 2416 | );
|
|---|
| 2417 | const dep = new CssIcssExportDependency(
|
|---|
| 2418 | className,
|
|---|
| 2419 | getReexport(className),
|
|---|
| 2420 | [classNameStart, classNameEnd],
|
|---|
| 2421 | true,
|
|---|
| 2422 | CssIcssExportDependency.EXPORT_MODE.NONE
|
|---|
| 2423 | );
|
|---|
| 2424 | const { line: sl, column: sc } = locConverter.get(classNameStart);
|
|---|
| 2425 | const { line: el, column: ec } = locConverter.get(classNameEnd);
|
|---|
| 2426 | dep.setLoc(sl, sc, el, ec);
|
|---|
| 2427 | module.addDependency(dep);
|
|---|
| 2428 | return value[2] ? classNameEnd : classNameEnd + 1;
|
|---|
| 2429 | };
|
|---|
| 2430 |
|
|---|
| 2431 | walkCssTokens(source, 0, {
|
|---|
| 2432 | comment,
|
|---|
| 2433 | leftCurlyBracket: (input, start, end) => {
|
|---|
| 2434 | const wasTopLevel = scope === CSS_MODE_TOP_LEVEL;
|
|---|
| 2435 | if (wasTopLevel) {
|
|---|
| 2436 | allowImportAtRule = false;
|
|---|
| 2437 | scope = CSS_MODE_IN_BLOCK;
|
|---|
| 2438 | } else if (scope !== CSS_MODE_IN_BLOCK) {
|
|---|
| 2439 | return end;
|
|---|
| 2440 | }
|
|---|
| 2441 | if (!isModules) return end;
|
|---|
| 2442 | if (pureMode) {
|
|---|
| 2443 | inAtRulePrelude = false;
|
|---|
| 2444 | if (wasTopLevel) seenTopLevelRule = true;
|
|---|
| 2445 | const isRulePrelude = isNextRulePrelude;
|
|---|
| 2446 | if (isRulePrelude) finalizeSelector();
|
|---|
| 2447 | const top = pureTop();
|
|---|
| 2448 | if (top) top.hasNestedBlock = true;
|
|---|
| 2449 | const inheritedSkip = top ? top.skipChildren : false;
|
|---|
| 2450 | pureBlockStack.push({
|
|---|
| 2451 | ignored: pureIgnorePending,
|
|---|
| 2452 | skipOwn: inheritedSkip,
|
|---|
| 2453 | skipChildren: nextBlockChildrenSkip || inheritedSkip,
|
|---|
| 2454 | treatAsLeaf: nextBlockTreatAsLeaf,
|
|---|
| 2455 | // "this rule is fully pure" (no impure comma-segment) OR any
|
|---|
| 2456 | // ancestor pure. Matches PCSL's `[isPureSelectorSymbol]`.
|
|---|
| 2457 | ancestorHadLocal:
|
|---|
| 2458 | parentEffectivePure() ||
|
|---|
| 2459 | (isRulePrelude && !currentRuleHasImpureSelector),
|
|---|
| 2460 | impure: isRulePrelude && currentRuleHasImpureSelector,
|
|---|
| 2461 | hasDirectDecl: false,
|
|---|
| 2462 | hasNestedBlock: false,
|
|---|
| 2463 | isRulePrelude,
|
|---|
| 2464 | preludeStart: currentRulePreludeStart,
|
|---|
| 2465 | preludeEnd: start
|
|---|
| 2466 | });
|
|---|
| 2467 | pureIgnorePending = false;
|
|---|
| 2468 | nextBlockChildrenSkip = false;
|
|---|
| 2469 | nextBlockTreatAsLeaf = false;
|
|---|
| 2470 | currentRuleHasImpureSelector = false;
|
|---|
| 2471 | currentSelectorHasLocal = false;
|
|---|
| 2472 | currentRulePreludeStart = end;
|
|---|
| 2473 | }
|
|---|
| 2474 | blockNestingLevel = wasTopLevel ? 1 : blockNestingLevel + 1;
|
|---|
| 2475 | isNextRulePrelude = isNextNestedSyntax(input, end);
|
|---|
| 2476 | return end;
|
|---|
| 2477 | },
|
|---|
| 2478 | rightCurlyBracket: (input, start, end) => {
|
|---|
| 2479 | if (scope !== CSS_MODE_IN_BLOCK) return end;
|
|---|
| 2480 | const closing = blockNestingLevel === 1;
|
|---|
| 2481 | if (closing) {
|
|---|
| 2482 | scope = CSS_MODE_TOP_LEVEL;
|
|---|
| 2483 | blockNestingLevel = 0;
|
|---|
| 2484 | if (!isModules) return end;
|
|---|
| 2485 | isNextRulePrelude = true;
|
|---|
| 2486 | modeData = undefined;
|
|---|
| 2487 | lastLocalIdentifiers = [];
|
|---|
| 2488 | currentRulePrevComposesFile = undefined;
|
|---|
| 2489 | currentRuleComposesFiles.clear();
|
|---|
| 2490 | } else {
|
|---|
| 2491 | blockNestingLevel--;
|
|---|
| 2492 | if (!isModules) return end;
|
|---|
| 2493 | isNextRulePrelude = isNextNestedSyntax(input, end);
|
|---|
| 2494 | }
|
|---|
| 2495 | if (pureMode) {
|
|---|
| 2496 | const frame = pureBlockStack.pop();
|
|---|
| 2497 | if (frame) {
|
|---|
| 2498 | // PCSL throws on impure rules whose body has any non-rule
|
|---|
| 2499 | // content (declaration, empty body). Rules whose body is
|
|---|
| 2500 | // only nested rules are skipped — child rules carry the
|
|---|
| 2501 | // check themselves.
|
|---|
| 2502 | if (
|
|---|
| 2503 | !pureNoCheck &&
|
|---|
| 2504 | !frame.ignored &&
|
|---|
| 2505 | !frame.skipOwn &&
|
|---|
| 2506 | frame.isRulePrelude &&
|
|---|
| 2507 | frame.impure &&
|
|---|
| 2508 | (frame.hasDirectDecl ||
|
|---|
| 2509 | !frame.hasNestedBlock ||
|
|---|
| 2510 | frame.treatAsLeaf)
|
|---|
| 2511 | ) {
|
|---|
| 2512 | reportPureRule(frame.preludeStart, frame.preludeEnd);
|
|---|
| 2513 | }
|
|---|
| 2514 | // Propagate "has direct declaration" through at-rule frames
|
|---|
| 2515 | // so a parent rule containing only e.g. `@media { decl }` is
|
|---|
| 2516 | // still treated as "rule with declarations".
|
|---|
| 2517 | if (!frame.isRulePrelude && frame.hasDirectDecl) {
|
|---|
| 2518 | const parent = pureTop();
|
|---|
| 2519 | if (parent) parent.hasDirectDecl = true;
|
|---|
| 2520 | }
|
|---|
| 2521 | }
|
|---|
| 2522 | currentRuleHasImpureSelector = false;
|
|---|
| 2523 | currentSelectorHasLocal = false;
|
|---|
| 2524 | currentRulePreludeStart = end;
|
|---|
| 2525 | }
|
|---|
| 2526 | return end;
|
|---|
| 2527 | },
|
|---|
| 2528 | url: (input, start, end, contentStart, contentEnd) => {
|
|---|
| 2529 | if (!this.options.url) {
|
|---|
| 2530 | return end;
|
|---|
| 2531 | }
|
|---|
| 2532 |
|
|---|
| 2533 | return processOldURLFunction(
|
|---|
| 2534 | input,
|
|---|
| 2535 | start,
|
|---|
| 2536 | end,
|
|---|
| 2537 | contentStart,
|
|---|
| 2538 | contentEnd
|
|---|
| 2539 | );
|
|---|
| 2540 | },
|
|---|
| 2541 | atKeyword: (input, start, end) => {
|
|---|
| 2542 | const name = input.slice(start, end).toLowerCase();
|
|---|
| 2543 | const wasTopLevel = scope === CSS_MODE_TOP_LEVEL;
|
|---|
| 2544 | if (pureMode) {
|
|---|
| 2545 | inAtRulePrelude = true;
|
|---|
| 2546 | // Match PCSL's `isPureCheckDisabled`: any non-comment top-level
|
|---|
| 2547 | // node (including `;`-terminated at-rules like `@import`) seals
|
|---|
| 2548 | // the leading-comments window.
|
|---|
| 2549 | if (wasTopLevel) seenTopLevelRule = true;
|
|---|
| 2550 | }
|
|---|
| 2551 |
|
|---|
| 2552 | let pos = end;
|
|---|
| 2553 | switch (name) {
|
|---|
| 2554 | case "@namespace": {
|
|---|
| 2555 | this._emitWarning(
|
|---|
| 2556 | state,
|
|---|
| 2557 | "'@namespace' is not supported in bundled CSS",
|
|---|
| 2558 | locConverter,
|
|---|
| 2559 | start,
|
|---|
| 2560 | end
|
|---|
| 2561 | );
|
|---|
| 2562 |
|
|---|
| 2563 | pos = eatUntilSemi(input, start);
|
|---|
| 2564 | break;
|
|---|
| 2565 | }
|
|---|
| 2566 | case "@charset": {
|
|---|
| 2567 | const atRuleEnd = eatUntilSemi(input, start);
|
|---|
| 2568 |
|
|---|
| 2569 | if (/** @type {CssModule} */ (module).exportType === "style") {
|
|---|
| 2570 | pos = atRuleEnd;
|
|---|
| 2571 | break;
|
|---|
| 2572 | }
|
|---|
| 2573 |
|
|---|
| 2574 | const dep = new ConstDependency("", [start, atRuleEnd + 1]);
|
|---|
| 2575 | module.addPresentationalDependency(dep);
|
|---|
| 2576 |
|
|---|
| 2577 | const value = walkCssTokens.eatString(input, end);
|
|---|
| 2578 |
|
|---|
| 2579 | if (!value) {
|
|---|
| 2580 | pos = atRuleEnd;
|
|---|
| 2581 | break;
|
|---|
| 2582 | }
|
|---|
| 2583 |
|
|---|
| 2584 | /** @type {BuildInfo} */
|
|---|
| 2585 | (module.buildInfo).charset = input
|
|---|
| 2586 | .slice(value[0] + 1, value[1] - 1)
|
|---|
| 2587 | .toUpperCase();
|
|---|
| 2588 |
|
|---|
| 2589 | pos = atRuleEnd;
|
|---|
| 2590 | break;
|
|---|
| 2591 | }
|
|---|
| 2592 | case "@import": {
|
|---|
| 2593 | if (!this.options.import) {
|
|---|
| 2594 | pos = eatUntilSemi(input, end);
|
|---|
| 2595 | break;
|
|---|
| 2596 | }
|
|---|
| 2597 |
|
|---|
| 2598 | if (!allowImportAtRule) {
|
|---|
| 2599 | this._emitWarning(
|
|---|
| 2600 | state,
|
|---|
| 2601 | "Any '@import' rules must precede all other rules",
|
|---|
| 2602 | locConverter,
|
|---|
| 2603 | start,
|
|---|
| 2604 | end
|
|---|
| 2605 | );
|
|---|
| 2606 | pos = eatUntilSemi(input, end);
|
|---|
| 2607 | break;
|
|---|
| 2608 | }
|
|---|
| 2609 |
|
|---|
| 2610 | pos = processAtImport(input, start, end);
|
|---|
| 2611 | break;
|
|---|
| 2612 | }
|
|---|
| 2613 | default: {
|
|---|
| 2614 | if (isModules) {
|
|---|
| 2615 | if (name === "@value") {
|
|---|
| 2616 | pos = processAtValue(input, start, end);
|
|---|
| 2617 | break;
|
|---|
| 2618 | } else if (
|
|---|
| 2619 | this.options.animation &&
|
|---|
| 2620 | OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE.test(name) &&
|
|---|
| 2621 | isLocalMode()
|
|---|
| 2622 | ) {
|
|---|
| 2623 | if (pureMode) {
|
|---|
| 2624 | nextBlockChildrenSkip = true;
|
|---|
| 2625 | nextBlockTreatAsLeaf = true;
|
|---|
| 2626 | }
|
|---|
| 2627 | pos = processLocalAtRule(input, end, {
|
|---|
| 2628 | string: true,
|
|---|
| 2629 | identifier: true
|
|---|
| 2630 | });
|
|---|
| 2631 | break;
|
|---|
| 2632 | } else if (
|
|---|
| 2633 | this.options.customIdents &&
|
|---|
| 2634 | name === "@counter-style" &&
|
|---|
| 2635 | isLocalMode()
|
|---|
| 2636 | ) {
|
|---|
| 2637 | if (pureMode) {
|
|---|
| 2638 | nextBlockChildrenSkip = true;
|
|---|
| 2639 | nextBlockTreatAsLeaf = true;
|
|---|
| 2640 | }
|
|---|
| 2641 | pos = processLocalAtRule(input, end, {
|
|---|
| 2642 | identifier: true
|
|---|
| 2643 | });
|
|---|
| 2644 | break;
|
|---|
| 2645 | } else if (
|
|---|
| 2646 | this.options.container &&
|
|---|
| 2647 | name === "@container" &&
|
|---|
| 2648 | isLocalMode()
|
|---|
| 2649 | ) {
|
|---|
| 2650 | pos = processLocalAtRule(input, end, {
|
|---|
| 2651 | identifier: /^(none|and|or|not)$/
|
|---|
| 2652 | });
|
|---|
| 2653 | break;
|
|---|
| 2654 | } else if (name === "@scope") {
|
|---|
| 2655 | isNextRulePrelude = true;
|
|---|
| 2656 | break;
|
|---|
| 2657 | }
|
|---|
| 2658 |
|
|---|
| 2659 | isNextRulePrelude = false;
|
|---|
| 2660 | }
|
|---|
| 2661 | }
|
|---|
| 2662 | }
|
|---|
| 2663 |
|
|---|
| 2664 | // If the at-rule consumed its own `;` (for `@import`/`@value`/
|
|---|
| 2665 | // `@charset`/`@namespace`), advance the prelude pointer so a
|
|---|
| 2666 | // later impure rule's reported selector doesn't include this
|
|---|
| 2667 | // at-rule's text. Body-bearing at-rules return at `{` — let
|
|---|
| 2668 | // `leftCurlyBracket` handle those.
|
|---|
| 2669 | if (pureMode && wasTopLevel && pos > end) {
|
|---|
| 2670 | let probe = pos - 1;
|
|---|
| 2671 | while (
|
|---|
| 2672 | probe > end &&
|
|---|
| 2673 | walkCssTokens.isWhiteSpace(input.charCodeAt(probe))
|
|---|
| 2674 | ) {
|
|---|
| 2675 | probe--;
|
|---|
| 2676 | }
|
|---|
| 2677 | if (input.charCodeAt(probe) === CC_SEMICOLON) {
|
|---|
| 2678 | currentRulePreludeStart = pos;
|
|---|
| 2679 | }
|
|---|
| 2680 | }
|
|---|
| 2681 |
|
|---|
| 2682 | return pos;
|
|---|
| 2683 | },
|
|---|
| 2684 | semicolon: (input, start, end) => {
|
|---|
| 2685 | if (isModules && scope === CSS_MODE_IN_BLOCK) {
|
|---|
| 2686 | isNextRulePrelude = isNextNestedSyntax(input, end);
|
|---|
| 2687 | }
|
|---|
| 2688 | if (pureMode) {
|
|---|
| 2689 | if (scope === CSS_MODE_IN_BLOCK) {
|
|---|
| 2690 | if (
|
|---|
| 2691 | balanced.length === 0 &&
|
|---|
| 2692 | !isNextRulePrelude &&
|
|---|
| 2693 | !inAtRulePrelude
|
|---|
| 2694 | ) {
|
|---|
| 2695 | const top = pureTop();
|
|---|
| 2696 | if (top) top.hasDirectDecl = true;
|
|---|
| 2697 | }
|
|---|
| 2698 | } else if (scope === CSS_MODE_TOP_LEVEL && balanced.length === 0) {
|
|---|
| 2699 | // Top-level `;` ends a statement (e.g. `@import "x";`).
|
|---|
| 2700 | // Advance the prelude pointer so a later impure rule's
|
|---|
| 2701 | // reported selector doesn't include the preceding text.
|
|---|
| 2702 | currentRulePreludeStart = end;
|
|---|
| 2703 | }
|
|---|
| 2704 | inAtRulePrelude = false;
|
|---|
| 2705 | }
|
|---|
| 2706 | return end;
|
|---|
| 2707 | },
|
|---|
| 2708 | identifier: (input, start, end) => {
|
|---|
| 2709 | if (isModules) {
|
|---|
| 2710 | const identifier = input.slice(start, end);
|
|---|
| 2711 |
|
|---|
| 2712 | if (
|
|---|
| 2713 | this.options.dashedIdents &&
|
|---|
| 2714 | isLocalMode() &&
|
|---|
| 2715 | isDashedIdentifier(identifier)
|
|---|
| 2716 | ) {
|
|---|
| 2717 | return processDashedIdent(input, start, end);
|
|---|
| 2718 | }
|
|---|
| 2719 |
|
|---|
| 2720 | if (icssDefinitions.has(identifier)) {
|
|---|
| 2721 | return processICSSSymbol(identifier, start, end);
|
|---|
| 2722 | }
|
|---|
| 2723 |
|
|---|
| 2724 | switch (scope) {
|
|---|
| 2725 | case CSS_MODE_IN_BLOCK: {
|
|---|
| 2726 | if (isModules && !isNextRulePrelude) {
|
|---|
| 2727 | if (balanced.length === 0 && !inAtRulePrelude) {
|
|---|
| 2728 | const top = pureTop();
|
|---|
| 2729 | if (top) top.hasDirectDecl = true;
|
|---|
| 2730 | }
|
|---|
| 2731 | // Handle only top level values and not inside functions
|
|---|
| 2732 | return processLocalDeclaration(input, start, end);
|
|---|
| 2733 | }
|
|---|
| 2734 | break;
|
|---|
| 2735 | }
|
|---|
| 2736 | }
|
|---|
| 2737 | }
|
|---|
| 2738 |
|
|---|
| 2739 | return end;
|
|---|
| 2740 | },
|
|---|
| 2741 | delim: (input, start, end) => {
|
|---|
| 2742 | const ch = input.charCodeAt(start);
|
|---|
| 2743 | if (ch === CC_FULL_STOP && isNextRulePrelude && isLocalMode()) {
|
|---|
| 2744 | return processClassSelector(input, start, end);
|
|---|
| 2745 | }
|
|---|
| 2746 | if (
|
|---|
| 2747 | ch === CC_AMPERSAND &&
|
|---|
| 2748 | isNextRulePrelude &&
|
|---|
| 2749 | parentEffectivePure() &&
|
|---|
| 2750 | pureMode
|
|---|
| 2751 | ) {
|
|---|
| 2752 | currentSelectorHasLocal = true;
|
|---|
| 2753 | }
|
|---|
| 2754 |
|
|---|
| 2755 | return end;
|
|---|
| 2756 | },
|
|---|
| 2757 | hash: (input, start, end, isID) => {
|
|---|
| 2758 | if (isNextRulePrelude && isLocalMode() && isID) {
|
|---|
| 2759 | return processIdSelector(input, start, end);
|
|---|
| 2760 | }
|
|---|
| 2761 |
|
|---|
| 2762 | return end;
|
|---|
| 2763 | },
|
|---|
| 2764 | colon: (input, start, end) => {
|
|---|
| 2765 | if (isModules) {
|
|---|
| 2766 | const ident = walkCssTokens.skipCommentsAndEatIdentSequence(
|
|---|
| 2767 | input,
|
|---|
| 2768 | end
|
|---|
| 2769 | );
|
|---|
| 2770 | if (!ident) return end;
|
|---|
| 2771 | const name = input.slice(ident[0], ident[1]).toLowerCase();
|
|---|
| 2772 |
|
|---|
| 2773 | switch (scope) {
|
|---|
| 2774 | case CSS_MODE_TOP_LEVEL: {
|
|---|
| 2775 | if (name === "import") {
|
|---|
| 2776 | const pos = processImportOrExport(0, input, ident[1]);
|
|---|
| 2777 | const dep = new ConstDependency("", [start, pos]);
|
|---|
| 2778 | module.addPresentationalDependency(dep);
|
|---|
| 2779 | return pos;
|
|---|
| 2780 | } else if (name === "export") {
|
|---|
| 2781 | const pos = processImportOrExport(1, input, ident[1]);
|
|---|
| 2782 | const dep = new ConstDependency("", [start, pos]);
|
|---|
| 2783 | module.addPresentationalDependency(dep);
|
|---|
| 2784 | return pos;
|
|---|
| 2785 | }
|
|---|
| 2786 | }
|
|---|
| 2787 | // falls through
|
|---|
| 2788 | default: {
|
|---|
| 2789 | if (isNextRulePrelude) {
|
|---|
| 2790 | const isFn = input.charCodeAt(ident[1]) === CC_LEFT_PARENTHESIS;
|
|---|
| 2791 |
|
|---|
| 2792 | if (isFn && name === "local") {
|
|---|
| 2793 | // Eat extra whitespace
|
|---|
| 2794 | const end = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 2795 | input,
|
|---|
| 2796 | ident[1] + 1
|
|---|
| 2797 | )[0];
|
|---|
| 2798 | modeData = LOCAL_MODE;
|
|---|
| 2799 | const dep = new ConstDependency("", [start, end]);
|
|---|
| 2800 | module.addPresentationalDependency(dep);
|
|---|
| 2801 | balanced.push([":local", start, end, true]);
|
|---|
| 2802 | return end;
|
|---|
| 2803 | } else if (name === "local") {
|
|---|
| 2804 | modeData = LOCAL_MODE;
|
|---|
| 2805 | const found = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 2806 | input,
|
|---|
| 2807 | ident[1]
|
|---|
| 2808 | );
|
|---|
| 2809 |
|
|---|
| 2810 | if (!found[1]) {
|
|---|
| 2811 | this._emitWarning(
|
|---|
| 2812 | state,
|
|---|
| 2813 | `Missing whitespace after ':local' in '${input.slice(
|
|---|
| 2814 | start,
|
|---|
| 2815 | eatUntilLeftCurly(input, end) + 1
|
|---|
| 2816 | )}'`,
|
|---|
| 2817 | locConverter,
|
|---|
| 2818 | start,
|
|---|
| 2819 | end
|
|---|
| 2820 | );
|
|---|
| 2821 | }
|
|---|
| 2822 |
|
|---|
| 2823 | end = walkCssTokens.eatWhitespace(input, ident[1]);
|
|---|
| 2824 | const dep = new ConstDependency("", [start, end]);
|
|---|
| 2825 | module.addPresentationalDependency(dep);
|
|---|
| 2826 | return end;
|
|---|
| 2827 | } else if (isFn && name === "global") {
|
|---|
| 2828 | // Eat extra whitespace
|
|---|
| 2829 | const end = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 2830 | input,
|
|---|
| 2831 | ident[1] + 1
|
|---|
| 2832 | )[0];
|
|---|
| 2833 | modeData = GLOBAL_MODE;
|
|---|
| 2834 | const dep = new ConstDependency("", [start, end]);
|
|---|
| 2835 | module.addPresentationalDependency(dep);
|
|---|
| 2836 | balanced.push([":global", start, end, true]);
|
|---|
| 2837 | return end;
|
|---|
| 2838 | } else if (name === "global") {
|
|---|
| 2839 | modeData = GLOBAL_MODE;
|
|---|
| 2840 | // Eat extra whitespace
|
|---|
| 2841 | const found = walkCssTokens.eatWhitespaceAndComments(
|
|---|
| 2842 | input,
|
|---|
| 2843 | ident[1]
|
|---|
| 2844 | );
|
|---|
| 2845 |
|
|---|
| 2846 | if (!found[1]) {
|
|---|
| 2847 | this._emitWarning(
|
|---|
| 2848 | state,
|
|---|
| 2849 | `Missing whitespace after ':global' in '${input.slice(
|
|---|
| 2850 | start,
|
|---|
| 2851 | eatUntilLeftCurly(input, end) + 1
|
|---|
| 2852 | )}'`,
|
|---|
| 2853 | locConverter,
|
|---|
| 2854 | start,
|
|---|
| 2855 | end
|
|---|
| 2856 | );
|
|---|
| 2857 | }
|
|---|
| 2858 |
|
|---|
| 2859 | end = walkCssTokens.eatWhitespace(input, ident[1]);
|
|---|
| 2860 | const dep = new ConstDependency("", [start, end]);
|
|---|
| 2861 | module.addPresentationalDependency(dep);
|
|---|
| 2862 | return end;
|
|---|
| 2863 | }
|
|---|
| 2864 | }
|
|---|
| 2865 | }
|
|---|
| 2866 | }
|
|---|
| 2867 | }
|
|---|
| 2868 |
|
|---|
| 2869 | lastTokenEndForComments = end;
|
|---|
| 2870 |
|
|---|
| 2871 | return end;
|
|---|
| 2872 | },
|
|---|
| 2873 | function: (input, start, end) => {
|
|---|
| 2874 | const name = input
|
|---|
| 2875 | .slice(start, end - 1)
|
|---|
| 2876 | .replace(/\\/g, "")
|
|---|
| 2877 | .toLowerCase();
|
|---|
| 2878 |
|
|---|
| 2879 | balanced.push([name, start, end]);
|
|---|
| 2880 |
|
|---|
| 2881 | switch (name) {
|
|---|
| 2882 | case "src":
|
|---|
| 2883 | case "url": {
|
|---|
| 2884 | if (!this.options.url) {
|
|---|
| 2885 | return end;
|
|---|
| 2886 | }
|
|---|
| 2887 |
|
|---|
| 2888 | return processURLFunction(input, end, name);
|
|---|
| 2889 | }
|
|---|
| 2890 | default: {
|
|---|
| 2891 | if (this.options.url && IMAGE_SET_FUNCTION.test(name)) {
|
|---|
| 2892 | return processImageSetFunction(input, start, end);
|
|---|
| 2893 | }
|
|---|
| 2894 |
|
|---|
| 2895 | if (isModules) {
|
|---|
| 2896 | if (
|
|---|
| 2897 | this.options.function &&
|
|---|
| 2898 | isLocalMode() &&
|
|---|
| 2899 | isDashedIdentifier(name)
|
|---|
| 2900 | ) {
|
|---|
| 2901 | return processDashedIdent(input, start, end);
|
|---|
| 2902 | }
|
|---|
| 2903 |
|
|---|
| 2904 | const type =
|
|---|
| 2905 | name === "local" ? 1 : name === "global" ? 2 : undefined;
|
|---|
| 2906 |
|
|---|
| 2907 | if (type && !isNextRulePrelude) {
|
|---|
| 2908 | return processLocalOrGlobalFunction(input, type, start, end);
|
|---|
| 2909 | }
|
|---|
| 2910 | }
|
|---|
| 2911 | }
|
|---|
| 2912 | }
|
|---|
| 2913 |
|
|---|
| 2914 | return end;
|
|---|
| 2915 | },
|
|---|
| 2916 | leftSquareBracket: (input, start, end) => {
|
|---|
| 2917 | if (isNextRulePrelude && isLocalMode()) {
|
|---|
| 2918 | return processAttributeSelector(input, start, end);
|
|---|
| 2919 | }
|
|---|
| 2920 | return end;
|
|---|
| 2921 | },
|
|---|
| 2922 | leftParenthesis: (input, start, end) => {
|
|---|
| 2923 | balanced.push(["(", start, end]);
|
|---|
| 2924 |
|
|---|
| 2925 | return end;
|
|---|
| 2926 | },
|
|---|
| 2927 | rightParenthesis: (input, start, end) => {
|
|---|
| 2928 | const popped = balanced.pop();
|
|---|
| 2929 |
|
|---|
| 2930 | if (isModules && popped) {
|
|---|
| 2931 | const isLocal = popped[0] === ":local";
|
|---|
| 2932 | const isGlobal = popped[0] === ":global";
|
|---|
| 2933 | if (isLocal || isGlobal) {
|
|---|
| 2934 | modeData = balanced[balanced.length - 1]
|
|---|
| 2935 | ? balanced[balanced.length - 1][0] === ":local"
|
|---|
| 2936 | ? LOCAL_MODE
|
|---|
| 2937 | : balanced[balanced.length - 1][0] === ":global"
|
|---|
| 2938 | ? GLOBAL_MODE
|
|---|
| 2939 | : undefined
|
|---|
| 2940 | : undefined;
|
|---|
| 2941 | if (popped[3] && isLocal) {
|
|---|
| 2942 | while (walkCssTokens.isWhiteSpace(input.charCodeAt(start - 1))) {
|
|---|
| 2943 | start -= 1;
|
|---|
| 2944 | }
|
|---|
| 2945 | }
|
|---|
| 2946 | const dep = new ConstDependency("", [start, end]);
|
|---|
| 2947 | module.addPresentationalDependency(dep);
|
|---|
| 2948 | } else if (isNextRulePrelude) {
|
|---|
| 2949 | modeData = undefined;
|
|---|
| 2950 | }
|
|---|
| 2951 | }
|
|---|
| 2952 |
|
|---|
| 2953 | return end;
|
|---|
| 2954 | },
|
|---|
| 2955 | comma: (input, start, end) => {
|
|---|
| 2956 | if (isModules && balanced.length === 0) {
|
|---|
| 2957 | // Reset stack for `:global .class :local .class-other` selector after
|
|---|
| 2958 | modeData = undefined;
|
|---|
| 2959 | if (pureMode && isNextRulePrelude) finalizeSelector();
|
|---|
| 2960 | }
|
|---|
| 2961 |
|
|---|
| 2962 | lastTokenEndForComments = start;
|
|---|
| 2963 |
|
|---|
| 2964 | return end;
|
|---|
| 2965 | }
|
|---|
| 2966 | });
|
|---|
| 2967 |
|
|---|
| 2968 | /** @type {BuildInfo} */
|
|---|
| 2969 | (module.buildInfo).strict = true;
|
|---|
| 2970 |
|
|---|
| 2971 | // Topologically sort the files referenced by `composes ... from`
|
|---|
| 2972 | // declarations and tag each file's first import dep with the
|
|---|
| 2973 | // resulting `sourceOrder`. `NormalModule#build` then reorders the
|
|---|
| 2974 | // deps via `sortWithSourceOrder` so the bundle loads them in
|
|---|
| 2975 | // cascade-correct order. Files stuck in a cycle are not visited
|
|---|
| 2976 | // and keep their natural loc-based position.
|
|---|
| 2977 | if (composesFirstFileImport.size > 1) {
|
|---|
| 2978 | topologicalSort(
|
|---|
| 2979 | composesGraph,
|
|---|
| 2980 | [...composesFirstFileImport.keys()],
|
|---|
| 2981 | (file, i) => {
|
|---|
| 2982 | /** @type {CssIcssImportDependency} */
|
|---|
| 2983 | (composesFirstFileImport.get(file)).sourceOrder = i;
|
|---|
| 2984 | }
|
|---|
| 2985 | );
|
|---|
| 2986 | }
|
|---|
| 2987 |
|
|---|
| 2988 | const buildMeta = /** @type {BuildMeta} */ (state.module.buildMeta);
|
|---|
| 2989 |
|
|---|
| 2990 | buildMeta.exportsType = this.options.namedExports ? "namespace" : "default";
|
|---|
| 2991 | buildMeta.defaultObject = this.options.namedExports
|
|---|
| 2992 | ? false
|
|---|
| 2993 | : "redirect-warn";
|
|---|
| 2994 |
|
|---|
| 2995 | if (
|
|---|
| 2996 | /** @type {CssModule} */ (module).exportType === "text" ||
|
|---|
| 2997 | /** @type {CssModule} */ (module).exportType === "css-style-sheet"
|
|---|
| 2998 | ) {
|
|---|
| 2999 | module.addDependency(new StaticExportsDependency(["default"], true));
|
|---|
| 3000 | } else {
|
|---|
| 3001 | module.addDependency(new StaticExportsDependency([], true));
|
|---|
| 3002 | }
|
|---|
| 3003 |
|
|---|
| 3004 | return state;
|
|---|
| 3005 | }
|
|---|
| 3006 |
|
|---|
| 3007 | /**
|
|---|
| 3008 | * Returns comments in the range.
|
|---|
| 3009 | * @param {Range} range range
|
|---|
| 3010 | * @returns {Comment[]} comments in the range
|
|---|
| 3011 | */
|
|---|
| 3012 | getComments(range) {
|
|---|
| 3013 | if (!this.comments) return [];
|
|---|
| 3014 | const [rangeStart, rangeEnd] = range;
|
|---|
| 3015 | /**
|
|---|
| 3016 | * Returns compared.
|
|---|
| 3017 | * @param {Comment} comment comment
|
|---|
| 3018 | * @param {number} needle needle
|
|---|
| 3019 | * @returns {number} compared
|
|---|
| 3020 | */
|
|---|
| 3021 | const compare = (comment, needle) =>
|
|---|
| 3022 | /** @type {Range} */ (comment.range)[0] - needle;
|
|---|
| 3023 | const comments = /** @type {Comment[]} */ (this.comments);
|
|---|
| 3024 | let idx = binarySearchBounds.ge(comments, rangeStart, compare);
|
|---|
| 3025 | /** @type {Comment[]} */
|
|---|
| 3026 | const commentsInRange = [];
|
|---|
| 3027 | while (
|
|---|
| 3028 | comments[idx] &&
|
|---|
| 3029 | /** @type {Range} */ (comments[idx].range)[1] <= rangeEnd
|
|---|
| 3030 | ) {
|
|---|
| 3031 | commentsInRange.push(comments[idx]);
|
|---|
| 3032 | idx++;
|
|---|
| 3033 | }
|
|---|
| 3034 |
|
|---|
| 3035 | return commentsInRange;
|
|---|
| 3036 | }
|
|---|
| 3037 |
|
|---|
| 3038 | /**
|
|---|
| 3039 | * Parses comment options.
|
|---|
| 3040 | * @param {Range} range range of the comment
|
|---|
| 3041 | * @returns {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: Comment })[] | null }} result
|
|---|
| 3042 | */
|
|---|
| 3043 | parseCommentOptions(range) {
|
|---|
| 3044 | const comments = this.getComments(range);
|
|---|
| 3045 | if (comments.length === 0) {
|
|---|
| 3046 | return EMPTY_COMMENT_OPTIONS;
|
|---|
| 3047 | }
|
|---|
| 3048 | /** @type {Record<string, EXPECTED_ANY>} */
|
|---|
| 3049 | const options = {};
|
|---|
| 3050 | /** @type {(Error & { comment: Comment })[]} */
|
|---|
| 3051 | const errors = [];
|
|---|
| 3052 | for (const comment of comments) {
|
|---|
| 3053 | const { value } = comment;
|
|---|
| 3054 | if (value && webpackCommentRegExp.test(value)) {
|
|---|
| 3055 | // try compile only if webpack options comment is present
|
|---|
| 3056 | try {
|
|---|
| 3057 | for (let [key, val] of Object.entries(
|
|---|
| 3058 | vm.runInContext(
|
|---|
| 3059 | `(function(){return {${value}};})()`,
|
|---|
| 3060 | this.magicCommentContext
|
|---|
| 3061 | )
|
|---|
| 3062 | )) {
|
|---|
| 3063 | if (typeof val === "object" && val !== null) {
|
|---|
| 3064 | val =
|
|---|
| 3065 | val.constructor.name === "RegExp"
|
|---|
| 3066 | ? new RegExp(val)
|
|---|
| 3067 | : JSON.parse(JSON.stringify(val));
|
|---|
| 3068 | }
|
|---|
| 3069 | options[key] = val;
|
|---|
| 3070 | }
|
|---|
| 3071 | } catch (err) {
|
|---|
| 3072 | const newErr = new Error(String(/** @type {Error} */ (err).message));
|
|---|
| 3073 | newErr.stack = String(/** @type {Error} */ (err).stack);
|
|---|
| 3074 | Object.assign(newErr, { comment });
|
|---|
| 3075 | errors.push(/** @type {(Error & { comment: Comment })} */ (newErr));
|
|---|
| 3076 | }
|
|---|
| 3077 | }
|
|---|
| 3078 | }
|
|---|
| 3079 | return { options, errors };
|
|---|
| 3080 | }
|
|---|
| 3081 | }
|
|---|
| 3082 |
|
|---|
| 3083 | module.exports = CssParser;
|
|---|
| 3084 | module.exports.escapeIdentifier = escapeIdentifier;
|
|---|
| 3085 | module.exports.unescapeIdentifier = unescapeIdentifier;
|
|---|