CssParser.js 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const vm = require("vm");
  7. const CommentCompilationWarning = require("../CommentCompilationWarning");
  8. const ModuleDependencyWarning = require("../ModuleDependencyWarning");
  9. const { CSS_MODULE_TYPE_AUTO } = require("../ModuleTypeConstants");
  10. const Parser = require("../Parser");
  11. const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
  12. const WebpackError = require("../WebpackError");
  13. const ConstDependency = require("../dependencies/ConstDependency");
  14. const CssIcssExportDependency = require("../dependencies/CssIcssExportDependency");
  15. const CssIcssImportDependency = require("../dependencies/CssIcssImportDependency");
  16. const CssIcssSymbolDependency = require("../dependencies/CssIcssSymbolDependency");
  17. const CssImportDependency = require("../dependencies/CssImportDependency");
  18. const CssUrlDependency = require("../dependencies/CssUrlDependency");
  19. const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
  20. const binarySearchBounds = require("../util/binarySearchBounds");
  21. const { parseResource } = require("../util/identifier");
  22. const {
  23. createMagicCommentContext,
  24. webpackCommentRegExp
  25. } = require("../util/magicComment");
  26. const walkCssTokens = require("./walkCssTokens");
  27. /** @typedef {import("../Module").BuildInfo} BuildInfo */
  28. /** @typedef {import("../Module").BuildMeta} BuildMeta */
  29. /** @typedef {import("../Parser").ParserState} ParserState */
  30. /** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
  31. /** @typedef {import("./walkCssTokens").CssTokenCallbacks} CssTokenCallbacks */
  32. /** @typedef {import("../../declarations/WebpackOptions").CssModuleParserOptions} CssModuleParserOptions */
  33. /** @typedef {import("../CssModule")} CssModule */
  34. /** @typedef {[number, number]} Range */
  35. /** @typedef {{ line: number, column: number }} Position */
  36. /** @typedef {{ value: string, range: Range, loc: { start: Position, end: Position } }} Comment */
  37. const CC_COLON = ":".charCodeAt(0);
  38. const CC_SEMICOLON = ";".charCodeAt(0);
  39. const CC_COMMA = ",".charCodeAt(0);
  40. const CC_LEFT_PARENTHESIS = "(".charCodeAt(0);
  41. const CC_RIGHT_PARENTHESIS = ")".charCodeAt(0);
  42. const CC_LOWER_F = "f".charCodeAt(0);
  43. const CC_UPPER_F = "F".charCodeAt(0);
  44. const CC_RIGHT_CURLY = "}".charCodeAt(0);
  45. const CC_HYPHEN_MINUS = "-".charCodeAt(0);
  46. const CC_TILDE = "~".charCodeAt(0);
  47. const CC_EQUAL = "=".charCodeAt(0);
  48. const CC_FULL_STOP = ".".charCodeAt(0);
  49. const CC_EXCLAMATION = "!".charCodeAt(0);
  50. // https://www.w3.org/TR/css-syntax-3/#newline
  51. // We don't have `preprocessing` stage, so we need specify all of them
  52. const STRING_MULTILINE = /\\[\n\r\f]/g;
  53. // https://www.w3.org/TR/css-syntax-3/#whitespace
  54. const TRIM_WHITE_SPACES = /(^[ \t\n\r\f]*|[ \t\n\r\f]*$)/g;
  55. const UNESCAPE = /\\([0-9a-f]{1,6}[ \t\n\r\f]?|[\s\S])/gi;
  56. const IMAGE_SET_FUNCTION = /^(?:-\w+-)?image-set$/i;
  57. const OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE = /^@(?:-\w+-)?keyframes$/;
  58. const COMPOSES_PROPERTY = /^(?:composes|compose-with)$/i;
  59. const IS_MODULES = /\.modules?\.[^.]+$/i;
  60. const CSS_COMMENT = /\/\*((?!\*\/)[\s\S]*?)\*\//g;
  61. /**
  62. * Returns matches.
  63. * @param {RegExp} regexp a regexp
  64. * @param {string} str a string
  65. * @returns {RegExpExecArray[]} matches
  66. */
  67. const matchAll = (regexp, str) => {
  68. /** @type {RegExpExecArray[]} */
  69. const result = [];
  70. /** @type {null | RegExpExecArray} */
  71. let match;
  72. // Use a while loop with exec() to find all matches
  73. while ((match = regexp.exec(str)) !== null) {
  74. result.push(match);
  75. }
  76. // Return an array to be easily iterable (note: a true spec-compliant polyfill
  77. // returns an iterator object, but an array spread often suffices for basic use)
  78. return result;
  79. };
  80. /**
  81. * Returns normalized url.
  82. * @param {string} str url string
  83. * @param {boolean} isString is url wrapped in quotes
  84. * @returns {string} normalized url
  85. */
  86. const normalizeUrl = (str, isString) => {
  87. // Remove extra spaces and newlines:
  88. // `url("im\
  89. // g.png")`
  90. if (isString) {
  91. str = str.replace(STRING_MULTILINE, "");
  92. }
  93. str = str
  94. // Remove unnecessary spaces from `url(" img.png ")`
  95. .replace(TRIM_WHITE_SPACES, "")
  96. // Unescape
  97. .replace(UNESCAPE, (match) => {
  98. if (match.length > 2) {
  99. return String.fromCharCode(Number.parseInt(match.slice(1).trim(), 16));
  100. }
  101. return match[1];
  102. });
  103. if (/^data:/i.test(str)) {
  104. return str;
  105. }
  106. if (str.includes("%")) {
  107. // Convert `url('%2E/img.png')` -> `url('./img.png')`
  108. try {
  109. str = decodeURIComponent(str);
  110. } catch (_err) {
  111. // Ignore
  112. }
  113. }
  114. return str;
  115. };
  116. const regexSingleEscape = /[ -,./:-@[\]^`{-~]/;
  117. const regexExcessiveSpaces =
  118. /(^|\\+)?(\\[A-F0-9]{1,6})\u0020(?![a-fA-F0-9\u0020])/g;
  119. /**
  120. * Returns escaped identifier.
  121. * @param {string} str string
  122. * @returns {string} escaped identifier
  123. */
  124. const escapeIdentifier = (str) => {
  125. let output = "";
  126. let counter = 0;
  127. while (counter < str.length) {
  128. const character = str.charAt(counter++);
  129. /** @type {string} */
  130. let value;
  131. if (/[\t\n\f\r\v]/.test(character)) {
  132. const codePoint = character.charCodeAt(0);
  133. value = `\\${codePoint.toString(16).toUpperCase()} `;
  134. } else if (character === "\\" || regexSingleEscape.test(character)) {
  135. value = `\\${character}`;
  136. } else {
  137. value = character;
  138. }
  139. output += value;
  140. }
  141. const firstChar = str.charAt(0);
  142. if (/^-[-\d]/.test(output)) {
  143. output = `\\-${output.slice(1)}`;
  144. } else if (/\d/.test(firstChar)) {
  145. output = `\\3${firstChar} ${output.slice(1)}`;
  146. }
  147. // Remove spaces after `\HEX` escapes that are not followed by a hex digit,
  148. // since they’re redundant. Note that this is only possible if the escape
  149. // sequence isn’t preceded by an odd number of backslashes.
  150. output = output.replace(regexExcessiveSpaces, ($0, $1, $2) => {
  151. if ($1 && $1.length % 2) {
  152. // It’s not safe to remove the space, so don’t.
  153. return $0;
  154. }
  155. // Strip the space.
  156. return ($1 || "") + $2;
  157. });
  158. return output;
  159. };
  160. const CONTAINS_ESCAPE = /\\/;
  161. /**
  162. * Returns hex.
  163. * @param {string} str string
  164. * @returns {[string, number] | undefined} hex
  165. */
  166. const gobbleHex = (str) => {
  167. const lower = str.toLowerCase();
  168. let hex = "";
  169. let spaceTerminated = false;
  170. for (let i = 0; i < 6 && lower[i] !== undefined; i++) {
  171. const code = lower.charCodeAt(i);
  172. // check to see if we are dealing with a valid hex char [a-f|0-9]
  173. const valid = (code >= 97 && code <= 102) || (code >= 48 && code <= 57);
  174. // https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
  175. spaceTerminated = code === 32;
  176. if (!valid) break;
  177. hex += lower[i];
  178. }
  179. if (hex.length === 0) return undefined;
  180. const codePoint = Number.parseInt(hex, 16);
  181. const isSurrogate = codePoint >= 0xd800 && codePoint <= 0xdfff;
  182. // Add special case for
  183. // "If this number is zero, or is for a surrogate, or is greater than the maximum allowed code point"
  184. // https://drafts.csswg.org/css-syntax/#maximum-allowed-code-point
  185. if (isSurrogate || codePoint === 0x0000 || codePoint > 0x10ffff) {
  186. return ["\uFFFD", hex.length + (spaceTerminated ? 1 : 0)];
  187. }
  188. return [
  189. String.fromCodePoint(codePoint),
  190. hex.length + (spaceTerminated ? 1 : 0)
  191. ];
  192. };
  193. /**
  194. * Unescape identifier.
  195. * @param {string} str string
  196. * @returns {string} unescaped string
  197. */
  198. const unescapeIdentifier = (str) => {
  199. const needToProcess = CONTAINS_ESCAPE.test(str);
  200. if (!needToProcess) return str;
  201. let ret = "";
  202. for (let i = 0; i < str.length; i++) {
  203. if (str[i] === "\\") {
  204. const gobbled = gobbleHex(str.slice(i + 1, i + 7));
  205. if (gobbled !== undefined) {
  206. ret += gobbled[0];
  207. i += gobbled[1];
  208. continue;
  209. }
  210. // Retain a pair of \\ if double escaped `\\\\`
  211. // https://github.com/postcss/postcss-selector-parser/commit/268c9a7656fb53f543dc620aa5b73a30ec3ff20e
  212. if (str[i + 1] === "\\") {
  213. ret += "\\";
  214. i += 1;
  215. continue;
  216. }
  217. // if \\ is at the end of the string retain it
  218. // https://github.com/postcss/postcss-selector-parser/commit/01a6b346e3612ce1ab20219acc26abdc259ccefb
  219. if (str.length === i + 1) {
  220. ret += str[i];
  221. }
  222. continue;
  223. }
  224. ret += str[i];
  225. }
  226. return ret;
  227. };
  228. /**
  229. * A custom property is any property whose name starts with two dashes (U+002D HYPHEN-MINUS), like --foo.
  230. * The <custom-property-name> production corresponds to this:
  231. * it’s defined as any <dashed-ident> (a valid identifier that starts with two dashes),
  232. * except -- itself, which is reserved for future use by CSS.
  233. * @param {string} identifier identifier
  234. * @returns {boolean} true when identifier is dashed, otherwise false
  235. */
  236. const isDashedIdentifier = (identifier) =>
  237. identifier.startsWith("--") && identifier.length >= 3;
  238. /** @type {Record<string, number>} */
  239. const PREDEFINED_COUNTER_STYLES = {
  240. decimal: 1,
  241. "decimal-leading-zero": 1,
  242. "arabic-indic": 1,
  243. armenian: 1,
  244. "upper-armenian": 1,
  245. "lower-armenian": 1,
  246. bengali: 1,
  247. cambodian: 1,
  248. khmer: 1,
  249. "cjk-decimal": 1,
  250. devanagari: 1,
  251. georgian: 1,
  252. gujarati: 1,
  253. /* cspell:disable-next-line */
  254. gurmukhi: 1,
  255. hebrew: 1,
  256. kannada: 1,
  257. lao: 1,
  258. malayalam: 1,
  259. mongolian: 1,
  260. myanmar: 1,
  261. oriya: 1,
  262. persian: 1,
  263. "lower-roman": 1,
  264. "upper-roman": 1,
  265. tamil: 1,
  266. telugu: 1,
  267. thai: 1,
  268. tibetan: 1,
  269. "lower-alpha": 1,
  270. "lower-latin": 1,
  271. "upper-alpha": 1,
  272. "upper-latin": 1,
  273. "lower-greek": 1,
  274. hiragana: 1,
  275. /* cspell:disable-next-line */
  276. "hiragana-iroha": 1,
  277. katakana: 1,
  278. /* cspell:disable-next-line */
  279. "katakana-iroha": 1,
  280. disc: 1,
  281. circle: 1,
  282. square: 1,
  283. "disclosure-open": 1,
  284. "disclosure-closed": 1,
  285. "cjk-earthly-branch": 1,
  286. "cjk-heavenly-stem": 1,
  287. "japanese-informal": 1,
  288. "japanese-formal": 1,
  289. "korean-hangul-formal": 1,
  290. /* cspell:disable-next-line */
  291. "korean-hanja-informal": 1,
  292. /* cspell:disable-next-line */
  293. "korean-hanja-formal": 1,
  294. "simp-chinese-informal": 1,
  295. "simp-chinese-formal": 1,
  296. "trad-chinese-informal": 1,
  297. "trad-chinese-formal": 1,
  298. "cjk-ideographic": 1,
  299. "ethiopic-numeric": 1
  300. };
  301. /** @type {Record<string, number>} */
  302. const GLOBAL_VALUES = {
  303. // Global values
  304. initial: Infinity,
  305. inherit: Infinity,
  306. unset: Infinity,
  307. revert: Infinity,
  308. "revert-layer": Infinity
  309. };
  310. /** @type {Record<string, number>} */
  311. const GRID_AREA_OR_COLUMN_OR_ROW = {
  312. auto: Infinity,
  313. span: Infinity,
  314. ...GLOBAL_VALUES
  315. };
  316. /** @type {Record<string, number>} */
  317. const GRID_AUTO_COLUMNS_OR_ROW = {
  318. "min-content": Infinity,
  319. "max-content": Infinity,
  320. auto: Infinity,
  321. ...GLOBAL_VALUES
  322. };
  323. /** @type {Record<string, number>} */
  324. const GRID_AUTO_FLOW = {
  325. row: 1,
  326. column: 1,
  327. dense: 1,
  328. ...GLOBAL_VALUES
  329. };
  330. /** @type {Record<string, number>} */
  331. const GRID_TEMPLATE_AREAS = {
  332. // Special
  333. none: 1,
  334. ...GLOBAL_VALUES
  335. };
  336. /** @type {Record<string, number>} */
  337. const GRID_TEMPLATE_COLUMNS_OR_ROWS = {
  338. none: 1,
  339. subgrid: 1,
  340. masonry: 1,
  341. "max-content": Infinity,
  342. "min-content": Infinity,
  343. auto: Infinity,
  344. ...GLOBAL_VALUES
  345. };
  346. /** @type {Record<string, number>} */
  347. const GRID_TEMPLATE = {
  348. ...GRID_TEMPLATE_AREAS,
  349. ...GRID_TEMPLATE_COLUMNS_OR_ROWS
  350. };
  351. /** @type {Record<string, number>} */
  352. const GRID = {
  353. "auto-flow": 1,
  354. dense: 1,
  355. ...GRID_AUTO_COLUMNS_OR_ROW,
  356. ...GRID_AUTO_FLOW,
  357. ...GRID_TEMPLATE_AREAS,
  358. ...GRID_TEMPLATE_COLUMNS_OR_ROWS
  359. };
  360. /**
  361. * Gets known properties.
  362. * @param {{ animation?: boolean, container?: boolean, customIdents?: boolean, grid?: boolean }=} options options
  363. * @returns {Map<string, Record<string, number>>} list of known properties
  364. */
  365. const getKnownProperties = (options = {}) => {
  366. /** @type {Map<string, Record<string, number>>} */
  367. const knownProperties = new Map();
  368. if (options.animation) {
  369. knownProperties.set("animation", {
  370. // animation-direction
  371. normal: 1,
  372. reverse: 1,
  373. alternate: 1,
  374. "alternate-reverse": 1,
  375. // animation-fill-mode
  376. forwards: 1,
  377. backwards: 1,
  378. both: 1,
  379. // animation-iteration-count
  380. infinite: 1,
  381. // animation-play-state
  382. paused: 1,
  383. running: 1,
  384. // animation-timing-function
  385. ease: 1,
  386. "ease-in": 1,
  387. "ease-out": 1,
  388. "ease-in-out": 1,
  389. linear: 1,
  390. "step-end": 1,
  391. "step-start": 1,
  392. // Special
  393. none: Infinity, // No matter how many times you write none, it will never be an animation name
  394. ...GLOBAL_VALUES
  395. });
  396. knownProperties.set("animation-name", {
  397. // Special
  398. none: Infinity, // No matter how many times you write none, it will never be an animation name
  399. ...GLOBAL_VALUES
  400. });
  401. }
  402. if (options.container) {
  403. knownProperties.set("container", {
  404. // container-type
  405. normal: 1,
  406. size: 1,
  407. "inline-size": 1,
  408. "scroll-state": 1,
  409. // Special
  410. none: Infinity,
  411. ...GLOBAL_VALUES
  412. });
  413. knownProperties.set("container-name", {
  414. // Special
  415. none: Infinity,
  416. ...GLOBAL_VALUES
  417. });
  418. }
  419. if (options.customIdents) {
  420. knownProperties.set("list-style", {
  421. // list-style-position
  422. inside: 1,
  423. outside: 1,
  424. // list-style-type
  425. ...PREDEFINED_COUNTER_STYLES,
  426. // Special
  427. none: Infinity,
  428. ...GLOBAL_VALUES
  429. });
  430. knownProperties.set("list-style-type", {
  431. // list-style-type
  432. ...PREDEFINED_COUNTER_STYLES,
  433. // Special
  434. none: Infinity,
  435. ...GLOBAL_VALUES
  436. });
  437. knownProperties.set("system", {
  438. cyclic: 1,
  439. numeric: 1,
  440. alphabetic: 1,
  441. symbolic: 1,
  442. additive: 1,
  443. fixed: 1,
  444. extends: 1,
  445. ...PREDEFINED_COUNTER_STYLES
  446. });
  447. knownProperties.set("fallback", {
  448. ...PREDEFINED_COUNTER_STYLES
  449. });
  450. knownProperties.set("speak-as", {
  451. auto: 1,
  452. bullets: 1,
  453. numbers: 1,
  454. words: 1,
  455. "spell-out": 1,
  456. ...PREDEFINED_COUNTER_STYLES
  457. });
  458. }
  459. if (options.grid) {
  460. knownProperties.set("grid", GRID);
  461. knownProperties.set("grid-area", GRID_AREA_OR_COLUMN_OR_ROW);
  462. knownProperties.set("grid-column", GRID_AREA_OR_COLUMN_OR_ROW);
  463. knownProperties.set("grid-column-end", GRID_AREA_OR_COLUMN_OR_ROW);
  464. knownProperties.set("grid-column-start", GRID_AREA_OR_COLUMN_OR_ROW);
  465. knownProperties.set("grid-row", GRID_AREA_OR_COLUMN_OR_ROW);
  466. knownProperties.set("grid-row-end", GRID_AREA_OR_COLUMN_OR_ROW);
  467. knownProperties.set("grid-row-start", GRID_AREA_OR_COLUMN_OR_ROW);
  468. knownProperties.set("grid-template", GRID_TEMPLATE);
  469. knownProperties.set("grid-template-areas", GRID_TEMPLATE_AREAS);
  470. knownProperties.set("grid-template-columns", GRID_TEMPLATE_COLUMNS_OR_ROWS);
  471. knownProperties.set("grid-template-rows", GRID_TEMPLATE_COLUMNS_OR_ROWS);
  472. }
  473. return knownProperties;
  474. };
  475. class LocConverter {
  476. /**
  477. * Creates an instance of LocConverter.
  478. * @param {string} input input
  479. */
  480. constructor(input) {
  481. this._input = input;
  482. this.line = 1;
  483. this.column = 0;
  484. this.pos = 0;
  485. }
  486. /**
  487. * Returns location converter.
  488. * @param {number} pos position
  489. * @returns {LocConverter} location converter
  490. */
  491. get(pos) {
  492. if (this.pos !== pos) {
  493. if (this.pos < pos) {
  494. const str = this._input.slice(this.pos, pos);
  495. let i = str.lastIndexOf("\n");
  496. if (i === -1) {
  497. this.column += str.length;
  498. } else {
  499. this.column = str.length - i - 1;
  500. this.line++;
  501. while (i > 0 && (i = str.lastIndexOf("\n", i - 1)) !== -1) {
  502. this.line++;
  503. }
  504. }
  505. } else {
  506. let i = this._input.lastIndexOf("\n", this.pos);
  507. while (i >= pos) {
  508. this.line--;
  509. i = i > 0 ? this._input.lastIndexOf("\n", i - 1) : -1;
  510. }
  511. this.column = pos - i;
  512. }
  513. this.pos = pos;
  514. }
  515. return this;
  516. }
  517. }
  518. const EMPTY_COMMENT_OPTIONS = {
  519. options: null,
  520. errors: null
  521. };
  522. const CSS_MODE_TOP_LEVEL = 0;
  523. const CSS_MODE_IN_BLOCK = 1;
  524. const LOCAL_MODE = 0;
  525. const GLOBAL_MODE = 1;
  526. const eatUntilSemi = walkCssTokens.eatUntil(";");
  527. const eatUntilLeftCurly = walkCssTokens.eatUntil("{");
  528. /**
  529. * Defines the css parser own options type used by this module.
  530. * @typedef {object} CssParserOwnOptions
  531. * @property {("pure" | "global" | "local" | "auto")=} defaultMode default mode
  532. */
  533. /** @typedef {CssModuleParserOptions & CssParserOwnOptions} CssParserOptions */
  534. class CssParser extends Parser {
  535. /**
  536. * Creates an instance of CssParser.
  537. * @param {CssParserOptions=} options options
  538. */
  539. constructor(options = {}) {
  540. super();
  541. this.defaultMode =
  542. typeof options.defaultMode !== "undefined" ? options.defaultMode : "pure";
  543. this.options = {
  544. url: true,
  545. import: true,
  546. namedExports: true,
  547. animation: true,
  548. container: true,
  549. customIdents: true,
  550. dashedIdents: true,
  551. function: true,
  552. grid: true,
  553. ...options
  554. };
  555. /** @type {Comment[] | undefined} */
  556. this.comments = undefined;
  557. this.magicCommentContext = createMagicCommentContext();
  558. }
  559. /**
  560. * Processes the provided state.
  561. * @param {ParserState} state parser state
  562. * @param {string} message warning message
  563. * @param {LocConverter} locConverter location converter
  564. * @param {number} start start offset
  565. * @param {number} end end offset
  566. */
  567. _emitWarning(state, message, locConverter, start, end) {
  568. const { line: sl, column: sc } = locConverter.get(start);
  569. const { line: el, column: ec } = locConverter.get(end);
  570. state.current.addWarning(
  571. new ModuleDependencyWarning(state.module, new WebpackError(message), {
  572. start: { line: sl, column: sc },
  573. end: { line: el, column: ec }
  574. })
  575. );
  576. }
  577. /**
  578. * Parses the provided source and updates the parser state.
  579. * @param {string | Buffer | PreparsedAst} source the source to parse
  580. * @param {ParserState} state the parser state
  581. * @returns {ParserState} the parser state
  582. */
  583. parse(source, state) {
  584. if (Buffer.isBuffer(source)) {
  585. source = source.toString("utf8");
  586. } else if (typeof source === "object") {
  587. throw new Error("webpackAst is unexpected for the CssParser");
  588. }
  589. if (source[0] === "\uFEFF") {
  590. source = source.slice(1);
  591. }
  592. let mode = this.defaultMode;
  593. const module = state.module;
  594. if (
  595. mode === "auto" &&
  596. module.type === CSS_MODULE_TYPE_AUTO &&
  597. IS_MODULES.test(
  598. parseResource(/** @type {string} */ (module.getResource())).path
  599. )
  600. ) {
  601. mode = "local";
  602. }
  603. const isModules = mode === "global" || mode === "local";
  604. const knownProperties = getKnownProperties({
  605. animation: this.options.animation,
  606. container: this.options.container,
  607. customIdents: this.options.customIdents,
  608. grid: this.options.grid
  609. });
  610. /** @type {BuildMeta} */
  611. (module.buildMeta).isCSSModule = isModules;
  612. const locConverter = new LocConverter(source);
  613. /** @type {number} */
  614. let scope = CSS_MODE_TOP_LEVEL;
  615. /** @type {boolean} */
  616. let allowImportAtRule = true;
  617. /** @type {[string, number, number, boolean?][]} */
  618. const balanced = [];
  619. let lastTokenEndForComments = 0;
  620. /** @type {boolean} */
  621. let isNextRulePrelude = isModules;
  622. /** @type {number} */
  623. let blockNestingLevel = 0;
  624. /** @type {0 | 1 | undefined} */
  625. let modeData;
  626. /** @type {number} */
  627. let counter = 0;
  628. /** @type {string[]} */
  629. let lastLocalIdentifiers = [];
  630. /** @typedef {{ value?: string, importName?: string, localName?: string }} IcssDefinition */
  631. /** @type {Map<string, IcssDefinition>} */
  632. const icssDefinitions = new Map();
  633. /**
  634. * Checks whether this css parser is next nested syntax.
  635. * @param {string} input input
  636. * @param {number} pos position
  637. * @returns {boolean} true, when next is nested syntax
  638. */
  639. const isNextNestedSyntax = (input, pos) => {
  640. pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
  641. if (
  642. input.charCodeAt(pos) === CC_RIGHT_CURLY ||
  643. (input.charCodeAt(pos) === CC_HYPHEN_MINUS &&
  644. input.charCodeAt(pos + 1) === CC_HYPHEN_MINUS)
  645. ) {
  646. return false;
  647. }
  648. const identifier = walkCssTokens.eatIdentSequence(input, pos);
  649. if (!identifier) {
  650. return true;
  651. }
  652. const leftCurly = eatUntilLeftCurly(input, pos);
  653. const content = input.slice(identifier[0], leftCurly);
  654. if (content.includes(";") || content.includes("}")) {
  655. return false;
  656. }
  657. return true;
  658. };
  659. /**
  660. * Checks whether this css parser is local mode.
  661. * @returns {boolean} true, when in local scope
  662. */
  663. const isLocalMode = () =>
  664. modeData === LOCAL_MODE || (mode === "local" && modeData === undefined);
  665. /**
  666. * Returns end.
  667. * @param {string} input input
  668. * @param {number} start start
  669. * @param {number} end end
  670. * @returns {number} end
  671. */
  672. const comment = (input, start, end) => {
  673. if (!this.comments) this.comments = [];
  674. const { line: sl, column: sc } = locConverter.get(start);
  675. const { line: el, column: ec } = locConverter.get(end);
  676. /** @type {Comment} */
  677. const comment = {
  678. value: input.slice(start + 2, end - 2),
  679. range: [start, end],
  680. loc: {
  681. start: { line: sl, column: sc },
  682. end: { line: el, column: ec }
  683. }
  684. };
  685. this.comments.push(comment);
  686. return end;
  687. };
  688. // Vanilla CSS stuff
  689. /**
  690. * Processes the provided input.
  691. * @param {string} input input
  692. * @param {number} start name start position
  693. * @param {number} end name end position
  694. * @returns {number} position after handling
  695. */
  696. const processAtImport = (input, start, end) => {
  697. const tokens = walkCssTokens.eatImportTokens(input, end, {
  698. comment
  699. });
  700. if (!tokens[3]) return end;
  701. const semi = tokens[3][1];
  702. if (!tokens[0]) {
  703. this._emitWarning(
  704. state,
  705. `Expected URL in '${input.slice(start, semi)}'`,
  706. locConverter,
  707. start,
  708. semi
  709. );
  710. return end;
  711. }
  712. const urlToken = tokens[0];
  713. const url = normalizeUrl(input.slice(urlToken[2], urlToken[3]), true);
  714. const newline = walkCssTokens.eatWhiteLine(input, semi);
  715. const { options, errors: commentErrors } = this.parseCommentOptions([
  716. end,
  717. urlToken[1]
  718. ]);
  719. if (commentErrors) {
  720. for (const e of commentErrors) {
  721. const { comment } = e;
  722. state.module.addWarning(
  723. new CommentCompilationWarning(
  724. `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
  725. comment.loc
  726. )
  727. );
  728. }
  729. }
  730. if (options && options.webpackIgnore !== undefined) {
  731. if (typeof options.webpackIgnore !== "boolean") {
  732. const { line: sl, column: sc } = locConverter.get(start);
  733. const { line: el, column: ec } = locConverter.get(newline);
  734. state.module.addWarning(
  735. new UnsupportedFeatureWarning(
  736. `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
  737. {
  738. start: { line: sl, column: sc },
  739. end: { line: el, column: ec }
  740. }
  741. )
  742. );
  743. } else if (options.webpackIgnore) {
  744. return newline;
  745. }
  746. }
  747. if (url.length === 0) {
  748. const { line: sl, column: sc } = locConverter.get(start);
  749. const { line: el, column: ec } = locConverter.get(newline);
  750. const dep = new ConstDependency("", [start, newline]);
  751. module.addPresentationalDependency(dep);
  752. dep.setLoc(sl, sc, el, ec);
  753. return newline;
  754. }
  755. /** @type {undefined | string} */
  756. let layer;
  757. if (tokens[1]) {
  758. layer = input.slice(tokens[1][0] + 6, tokens[1][1] - 1).trim();
  759. }
  760. /** @type {undefined | string} */
  761. let supports;
  762. if (tokens[2]) {
  763. supports = input.slice(tokens[2][0] + 9, tokens[2][1] - 1).trim();
  764. }
  765. const last = tokens[2] || tokens[1] || tokens[0];
  766. const mediaStart = walkCssTokens.eatWhitespaceAndComments(
  767. input,
  768. last[1]
  769. )[0];
  770. /** @type {undefined | string} */
  771. let media;
  772. if (mediaStart !== semi - 1) {
  773. media = input.slice(mediaStart, semi - 1).trim();
  774. }
  775. const { line: sl, column: sc } = locConverter.get(start);
  776. const { line: el, column: ec } = locConverter.get(newline);
  777. const dep = new CssImportDependency(
  778. url,
  779. [start, newline],
  780. mode === "local" || mode === "global" ? mode : undefined,
  781. layer,
  782. supports && supports.length > 0 ? supports : undefined,
  783. media && media.length > 0 ? media : undefined
  784. );
  785. dep.setLoc(sl, sc, el, ec);
  786. module.addDependency(dep);
  787. return newline;
  788. };
  789. /**
  790. * Process url function.
  791. * @param {string} input input
  792. * @param {number} end end position
  793. * @param {string} name the name of function
  794. * @returns {number} position after handling
  795. */
  796. const processURLFunction = (input, end, name) => {
  797. const string = walkCssTokens.eatString(input, end);
  798. if (!string) return end;
  799. const { options, errors: commentErrors } = this.parseCommentOptions([
  800. lastTokenEndForComments,
  801. end
  802. ]);
  803. if (commentErrors) {
  804. for (const e of commentErrors) {
  805. const { comment } = e;
  806. state.module.addWarning(
  807. new CommentCompilationWarning(
  808. `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
  809. comment.loc
  810. )
  811. );
  812. }
  813. }
  814. if (options && options.webpackIgnore !== undefined) {
  815. if (typeof options.webpackIgnore !== "boolean") {
  816. const { line: sl, column: sc } = locConverter.get(string[0]);
  817. const { line: el, column: ec } = locConverter.get(string[1]);
  818. state.module.addWarning(
  819. new UnsupportedFeatureWarning(
  820. `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
  821. {
  822. start: { line: sl, column: sc },
  823. end: { line: el, column: ec }
  824. }
  825. )
  826. );
  827. } else if (options.webpackIgnore) {
  828. return end;
  829. }
  830. }
  831. const value = normalizeUrl(
  832. input.slice(string[0] + 1, string[1] - 1),
  833. true
  834. );
  835. // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
  836. if (value.length === 0) return end;
  837. const isUrl = name === "url" || name === "src";
  838. const dep = new CssUrlDependency(
  839. value,
  840. [string[0], string[1]],
  841. isUrl ? "string" : "url"
  842. );
  843. const { line: sl, column: sc } = locConverter.get(string[0]);
  844. const { line: el, column: ec } = locConverter.get(string[1]);
  845. dep.setLoc(sl, sc, el, ec);
  846. module.addDependency(dep);
  847. module.addCodeGenerationDependency(dep);
  848. return string[1];
  849. };
  850. /**
  851. * Process old url function.
  852. * @param {string} input input
  853. * @param {number} start start position
  854. * @param {number} end end position
  855. * @param {number} contentStart start position
  856. * @param {number} contentEnd end position
  857. * @returns {number} position after handling
  858. */
  859. const processOldURLFunction = (
  860. input,
  861. start,
  862. end,
  863. contentStart,
  864. contentEnd
  865. ) => {
  866. const { options, errors: commentErrors } = this.parseCommentOptions([
  867. lastTokenEndForComments,
  868. end
  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(
  884. lastTokenEndForComments
  885. );
  886. const { line: el, column: ec } = locConverter.get(end);
  887. state.module.addWarning(
  888. new UnsupportedFeatureWarning(
  889. `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
  890. {
  891. start: { line: sl, column: sc },
  892. end: { line: el, column: ec }
  893. }
  894. )
  895. );
  896. } else if (options.webpackIgnore) {
  897. return end;
  898. }
  899. }
  900. const value = normalizeUrl(input.slice(contentStart, contentEnd), false);
  901. // Ignore `url()`, `url('')` and `url("")`, they are valid by spec
  902. if (value.length === 0) return end;
  903. const dep = new CssUrlDependency(value, [start, end], "url");
  904. const { line: sl, column: sc } = locConverter.get(start);
  905. const { line: el, column: ec } = locConverter.get(end);
  906. dep.setLoc(sl, sc, el, ec);
  907. module.addDependency(dep);
  908. module.addCodeGenerationDependency(dep);
  909. return end;
  910. };
  911. /**
  912. * Process image set function.
  913. * @param {string} input input
  914. * @param {number} start start position
  915. * @param {number} end end position
  916. * @returns {number} position after handling
  917. */
  918. const processImageSetFunction = (input, start, end) => {
  919. lastTokenEndForComments = end;
  920. const values = walkCssTokens.eatImageSetStrings(input, end, {
  921. comment
  922. });
  923. if (values.length === 0) return end;
  924. for (const [index, string] of values.entries()) {
  925. const value = normalizeUrl(
  926. input.slice(string[0] + 1, string[1] - 1),
  927. true
  928. );
  929. if (value.length === 0) return end;
  930. const { options, errors: commentErrors } = this.parseCommentOptions([
  931. index === 0 ? start : values[index - 1][1],
  932. string[1]
  933. ]);
  934. if (commentErrors) {
  935. for (const e of commentErrors) {
  936. const { comment } = e;
  937. state.module.addWarning(
  938. new CommentCompilationWarning(
  939. `Compilation error while processing magic comment(-s): /*${comment.value}*/: ${e.message}`,
  940. comment.loc
  941. )
  942. );
  943. }
  944. }
  945. if (options && options.webpackIgnore !== undefined) {
  946. if (typeof options.webpackIgnore !== "boolean") {
  947. const { line: sl, column: sc } = locConverter.get(string[0]);
  948. const { line: el, column: ec } = locConverter.get(string[1]);
  949. state.module.addWarning(
  950. new UnsupportedFeatureWarning(
  951. `\`webpackIgnore\` expected a boolean, but received: ${options.webpackIgnore}.`,
  952. {
  953. start: { line: sl, column: sc },
  954. end: { line: el, column: ec }
  955. }
  956. )
  957. );
  958. } else if (options.webpackIgnore) {
  959. continue;
  960. }
  961. }
  962. const dep = new CssUrlDependency(value, [string[0], string[1]], "url");
  963. const { line: sl, column: sc } = locConverter.get(string[0]);
  964. const { line: el, column: ec } = locConverter.get(string[1]);
  965. dep.setLoc(sl, sc, el, ec);
  966. module.addDependency(dep);
  967. module.addCodeGenerationDependency(dep);
  968. }
  969. // Can contain `url()` inside, so let's return end to allow parse them
  970. return end;
  971. };
  972. // CSS modules stuff
  973. /**
  974. * Returns resolved reexport (localName and importName).
  975. * @param {string} value value to resolve
  976. * @param {string=} localName override local name
  977. * @param {boolean=} isCustomProperty true when it is custom property, otherwise false
  978. * @returns {string | [string, string]} resolved reexport (`localName` and `importName`)
  979. */
  980. const getReexport = (value, localName, isCustomProperty) => {
  981. const reexport = icssDefinitions.get(
  982. isCustomProperty ? `--${value}` : value
  983. );
  984. if (reexport) {
  985. if (reexport.importName) {
  986. return [
  987. reexport.localName || (isCustomProperty ? `--${value}` : value),
  988. reexport.importName
  989. ];
  990. }
  991. if (isCustomProperty) {
  992. return /** @type {string} */ (reexport.value).slice(2);
  993. }
  994. return /** @type {string} */ (reexport.value);
  995. }
  996. if (localName) {
  997. return [localName, value];
  998. }
  999. return value;
  1000. };
  1001. /**
  1002. * Process import or export.
  1003. * @param {0 | 1} type import or export
  1004. * @param {string} input input
  1005. * @param {number} pos start position
  1006. * @returns {number} position after parse
  1007. */
  1008. const processImportOrExport = (type, input, pos) => {
  1009. pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
  1010. /** @type {string | undefined} */
  1011. let request;
  1012. if (type === 0) {
  1013. let cc = input.charCodeAt(pos);
  1014. if (cc !== CC_LEFT_PARENTHESIS) {
  1015. this._emitWarning(
  1016. state,
  1017. `Unexpected '${input[pos]}' at ${pos} during parsing of ':import' (expected '(')`,
  1018. locConverter,
  1019. pos,
  1020. pos
  1021. );
  1022. return pos;
  1023. }
  1024. pos++;
  1025. const stringStart = pos;
  1026. const str = walkCssTokens.eatString(input, pos);
  1027. if (!str) {
  1028. this._emitWarning(
  1029. state,
  1030. `Unexpected '${input[pos]}' at ${pos} during parsing of '${type === 0 ? ":import" : ":export"}' (expected string)`,
  1031. locConverter,
  1032. stringStart,
  1033. pos
  1034. );
  1035. return pos;
  1036. }
  1037. request = input.slice(str[0] + 1, str[1] - 1);
  1038. pos = str[1];
  1039. pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
  1040. cc = input.charCodeAt(pos);
  1041. if (cc !== CC_RIGHT_PARENTHESIS) {
  1042. this._emitWarning(
  1043. state,
  1044. `Unexpected '${input[pos]}' at ${pos} during parsing of ':import' (expected ')')`,
  1045. locConverter,
  1046. pos,
  1047. pos
  1048. );
  1049. return pos;
  1050. }
  1051. pos++;
  1052. pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
  1053. }
  1054. /**
  1055. * Creates a dep from the provided name.
  1056. * @param {string} name name
  1057. * @param {string} value value
  1058. * @param {number} start start of position
  1059. * @param {number} end end of position
  1060. */
  1061. const createDep = (name, value, start, end) => {
  1062. if (type === 0) {
  1063. const dep = new CssIcssImportDependency(
  1064. /** @type {string} */
  1065. (request),
  1066. [0, 0],
  1067. /** @type {"local" | "global"} */
  1068. (mode),
  1069. value,
  1070. name
  1071. );
  1072. const { line: sl, column: sc } = locConverter.get(start);
  1073. const { line: el, column: ec } = locConverter.get(end);
  1074. dep.setLoc(sl, sc, el, ec);
  1075. module.addDependency(dep);
  1076. icssDefinitions.set(name, { importName: value });
  1077. } else if (type === 1) {
  1078. const dep = new CssIcssExportDependency(name, getReexport(value));
  1079. const { line: sl, column: sc } = locConverter.get(start);
  1080. const { line: el, column: ec } = locConverter.get(end);
  1081. dep.setLoc(sl, sc, el, ec);
  1082. module.addDependency(dep);
  1083. }
  1084. };
  1085. let needTerminate = false;
  1086. let balanced = 0;
  1087. /** @type {undefined | 0 | 1 | 2} */
  1088. let scope;
  1089. /** @typedef {[number, number]} Name */
  1090. /** @type {Name | undefined} */
  1091. let name;
  1092. /** @type {number | undefined} */
  1093. let value;
  1094. /** @type {CssTokenCallbacks} */
  1095. const callbacks = {
  1096. leftCurlyBracket: (_input, _start, end) => {
  1097. balanced++;
  1098. if (scope === undefined) {
  1099. scope = 0;
  1100. }
  1101. return end;
  1102. },
  1103. rightCurlyBracket: (_input, _start, end) => {
  1104. balanced--;
  1105. if (scope === 2) {
  1106. const [nameStart, nameEnd] = /** @type {Name} */ (name);
  1107. createDep(
  1108. input.slice(nameStart, nameEnd),
  1109. input.slice(value, end - 1).trim(),
  1110. nameEnd,
  1111. end - 1
  1112. );
  1113. scope = 0;
  1114. }
  1115. if (balanced === 0 && scope === 0) {
  1116. needTerminate = true;
  1117. }
  1118. return end;
  1119. },
  1120. identifier: (_input, start, end) => {
  1121. if (scope === 0) {
  1122. name = [start, end];
  1123. scope = 1;
  1124. }
  1125. return end;
  1126. },
  1127. colon: (_input, _start, end) => {
  1128. if (scope === 1) {
  1129. scope = 2;
  1130. value = walkCssTokens.eatWhitespace(input, end);
  1131. return value;
  1132. }
  1133. return end;
  1134. },
  1135. semicolon: (input, _start, end) => {
  1136. if (scope === 2) {
  1137. const [nameStart, nameEnd] = /** @type {Name} */ (name);
  1138. createDep(
  1139. input.slice(nameStart, nameEnd),
  1140. input.slice(value, end - 1),
  1141. nameEnd,
  1142. end - 1
  1143. );
  1144. scope = 0;
  1145. }
  1146. return end;
  1147. },
  1148. needTerminate: () => needTerminate
  1149. };
  1150. pos = walkCssTokens(input, pos, callbacks);
  1151. pos = walkCssTokens.eatWhiteLine(input, pos);
  1152. return pos;
  1153. };
  1154. /** @typedef {{ from: string, items: ({ localName: string, importName: string })[] }} ValueAtRuleImport */
  1155. /** @typedef {{ localName: string, value: string }} ValueAtRuleValue */
  1156. /**
  1157. * Parses value at rule params.
  1158. * @param {string} str value at-rule params
  1159. * @returns {ValueAtRuleImport | ValueAtRuleValue} parsed result
  1160. */
  1161. const parseValueAtRuleParams = (str) => {
  1162. if (/from(\/\*|\s)(?:[\s\S]+)$/i.test(str)) {
  1163. str = str.replace(CSS_COMMENT, " ").trim().replace(/;$/, "");
  1164. const fromIdx = str.lastIndexOf("from");
  1165. const path = str
  1166. .slice(fromIdx + 5)
  1167. .trim()
  1168. .replace(/['"]/g, "");
  1169. let content = str.slice(0, fromIdx).trim();
  1170. if (content.startsWith("(") && content.endsWith(")")) {
  1171. content = content.slice(1, -1);
  1172. }
  1173. return {
  1174. from: path,
  1175. items: content.split(",").map((item) => {
  1176. item = item.trim();
  1177. if (item.includes(":")) {
  1178. const [local, remote] = item.split(":");
  1179. return { localName: local.trim(), importName: remote.trim() };
  1180. }
  1181. const asParts = item.split(/\s+as\s+/);
  1182. if (asParts.length === 2) {
  1183. return {
  1184. localName: asParts[1].trim(),
  1185. importName: asParts[0].trim()
  1186. };
  1187. }
  1188. return { localName: item, importName: item };
  1189. })
  1190. };
  1191. }
  1192. /** @type {string} */
  1193. let localName;
  1194. /** @type {string} */
  1195. let value;
  1196. const idx = str.indexOf(":");
  1197. if (idx !== -1) {
  1198. localName = str.slice(0, idx).replace(CSS_COMMENT, "").trim();
  1199. value = str.slice(idx + 1);
  1200. } else {
  1201. const mask = str.replace(CSS_COMMENT, (m) => " ".repeat(m.length));
  1202. const idx = mask.search(/\S\s/) + 1;
  1203. localName = str.slice(0, idx).replace(CSS_COMMENT, "").trim();
  1204. value = str.slice(idx + (str[idx] === " " ? 1 : 0));
  1205. }
  1206. if (value.length > 0 && !/^\s+$/.test(value.replace(CSS_COMMENT, ""))) {
  1207. value = value.trim();
  1208. }
  1209. return { localName, value };
  1210. };
  1211. /**
  1212. * Processes the provided input.
  1213. * @param {string} input input
  1214. * @param {number} start name start position
  1215. * @param {number} end name end position
  1216. * @returns {number} position after handling
  1217. */
  1218. const processAtValue = (input, start, end) => {
  1219. const semi = eatUntilSemi(input, end);
  1220. const atRuleEnd = semi + 1;
  1221. const params = input.slice(end, semi);
  1222. const parsed = parseValueAtRuleParams(params);
  1223. if (
  1224. typeof (/** @type {ValueAtRuleImport} */ (parsed).from) !== "undefined"
  1225. ) {
  1226. if (/** @type {ValueAtRuleImport} */ (parsed).from.length === 0) {
  1227. this._emitWarning(
  1228. state,
  1229. `Broken '@value' at-rule: ${input.slice(start, atRuleEnd)}'`,
  1230. locConverter,
  1231. start,
  1232. atRuleEnd
  1233. );
  1234. const dep = new ConstDependency("", [start, atRuleEnd]);
  1235. module.addPresentationalDependency(dep);
  1236. return atRuleEnd;
  1237. }
  1238. let { from, items } = /** @type {ValueAtRuleImport} */ (parsed);
  1239. for (const { importName, localName } of items) {
  1240. {
  1241. const reexport = icssDefinitions.get(from);
  1242. if (reexport && reexport.value) {
  1243. from = reexport.value.slice(1, -1);
  1244. }
  1245. const dep = new CssIcssImportDependency(
  1246. from,
  1247. [0, 0],
  1248. /** @type {"local" | "global"} */
  1249. (mode),
  1250. importName,
  1251. localName
  1252. );
  1253. const { line: sl, column: sc } = locConverter.get(start);
  1254. const { line: el, column: ec } = locConverter.get(end);
  1255. dep.setLoc(sl, sc, el, ec);
  1256. module.addDependency(dep);
  1257. icssDefinitions.set(localName, { importName });
  1258. }
  1259. {
  1260. const dep = new CssIcssExportDependency(
  1261. localName,
  1262. getReexport(localName),
  1263. undefined,
  1264. false,
  1265. CssIcssExportDependency.EXPORT_MODE.REPLACE
  1266. );
  1267. const { line: sl, column: sc } = locConverter.get(start);
  1268. const { line: el, column: ec } = locConverter.get(end);
  1269. dep.setLoc(sl, sc, el, ec);
  1270. module.addDependency(dep);
  1271. }
  1272. }
  1273. } else {
  1274. if (/** @type {ValueAtRuleValue} */ (parsed).localName.length === 0) {
  1275. this._emitWarning(
  1276. state,
  1277. `Broken '@value' at-rule: ${input.slice(start, atRuleEnd)}'`,
  1278. locConverter,
  1279. start,
  1280. atRuleEnd
  1281. );
  1282. const dep = new ConstDependency("", [start, atRuleEnd]);
  1283. module.addPresentationalDependency(dep);
  1284. return atRuleEnd;
  1285. }
  1286. const { localName, value } = /** @type {ValueAtRuleValue} */ (parsed);
  1287. const { line: sl, column: sc } = locConverter.get(start);
  1288. const { line: el, column: ec } = locConverter.get(end);
  1289. if (icssDefinitions.has(value)) {
  1290. const def =
  1291. /** @type {IcssDefinition} */
  1292. (icssDefinitions.get(value));
  1293. def.localName = value;
  1294. icssDefinitions.set(localName, def);
  1295. const dep = new CssIcssExportDependency(
  1296. localName,
  1297. getReexport(value)
  1298. );
  1299. dep.setLoc(sl, sc, el, ec);
  1300. module.addDependency(dep);
  1301. } else {
  1302. icssDefinitions.set(localName, { value });
  1303. const dep = new CssIcssExportDependency(localName, value);
  1304. dep.setLoc(sl, sc, el, ec);
  1305. module.addDependency(dep);
  1306. }
  1307. }
  1308. const dep = new ConstDependency("", [start, atRuleEnd]);
  1309. module.addPresentationalDependency(dep);
  1310. return atRuleEnd;
  1311. };
  1312. /**
  1313. * Process icss symbol.
  1314. * @param {string} name ICSS symbol name
  1315. * @param {number} start start position
  1316. * @param {number} end end position
  1317. * @returns {number} position after handling
  1318. */
  1319. const processICSSSymbol = (name, start, end) => {
  1320. const def =
  1321. /** @type {IcssDefinition} */
  1322. (icssDefinitions.get(name));
  1323. const { line: sl, column: sc } = locConverter.get(start);
  1324. const { line: el, column: ec } = locConverter.get(end);
  1325. const dep = new CssIcssSymbolDependency(
  1326. def.localName || name,
  1327. [start, end],
  1328. def.value,
  1329. def.importName
  1330. );
  1331. dep.setLoc(sl, sc, el, ec);
  1332. module.addDependency(dep);
  1333. return end;
  1334. };
  1335. /**
  1336. * Process local or global function.
  1337. * @param {string} input input
  1338. * @param {1 | 2} type type of function
  1339. * @param {number} start start position
  1340. * @param {number} end end position
  1341. * @returns {number} position after handling
  1342. */
  1343. const processLocalOrGlobalFunction = (input, type, start, end) => {
  1344. // Replace `local(`/` or `global(` (handle legacy `:local(` or `:global(` too)
  1345. {
  1346. const isColon = input.charCodeAt(start - 1) === CC_COLON;
  1347. const dep = new ConstDependency("", [isColon ? start - 1 : start, end]);
  1348. module.addPresentationalDependency(dep);
  1349. }
  1350. end = walkCssTokens.consumeUntil(
  1351. input,
  1352. start,
  1353. {
  1354. identifier(input, start, end) {
  1355. if (type === 1) {
  1356. let identifier = unescapeIdentifier(input.slice(start, end));
  1357. const { line: sl, column: sc } = locConverter.get(start);
  1358. const { line: el, column: ec } = locConverter.get(end);
  1359. const isDashedIdent = isDashedIdentifier(identifier);
  1360. if (isDashedIdent) {
  1361. identifier = identifier.slice(2);
  1362. }
  1363. const dep = new CssIcssExportDependency(
  1364. identifier,
  1365. getReexport(identifier),
  1366. [start, end],
  1367. true,
  1368. CssIcssExportDependency.EXPORT_MODE.ONCE,
  1369. isDashedIdent
  1370. ? CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
  1371. : CssIcssExportDependency.EXPORT_TYPE.NORMAL
  1372. );
  1373. dep.setLoc(sl, sc, el, ec);
  1374. module.addDependency(dep);
  1375. }
  1376. return end;
  1377. }
  1378. },
  1379. {},
  1380. { onlyTopLevel: true, functionValue: true }
  1381. );
  1382. {
  1383. // Replace the last `)`
  1384. const dep = new ConstDependency("", [end, end + 1]);
  1385. module.addPresentationalDependency(dep);
  1386. }
  1387. return end;
  1388. };
  1389. /**
  1390. * Process local at rule.
  1391. * @param {string} input input
  1392. * @param {number} end name end position
  1393. * @param {{ string?: boolean, identifier?: boolean | RegExp }} options types which allowed to handle
  1394. * @returns {number} position after handling
  1395. */
  1396. const processLocalAtRule = (input, end, options) => {
  1397. let found = false;
  1398. return walkCssTokens.consumeUntil(
  1399. input,
  1400. end,
  1401. {
  1402. string(_input, start, end) {
  1403. if (!found && options.string) {
  1404. const value = unescapeIdentifier(input.slice(start + 1, end - 1));
  1405. const { line: sl, column: sc } = locConverter.get(start);
  1406. const { line: el, column: ec } = locConverter.get(end);
  1407. const dep = new CssIcssExportDependency(
  1408. value,
  1409. value,
  1410. [start, end],
  1411. true,
  1412. CssIcssExportDependency.EXPORT_MODE.ONCE
  1413. );
  1414. dep.setLoc(sl, sc, el, ec);
  1415. module.addDependency(dep);
  1416. found = true;
  1417. }
  1418. return end;
  1419. },
  1420. identifier(input, start, end) {
  1421. if (!found) {
  1422. const value = input.slice(start, end);
  1423. if (options.identifier) {
  1424. const identifier = unescapeIdentifier(value);
  1425. if (
  1426. options.identifier instanceof RegExp &&
  1427. options.identifier.test(identifier)
  1428. ) {
  1429. return end;
  1430. }
  1431. const { line: sl, column: sc } = locConverter.get(start);
  1432. const { line: el, column: ec } = locConverter.get(end);
  1433. const dep = new CssIcssExportDependency(
  1434. identifier,
  1435. getReexport(identifier),
  1436. [start, end],
  1437. true,
  1438. CssIcssExportDependency.EXPORT_MODE.ONCE,
  1439. CssIcssExportDependency.EXPORT_TYPE.NORMAL
  1440. );
  1441. dep.setLoc(sl, sc, el, ec);
  1442. module.addDependency(dep);
  1443. found = true;
  1444. }
  1445. }
  1446. return end;
  1447. }
  1448. },
  1449. {
  1450. function: (input, start, end) => {
  1451. // No need to handle `:` (COLON), because it's always a function
  1452. const name = input
  1453. .slice(start, end - 1)
  1454. .replace(/\\/g, "")
  1455. .toLowerCase();
  1456. const type =
  1457. name === "local" ? 1 : name === "global" ? 2 : undefined;
  1458. if (!found && type) {
  1459. found = true;
  1460. return processLocalOrGlobalFunction(input, type, start, end);
  1461. }
  1462. if (
  1463. this.options.dashedIdents &&
  1464. isLocalMode() &&
  1465. (name === "var" || name === "style")
  1466. ) {
  1467. return processDashedIdent(input, end, end);
  1468. }
  1469. return end;
  1470. }
  1471. },
  1472. { onlyTopLevel: true, atRulePrelude: true }
  1473. );
  1474. };
  1475. /**
  1476. * Process dashed ident.
  1477. * @param {string} input input
  1478. * @param {number} start start position
  1479. * @param {number} end end position
  1480. * @returns {number} position after handling
  1481. */
  1482. const processDashedIdent = (input, start, end) => {
  1483. const customIdent = walkCssTokens.eatIdentSequence(input, start);
  1484. if (!customIdent) return end;
  1485. const identifier = unescapeIdentifier(
  1486. input.slice(customIdent[0] + 2, customIdent[1])
  1487. );
  1488. const afterCustomIdent = walkCssTokens.eatWhitespaceAndComments(
  1489. input,
  1490. customIdent[1]
  1491. )[0];
  1492. if (
  1493. input.charCodeAt(afterCustomIdent) === CC_LOWER_F ||
  1494. input.charCodeAt(afterCustomIdent) === CC_UPPER_F
  1495. ) {
  1496. const fromWord = walkCssTokens.eatIdentSequence(
  1497. input,
  1498. afterCustomIdent
  1499. );
  1500. if (
  1501. !fromWord ||
  1502. input.slice(fromWord[0], fromWord[1]).toLowerCase() !== "from"
  1503. ) {
  1504. return end;
  1505. }
  1506. const from = walkCssTokens.eatIdentSequenceOrString(
  1507. input,
  1508. walkCssTokens.eatWhitespaceAndComments(input, fromWord[1])[0]
  1509. );
  1510. if (!from) {
  1511. return end;
  1512. }
  1513. const path = input.slice(from[0], from[1]);
  1514. if (from[2] === true && path === "global") {
  1515. const dep = new ConstDependency("", [customIdent[1], from[1]]);
  1516. module.addPresentationalDependency(dep);
  1517. return end;
  1518. } else if (from[2] === false) {
  1519. const { line: sl, column: sc } = locConverter.get(customIdent[0]);
  1520. const { line: el, column: ec } = locConverter.get(from[1] - 1);
  1521. const localName = `__ICSS_IMPORT_${counter++}__`;
  1522. {
  1523. const dep = new CssIcssImportDependency(
  1524. path.slice(1, -1),
  1525. [customIdent[0], from[1] - 1],
  1526. /** @type {"local" | "global"} */
  1527. (mode),
  1528. identifier,
  1529. localName
  1530. );
  1531. dep.setLoc(sl, sc, el, ec);
  1532. module.addDependency(dep);
  1533. }
  1534. {
  1535. const dep = new CssIcssExportDependency(
  1536. identifier,
  1537. getReexport(identifier, localName, true),
  1538. [customIdent[0], from[1] - 1],
  1539. true,
  1540. CssIcssExportDependency.EXPORT_MODE.ONCE,
  1541. CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
  1542. );
  1543. dep.setLoc(sl, sc, el, ec);
  1544. module.addDependency(dep);
  1545. }
  1546. {
  1547. const dep = new ConstDependency("", [fromWord[0], from[1]]);
  1548. module.addPresentationalDependency(dep);
  1549. return end;
  1550. }
  1551. }
  1552. } else {
  1553. const { line: sl, column: sc } = locConverter.get(customIdent[0]);
  1554. const { line: el, column: ec } = locConverter.get(customIdent[1]);
  1555. const dep = new CssIcssExportDependency(
  1556. identifier,
  1557. getReexport(identifier, undefined, true),
  1558. [customIdent[0], customIdent[1]],
  1559. true,
  1560. CssIcssExportDependency.EXPORT_MODE.ONCE,
  1561. CssIcssExportDependency.EXPORT_TYPE.CUSTOM_VARIABLE
  1562. );
  1563. dep.setLoc(sl, sc, el, ec);
  1564. module.addDependency(dep);
  1565. return end;
  1566. }
  1567. return end;
  1568. };
  1569. /**
  1570. * Process local declaration.
  1571. * @param {string} input input
  1572. * @param {number} pos name start position
  1573. * @param {number} end name end position
  1574. * @returns {number} position after handling
  1575. */
  1576. const processLocalDeclaration = (input, pos, end) => {
  1577. pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
  1578. const identifier = walkCssTokens.eatIdentSequence(input, pos);
  1579. if (!identifier) {
  1580. return end;
  1581. }
  1582. const propertyNameStart = identifier[0];
  1583. pos = walkCssTokens.eatWhitespaceAndComments(input, identifier[1])[0];
  1584. if (input.charCodeAt(pos) !== CC_COLON) {
  1585. return end;
  1586. }
  1587. pos += 1;
  1588. // Remove prefix and lowercase
  1589. const propertyName = input
  1590. .slice(identifier[0], identifier[1])
  1591. .replace(/^(-\w+-)/, "")
  1592. .toLowerCase();
  1593. if (isLocalMode() && knownProperties.has(propertyName)) {
  1594. /** @type {[number, number, boolean?][]} */
  1595. const values = [];
  1596. /** @type {Record<string, number>} */
  1597. let parsedKeywords = Object.create(null);
  1598. const isGridProperty = Boolean(propertyName.startsWith("grid"));
  1599. const isGridTemplate = isGridProperty
  1600. ? Boolean(
  1601. propertyName === "grid" ||
  1602. propertyName === "grid-template" ||
  1603. propertyName === "grid-template-columns" ||
  1604. propertyName === "grid-template-rows"
  1605. )
  1606. : false;
  1607. let afterExclamation = false;
  1608. const end = walkCssTokens.consumeUntil(
  1609. input,
  1610. pos,
  1611. {
  1612. delim(input, start, end) {
  1613. afterExclamation = input.charCodeAt(start) === CC_EXCLAMATION;
  1614. return end;
  1615. },
  1616. leftSquareBracket(input, start, end) {
  1617. let i = end;
  1618. while (true) {
  1619. i = walkCssTokens.eatWhitespaceAndComments(input, i)[0];
  1620. const name = walkCssTokens.eatIdentSequence(input, i);
  1621. if (!name) {
  1622. break;
  1623. }
  1624. values.push(name);
  1625. i = name[1];
  1626. }
  1627. return end;
  1628. },
  1629. string(_input, start, end) {
  1630. if (
  1631. propertyName === "animation" ||
  1632. propertyName === "animation-name"
  1633. ) {
  1634. values.push([start, end, true]);
  1635. }
  1636. if (
  1637. propertyName === "grid" ||
  1638. propertyName === "grid-template" ||
  1639. propertyName === "grid-template-areas"
  1640. ) {
  1641. const areas = unescapeIdentifier(
  1642. input.slice(start + 1, end - 1)
  1643. );
  1644. const matches = matchAll(/\b\w+\b/g, areas);
  1645. for (const match of matches) {
  1646. const areaStart = start + 1 + match.index;
  1647. values.push([areaStart, areaStart + match[0].length, false]);
  1648. }
  1649. }
  1650. return end;
  1651. },
  1652. identifier(input, start, end) {
  1653. if (isGridTemplate) {
  1654. return end;
  1655. }
  1656. if (afterExclamation) {
  1657. afterExclamation = false;
  1658. return end;
  1659. }
  1660. const identifier = input.slice(start, end);
  1661. const keyword = identifier.toLowerCase();
  1662. parsedKeywords[keyword] =
  1663. typeof parsedKeywords[keyword] !== "undefined"
  1664. ? parsedKeywords[keyword] + 1
  1665. : 0;
  1666. const keywords =
  1667. /** @type {Record<string, number>} */
  1668. (knownProperties.get(propertyName));
  1669. if (
  1670. keywords[keyword] &&
  1671. parsedKeywords[keyword] < keywords[keyword]
  1672. ) {
  1673. return end;
  1674. }
  1675. values.push([start, end]);
  1676. return end;
  1677. },
  1678. comma(_input, _start, end) {
  1679. parsedKeywords = {};
  1680. return end;
  1681. }
  1682. },
  1683. {
  1684. function: (input, start, end) => {
  1685. const name = input
  1686. .slice(start, end - 1)
  1687. .replace(/\\/g, "")
  1688. .toLowerCase();
  1689. const type =
  1690. name === "local" ? 1 : name === "global" ? 2 : undefined;
  1691. if (type) {
  1692. return processLocalOrGlobalFunction(input, type, start, end);
  1693. }
  1694. if (
  1695. this.options.dashedIdents &&
  1696. isLocalMode() &&
  1697. name === "var"
  1698. ) {
  1699. return processDashedIdent(input, end, end);
  1700. }
  1701. if (this.options.url) {
  1702. if (name === "src" || name === "url") {
  1703. return processURLFunction(input, end, name);
  1704. } else if (IMAGE_SET_FUNCTION.test(name)) {
  1705. return processImageSetFunction(input, start, end);
  1706. }
  1707. }
  1708. return end;
  1709. }
  1710. },
  1711. {
  1712. onlyTopLevel: !isGridTemplate,
  1713. declarationValue: true
  1714. }
  1715. );
  1716. if (values.length > 0) {
  1717. for (const value of values) {
  1718. const { line: sl, column: sc } = locConverter.get(value[0]);
  1719. const { line: el, column: ec } = locConverter.get(value[1]);
  1720. const [start, end, isString] = value;
  1721. const name = unescapeIdentifier(
  1722. isString
  1723. ? input.slice(start + 1, end - 1)
  1724. : input.slice(start, end)
  1725. );
  1726. const dep = new CssIcssExportDependency(
  1727. name,
  1728. getReexport(name),
  1729. [start, end],
  1730. true,
  1731. CssIcssExportDependency.EXPORT_MODE.ONCE,
  1732. isGridProperty
  1733. ? CssIcssExportDependency.EXPORT_TYPE.GRID_CUSTOM_IDENTIFIER
  1734. : CssIcssExportDependency.EXPORT_TYPE.NORMAL
  1735. );
  1736. dep.setLoc(sl, sc, el, ec);
  1737. module.addDependency(dep);
  1738. }
  1739. }
  1740. return end;
  1741. } else if (COMPOSES_PROPERTY.test(propertyName)) {
  1742. if (lastLocalIdentifiers.length > 1) {
  1743. const end = eatUntilSemi(input, pos);
  1744. this._emitWarning(
  1745. state,
  1746. `Composition is only allowed when selector is single local class name not in "${lastLocalIdentifiers.join('", "')}"`,
  1747. locConverter,
  1748. pos,
  1749. end
  1750. );
  1751. return end;
  1752. }
  1753. if (lastLocalIdentifiers.length !== 1) return pos;
  1754. const lastLocalIdentifier = lastLocalIdentifiers[0];
  1755. let end = pos;
  1756. /** @type {Set<[number, number]>} */
  1757. const classNames = new Set();
  1758. while (true) {
  1759. pos = walkCssTokens.eatWhitespaceAndComments(input, pos)[0];
  1760. let className = walkCssTokens.eatIdentSequence(input, pos);
  1761. const ifFunction =
  1762. className && input.charCodeAt(className[1]) === CC_LEFT_PARENTHESIS;
  1763. let isGlobalFunction = false;
  1764. if (className && ifFunction) {
  1765. const name = input
  1766. .slice(className[0], className[1])
  1767. .replace(/\\/g, "")
  1768. .toLowerCase();
  1769. isGlobalFunction = name === "global";
  1770. pos = walkCssTokens.eatWhitespaceAndComments(
  1771. input,
  1772. className[1] + 1
  1773. )[0];
  1774. className = walkCssTokens.eatIdentSequence(input, pos);
  1775. if (className) {
  1776. pos = walkCssTokens.eatWhitespaceAndComments(
  1777. input,
  1778. className[1]
  1779. )[0];
  1780. pos += 1;
  1781. }
  1782. } else if (className) {
  1783. pos = walkCssTokens.eatWhitespaceAndComments(
  1784. input,
  1785. className[1]
  1786. )[0];
  1787. pos = className[1];
  1788. }
  1789. // True when we have multiple values
  1790. const isComma = input.charCodeAt(pos) === CC_COMMA;
  1791. const isSemicolon = input.charCodeAt(pos) === CC_SEMICOLON;
  1792. const isRightCurly = input.charCodeAt(pos) === CC_RIGHT_CURLY;
  1793. if (isComma || isSemicolon || isRightCurly) {
  1794. if (className) {
  1795. classNames.add(className);
  1796. }
  1797. for (const className of classNames) {
  1798. const [start, end] = className;
  1799. const identifier = unescapeIdentifier(input.slice(start, end));
  1800. const dep = new CssIcssExportDependency(
  1801. lastLocalIdentifier,
  1802. getReexport(identifier),
  1803. [start, end],
  1804. !isGlobalFunction,
  1805. isGlobalFunction
  1806. ? CssIcssExportDependency.EXPORT_MODE.APPEND
  1807. : CssIcssExportDependency.EXPORT_MODE.SELF_REFERENCE,
  1808. CssIcssExportDependency.EXPORT_TYPE.COMPOSES
  1809. );
  1810. const { line: sl, column: sc } = locConverter.get(start);
  1811. const { line: el, column: ec } = locConverter.get(end);
  1812. dep.setLoc(sl, sc, el, ec);
  1813. module.addDependency(dep);
  1814. }
  1815. classNames.clear();
  1816. if (isSemicolon || isRightCurly) {
  1817. end = isSemicolon
  1818. ? walkCssTokens.eatWhitespace(input, pos + 1)
  1819. : pos;
  1820. break;
  1821. }
  1822. pos += 1;
  1823. } else if (
  1824. classNames.size > 0 &&
  1825. className &&
  1826. input.slice(className[0], className[1]).toLowerCase() === "from"
  1827. ) {
  1828. let from = walkCssTokens.eatString(input, pos);
  1829. if (from) {
  1830. const request = input.slice(from[0] + 1, from[1] - 1);
  1831. for (const className of classNames) {
  1832. const [start, end] = className;
  1833. const identifier = unescapeIdentifier(input.slice(start, end));
  1834. const { line: sl, column: sc } = locConverter.get(start);
  1835. const { line: el, column: ec } = locConverter.get(end);
  1836. const localName = `__ICSS_IMPORT_${counter++}__`;
  1837. {
  1838. const dep = new CssIcssImportDependency(
  1839. request,
  1840. [start, end],
  1841. /** @type {"local" | "global"} */
  1842. (mode),
  1843. identifier,
  1844. localName
  1845. );
  1846. dep.setLoc(sl, sc, el, ec);
  1847. module.addDependency(dep);
  1848. }
  1849. {
  1850. const dep = new CssIcssExportDependency(
  1851. lastLocalIdentifier,
  1852. getReexport(identifier, localName),
  1853. [start, end],
  1854. true,
  1855. CssIcssExportDependency.EXPORT_MODE.APPEND,
  1856. CssIcssExportDependency.EXPORT_TYPE.COMPOSES
  1857. );
  1858. dep.setLoc(sl, sc, el, ec);
  1859. module.addDependency(dep);
  1860. }
  1861. }
  1862. classNames.clear();
  1863. pos = from[1];
  1864. } else {
  1865. from = walkCssTokens.eatIdentSequence(input, pos);
  1866. if (from && input.slice(from[0], from[1]) === "global") {
  1867. for (const className of classNames) {
  1868. const [start, end] = className;
  1869. const identifier = unescapeIdentifier(
  1870. input.slice(start, end)
  1871. );
  1872. const dep = new CssIcssExportDependency(
  1873. /** @type {string} */
  1874. (lastLocalIdentifier),
  1875. getReexport(identifier),
  1876. [start, end],
  1877. false,
  1878. CssIcssExportDependency.EXPORT_MODE.APPEND,
  1879. CssIcssExportDependency.EXPORT_TYPE.COMPOSES
  1880. );
  1881. const { line: sl, column: sc } = locConverter.get(start);
  1882. const { line: el, column: ec } = locConverter.get(end);
  1883. dep.setLoc(sl, sc, el, ec);
  1884. module.addDependency(dep);
  1885. }
  1886. classNames.clear();
  1887. pos = from[1];
  1888. } else {
  1889. const end = eatUntilSemi(input, pos);
  1890. this._emitWarning(
  1891. state,
  1892. "Incorrect composition, expected global keyword or string value",
  1893. locConverter,
  1894. pos,
  1895. end
  1896. );
  1897. return end;
  1898. }
  1899. }
  1900. } else if (className) {
  1901. classNames.add(className);
  1902. } else {
  1903. const end = eatUntilSemi(input, pos);
  1904. this._emitWarning(
  1905. state,
  1906. "Incorrect composition, expected class named",
  1907. locConverter,
  1908. pos,
  1909. end
  1910. );
  1911. return end;
  1912. }
  1913. }
  1914. // Remove `composes` from source code
  1915. const dep = new ConstDependency("", [propertyNameStart, end]);
  1916. module.addPresentationalDependency(dep);
  1917. }
  1918. return pos;
  1919. };
  1920. /**
  1921. * Process id selector.
  1922. * @param {string} input input
  1923. * @param {number} start start position
  1924. * @param {number} end end position
  1925. * @returns {number} position after handling
  1926. */
  1927. const processIdSelector = (input, start, end) => {
  1928. const valueStart = start + 1;
  1929. const name = unescapeIdentifier(input.slice(valueStart, end));
  1930. const dep = new CssIcssExportDependency(
  1931. name,
  1932. getReexport(name),
  1933. [valueStart, end],
  1934. true,
  1935. CssIcssExportDependency.EXPORT_MODE.ONCE
  1936. );
  1937. const { line: sl, column: sc } = locConverter.get(start);
  1938. const { line: el, column: ec } = locConverter.get(end);
  1939. dep.setLoc(sl, sc, el, ec);
  1940. module.addDependency(dep);
  1941. return end;
  1942. };
  1943. /**
  1944. * Process class selector.
  1945. * @param {string} input input
  1946. * @param {number} start start position
  1947. * @param {number} end end position
  1948. * @returns {number} position after handling
  1949. */
  1950. const processClassSelector = (input, start, end) => {
  1951. const ident = walkCssTokens.skipCommentsAndEatIdentSequence(input, end);
  1952. if (!ident) return end;
  1953. const name = unescapeIdentifier(input.slice(ident[0], ident[1]));
  1954. lastLocalIdentifiers.push(name);
  1955. const dep = new CssIcssExportDependency(
  1956. name,
  1957. getReexport(name),
  1958. [ident[0], ident[1]],
  1959. true,
  1960. CssIcssExportDependency.EXPORT_MODE.ONCE
  1961. );
  1962. const { line: sl, column: sc } = locConverter.get(ident[0]);
  1963. const { line: el, column: ec } = locConverter.get(ident[1]);
  1964. dep.setLoc(sl, sc, el, ec);
  1965. module.addDependency(dep);
  1966. return ident[1];
  1967. };
  1968. /**
  1969. * Process attribute selector.
  1970. * @param {string} input input
  1971. * @param {number} start start position
  1972. * @param {number} end end position
  1973. * @returns {number} position after handling
  1974. */
  1975. const processAttributeSelector = (input, start, end) => {
  1976. end = walkCssTokens.eatWhitespaceAndComments(input, end)[0];
  1977. const identifier = walkCssTokens.eatIdentSequence(input, end);
  1978. if (!identifier) return end;
  1979. const name = unescapeIdentifier(
  1980. input.slice(identifier[0], identifier[1])
  1981. );
  1982. if (name.toLowerCase() !== "class") {
  1983. return end;
  1984. }
  1985. end = walkCssTokens.eatWhitespaceAndComments(input, identifier[1])[0];
  1986. const isTilde = input.charCodeAt(end) === CC_TILDE;
  1987. if (
  1988. input.charCodeAt(end) !== CC_EQUAL &&
  1989. input.charCodeAt(end) !== CC_TILDE
  1990. ) {
  1991. return end;
  1992. }
  1993. end += 1;
  1994. if (isTilde) {
  1995. if (input.charCodeAt(end) !== CC_EQUAL) {
  1996. return end;
  1997. }
  1998. end += 1;
  1999. }
  2000. end = walkCssTokens.eatWhitespaceAndComments(input, end)[0];
  2001. const value = walkCssTokens.eatIdentSequenceOrString(input, end);
  2002. if (!value) {
  2003. return end;
  2004. }
  2005. const classNameStart = value[2] ? value[0] : value[0] + 1;
  2006. const classNameEnd = value[2] ? value[1] : value[1] - 1;
  2007. const className = unescapeIdentifier(
  2008. input.slice(classNameStart, classNameEnd)
  2009. );
  2010. const dep = new CssIcssExportDependency(
  2011. className,
  2012. getReexport(className),
  2013. [classNameStart, classNameEnd],
  2014. true,
  2015. CssIcssExportDependency.EXPORT_MODE.NONE
  2016. );
  2017. const { line: sl, column: sc } = locConverter.get(classNameStart);
  2018. const { line: el, column: ec } = locConverter.get(classNameEnd);
  2019. dep.setLoc(sl, sc, el, ec);
  2020. module.addDependency(dep);
  2021. return value[2] ? classNameEnd : classNameEnd + 1;
  2022. };
  2023. walkCssTokens(source, 0, {
  2024. comment,
  2025. leftCurlyBracket: (input, start, end) => {
  2026. switch (scope) {
  2027. case CSS_MODE_TOP_LEVEL: {
  2028. allowImportAtRule = false;
  2029. scope = CSS_MODE_IN_BLOCK;
  2030. if (isModules) {
  2031. blockNestingLevel = 1;
  2032. isNextRulePrelude = isNextNestedSyntax(input, end);
  2033. }
  2034. break;
  2035. }
  2036. case CSS_MODE_IN_BLOCK: {
  2037. if (isModules) {
  2038. blockNestingLevel++;
  2039. isNextRulePrelude = isNextNestedSyntax(input, end);
  2040. }
  2041. break;
  2042. }
  2043. }
  2044. return end;
  2045. },
  2046. rightCurlyBracket: (input, start, end) => {
  2047. switch (scope) {
  2048. case CSS_MODE_IN_BLOCK: {
  2049. if (--blockNestingLevel === 0) {
  2050. scope = CSS_MODE_TOP_LEVEL;
  2051. if (isModules) {
  2052. isNextRulePrelude = true;
  2053. modeData = undefined;
  2054. lastLocalIdentifiers = [];
  2055. }
  2056. } else if (isModules) {
  2057. isNextRulePrelude = isNextNestedSyntax(input, end);
  2058. }
  2059. break;
  2060. }
  2061. }
  2062. return end;
  2063. },
  2064. url: (input, start, end, contentStart, contentEnd) => {
  2065. if (!this.options.url) {
  2066. return end;
  2067. }
  2068. return processOldURLFunction(
  2069. input,
  2070. start,
  2071. end,
  2072. contentStart,
  2073. contentEnd
  2074. );
  2075. },
  2076. atKeyword: (input, start, end) => {
  2077. const name = input.slice(start, end).toLowerCase();
  2078. switch (name) {
  2079. case "@namespace": {
  2080. this._emitWarning(
  2081. state,
  2082. "'@namespace' is not supported in bundled CSS",
  2083. locConverter,
  2084. start,
  2085. end
  2086. );
  2087. return eatUntilSemi(input, start);
  2088. }
  2089. case "@charset": {
  2090. const atRuleEnd = eatUntilSemi(input, start);
  2091. if (/** @type {CssModule} */ (module).exportType === "style") {
  2092. return atRuleEnd;
  2093. }
  2094. const dep = new ConstDependency("", [start, atRuleEnd + 1]);
  2095. module.addPresentationalDependency(dep);
  2096. const value = walkCssTokens.eatString(input, end);
  2097. if (!value) {
  2098. return atRuleEnd;
  2099. }
  2100. /** @type {BuildInfo} */
  2101. (module.buildInfo).charset = input
  2102. .slice(value[0] + 1, value[1] - 1)
  2103. .toUpperCase();
  2104. return atRuleEnd;
  2105. }
  2106. case "@import": {
  2107. if (!this.options.import) {
  2108. return eatUntilSemi(input, end);
  2109. }
  2110. if (!allowImportAtRule) {
  2111. this._emitWarning(
  2112. state,
  2113. "Any '@import' rules must precede all other rules",
  2114. locConverter,
  2115. start,
  2116. end
  2117. );
  2118. return eatUntilSemi(input, end);
  2119. }
  2120. return processAtImport(input, start, end);
  2121. }
  2122. default: {
  2123. if (isModules) {
  2124. if (name === "@value") {
  2125. return processAtValue(input, start, end);
  2126. } else if (
  2127. this.options.animation &&
  2128. OPTIONALLY_VENDOR_PREFIXED_KEYFRAMES_AT_RULE.test(name) &&
  2129. isLocalMode()
  2130. ) {
  2131. return processLocalAtRule(input, end, {
  2132. string: true,
  2133. identifier: true
  2134. });
  2135. } else if (
  2136. this.options.customIdents &&
  2137. name === "@counter-style" &&
  2138. isLocalMode()
  2139. ) {
  2140. return processLocalAtRule(input, end, {
  2141. identifier: true
  2142. });
  2143. } else if (
  2144. this.options.container &&
  2145. name === "@container" &&
  2146. isLocalMode()
  2147. ) {
  2148. return processLocalAtRule(input, end, {
  2149. identifier: /^(none|and|or|not)$/
  2150. });
  2151. } else if (name === "@scope") {
  2152. isNextRulePrelude = true;
  2153. return end;
  2154. }
  2155. isNextRulePrelude = false;
  2156. }
  2157. }
  2158. }
  2159. return end;
  2160. },
  2161. semicolon: (input, start, end) => {
  2162. if (isModules && scope === CSS_MODE_IN_BLOCK) {
  2163. isNextRulePrelude = isNextNestedSyntax(input, end);
  2164. }
  2165. return end;
  2166. },
  2167. identifier: (input, start, end) => {
  2168. if (isModules) {
  2169. const identifier = input.slice(start, end);
  2170. if (
  2171. this.options.dashedIdents &&
  2172. isLocalMode() &&
  2173. isDashedIdentifier(identifier)
  2174. ) {
  2175. return processDashedIdent(input, start, end);
  2176. }
  2177. if (icssDefinitions.has(identifier)) {
  2178. return processICSSSymbol(identifier, start, end);
  2179. }
  2180. switch (scope) {
  2181. case CSS_MODE_IN_BLOCK: {
  2182. if (isModules && !isNextRulePrelude) {
  2183. // Handle only top level values and not inside functions
  2184. return processLocalDeclaration(input, start, end);
  2185. }
  2186. break;
  2187. }
  2188. }
  2189. }
  2190. return end;
  2191. },
  2192. delim: (input, start, end) => {
  2193. if (
  2194. input.charCodeAt(start) === CC_FULL_STOP &&
  2195. isNextRulePrelude &&
  2196. isLocalMode()
  2197. ) {
  2198. return processClassSelector(input, start, end);
  2199. }
  2200. return end;
  2201. },
  2202. hash: (input, start, end, isID) => {
  2203. if (isNextRulePrelude && isLocalMode() && isID) {
  2204. return processIdSelector(input, start, end);
  2205. }
  2206. return end;
  2207. },
  2208. colon: (input, start, end) => {
  2209. if (isModules) {
  2210. const ident = walkCssTokens.skipCommentsAndEatIdentSequence(
  2211. input,
  2212. end
  2213. );
  2214. if (!ident) return end;
  2215. const name = input.slice(ident[0], ident[1]).toLowerCase();
  2216. switch (scope) {
  2217. case CSS_MODE_TOP_LEVEL: {
  2218. if (name === "import") {
  2219. const pos = processImportOrExport(0, input, ident[1]);
  2220. const dep = new ConstDependency("", [start, pos]);
  2221. module.addPresentationalDependency(dep);
  2222. return pos;
  2223. } else if (name === "export") {
  2224. const pos = processImportOrExport(1, input, ident[1]);
  2225. const dep = new ConstDependency("", [start, pos]);
  2226. module.addPresentationalDependency(dep);
  2227. return pos;
  2228. }
  2229. }
  2230. // falls through
  2231. default: {
  2232. if (isNextRulePrelude) {
  2233. const isFn = input.charCodeAt(ident[1]) === CC_LEFT_PARENTHESIS;
  2234. if (isFn && name === "local") {
  2235. // Eat extra whitespace
  2236. const end = walkCssTokens.eatWhitespaceAndComments(
  2237. input,
  2238. ident[1] + 1
  2239. )[0];
  2240. modeData = LOCAL_MODE;
  2241. const dep = new ConstDependency("", [start, end]);
  2242. module.addPresentationalDependency(dep);
  2243. balanced.push([":local", start, end, true]);
  2244. return end;
  2245. } else if (name === "local") {
  2246. modeData = LOCAL_MODE;
  2247. const found = walkCssTokens.eatWhitespaceAndComments(
  2248. input,
  2249. ident[1]
  2250. );
  2251. if (!found[1]) {
  2252. this._emitWarning(
  2253. state,
  2254. `Missing whitespace after ':local' in '${input.slice(
  2255. start,
  2256. eatUntilLeftCurly(input, end) + 1
  2257. )}'`,
  2258. locConverter,
  2259. start,
  2260. end
  2261. );
  2262. }
  2263. end = walkCssTokens.eatWhitespace(input, ident[1]);
  2264. const dep = new ConstDependency("", [start, end]);
  2265. module.addPresentationalDependency(dep);
  2266. return end;
  2267. } else if (isFn && name === "global") {
  2268. // Eat extra whitespace
  2269. const end = walkCssTokens.eatWhitespaceAndComments(
  2270. input,
  2271. ident[1] + 1
  2272. )[0];
  2273. modeData = GLOBAL_MODE;
  2274. const dep = new ConstDependency("", [start, end]);
  2275. module.addPresentationalDependency(dep);
  2276. balanced.push([":global", start, end, true]);
  2277. return end;
  2278. } else if (name === "global") {
  2279. modeData = GLOBAL_MODE;
  2280. // Eat extra whitespace
  2281. const found = walkCssTokens.eatWhitespaceAndComments(
  2282. input,
  2283. ident[1]
  2284. );
  2285. if (!found[1]) {
  2286. this._emitWarning(
  2287. state,
  2288. `Missing whitespace after ':global' in '${input.slice(
  2289. start,
  2290. eatUntilLeftCurly(input, end) + 1
  2291. )}'`,
  2292. locConverter,
  2293. start,
  2294. end
  2295. );
  2296. }
  2297. end = walkCssTokens.eatWhitespace(input, ident[1]);
  2298. const dep = new ConstDependency("", [start, end]);
  2299. module.addPresentationalDependency(dep);
  2300. return end;
  2301. }
  2302. }
  2303. }
  2304. }
  2305. }
  2306. lastTokenEndForComments = end;
  2307. return end;
  2308. },
  2309. function: (input, start, end) => {
  2310. const name = input
  2311. .slice(start, end - 1)
  2312. .replace(/\\/g, "")
  2313. .toLowerCase();
  2314. balanced.push([name, start, end]);
  2315. switch (name) {
  2316. case "src":
  2317. case "url": {
  2318. if (!this.options.url) {
  2319. return end;
  2320. }
  2321. return processURLFunction(input, end, name);
  2322. }
  2323. default: {
  2324. if (this.options.url && IMAGE_SET_FUNCTION.test(name)) {
  2325. return processImageSetFunction(input, start, end);
  2326. }
  2327. if (isModules) {
  2328. if (
  2329. this.options.function &&
  2330. isLocalMode() &&
  2331. isDashedIdentifier(name)
  2332. ) {
  2333. return processDashedIdent(input, start, end);
  2334. }
  2335. const type =
  2336. name === "local" ? 1 : name === "global" ? 2 : undefined;
  2337. if (type && !isNextRulePrelude) {
  2338. return processLocalOrGlobalFunction(input, type, start, end);
  2339. }
  2340. }
  2341. }
  2342. }
  2343. return end;
  2344. },
  2345. leftSquareBracket: (input, start, end) => {
  2346. if (isNextRulePrelude && isLocalMode()) {
  2347. return processAttributeSelector(input, start, end);
  2348. }
  2349. return end;
  2350. },
  2351. leftParenthesis: (input, start, end) => {
  2352. balanced.push(["(", start, end]);
  2353. return end;
  2354. },
  2355. rightParenthesis: (input, start, end) => {
  2356. const popped = balanced.pop();
  2357. if (isModules && popped) {
  2358. const isLocal = popped[0] === ":local";
  2359. const isGlobal = popped[0] === ":global";
  2360. if (isLocal || isGlobal) {
  2361. modeData = balanced[balanced.length - 1]
  2362. ? balanced[balanced.length - 1][0] === ":local"
  2363. ? LOCAL_MODE
  2364. : balanced[balanced.length - 1][0] === ":global"
  2365. ? GLOBAL_MODE
  2366. : undefined
  2367. : undefined;
  2368. if (popped[3] && isLocal) {
  2369. while (walkCssTokens.isWhiteSpace(input.charCodeAt(start - 1))) {
  2370. start -= 1;
  2371. }
  2372. }
  2373. const dep = new ConstDependency("", [start, end]);
  2374. module.addPresentationalDependency(dep);
  2375. } else if (isNextRulePrelude) {
  2376. modeData = undefined;
  2377. }
  2378. }
  2379. return end;
  2380. },
  2381. comma: (input, start, end) => {
  2382. if (isModules && balanced.length === 0) {
  2383. // Reset stack for `:global .class :local .class-other` selector after
  2384. modeData = undefined;
  2385. }
  2386. lastTokenEndForComments = start;
  2387. return end;
  2388. }
  2389. });
  2390. /** @type {BuildInfo} */
  2391. (module.buildInfo).strict = true;
  2392. const buildMeta = /** @type {BuildMeta} */ (state.module.buildMeta);
  2393. buildMeta.exportsType = this.options.namedExports ? "namespace" : "default";
  2394. buildMeta.defaultObject = this.options.namedExports
  2395. ? false
  2396. : "redirect-warn";
  2397. if (
  2398. /** @type {CssModule} */ (module).exportType === "text" ||
  2399. /** @type {CssModule} */ (module).exportType === "css-style-sheet"
  2400. ) {
  2401. module.addDependency(new StaticExportsDependency(["default"], true));
  2402. } else {
  2403. module.addDependency(new StaticExportsDependency([], true));
  2404. }
  2405. return state;
  2406. }
  2407. /**
  2408. * Returns comments in the range.
  2409. * @param {Range} range range
  2410. * @returns {Comment[]} comments in the range
  2411. */
  2412. getComments(range) {
  2413. if (!this.comments) return [];
  2414. const [rangeStart, rangeEnd] = range;
  2415. /**
  2416. * Returns compared.
  2417. * @param {Comment} comment comment
  2418. * @param {number} needle needle
  2419. * @returns {number} compared
  2420. */
  2421. const compare = (comment, needle) =>
  2422. /** @type {Range} */ (comment.range)[0] - needle;
  2423. const comments = /** @type {Comment[]} */ (this.comments);
  2424. let idx = binarySearchBounds.ge(comments, rangeStart, compare);
  2425. /** @type {Comment[]} */
  2426. const commentsInRange = [];
  2427. while (
  2428. comments[idx] &&
  2429. /** @type {Range} */ (comments[idx].range)[1] <= rangeEnd
  2430. ) {
  2431. commentsInRange.push(comments[idx]);
  2432. idx++;
  2433. }
  2434. return commentsInRange;
  2435. }
  2436. /**
  2437. * Parses comment options.
  2438. * @param {Range} range range of the comment
  2439. * @returns {{ options: Record<string, EXPECTED_ANY> | null, errors: (Error & { comment: Comment })[] | null }} result
  2440. */
  2441. parseCommentOptions(range) {
  2442. const comments = this.getComments(range);
  2443. if (comments.length === 0) {
  2444. return EMPTY_COMMENT_OPTIONS;
  2445. }
  2446. /** @type {Record<string, EXPECTED_ANY>} */
  2447. const options = {};
  2448. /** @type {(Error & { comment: Comment })[]} */
  2449. const errors = [];
  2450. for (const comment of comments) {
  2451. const { value } = comment;
  2452. if (value && webpackCommentRegExp.test(value)) {
  2453. // try compile only if webpack options comment is present
  2454. try {
  2455. for (let [key, val] of Object.entries(
  2456. vm.runInContext(
  2457. `(function(){return {${value}};})()`,
  2458. this.magicCommentContext
  2459. )
  2460. )) {
  2461. if (typeof val === "object" && val !== null) {
  2462. val =
  2463. val.constructor.name === "RegExp"
  2464. ? new RegExp(val)
  2465. : JSON.parse(JSON.stringify(val));
  2466. }
  2467. options[key] = val;
  2468. }
  2469. } catch (err) {
  2470. const newErr = new Error(String(/** @type {Error} */ (err).message));
  2471. newErr.stack = String(/** @type {Error} */ (err).stack);
  2472. Object.assign(newErr, { comment });
  2473. errors.push(/** @type {(Error & { comment: Comment })} */ (newErr));
  2474. }
  2475. }
  2476. }
  2477. return { options, errors };
  2478. }
  2479. }
  2480. module.exports = CssParser;
  2481. module.exports.escapeIdentifier = escapeIdentifier;
  2482. module.exports.unescapeIdentifier = unescapeIdentifier;