| | |
| | | |
| | | const vm = require("vm"); |
| | | const CommentCompilationWarning = require("../CommentCompilationWarning"); |
| | | const CssModule = require("../CssModule"); |
| | | const ModuleDependencyWarning = require("../ModuleDependencyWarning"); |
| | | const { CSS_MODULE_TYPE_AUTO } = require("../ModuleTypeConstants"); |
| | | const Parser = require("../Parser"); |
| | |
| | | const WebpackError = require("../WebpackError"); |
| | | const ConstDependency = require("../dependencies/ConstDependency"); |
| | | const CssIcssExportDependency = require("../dependencies/CssIcssExportDependency"); |
| | | const CssIcssFromIdentifierDependency = require("../dependencies/CssIcssFromIdentifierDependency"); |
| | | const CssIcssGlobalIdentifierDependency = require("../dependencies/CssIcssGlobalIdentifierDependency"); |
| | | const CssIcssImportDependency = require("../dependencies/CssIcssImportDependency"); |
| | | const CssIcssLocalIdentifierDependency = require("../dependencies/CssIcssLocalIdentifierDependency"); |
| | | const CssIcssSelfLocalIdentifierDependency = require("../dependencies/CssIcssSelfLocalIdentifierDependency"); |
| | | const CssIcssSymbolDependency = require("../dependencies/CssIcssSymbolDependency"); |
| | | const CssImportDependency = require("../dependencies/CssImportDependency"); |
| | | const CssUrlDependency = require("../dependencies/CssUrlDependency"); |
| | |
| | | /** @typedef {import("../Parser").ParserState} ParserState */ |
| | | /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */ |
| | | /** @typedef {import("./walkCssTokens").CssTokenCallbacks} CssTokenCallbacks */ |
| | | /** @typedef {import("../../declarations/WebpackOptions").CssParserExportType} CssParserExportType */ |
| | | /** @typedef {import("../../declarations/WebpackOptions").CssModuleParserOptions} CssModuleParserOptions */ |
| | | /** @typedef {import("../CssModule")} CssModule */ |
| | | |
| | | /** @typedef {[number, number]} Range */ |
| | | /** @typedef {{ line: number, column: number }} Position */ |
| | |
| | | const CC_COLON = ":".charCodeAt(0); |
| | | const CC_SEMICOLON = ";".charCodeAt(0); |
| | | const CC_COMMA = ",".charCodeAt(0); |
| | | const CC_SLASH = "/".charCodeAt(0); |
| | | const CC_LEFT_PARENTHESIS = "(".charCodeAt(0); |
| | | const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0); |
| | | const CC_LOWER_F = "f".charCodeAt(0); |
| | | const CC_UPPER_F = "F".charCodeAt(0); |
| | | const CC_RIGHT_CURLY = "}".charCodeAt(0); |
| | | const CC_HYPHEN_MINUS = "-".charCodeAt(0); |
| | | const CC_TILDE = "~".charCodeAt(0); |
| | | const CC_EQUAL = "=".charCodeAt(0); |
| | | const CC_FULL_STOP = ".".charCodeAt(0); |
| | | const CC_EXCLAMATION = "!".charCodeAt(0); |
| | | |
| | | // https://www.w3.org/TR/css-syntax-3/#newline |
| | | // We don't have `preprocessing` stage, so we need specify all of them |
| | | const STRING_MULTILINE = /\\[\n\r\f]/g; |
| | | // https://www.w3.org/TR/css-syntax-3/#whitespace |
| | | const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g; |
| | | const UNESCAPE = /\\([0-9a-fA-F]{1,6}[ \t\n\r\f]?|[\s\S])/g; |
| | | const IMAGE_SET_FUNCTION = /^(-\w+-)?image-set$/i; |
| | | const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(-\w+-)?keyframes$/; |
| | | const OPTIONALLY_VENDOR_PREFIXED_ANIMATION_PROPERTY = |
| | | /^(-\w+-)?animation(-name)?$/i; |
| | | const COMPOSES_PROPERTY = /^(composes|compose-with)$/i; |
| | | const IS_MODULES = /\.module(s)?\.[^.]+$/i; |
| | | const CSS_COMMENT = /\/\*((?!\*\/).*?)\*\//g; |
| | | const UNESCAPE = /\\([0-9a-f]{1,6}[ \t\n\r\f]?|[\s\S])/gi; |
| | | const IMAGE_SET_FUNCTION = /^(?:-\w+-)?image-set$/i; |
| | | const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(?:-\w+-)?keyframes$/; |
| | | const COMPOSES_PROPERTY = /^(?:composes|compose-with)$/i; |
| | | const IS_MODULES = /\.modules?\.[^.]+$/i; |
| | | const CSS_COMMENT = /\/\*((?!\*\/)[\s\S]*?)\*\//g; |
| | | |
| | | /** |
| | | * Returns matches. |
| | | * @param {RegExp} regexp a regexp |
| | | * @param {string} str a string |
| | | * @returns {RegExpExecArray[]} matches |
| | | */ |
| | | const matchAll = (regexp, str) => { |
| | | /** @type {RegExpExecArray[]} */ |
| | | const result = []; |
| | | |
| | | /** @type {null | RegExpExecArray} */ |
| | | let match; |
| | | |
| | | // Use a while loop with exec() to find all matches |
| | | while ((match = regexp.exec(str)) !== null) { |
| | | result.push(match); |
| | | } |
| | | // Return an array to be easily iterable (note: a true spec-compliant polyfill |
| | | // returns an iterator object, but an array spread often suffices for basic use) |
| | | return result; |
| | | }; |
| | | |
| | | /** |
| | | * Returns normalized url. |
| | | * @param {string} str url string |
| | | * @param {boolean} isString is url wrapped in quotes |
| | | * @returns {string} normalized url |
| | |
| | | return str; |
| | | }; |
| | | |
| | | // eslint-disable-next-line no-useless-escape |
| | | const regexSingleEscape = /[ -,.\/:-@[\]\^`{-~]/; |
| | | const regexSingleEscape = /[ -,./:-@[\]^`{-~]/; |
| | | const regexExcessiveSpaces = |
| | | /(^|\\+)?(\\[A-F0-9]{1,6})\u0020(?![a-fA-F0-9\u0020])/g; |
| | | |
| | | /** |
| | | * Returns escaped identifier. |
| | | * @param {string} str string |
| | | * @returns {string} escaped identifier |
| | | */ |
| | |
| | | while (counter < str.length) { |
| | | const character = str.charAt(counter++); |
| | | |
| | | /** @type {string} */ |
| | | let value; |
| | | |
| | | // eslint-disable-next-line no-control-regex |
| | | if (/[\t\n\f\r\u000B]/.test(character)) { |
| | | if (/[\t\n\f\r\v]/.test(character)) { |
| | | const codePoint = character.charCodeAt(0); |
| | | |
| | | value = `\\${codePoint.toString(16).toUpperCase()} `; |
| | |
| | | const CONTAINS_ESCAPE = /\\/; |
| | | |
| | | /** |
| | | * Returns hex. |
| | | * @param {string} str string |
| | | * @returns {[string, number] | undefined} hex |
| | | */ |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Unescape identifier. |
| | | * @param {string} str string |
| | | * @returns {string} unescaped string |
| | | */ |
| | |
| | | return ret; |
| | | }; |
| | | |
| | | /** |
| | | * A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo. |
| | | * The <custom-property-name> production corresponds to this: |
| | | * it’s defined as any <dashed-ident> (a valid identifier that starts with two dashes), |
| | | * except -- itself, which is reserved for future use by CSS. |
| | | * @param {string} identifier identifier |
| | | * @returns {boolean} true when identifier is dashed, otherwise false |
| | | */ |
| | | const isDashedIdentifier = (identifier) => |
| | | identifier.startsWith("--") && identifier.length >= 3; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const ANIMATION_KEYWORDS = { |
| | | // animation-direction |
| | | normal: 1, |
| | | reverse: 1, |
| | | alternate: 1, |
| | | "alternate-reverse": 1, |
| | | // animation-fill-mode |
| | | forwards: 1, |
| | | backwards: 1, |
| | | both: 1, |
| | | // animation-iteration-count |
| | | infinite: 1, |
| | | // animation-play-state |
| | | paused: 1, |
| | | running: 1, |
| | | // animation-timing-function |
| | | ease: 1, |
| | | "ease-in": 1, |
| | | "ease-out": 1, |
| | | "ease-in-out": 1, |
| | | linear: 1, |
| | | "step-end": 1, |
| | | "step-start": 1, |
| | | // Special |
| | | none: Infinity, // No matter how many times you write none, it will never be an animation name |
| | | const PREDEFINED_COUNTER_STYLES = { |
| | | decimal: 1, |
| | | "decimal-leading-zero": 1, |
| | | "arabic-indic": 1, |
| | | armenian: 1, |
| | | "upper-armenian": 1, |
| | | "lower-armenian": 1, |
| | | bengali: 1, |
| | | cambodian: 1, |
| | | khmer: 1, |
| | | "cjk-decimal": 1, |
| | | devanagari: 1, |
| | | georgian: 1, |
| | | gujarati: 1, |
| | | /* cspell:disable-next-line */ |
| | | gurmukhi: 1, |
| | | hebrew: 1, |
| | | kannada: 1, |
| | | lao: 1, |
| | | malayalam: 1, |
| | | mongolian: 1, |
| | | myanmar: 1, |
| | | oriya: 1, |
| | | persian: 1, |
| | | "lower-roman": 1, |
| | | "upper-roman": 1, |
| | | tamil: 1, |
| | | telugu: 1, |
| | | thai: 1, |
| | | tibetan: 1, |
| | | |
| | | "lower-alpha": 1, |
| | | "lower-latin": 1, |
| | | "upper-alpha": 1, |
| | | "upper-latin": 1, |
| | | "lower-greek": 1, |
| | | hiragana: 1, |
| | | /* cspell:disable-next-line */ |
| | | "hiragana-iroha": 1, |
| | | katakana: 1, |
| | | /* cspell:disable-next-line */ |
| | | "katakana-iroha": 1, |
| | | |
| | | disc: 1, |
| | | circle: 1, |
| | | square: 1, |
| | | "disclosure-open": 1, |
| | | "disclosure-closed": 1, |
| | | |
| | | "cjk-earthly-branch": 1, |
| | | "cjk-heavenly-stem": 1, |
| | | |
| | | "japanese-informal": 1, |
| | | "japanese-formal": 1, |
| | | |
| | | "korean-hangul-formal": 1, |
| | | /* cspell:disable-next-line */ |
| | | "korean-hanja-informal": 1, |
| | | /* cspell:disable-next-line */ |
| | | "korean-hanja-formal": 1, |
| | | |
| | | "simp-chinese-informal": 1, |
| | | "simp-chinese-formal": 1, |
| | | "trad-chinese-informal": 1, |
| | | "trad-chinese-formal": 1, |
| | | "cjk-ideographic": 1, |
| | | |
| | | "ethiopic-numeric": 1 |
| | | }; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const GLOBAL_VALUES = { |
| | | // Global values |
| | | initial: Infinity, |
| | | inherit: Infinity, |
| | |
| | | "revert-layer": Infinity |
| | | }; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const GRID_AREA_OR_COLUMN_OR_ROW = { |
| | | auto: Infinity, |
| | | span: Infinity, |
| | | ...GLOBAL_VALUES |
| | | }; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const GRID_AUTO_COLUMNS_OR_ROW = { |
| | | "min-content": Infinity, |
| | | "max-content": Infinity, |
| | | auto: Infinity, |
| | | ...GLOBAL_VALUES |
| | | }; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const GRID_AUTO_FLOW = { |
| | | row: 1, |
| | | column: 1, |
| | | dense: 1, |
| | | ...GLOBAL_VALUES |
| | | }; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const GRID_TEMPLATE_AREAS = { |
| | | // Special |
| | | none: 1, |
| | | ...GLOBAL_VALUES |
| | | }; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const GRID_TEMPLATE_COLUMNS_OR_ROWS = { |
| | | none: 1, |
| | | subgrid: 1, |
| | | masonry: 1, |
| | | "max-content": Infinity, |
| | | "min-content": Infinity, |
| | | auto: Infinity, |
| | | ...GLOBAL_VALUES |
| | | }; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const GRID_TEMPLATE = { |
| | | ...GRID_TEMPLATE_AREAS, |
| | | ...GRID_TEMPLATE_COLUMNS_OR_ROWS |
| | | }; |
| | | |
| | | /** @type {Record<string, number>} */ |
| | | const GRID = { |
| | | "auto-flow": 1, |
| | | dense: 1, |
| | | ...GRID_AUTO_COLUMNS_OR_ROW, |
| | | ...GRID_AUTO_FLOW, |
| | | ...GRID_TEMPLATE_AREAS, |
| | | ...GRID_TEMPLATE_COLUMNS_OR_ROWS |
| | | }; |
| | | |
| | | /** |
| | | * Gets known properties. |
| | | * @param {{ animation?: boolean, container?: boolean, customIdents?: boolean, grid?: boolean }=} options options |
| | | * @returns {Map<string, Record<string, number>>} list of known properties |
| | | */ |
| | | const getKnownProperties = (options = {}) => { |
| | | /** @type {Map<string, Record<string, number>>} */ |
| | | const knownProperties = new Map(); |
| | | |
| | | if (options.animation) { |
| | | knownProperties.set("animation", { |
| | | // animation-direction |
| | | normal: 1, |
| | | reverse: 1, |
| | | alternate: 1, |
| | | "alternate-reverse": 1, |
| | | // animation-fill-mode |
| | | forwards: 1, |
| | | backwards: 1, |
| | | both: 1, |
| | | // animation-iteration-count |
| | | infinite: 1, |
| | | // animation-play-state |
| | | paused: 1, |
| | | running: 1, |
| | | // animation-timing-function |
| | | ease: 1, |
| | | "ease-in": 1, |
| | | "ease-out": 1, |
| | | "ease-in-out": 1, |
| | | linear: 1, |
| | | "step-end": 1, |
| | | "step-start": 1, |
| | | // Special |
| | | none: Infinity, // No matter how many times you write none, it will never be an animation name |
| | | ...GLOBAL_VALUES |
| | | }); |
| | | knownProperties.set("animation-name", { |
| | | // Special |
| | | none: Infinity, // No matter how many times you write none, it will never be an animation name |
| | | ...GLOBAL_VALUES |
| | | }); |
| | | } |
| | | |
| | | if (options.container) { |
| | | knownProperties.set("container", { |
| | | // container-type |
| | | normal: 1, |
| | | size: 1, |
| | | "inline-size": 1, |
| | | "scroll-state": 1, |
| | | // Special |
| | | none: Infinity, |
| | | ...GLOBAL_VALUES |
| | | }); |
| | | knownProperties.set("container-name", { |
| | | // Special |
| | | none: Infinity, |
| | | ...GLOBAL_VALUES |
| | | }); |
| | | } |
| | | |
| | | if (options.customIdents) { |
| | | knownProperties.set("list-style", { |
| | | // list-style-position |
| | | inside: 1, |
| | | outside: 1, |
| | | // list-style-type |
| | | ...PREDEFINED_COUNTER_STYLES, |
| | | // Special |
| | | none: Infinity, |
| | | ...GLOBAL_VALUES |
| | | }); |
| | | knownProperties.set("list-style-type", { |
| | | // list-style-type |
| | | ...PREDEFINED_COUNTER_STYLES, |
| | | // Special |
| | | none: Infinity, |
| | | ...GLOBAL_VALUES |
| | | }); |
| | | knownProperties.set("system", { |
| | | cyclic: 1, |
| | | numeric: 1, |
| | | alphabetic: 1, |
| | | symbolic: 1, |
| | | additive: 1, |
| | | fixed: 1, |
| | | extends: 1, |
| | | ...PREDEFINED_COUNTER_STYLES |
| | | }); |
| | | knownProperties.set("fallback", { |
| | | ...PREDEFINED_COUNTER_STYLES |
| | | }); |
| | | knownProperties.set("speak-as", { |
| | | auto: 1, |
| | | bullets: 1, |
| | | numbers: 1, |
| | | words: 1, |
| | | "spell-out": 1, |
| | | ...PREDEFINED_COUNTER_STYLES |
| | | }); |
| | | } |
| | | |
| | | if (options.grid) { |
| | | knownProperties.set("grid", GRID); |
| | | knownProperties.set("grid-area", GRID_AREA_OR_COLUMN_OR_ROW); |
| | | knownProperties.set("grid-column", GRID_AREA_OR_COLUMN_OR_ROW); |
| | | knownProperties.set("grid-column-end", GRID_AREA_OR_COLUMN_OR_ROW); |
| | | knownProperties.set("grid-column-start", GRID_AREA_OR_COLUMN_OR_ROW); |
| | | knownProperties.set("grid-row", GRID_AREA_OR_COLUMN_OR_ROW); |
| | | knownProperties.set("grid-row-end", GRID_AREA_OR_COLUMN_OR_ROW); |
| | | knownProperties.set("grid-row-start", GRID_AREA_OR_COLUMN_OR_ROW); |
| | | knownProperties.set("grid-template", GRID_TEMPLATE); |
| | | knownProperties.set("grid-template-areas", GRID_TEMPLATE_AREAS); |
| | | knownProperties.set("grid-template-columns", GRID_TEMPLATE_COLUMNS_OR_ROWS); |
| | | knownProperties.set("grid-template-rows", GRID_TEMPLATE_COLUMNS_OR_ROWS); |
| | | } |
| | | |
| | | return knownProperties; |
| | | }; |
| | | |
| | | class LocConverter { |
| | | /** |
| | | * Creates an instance of LocConverter. |
| | | * @param {string} input input |
| | | */ |
| | | constructor(input) { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns location converter. |
| | | * @param {number} pos position |
| | | * @returns {LocConverter} location converter |
| | | */ |
| | |
| | | const CSS_MODE_TOP_LEVEL = 0; |
| | | const CSS_MODE_IN_BLOCK = 1; |
| | | |
| | | const LOCAL_MODE = 0; |
| | | const GLOBAL_MODE = 1; |
| | | |
| | | const eatUntilSemi = walkCssTokens.eatUntil(";"); |
| | | const eatUntilLeftCurly = walkCssTokens.eatUntil("{"); |
| | | const eatSemi = walkCssTokens.eatUntil(";"); |
| | | |
| | | /** |
| | | * @typedef {object} CssParserOptions |
| | | * @property {boolean=} importOption need handle `@import` |
| | | * @property {boolean=} url need handle URLs |
| | | * Defines the css parser own options type used by this module. |
| | | * @typedef {object} CssParserOwnOptions |
| | | * @property {("pure" | "global" | "local" | "auto")=} defaultMode default mode |
| | | * @property {boolean=} namedExports is named exports |
| | | * @property {CssParserExportType=} exportType export type |
| | | */ |
| | | |
| | | /** @typedef {CssModuleParserOptions & CssParserOwnOptions} CssParserOptions */ |
| | | |
| | | class CssParser extends Parser { |
| | | /** |
| | | * Creates an instance of CssParser. |
| | | * @param {CssParserOptions=} options options |
| | | */ |
| | | constructor({ |
| | | defaultMode = "pure", |
| | | importOption = true, |
| | | url = true, |
| | | namedExports = true, |
| | | exportType |
| | | } = {}) { |
| | | constructor(options = {}) { |
| | | super(); |
| | | this.defaultMode = defaultMode; |
| | | this.import = importOption; |
| | | this.url = url; |
| | | this.namedExports = namedExports; |
| | | this.exportType = exportType; |
| | | this.defaultMode = |
| | | typeof options.defaultMode !== "undefined" ? options.defaultMode : "pure"; |
| | | this.options = { |
| | | url: true, |
| | | import: true, |
| | | namedExports: true, |
| | | animation: true, |
| | | container: true, |
| | | customIdents: true, |
| | | dashedIdents: true, |
| | | function: true, |
| | | grid: true, |
| | | ...options |
| | | }; |
| | | /** @type {Comment[] | undefined} */ |
| | | this.comments = undefined; |
| | | this.magicCommentContext = createMagicCommentContext(); |
| | | } |
| | | |
| | | /** |
| | | * Processes the provided state. |
| | | * @param {ParserState} state parser state |
| | | * @param {string} message warning message |
| | | * @param {LocConverter} locConverter location converter |
| | |
| | | } |
| | | |
| | | /** |
| | | * Parses the provided source and updates the parser state. |
| | | * @param {string | Buffer | PreparsedAst} source the source to parse |
| | | * @param {ParserState} state the parser state |
| | | * @returns {ParserState} the parser state |
| | |
| | | } |
| | | |
| | | const isModules = mode === "global" || mode === "local"; |
| | | const knownProperties = getKnownProperties({ |
| | | animation: this.options.animation, |
| | | container: this.options.container, |
| | | customIdents: this.options.customIdents, |
| | | grid: this.options.grid |
| | | }); |
| | | |
| | | /** @type {BuildMeta} */ |
| | | (module.buildMeta).isCSSModule = isModules; |
| | |
| | | let scope = CSS_MODE_TOP_LEVEL; |
| | | /** @type {boolean} */ |
| | | let allowImportAtRule = true; |
| | | /** @type {[string, number, number][]} */ |
| | | /** @type {[string, number, number, boolean?][]} */ |
| | | const balanced = []; |
| | | let lastTokenEndForComments = 0; |
| | | |
| | |
| | | let isNextRulePrelude = isModules; |
| | | /** @type {number} */ |
| | | let blockNestingLevel = 0; |
| | | /** @type {"local" | "global" | undefined} */ |
| | | /** @type {0 | 1 | undefined} */ |
| | | let modeData; |
| | | /** @type {Set<string>} */ |
| | | const declaredCssVariables = new Set(); |
| | | /** @type {number} */ |
| | | let counter = 0; |
| | | |
| | | /** @type {string[]} */ |
| | | let lastLocalIdentifiers = []; |
| | | |
| | | /** @typedef {{ value: string, isReference?: boolean }} IcssDefinition */ |
| | | /** @typedef {{ value?: string, importName?: string, localName?: string }} IcssDefinition */ |
| | | /** @type {Map<string, IcssDefinition>} */ |
| | | const icssDefinitions = new Map(); |
| | | |
| | | /** |
| | | * Checks whether this css parser is next nested syntax. |
| | | * @param {string} input input |
| | | * @param {number} pos position |
| | | * @returns {boolean} true, when next is nested syntax |
| | | */ |
| | | const isNextNestedSyntax = (input, pos) => { |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos); |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0]; |
| | | |
| | | if (input[pos] === "}") { |
| | | if ( |
| | | input.charCodeAt(pos) === CC_RIGHT_CURLY || |
| | | (input.charCodeAt(pos) === CC_HYPHEN_MINUS && |
| | | input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS) |
| | | ) { |
| | | return false; |
| | | } |
| | | |
| | | // According spec only identifier can be used as a property name |
| | | const isIdentifier = walkCssTokens.isIdentStartCodePoint( |
| | | input.charCodeAt(pos) |
| | | ); |
| | | const identifier = walkCssTokens.eatIdentSequence(input, pos); |
| | | |
| | | return !isIdentifier; |
| | | if (!identifier) { |
| | | return true; |
| | | } |
| | | |
| | | const leftCurly = eatUntilLeftCurly(input, pos); |
| | | const content = input.slice(identifier[0], leftCurly); |
| | | |
| | | if (content.includes(";") || content.includes("}")) { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | }; |
| | | /** |
| | | * Checks whether this css parser is local mode. |
| | | * @returns {boolean} true, when in local scope |
| | | */ |
| | | const isLocalMode = () => |
| | | modeData === "local" || (mode === "local" && modeData === undefined); |
| | | modeData === LOCAL_MODE || (mode === "local" && modeData === undefined); |
| | | |
| | | /** |
| | | * @param {string} input input |
| | | * @param {number} pos start position |
| | | * @param {(input: string, pos: number) => number} eater eater |
| | | * @returns {[number,string]} new position and text |
| | | */ |
| | | const eatText = (input, pos, eater) => { |
| | | let text = ""; |
| | | for (;;) { |
| | | if (input.charCodeAt(pos) === CC_SLASH) { |
| | | const newPos = walkCssTokens.eatComments(input, pos); |
| | | if (pos !== newPos) { |
| | | pos = newPos; |
| | | if (pos === input.length) break; |
| | | } else { |
| | | text += "/"; |
| | | pos++; |
| | | if (pos === input.length) break; |
| | | } |
| | | } |
| | | const newPos = eater(input, pos); |
| | | if (pos !== newPos) { |
| | | text += input.slice(pos, newPos); |
| | | pos = newPos; |
| | | } else { |
| | | break; |
| | | } |
| | | if (pos === input.length) break; |
| | | } |
| | | return [pos, text.trimEnd()]; |
| | | }; |
| | | |
| | | const eatPropertyName = walkCssTokens.eatUntil(":{};"); |
| | | |
| | | /** |
| | | * Returns end. |
| | | * @param {string} input input |
| | | * @param {number} start start |
| | | * @param {number} end end |
| | |
| | | // Vanilla CSS stuff |
| | | |
| | | /** |
| | | * Processes the provided input. |
| | | * @param {string} input input |
| | | * @param {number} start name start position |
| | | * @param {number} end name end position |
| | |
| | | return newline; |
| | | } |
| | | |
| | | /** @type {undefined | string} */ |
| | | let layer; |
| | | |
| | | if (tokens[1]) { |
| | | layer = input.slice(tokens[1][0] + 6, tokens[1][1] - 1).trim(); |
| | | } |
| | | |
| | | /** @type {undefined | string} */ |
| | | let supports; |
| | | |
| | | if (tokens[2]) { |
| | |
| | | } |
| | | |
| | | const last = tokens[2] || tokens[1] || tokens[0]; |
| | | const mediaStart = walkCssTokens.eatWhitespaceAndComments(input, last[1]); |
| | | const mediaStart = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | last[1] |
| | | )[0]; |
| | | |
| | | /** @type {undefined | string} */ |
| | | let media; |
| | | |
| | | if (mediaStart !== semi - 1) { |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Process url function. |
| | | * @param {string} input input |
| | | * @param {number} end end position |
| | | * @param {string} name the name of function |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Process old url function. |
| | | * @param {string} input input |
| | | * @param {number} start start position |
| | | * @param {number} end end position |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Process image set function. |
| | | * @param {string} input input |
| | | * @param {number} start start position |
| | | * @param {number} end end position |
| | |
| | | // CSS modules stuff |
| | | |
| | | /** |
| | | * Returns resolved reexport (localName and importName). |
| | | * @param {string} value value to resolve |
| | | * @param {string=} localName override local name |
| | | * @param {boolean=} isCustomProperty true when it is custom property, otherwise false |
| | | * @returns {string | [string, string]} resolved reexport (`localName` and `importName`) |
| | | */ |
| | | const getReexport = (value, localName, isCustomProperty) => { |
| | | const reexport = icssDefinitions.get( |
| | | isCustomProperty ? `--${value}` : value |
| | | ); |
| | | |
| | | if (reexport) { |
| | | if (reexport.importName) { |
| | | return [ |
| | | reexport.localName || (isCustomProperty ? `--${value}` : value), |
| | | reexport.importName |
| | | ]; |
| | | } |
| | | |
| | | if (isCustomProperty) { |
| | | return /** @type {string} */ (reexport.value).slice(2); |
| | | } |
| | | |
| | | return /** @type {string} */ (reexport.value); |
| | | } |
| | | |
| | | if (localName) { |
| | | return [localName, value]; |
| | | } |
| | | |
| | | return value; |
| | | }; |
| | | |
| | | /** |
| | | * Process import or export. |
| | | * @param {0 | 1} type import or export |
| | | * @param {string} input input |
| | | * @param {number} pos start position |
| | | * @returns {number} position after parse |
| | | */ |
| | | const processImportOrExport = (type, input, pos) => { |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos); |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0]; |
| | | /** @type {string | undefined} */ |
| | | let request; |
| | | if (type === 0) { |
| | |
| | | if (!str) { |
| | | this._emitWarning( |
| | | state, |
| | | `Unexpected '${input[pos]}' at ${pos} during parsing of '${type ? ":import" : ":export"}' (expected string)`, |
| | | `Unexpected '${input[pos]}' at ${pos} during parsing of '${type === 0 ? ":import" : ":export"}' (expected string)`, |
| | | locConverter, |
| | | stringStart, |
| | | pos |
| | |
| | | } |
| | | request = input.slice(str[0] + 1, str[1] - 1); |
| | | pos = str[1]; |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos); |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0]; |
| | | cc = input.charCodeAt(pos); |
| | | if (cc !== CC_RIGHT_PARENTHESIS) { |
| | | this._emitWarning( |
| | |
| | | return pos; |
| | | } |
| | | pos++; |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos); |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0]; |
| | | } |
| | | |
| | | /** |
| | | * Creates a dep from the provided name. |
| | | * @param {string} name name |
| | | * @param {string} value value |
| | | * @param {number} start start of position |
| | |
| | | [0, 0], |
| | | /** @type {"local" | "global"} */ |
| | | (mode), |
| | | value |
| | | value, |
| | | name |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | |
| | | icssDefinitions.set(name, { value, isReference: true }); |
| | | icssDefinitions.set(name, { importName: value }); |
| | | } else if (type === 1) { |
| | | const reexport = icssDefinitions.get(value); |
| | | const dep = new CssIcssExportDependency( |
| | | name, |
| | | value, |
| | | reexport && reexport.isReference ? reexport.value : undefined, |
| | | undefined |
| | | ); |
| | | const dep = new CssIcssExportDependency(name, getReexport(value)); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | |
| | | return pos; |
| | | }; |
| | | |
| | | /** @typedef {{ from: string, items: ({ localName: string, importName: string })[] }} ValueAtRuleImport */ |
| | | /** @typedef {{ localName: string, value: string }} ValueAtRuleValue */ |
| | | /** |
| | | * Parses value at rule params. |
| | | * @param {string} str value at-rule params |
| | | * @returns {ValueAtRuleImport | ValueAtRuleValue} parsed result |
| | | */ |
| | | const parseValueAtRuleParams = (str) => { |
| | | if (/from(\/\*|\s)(?:[\s\S]+)$/i.test(str)) { |
| | | str = str.replace(CSS_COMMENT, " ").trim().replace(/;$/, ""); |
| | | const fromIdx = str.lastIndexOf("from"); |
| | | const path = str |
| | | .slice(fromIdx + 5) |
| | | .trim() |
| | | .replace(/['"]/g, ""); |
| | | let content = str.slice(0, fromIdx).trim(); |
| | | |
| | | if (content.startsWith("(") && content.endsWith(")")) { |
| | | content = content.slice(1, -1); |
| | | } |
| | | |
| | | return { |
| | | from: path, |
| | | items: content.split(",").map((item) => { |
| | | item = item.trim(); |
| | | |
| | | if (item.includes(":")) { |
| | | const [local, remote] = item.split(":"); |
| | | |
| | | return { localName: local.trim(), importName: remote.trim() }; |
| | | } |
| | | |
| | | const asParts = item.split(/\s+as\s+/); |
| | | |
| | | if (asParts.length === 2) { |
| | | return { |
| | | localName: asParts[1].trim(), |
| | | importName: asParts[0].trim() |
| | | }; |
| | | } |
| | | |
| | | return { localName: item, importName: item }; |
| | | }) |
| | | }; |
| | | } |
| | | |
| | | /** @type {string} */ |
| | | let localName; |
| | | /** @type {string} */ |
| | | let value; |
| | | |
| | | const idx = str.indexOf(":"); |
| | | |
| | | if (idx !== -1) { |
| | | localName = str.slice(0, idx).replace(CSS_COMMENT, "").trim(); |
| | | value = str.slice(idx + 1); |
| | | } else { |
| | | const mask = str.replace(CSS_COMMENT, (m) => " ".repeat(m.length)); |
| | | const idx = mask.search(/\S\s/) + 1; |
| | | |
| | | localName = str.slice(0, idx).replace(CSS_COMMENT, "").trim(); |
| | | value = str.slice(idx + (str[idx] === " " ? 1 : 0)); |
| | | } |
| | | |
| | | if (value.length > 0 && !/^\s+$/.test(value.replace(CSS_COMMENT, ""))) { |
| | | value = value.trim(); |
| | | } |
| | | |
| | | return { localName, value }; |
| | | }; |
| | | |
| | | /** |
| | | * Processes the provided input. |
| | | * @param {string} input input |
| | | * @param {number} start name start position |
| | | * @param {number} end name end position |
| | |
| | | const semi = eatUntilSemi(input, end); |
| | | const atRuleEnd = semi + 1; |
| | | const params = input.slice(end, semi); |
| | | let [alias, request] = params.split(/\s*from\s*/); |
| | | const parsed = parseValueAtRuleParams(params); |
| | | |
| | | if (request) { |
| | | const aliases = alias |
| | | .replace(CSS_COMMENT, " ") |
| | | .trim() |
| | | .replace(/^\(\s*|\s*\)$/g, "") |
| | | .split(/\s*,\s*/); |
| | | |
| | | request = request.replace(CSS_COMMENT, "").trim(); |
| | | |
| | | const isExplicitImport = request[0] === "'" || request[0] === '"'; |
| | | |
| | | if (isExplicitImport) { |
| | | request = request.slice(1, -1); |
| | | } |
| | | |
| | | for (const alias of aliases) { |
| | | const [name, aliasName] = alias.split(/\s+as\s+/); |
| | | |
| | | { |
| | | const reexport = icssDefinitions.get(request); |
| | | |
| | | if (reexport) { |
| | | request = reexport.value.slice(1, -1); |
| | | } |
| | | |
| | | const dep = new CssIcssImportDependency( |
| | | request, |
| | | [0, 0], |
| | | /** @type {"local" | "global"} */ |
| | | (mode), |
| | | name |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | |
| | | icssDefinitions.set(aliasName || name, { |
| | | value: name, |
| | | isReference: true |
| | | }); |
| | | } |
| | | |
| | | if (aliasName) { |
| | | const reexport = icssDefinitions.get(aliasName); |
| | | const dep = new CssIcssExportDependency( |
| | | aliasName, |
| | | name, |
| | | reexport && reexport.isReference ? reexport.value : undefined, |
| | | undefined |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | |
| | | { |
| | | const reexport = icssDefinitions.get(name); |
| | | const dep = new CssIcssExportDependency( |
| | | name, |
| | | name, |
| | | reexport && reexport.isReference ? reexport.value : undefined, |
| | | undefined |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | } |
| | | } else { |
| | | const ident = walkCssTokens.eatIdentSequence(alias, 0); |
| | | |
| | | if (!ident) { |
| | | if ( |
| | | typeof (/** @type {ValueAtRuleImport} */ (parsed).from) !== "undefined" |
| | | ) { |
| | | if (/** @type {ValueAtRuleImport} */ (parsed).from.length === 0) { |
| | | this._emitWarning( |
| | | state, |
| | | `Broken '@value' at-rule: ${input.slice(start, atRuleEnd)}'`, |
| | |
| | | return atRuleEnd; |
| | | } |
| | | |
| | | const pos = walkCssTokens.eatWhitespaceAndComments(alias, ident[1]); |
| | | let { from, items } = /** @type {ValueAtRuleImport} */ (parsed); |
| | | |
| | | const name = alias.slice(ident[0], ident[1]); |
| | | let value = |
| | | alias.charCodeAt(pos) === CC_COLON |
| | | ? alias.slice(pos + 1) |
| | | : alias.slice(ident[1]); |
| | | for (const { importName, localName } of items) { |
| | | { |
| | | const reexport = icssDefinitions.get(from); |
| | | |
| | | if (value && !/^\s+$/.test(value.replace(CSS_COMMENT, ""))) { |
| | | value = value.trim(); |
| | | if (reexport && reexport.value) { |
| | | from = reexport.value.slice(1, -1); |
| | | } |
| | | |
| | | const dep = new CssIcssImportDependency( |
| | | from, |
| | | [0, 0], |
| | | /** @type {"local" | "global"} */ |
| | | (mode), |
| | | importName, |
| | | localName |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | |
| | | icssDefinitions.set(localName, { importName }); |
| | | } |
| | | |
| | | { |
| | | const dep = new CssIcssExportDependency( |
| | | localName, |
| | | getReexport(localName), |
| | | undefined, |
| | | false, |
| | | CssIcssExportDependency.EXPORT_MODE.REPLACE |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | } |
| | | } else { |
| | | if (/** @type {ValueAtRuleValue} */ (parsed).localName.length === 0) { |
| | | this._emitWarning( |
| | | state, |
| | | `Broken '@value' at-rule: ${input.slice(start, atRuleEnd)}'`, |
| | | locConverter, |
| | | start, |
| | | atRuleEnd |
| | | ); |
| | | |
| | | const dep = new ConstDependency("", [start, atRuleEnd]); |
| | | module.addPresentationalDependency(dep); |
| | | return atRuleEnd; |
| | | } |
| | | |
| | | const { localName, value } = /** @type {ValueAtRuleValue} */ (parsed); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | |
| | | if (icssDefinitions.has(value)) { |
| | | const def = |
| | | /** @type {IcssDefinition} */ |
| | | (icssDefinitions.get(value)); |
| | | |
| | | value = def.value; |
| | | def.localName = value; |
| | | |
| | | icssDefinitions.set(localName, def); |
| | | |
| | | const dep = new CssIcssExportDependency( |
| | | localName, |
| | | getReexport(value) |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } else { |
| | | icssDefinitions.set(localName, { value }); |
| | | |
| | | const dep = new CssIcssExportDependency(localName, value); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | |
| | | icssDefinitions.set(name, { value }); |
| | | |
| | | const dep = new CssIcssExportDependency(name, value); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | |
| | | const dep = new ConstDependency("", [start, atRuleEnd]); |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Process icss symbol. |
| | | * @param {string} name ICSS symbol name |
| | | * @param {number} start start position |
| | | * @param {number} end end position |
| | | * @returns {number} position after handling |
| | | */ |
| | | const processICSSSymbol = (name, start, end) => { |
| | | const { value, isReference } = |
| | | const def = |
| | | /** @type {IcssDefinition} */ |
| | | (icssDefinitions.get(name)); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | const dep = new CssIcssSymbolDependency( |
| | | name, |
| | | value, |
| | | def.localName || name, |
| | | [start, end], |
| | | isReference |
| | | def.value, |
| | | def.importName |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Process local or global function. |
| | | * @param {string} input input |
| | | * @param {1 | 2} type type of function |
| | | * @param {number} start start position |
| | |
| | | end = walkCssTokens.consumeUntil( |
| | | input, |
| | | start, |
| | | type === 1 |
| | | ? { |
| | | identifier(input, start, end) { |
| | | const name = unescapeIdentifier(input.slice(start, end)); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | const dep = new CssIcssLocalIdentifierDependency(name, [ |
| | | start, |
| | | end |
| | | ]); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | { |
| | | identifier(input, start, end) { |
| | | if (type === 1) { |
| | | let identifier = unescapeIdentifier(input.slice(start, end)); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | const isDashedIdent = isDashedIdentifier(identifier); |
| | | |
| | | return end; |
| | | if (isDashedIdent) { |
| | | identifier = identifier.slice(2); |
| | | } |
| | | |
| | | const dep = new CssIcssExportDependency( |
| | | identifier, |
| | | getReexport(identifier), |
| | | [start, end], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.ONCE, |
| | | isDashedIdent |
| | | ? CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE |
| | | : CssIcssExportDependency.EXPORT_TYPE.NORMAL |
| | | ); |
| | | |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | : {}, |
| | | |
| | | return end; |
| | | } |
| | | }, |
| | | {}, |
| | | { onlyTopLevel: true, functionValue: true } |
| | | ); |
| | |
| | | }; |
| | | |
| | | /** |
| | | * Process local at rule. |
| | | * @param {string} input input |
| | | * @param {number} end name end position |
| | | * @param {{ string?: boolean, identifier: boolean, validate?: (name: string) => boolean, dashed?: boolean }} options types which allowed to handle |
| | | * @param {{ string?: boolean, identifier?: boolean | RegExp }} options types which allowed to handle |
| | | * @returns {number} position after handling |
| | | */ |
| | | const processLocalAtRule = (input, end, options) => { |
| | | /** @type {[number, number, boolean] | undefined} */ |
| | | let value; |
| | | let found = false; |
| | | |
| | | walkCssTokens.consumeUntil( |
| | | return walkCssTokens.consumeUntil( |
| | | input, |
| | | end, |
| | | { |
| | | string(_input, start, end) { |
| | | if (!found && options.string) { |
| | | const value = unescapeIdentifier(input.slice(start + 1, end - 1)); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | const dep = new CssIcssExportDependency( |
| | | value, |
| | | value, |
| | | [start, end], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.ONCE |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | found = true; |
| | | value = [start, end, true]; |
| | | } |
| | | return end; |
| | | }, |
| | | identifier(_input, start, end) { |
| | | if (!found && options.identifier) { |
| | | found = true; |
| | | value = [start, end, false]; |
| | | identifier(input, start, end) { |
| | | if (!found) { |
| | | const value = input.slice(start, end); |
| | | |
| | | if (options.identifier) { |
| | | const identifier = unescapeIdentifier(value); |
| | | |
| | | if ( |
| | | options.identifier instanceof RegExp && |
| | | options.identifier.test(identifier) |
| | | ) { |
| | | return end; |
| | | } |
| | | |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | |
| | | const dep = new CssIcssExportDependency( |
| | | identifier, |
| | | getReexport(identifier), |
| | | [start, end], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.ONCE, |
| | | CssIcssExportDependency.EXPORT_TYPE.NORMAL |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | found = true; |
| | | } |
| | | } |
| | | return end; |
| | | } |
| | | }, |
| | | { |
| | | function(input, start, end) { |
| | | function: (input, start, end) => { |
| | | // No need to handle `:` (COLON), because it's always a function |
| | | const name = input |
| | | .slice(start, end - 1) |
| | |
| | | return processLocalOrGlobalFunction(input, type, start, end); |
| | | } |
| | | |
| | | if ( |
| | | this.options.dashedIdents && |
| | | isLocalMode() && |
| | | (name === "var" || name === "style") |
| | | ) { |
| | | return processDashedIdent(input, end, end); |
| | | } |
| | | |
| | | return end; |
| | | } |
| | | }, |
| | | { onlyTopLevel: true, atRulePrelude: true } |
| | | ); |
| | | if (!value) return end; |
| | | let name = value[2] |
| | | ? input.slice(value[0] + 1, value[1] - 1) |
| | | : input.slice(value[0], value[1]); |
| | | if (options.validate && !options.validate(name)) return end; |
| | | name = unescapeIdentifier(name); |
| | | const { line: sl, column: sc } = locConverter.get(value[0]); |
| | | const { line: el, column: ec } = locConverter.get(value[1]); |
| | | if (options.dashed) { |
| | | name = name.slice(2); |
| | | declaredCssVariables.add(name); |
| | | } |
| | | const dep = new CssIcssLocalIdentifierDependency( |
| | | name, |
| | | [value[0], value[1]], |
| | | options.dashed ? "--" : "" |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | return value[1]; |
| | | }; |
| | | /** |
| | | * Process dashed ident. |
| | | * @param {string} input input |
| | | * @param {number} start start position |
| | | * @param {number} end end position |
| | | * @returns {number} position after handling |
| | | */ |
| | | const processVarFunction = (input, end) => { |
| | | const customIdent = walkCssTokens.eatIdentSequence(input, end); |
| | | const processDashedIdent = (input, start, end) => { |
| | | const customIdent = walkCssTokens.eatIdentSequence(input, start); |
| | | if (!customIdent) return end; |
| | | let name = input.slice(customIdent[0], customIdent[1]); |
| | | // A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo. |
| | | // The <custom-property-name> production corresponds to this: |
| | | // it’s defined as any <dashed-ident> (a valid identifier that starts with two dashes), |
| | | // except -- itself, which is reserved for future use by CSS. |
| | | if (!name.startsWith("--") || name.length < 3) return end; |
| | | name = unescapeIdentifier( |
| | | const identifier = unescapeIdentifier( |
| | | input.slice(customIdent[0] + 2, customIdent[1]) |
| | | ); |
| | | const afterCustomIdent = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | customIdent[1] |
| | | ); |
| | | )[0]; |
| | | if ( |
| | | input.charCodeAt(afterCustomIdent) === CC_LOWER_F || |
| | | input.charCodeAt(afterCustomIdent) === CC_UPPER_F |
| | |
| | | } |
| | | const from = walkCssTokens.eatIdentSequenceOrString( |
| | | input, |
| | | walkCssTokens.eatWhitespaceAndComments(input, fromWord[1]) |
| | | walkCssTokens.eatWhitespaceAndComments(input, fromWord[1])[0] |
| | | ); |
| | | if (!from) { |
| | | return end; |
| | |
| | | } else if (from[2] === false) { |
| | | const { line: sl, column: sc } = locConverter.get(customIdent[0]); |
| | | const { line: el, column: ec } = locConverter.get(from[1] - 1); |
| | | const dep = new CssIcssFromIdentifierDependency( |
| | | path.slice(1, -1), |
| | | /** @type {"local" | "global"} */ |
| | | (mode), |
| | | [customIdent[0], from[1] - 1], |
| | | name, |
| | | name, |
| | | "--" |
| | | ); |
| | | const localName = `__ICSS_IMPORT_${counter++}__`; |
| | | |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | { |
| | | const dep = new CssIcssImportDependency( |
| | | path.slice(1, -1), |
| | | [customIdent[0], from[1] - 1], |
| | | /** @type {"local" | "global"} */ |
| | | (mode), |
| | | identifier, |
| | | localName |
| | | ); |
| | | |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | |
| | | { |
| | | const dep = new CssIcssExportDependency( |
| | | identifier, |
| | | getReexport(identifier, localName, true), |
| | | [customIdent[0], from[1] - 1], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.ONCE, |
| | | CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE |
| | | ); |
| | | |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | |
| | | { |
| | | const dep = new ConstDependency("", [fromWord[0], from[1]]); |
| | |
| | | } else { |
| | | const { line: sl, column: sc } = locConverter.get(customIdent[0]); |
| | | const { line: el, column: ec } = locConverter.get(customIdent[1]); |
| | | const dep = new CssIcssSelfLocalIdentifierDependency( |
| | | name, |
| | | undefined, |
| | | const dep = new CssIcssExportDependency( |
| | | identifier, |
| | | getReexport(identifier, undefined, true), |
| | | [customIdent[0], customIdent[1]], |
| | | "--", |
| | | declaredCssVariables |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.ONCE, |
| | | CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | |
| | | return end; |
| | | }; |
| | | /** |
| | | * Process local declaration. |
| | | * @param {string} input input |
| | | * @param {number} pos name start position |
| | | * @param {number} end name end position |
| | | * @returns {number} position after handling |
| | | */ |
| | | const processLocalDeclaration = (input, pos, end) => { |
| | | modeData = undefined; |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos); |
| | | const propertyNameStart = pos; |
| | | const [propertyNameEnd, propertyName] = eatText( |
| | | input, |
| | | pos, |
| | | eatPropertyName |
| | | ); |
| | | if (input.charCodeAt(propertyNameEnd) !== CC_COLON) return end; |
| | | pos = propertyNameEnd + 1; |
| | | if (propertyName.startsWith("--") && propertyName.length >= 3) { |
| | | // CSS Variable |
| | | const { line: sl, column: sc } = locConverter.get(propertyNameStart); |
| | | const { line: el, column: ec } = locConverter.get(propertyNameEnd); |
| | | const name = unescapeIdentifier(propertyName.slice(2)); |
| | | const dep = new CssIcssLocalIdentifierDependency( |
| | | name, |
| | | [propertyNameStart, propertyNameEnd], |
| | | "--" |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | declaredCssVariables.add(name); |
| | | } else if ( |
| | | OPTIONALLY_VENDOR_PREFIXED_ANIMATION_PROPERTY.test(propertyName) |
| | | ) { |
| | | /** @type {[number, number, boolean][]} */ |
| | | const animationNames = []; |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0]; |
| | | const identifier = walkCssTokens.eatIdentSequence(input, pos); |
| | | |
| | | if (!identifier) { |
| | | return end; |
| | | } |
| | | |
| | | const propertyNameStart = identifier[0]; |
| | | |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, identifier[1])[0]; |
| | | |
| | | if (input.charCodeAt(pos) !== CC_COLON) { |
| | | return end; |
| | | } |
| | | |
| | | pos += 1; |
| | | |
| | | // Remove prefix and lowercase |
| | | const propertyName = input |
| | | .slice(identifier[0], identifier[1]) |
| | | .replace(/^(-\w+-)/, "") |
| | | .toLowerCase(); |
| | | |
| | | if (isLocalMode() && knownProperties.has(propertyName)) { |
| | | /** @type {[number, number, boolean?][]} */ |
| | | const values = []; |
| | | /** @type {Record<string, number>} */ |
| | | let parsedAnimationKeywords = Object.create(null); |
| | | let parsedKeywords = Object.create(null); |
| | | |
| | | const isGridProperty = Boolean(propertyName.startsWith("grid")); |
| | | const isGridTemplate = isGridProperty |
| | | ? Boolean( |
| | | propertyName === "grid" || |
| | | propertyName === "grid-template" || |
| | | propertyName === "grid-template-columns" || |
| | | propertyName === "grid-template-rows" |
| | | ) |
| | | : false; |
| | | |
| | | let afterExclamation = false; |
| | | |
| | | const end = walkCssTokens.consumeUntil( |
| | | input, |
| | | pos, |
| | | { |
| | | delim(input, start, end) { |
| | | afterExclamation = input.charCodeAt(start) === CC_EXCLAMATION; |
| | | return end; |
| | | }, |
| | | leftSquareBracket(input, start, end) { |
| | | let i = end; |
| | | |
| | | while (true) { |
| | | i = walkCssTokens.eatWhitespaceAndComments(input, i)[0]; |
| | | const name = walkCssTokens.eatIdentSequence(input, i); |
| | | |
| | | if (!name) { |
| | | break; |
| | | } |
| | | |
| | | values.push(name); |
| | | i = name[1]; |
| | | } |
| | | |
| | | return end; |
| | | }, |
| | | string(_input, start, end) { |
| | | animationNames.push([start, end, true]); |
| | | if ( |
| | | propertyName === "animation" || |
| | | propertyName === "animation-name" |
| | | ) { |
| | | values.push([start, end, true]); |
| | | } |
| | | |
| | | if ( |
| | | propertyName === "grid" || |
| | | propertyName === "grid-template" || |
| | | propertyName === "grid-template-areas" |
| | | ) { |
| | | const areas = unescapeIdentifier( |
| | | input.slice(start + 1, end - 1) |
| | | ); |
| | | const matches = matchAll(/\b\w+\b/g, areas); |
| | | |
| | | for (const match of matches) { |
| | | const areaStart = start + 1 + match.index; |
| | | values.push([areaStart, areaStart + match[0].length, false]); |
| | | } |
| | | } |
| | | |
| | | return end; |
| | | }, |
| | | identifier(input, start, end) { |
| | | const keyword = input.slice(start, end).toLowerCase(); |
| | | if (isGridTemplate) { |
| | | return end; |
| | | } |
| | | |
| | | parsedAnimationKeywords[keyword] = |
| | | typeof parsedAnimationKeywords[keyword] !== "undefined" |
| | | ? parsedAnimationKeywords[keyword] + 1 |
| | | if (afterExclamation) { |
| | | afterExclamation = false; |
| | | return end; |
| | | } |
| | | |
| | | const identifier = input.slice(start, end); |
| | | const keyword = identifier.toLowerCase(); |
| | | |
| | | parsedKeywords[keyword] = |
| | | typeof parsedKeywords[keyword] !== "undefined" |
| | | ? parsedKeywords[keyword] + 1 |
| | | : 0; |
| | | const keywords = |
| | | /** @type {Record<string, number>} */ |
| | | (knownProperties.get(propertyName)); |
| | | |
| | | if ( |
| | | ANIMATION_KEYWORDS[keyword] && |
| | | parsedAnimationKeywords[keyword] < ANIMATION_KEYWORDS[keyword] |
| | | keywords[keyword] && |
| | | parsedKeywords[keyword] < keywords[keyword] |
| | | ) { |
| | | return end; |
| | | } |
| | | |
| | | animationNames.push([start, end, false]); |
| | | values.push([start, end]); |
| | | return end; |
| | | }, |
| | | comma(_input, _start, end) { |
| | | parsedAnimationKeywords = {}; |
| | | parsedKeywords = {}; |
| | | |
| | | return end; |
| | | } |
| | | }, |
| | | { |
| | | function(input, start, end) { |
| | | function: (input, start, end) => { |
| | | const name = input |
| | | .slice(start, end - 1) |
| | | .replace(/\\/g, "") |
| | | .toLowerCase(); |
| | | |
| | | if (isLocalMode() && name === "var") { |
| | | return processVarFunction(input, end); |
| | | } |
| | | |
| | | const type = |
| | | name === "local" ? 1 : name === "global" ? 2 : undefined; |
| | |
| | | return processLocalOrGlobalFunction(input, type, start, end); |
| | | } |
| | | |
| | | if ( |
| | | this.options.dashedIdents && |
| | | isLocalMode() && |
| | | name === "var" |
| | | ) { |
| | | return processDashedIdent(input, end, end); |
| | | } |
| | | |
| | | if (this.options.url) { |
| | | if (name === "src" || name === "url") { |
| | | return processURLFunction(input, end, name); |
| | | } else if (IMAGE_SET_FUNCTION.test(name)) { |
| | | return processImageSetFunction(input, start, end); |
| | | } |
| | | } |
| | | |
| | | return end; |
| | | } |
| | | }, |
| | | { onlyTopLevel: true, declarationValue: true } |
| | | { |
| | | onlyTopLevel: !isGridTemplate, |
| | | declarationValue: true |
| | | } |
| | | ); |
| | | |
| | | if (animationNames.length > 0) { |
| | | for (const animationName of animationNames) { |
| | | const { line: sl, column: sc } = locConverter.get(animationName[0]); |
| | | const { line: el, column: ec } = locConverter.get(animationName[1]); |
| | | const [start, end, isString] = animationName; |
| | | if (values.length > 0) { |
| | | for (const value of values) { |
| | | const { line: sl, column: sc } = locConverter.get(value[0]); |
| | | const { line: el, column: ec } = locConverter.get(value[1]); |
| | | const [start, end, isString] = value; |
| | | const name = unescapeIdentifier( |
| | | isString |
| | | ? input.slice(start + 1, end - 1) |
| | | : input.slice(start, end) |
| | | ); |
| | | const dep = new CssIcssSelfLocalIdentifierDependency( |
| | | const dep = new CssIcssExportDependency( |
| | | name, |
| | | undefined, |
| | | [start, end] |
| | | getReexport(name), |
| | | [start, end], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.ONCE, |
| | | isGridProperty |
| | | ? CssIcssExportDependency.EXPORT_TYPE.GRID_CUSTOM_IDENTIFIER |
| | | : CssIcssExportDependency.EXPORT_TYPE.NORMAL |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | |
| | | const classNames = new Set(); |
| | | |
| | | while (true) { |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos); |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0]; |
| | | |
| | | let className = walkCssTokens.eatIdentSequence(input, pos); |
| | | |
| | |
| | | pos = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | className[1] + 1 |
| | | ); |
| | | )[0]; |
| | | className = walkCssTokens.eatIdentSequence(input, pos); |
| | | if (className) { |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, className[1]); |
| | | pos = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | className[1] |
| | | )[0]; |
| | | pos += 1; |
| | | } |
| | | } else if (className) { |
| | | pos = walkCssTokens.eatWhitespaceAndComments(input, className[1]); |
| | | pos = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | className[1] |
| | | )[0]; |
| | | pos = className[1]; |
| | | } |
| | | |
| | |
| | | for (const className of classNames) { |
| | | const [start, end] = className; |
| | | const identifier = unescapeIdentifier(input.slice(start, end)); |
| | | const reexport = icssDefinitions.get(identifier); |
| | | const dep = isGlobalFunction |
| | | ? new CssIcssGlobalIdentifierDependency( |
| | | lastLocalIdentifier, |
| | | identifier, |
| | | reexport && reexport.isReference |
| | | ? reexport.value |
| | | : undefined, |
| | | [start, end] |
| | | ) |
| | | : new CssIcssSelfLocalIdentifierDependency( |
| | | lastLocalIdentifier, |
| | | identifier, |
| | | [start, end], |
| | | undefined, |
| | | undefined, |
| | | reexport && reexport.isReference |
| | | ? reexport.value |
| | | : undefined |
| | | ); |
| | | const dep = new CssIcssExportDependency( |
| | | lastLocalIdentifier, |
| | | getReexport(identifier), |
| | | [start, end], |
| | | !isGlobalFunction, |
| | | isGlobalFunction |
| | | ? CssIcssExportDependency.EXPORT_MODE.APPEND |
| | | : CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE, |
| | | CssIcssExportDependency.EXPORT_TYPE.COMPOSES |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | |
| | | for (const className of classNames) { |
| | | const [start, end] = className; |
| | | const identifier = unescapeIdentifier(input.slice(start, end)); |
| | | const dep = new CssIcssFromIdentifierDependency( |
| | | request, |
| | | /** @type {"local" | "global"} */ |
| | | (mode), |
| | | [start, end], |
| | | identifier, |
| | | /** @type {string} */ |
| | | (lastLocalIdentifier) |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | const localName = `__ICSS_IMPORT_${counter++}__`; |
| | | |
| | | { |
| | | const dep = new CssIcssImportDependency( |
| | | request, |
| | | [start, end], |
| | | /** @type {"local" | "global"} */ |
| | | (mode), |
| | | identifier, |
| | | localName |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | |
| | | { |
| | | const dep = new CssIcssExportDependency( |
| | | lastLocalIdentifier, |
| | | getReexport(identifier, localName), |
| | | [start, end], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.APPEND, |
| | | CssIcssExportDependency.EXPORT_TYPE.COMPOSES |
| | | ); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | } |
| | | } |
| | | |
| | | classNames.clear(); |
| | |
| | | const identifier = unescapeIdentifier( |
| | | input.slice(start, end) |
| | | ); |
| | | const reexport = icssDefinitions.get(identifier); |
| | | const dep = new CssIcssGlobalIdentifierDependency( |
| | | const dep = new CssIcssExportDependency( |
| | | /** @type {string} */ |
| | | (lastLocalIdentifier), |
| | | identifier, |
| | | reexport && reexport.isReference |
| | | ? reexport.value |
| | | : undefined, |
| | | [start, end] |
| | | getReexport(identifier), |
| | | [start, end], |
| | | false, |
| | | CssIcssExportDependency.EXPORT_MODE.APPEND, |
| | | CssIcssExportDependency.EXPORT_TYPE.COMPOSES |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | |
| | | const dep = new ConstDependency("", [propertyNameStart, end]); |
| | | module.addPresentationalDependency(dep); |
| | | } |
| | | |
| | | return pos; |
| | | }; |
| | | |
| | | /** |
| | | * Process id selector. |
| | | * @param {string} input input |
| | | * @param {number} start start position |
| | | * @param {number} end end position |
| | | * @returns {number} position after handling |
| | | */ |
| | | const processHashID = (input, start, end) => { |
| | | const processIdSelector = (input, start, end) => { |
| | | const valueStart = start + 1; |
| | | const name = unescapeIdentifier(input.slice(valueStart, end)); |
| | | const dep = new CssIcssLocalIdentifierDependency(name, [valueStart, end]); |
| | | const dep = new CssIcssExportDependency( |
| | | name, |
| | | getReexport(name), |
| | | [valueStart, end], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.ONCE |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(start); |
| | | const { line: el, column: ec } = locConverter.get(end); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | return end; |
| | | }; |
| | | |
| | | /** |
| | | * Process class selector. |
| | | * @param {string} input input |
| | | * @param {number} start start position |
| | | * @param {number} end end position |
| | | * @returns {number} position after handling |
| | | */ |
| | | const processClassSelector = (input, start, end) => { |
| | | const ident = walkCssTokens.skipCommentsAndEatIdentSequence(input, end); |
| | | if (!ident) return end; |
| | | const name = unescapeIdentifier(input.slice(ident[0], ident[1])); |
| | | lastLocalIdentifiers.push(name); |
| | | const dep = new CssIcssExportDependency( |
| | | name, |
| | | getReexport(name), |
| | | [ident[0], ident[1]], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.ONCE |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(ident[0]); |
| | | const { line: el, column: ec } = locConverter.get(ident[1]); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | return ident[1]; |
| | | }; |
| | | |
| | | /** |
| | | * Process attribute selector. |
| | | * @param {string} input input |
| | | * @param {number} start start position |
| | | * @param {number} end end position |
| | | * @returns {number} position after handling |
| | | */ |
| | | const processAttributeSelector = (input, start, end) => { |
| | | end = walkCssTokens.eatWhitespaceAndComments(input, end)[0]; |
| | | const identifier = walkCssTokens.eatIdentSequence(input, end); |
| | | if (!identifier) return end; |
| | | const name = unescapeIdentifier( |
| | | input.slice(identifier[0], identifier[1]) |
| | | ); |
| | | if (name.toLowerCase() !== "class") { |
| | | return end; |
| | | } |
| | | end = walkCssTokens.eatWhitespaceAndComments(input, identifier[1])[0]; |
| | | |
| | | const isTilde = input.charCodeAt(end) === CC_TILDE; |
| | | |
| | | if ( |
| | | input.charCodeAt(end) !== CC_EQUAL && |
| | | input.charCodeAt(end) !== CC_TILDE |
| | | ) { |
| | | return end; |
| | | } |
| | | |
| | | end += 1; |
| | | |
| | | if (isTilde) { |
| | | if (input.charCodeAt(end) !== CC_EQUAL) { |
| | | return end; |
| | | } |
| | | |
| | | end += 1; |
| | | } |
| | | |
| | | end = walkCssTokens.eatWhitespaceAndComments(input, end)[0]; |
| | | const value = walkCssTokens.eatIdentSequenceOrString(input, end); |
| | | |
| | | if (!value) { |
| | | return end; |
| | | } |
| | | |
| | | const classNameStart = value[2] ? value[0] : value[0] + 1; |
| | | const classNameEnd = value[2] ? value[1] : value[1] - 1; |
| | | const className = unescapeIdentifier( |
| | | input.slice(classNameStart, classNameEnd) |
| | | ); |
| | | const dep = new CssIcssExportDependency( |
| | | className, |
| | | getReexport(className), |
| | | [classNameStart, classNameEnd], |
| | | true, |
| | | CssIcssExportDependency.EXPORT_MODE.NONE |
| | | ); |
| | | const { line: sl, column: sc } = locConverter.get(classNameStart); |
| | | const { line: el, column: ec } = locConverter.get(classNameEnd); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | return value[2] ? classNameEnd : classNameEnd + 1; |
| | | }; |
| | | |
| | | walkCssTokens(source, 0, { |
| | |
| | | return end; |
| | | }, |
| | | url: (input, start, end, contentStart, contentEnd) => { |
| | | if (!this.url) { |
| | | if (!this.options.url) { |
| | | return end; |
| | | } |
| | | |
| | |
| | | |
| | | return eatUntilSemi(input, start); |
| | | } |
| | | case "@charset": { |
| | | const atRuleEnd = eatUntilSemi(input, start); |
| | | |
| | | if (/** @type {CssModule} */ (module).exportType === "style") { |
| | | return atRuleEnd; |
| | | } |
| | | |
| | | const dep = new ConstDependency("", [start, atRuleEnd + 1]); |
| | | module.addPresentationalDependency(dep); |
| | | |
| | | const value = walkCssTokens.eatString(input, end); |
| | | |
| | | if (!value) { |
| | | return atRuleEnd; |
| | | } |
| | | |
| | | /** @type {BuildInfo} */ |
| | | (module.buildInfo).charset = input |
| | | .slice(value[0] + 1, value[1] - 1) |
| | | .toUpperCase(); |
| | | |
| | | return atRuleEnd; |
| | | } |
| | | case "@import": { |
| | | if (!this.import) { |
| | | return eatSemi(input, end); |
| | | if (!this.options.import) { |
| | | return eatUntilSemi(input, end); |
| | | } |
| | | |
| | | if (!allowImportAtRule) { |
| | |
| | | start, |
| | | end |
| | | ); |
| | | return end; |
| | | return eatUntilSemi(input, end); |
| | | } |
| | | |
| | | return processAtImport(input, start, end); |
| | |
| | | if (name === "@value") { |
| | | return processAtValue(input, start, end); |
| | | } else if ( |
| | | this.options.animation && |
| | | OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE.test(name) && |
| | | isLocalMode() |
| | | ) { |
| | |
| | | string: true, |
| | | identifier: true |
| | | }); |
| | | } else if (name === "@property" && isLocalMode()) { |
| | | } else if ( |
| | | this.options.customIdents && |
| | | name === "@counter-style" && |
| | | isLocalMode() |
| | | ) { |
| | | return processLocalAtRule(input, end, { |
| | | identifier: true, |
| | | dashed: true, |
| | | validate: (name) => name.startsWith("--") && name.length >= 3 |
| | | identifier: true |
| | | }); |
| | | } else if ( |
| | | this.options.container && |
| | | name === "@container" && |
| | | isLocalMode() |
| | | ) { |
| | | return processLocalAtRule(input, end, { |
| | | identifier: /^(none|and|or|not)$/ |
| | | }); |
| | | } else if (name === "@scope") { |
| | | isNextRulePrelude = true; |
| | |
| | | }, |
| | | identifier: (input, start, end) => { |
| | | if (isModules) { |
| | | const name = input.slice(start, end); |
| | | const identifier = input.slice(start, end); |
| | | |
| | | if (icssDefinitions.has(name)) { |
| | | return processICSSSymbol(name, start, end); |
| | | if ( |
| | | this.options.dashedIdents && |
| | | isLocalMode() && |
| | | isDashedIdentifier(identifier) |
| | | ) { |
| | | return processDashedIdent(input, start, end); |
| | | } |
| | | |
| | | if (icssDefinitions.has(identifier)) { |
| | | return processICSSSymbol(identifier, start, end); |
| | | } |
| | | |
| | | switch (scope) { |
| | | case CSS_MODE_IN_BLOCK: { |
| | | if (isLocalMode()) { |
| | | if (isModules && !isNextRulePrelude) { |
| | | // Handle only top level values and not inside functions |
| | | return processLocalDeclaration(input, start, end); |
| | | } |
| | |
| | | return end; |
| | | }, |
| | | delim: (input, start, end) => { |
| | | if (isNextRulePrelude && isLocalMode()) { |
| | | const ident = walkCssTokens.skipCommentsAndEatIdentSequence( |
| | | input, |
| | | end |
| | | ); |
| | | if (!ident) return end; |
| | | const name = unescapeIdentifier(input.slice(ident[0], ident[1])); |
| | | lastLocalIdentifiers.push(name); |
| | | const dep = new CssIcssLocalIdentifierDependency(name, [ |
| | | ident[0], |
| | | ident[1] |
| | | ]); |
| | | const { line: sl, column: sc } = locConverter.get(ident[0]); |
| | | const { line: el, column: ec } = locConverter.get(ident[1]); |
| | | dep.setLoc(sl, sc, el, ec); |
| | | module.addDependency(dep); |
| | | return ident[1]; |
| | | if ( |
| | | input.charCodeAt(start) === CC_FULL_STOP && |
| | | isNextRulePrelude && |
| | | isLocalMode() |
| | | ) { |
| | | return processClassSelector(input, start, end); |
| | | } |
| | | |
| | | return end; |
| | | }, |
| | | hash: (input, start, end, isID) => { |
| | | if (isNextRulePrelude && isLocalMode() && isID) { |
| | | return processHashID(input, start, end); |
| | | return processIdSelector(input, start, end); |
| | | } |
| | | |
| | | return end; |
| | |
| | | |
| | | if (isFn && name === "local") { |
| | | // Eat extra whitespace |
| | | const end = walkCssTokens.eatWhitespace(input, ident[1] + 1); |
| | | modeData = "local"; |
| | | const end = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | ident[1] + 1 |
| | | )[0]; |
| | | modeData = LOCAL_MODE; |
| | | const dep = new ConstDependency("", [start, end]); |
| | | module.addPresentationalDependency(dep); |
| | | balanced.push([":local", start, end]); |
| | | balanced.push([":local", start, end, true]); |
| | | return end; |
| | | } else if (name === "local") { |
| | | modeData = "local"; |
| | | // Eat extra whitespace |
| | | end = walkCssTokens.eatWhitespace(input, ident[1]); |
| | | modeData = LOCAL_MODE; |
| | | const found = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | ident[1] |
| | | ); |
| | | |
| | | if (ident[1] === end) { |
| | | if (!found[1]) { |
| | | this._emitWarning( |
| | | state, |
| | | `Missing whitespace after ':local' in '${input.slice( |
| | |
| | | ); |
| | | } |
| | | |
| | | end = walkCssTokens.eatWhitespace(input, ident[1]); |
| | | const dep = new ConstDependency("", [start, end]); |
| | | module.addPresentationalDependency(dep); |
| | | return end; |
| | | } else if (isFn && name === "global") { |
| | | // Eat extra whitespace |
| | | const end = walkCssTokens.eatWhitespace(input, ident[1] + 1); |
| | | modeData = "global"; |
| | | const end = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | ident[1] + 1 |
| | | )[0]; |
| | | modeData = GLOBAL_MODE; |
| | | const dep = new ConstDependency("", [start, end]); |
| | | module.addPresentationalDependency(dep); |
| | | balanced.push([":global", start, end]); |
| | | balanced.push([":global", start, end, true]); |
| | | return end; |
| | | } else if (name === "global") { |
| | | modeData = "global"; |
| | | modeData = GLOBAL_MODE; |
| | | // Eat extra whitespace |
| | | end = walkCssTokens.eatWhitespace(input, ident[1]); |
| | | const found = walkCssTokens.eatWhitespaceAndComments( |
| | | input, |
| | | ident[1] |
| | | ); |
| | | |
| | | if (ident[1] === end) { |
| | | if (!found[1]) { |
| | | this._emitWarning( |
| | | state, |
| | | `Missing whitespace after ':global' in '${input.slice( |
| | |
| | | ); |
| | | } |
| | | |
| | | end = walkCssTokens.eatWhitespace(input, ident[1]); |
| | | const dep = new ConstDependency("", [start, end]); |
| | | module.addPresentationalDependency(dep); |
| | | return end; |
| | |
| | | switch (name) { |
| | | case "src": |
| | | case "url": { |
| | | if (!this.url) { |
| | | if (!this.options.url) { |
| | | return end; |
| | | } |
| | | |
| | | return processURLFunction(input, end, name); |
| | | } |
| | | default: { |
| | | if (this.url && IMAGE_SET_FUNCTION.test(name)) { |
| | | if (this.options.url && IMAGE_SET_FUNCTION.test(name)) { |
| | | return processImageSetFunction(input, start, end); |
| | | } else if (isLocalMode() && name === "var") { |
| | | return processVarFunction(input, end); |
| | | } |
| | | |
| | | if (isModules) { |
| | | if ( |
| | | this.options.function && |
| | | isLocalMode() && |
| | | isDashedIdentifier(name) |
| | | ) { |
| | | return processDashedIdent(input, start, end); |
| | | } |
| | | |
| | | const type = |
| | | name === "local" ? 1 : name === "global" ? 2 : undefined; |
| | | |
| | | if (type && !isNextRulePrelude) { |
| | | return processLocalOrGlobalFunction(input, type, start, end); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | return end; |
| | | }, |
| | | leftSquareBracket: (input, start, end) => { |
| | | if (isNextRulePrelude && isLocalMode()) { |
| | | return processAttributeSelector(input, start, end); |
| | | } |
| | | return end; |
| | | }, |
| | | leftParenthesis: (input, start, end) => { |
| | |
| | | rightParenthesis: (input, start, end) => { |
| | | const popped = balanced.pop(); |
| | | |
| | | if ( |
| | | isModules && |
| | | popped && |
| | | (popped[0] === ":local" || popped[0] === ":global") |
| | | ) { |
| | | modeData = balanced[balanced.length - 1] |
| | | ? /** @type {"local" | "global"} */ |
| | | (balanced[balanced.length - 1][0]) |
| | | : undefined; |
| | | const dep = new ConstDependency("", [start, end]); |
| | | module.addPresentationalDependency(dep); |
| | | if (isModules && popped) { |
| | | const isLocal = popped[0] === ":local"; |
| | | const isGlobal = popped[0] === ":global"; |
| | | if (isLocal || isGlobal) { |
| | | modeData = balanced[balanced.length - 1] |
| | | ? balanced[balanced.length - 1][0] === ":local" |
| | | ? LOCAL_MODE |
| | | : balanced[balanced.length - 1][0] === ":global" |
| | | ? GLOBAL_MODE |
| | | : undefined |
| | | : undefined; |
| | | if (popped[3] && isLocal) { |
| | | while (walkCssTokens.isWhiteSpace(input.charCodeAt(start - 1))) { |
| | | start -= 1; |
| | | } |
| | | } |
| | | const dep = new ConstDependency("", [start, end]); |
| | | module.addPresentationalDependency(dep); |
| | | } else if (isNextRulePrelude) { |
| | | modeData = undefined; |
| | | } |
| | | } |
| | | |
| | | return end; |
| | | }, |
| | | comma: (input, start, end) => { |
| | | if (isModules) { |
| | | const popped = balanced.pop(); |
| | | |
| | | if (!popped) { |
| | | // Reset stack for `:global .class :local .class-other` selector after |
| | | modeData = undefined; |
| | | } |
| | | if (isModules && balanced.length === 0) { |
| | | // Reset stack for `:global .class :local .class-other` selector after |
| | | modeData = undefined; |
| | | } |
| | | |
| | | lastTokenEndForComments = start; |
| | |
| | | |
| | | const buildMeta = /** @type {BuildMeta} */ (state.module.buildMeta); |
| | | |
| | | buildMeta.exportsType = this.namedExports ? "namespace" : "default"; |
| | | buildMeta.defaultObject = this.namedExports ? false : "redirect-warn"; |
| | | buildMeta.exportType = this.exportType; |
| | | buildMeta.exportsType = this.options.namedExports ? "namespace" : "default"; |
| | | buildMeta.defaultObject = this.options.namedExports |
| | | ? false |
| | | : "redirect-warn"; |
| | | |
| | | if (!buildMeta.exportType) { |
| | | // Inherit exportType from parent module to ensure consistency. |
| | | // When a CSS file is imported with syntax like `import "./basic.css" with { type: "css" }`, |
| | | // the parent module's exportType is set to "css-style-sheet". |
| | | // Child modules imported via @import should inherit this exportType |
| | | // instead of using the default "link", ensuring that the entire |
| | | // import chain uses the same export format. |
| | | const parent = state.compilation.moduleGraph.getIssuer(module); |
| | | if (parent instanceof CssModule) { |
| | | buildMeta.exportType = /** @type {BuildMeta} */ ( |
| | | parent.buildMeta |
| | | ).exportType; |
| | | } |
| | | } |
| | | |
| | | // TODO this.namedExports? |
| | | if ( |
| | | buildMeta.exportType === "text" || |
| | | buildMeta.exportType === "css-style-sheet" |
| | | /** @type {CssModule} */ (module).exportType === "text" || |
| | | /** @type {CssModule} */ (module).exportType === "css-style-sheet" |
| | | ) { |
| | | module.addDependency(new StaticExportsDependency(["default"], true)); |
| | | } else { |
| | |
| | | } |
| | | |
| | | /** |
| | | * Returns comments in the range. |
| | | * @param {Range} range range |
| | | * @returns {Comment[]} comments in the range |
| | | */ |
| | |
| | | if (!this.comments) return []; |
| | | const [rangeStart, rangeEnd] = range; |
| | | /** |
| | | * Returns compared. |
| | | * @param {Comment} comment comment |
| | | * @param {number} needle needle |
| | | * @returns {number} compared |
| | |
| | | } |
| | | |
| | | /** |
| | | * Parses comment options. |
| | | * @param {Range} range range of the comment |
| | | * @returns {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: Comment })[] | null }} result |
| | | */ |
| | |
| | | if (comments.length === 0) { |
| | | return EMPTY_COMMENT_OPTIONS; |
| | | } |
| | | /** @type {Record<string, EXPECTED_ANY> } */ |
| | | /** @type {Record<string, EXPECTED_ANY>} */ |
| | | const options = {}; |
| | | /** @type {(Error & { comment: Comment })[]} */ |
| | | const errors = []; |