CssModulesPlugin.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource, PrefixSource } = require("webpack-sources");
  7. const CssModule = require("../CssModule");
  8. const HotUpdateChunk = require("../HotUpdateChunk");
  9. const {
  10. CSS_MODULE_TYPE,
  11. CSS_MODULE_TYPE_GLOBAL,
  12. CSS_MODULE_TYPE_MODULE,
  13. CSS_MODULE_TYPE_AUTO
  14. } = require("../ModuleTypeConstants");
  15. const RuntimeGlobals = require("../RuntimeGlobals");
  16. const SelfModuleFactory = require("../SelfModuleFactory");
  17. const WebpackError = require("../WebpackError");
  18. const CssExportDependency = require("../dependencies/CssExportDependency");
  19. const CssImportDependency = require("../dependencies/CssImportDependency");
  20. const CssLocalIdentifierDependency = require("../dependencies/CssLocalIdentifierDependency");
  21. const CssSelfLocalIdentifierDependency = require("../dependencies/CssSelfLocalIdentifierDependency");
  22. const CssUrlDependency = require("../dependencies/CssUrlDependency");
  23. const StaticExportsDependency = require("../dependencies/StaticExportsDependency");
  24. const { compareModulesByIdentifier } = require("../util/comparators");
  25. const createSchemaValidation = require("../util/create-schema-validation");
  26. const createHash = require("../util/createHash");
  27. const memoize = require("../util/memoize");
  28. const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
  29. const CssExportsGenerator = require("./CssExportsGenerator");
  30. const CssGenerator = require("./CssGenerator");
  31. const CssParser = require("./CssParser");
  32. /** @typedef {import("webpack-sources").Source} Source */
  33. /** @typedef {import("../../declarations/WebpackOptions").Output} OutputOptions */
  34. /** @typedef {import("../Chunk")} Chunk */
  35. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  36. /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
  37. /** @typedef {import("../Compilation")} Compilation */
  38. /** @typedef {import("../Compiler")} Compiler */
  39. /** @typedef {import("../Module")} Module */
  40. /** @typedef {import("../util/memoize")} Memoize */
  41. const getCssLoadingRuntimeModule = memoize(() =>
  42. require("./CssLoadingRuntimeModule")
  43. );
  44. /**
  45. * @param {string} name name
  46. * @returns {{oneOf: [{$ref: string}], definitions: *}} schema
  47. */
  48. const getSchema = name => {
  49. const { definitions } = require("../../schemas/WebpackOptions.json");
  50. return {
  51. definitions,
  52. oneOf: [{ $ref: `#/definitions/${name}` }]
  53. };
  54. };
  55. const generatorValidationOptions = {
  56. name: "Css Modules Plugin",
  57. baseDataPath: "generator"
  58. };
  59. const validateGeneratorOptions = {
  60. css: createSchemaValidation(
  61. require("../../schemas/plugins/css/CssGeneratorOptions.check.js"),
  62. () => getSchema("CssGeneratorOptions"),
  63. generatorValidationOptions
  64. ),
  65. "css/auto": createSchemaValidation(
  66. require("../../schemas/plugins/css/CssAutoGeneratorOptions.check.js"),
  67. () => getSchema("CssAutoGeneratorOptions"),
  68. generatorValidationOptions
  69. ),
  70. "css/module": createSchemaValidation(
  71. require("../../schemas/plugins/css/CssModuleGeneratorOptions.check.js"),
  72. () => getSchema("CssModuleGeneratorOptions"),
  73. generatorValidationOptions
  74. ),
  75. "css/global": createSchemaValidation(
  76. require("../../schemas/plugins/css/CssGlobalGeneratorOptions.check.js"),
  77. () => getSchema("CssGlobalGeneratorOptions"),
  78. generatorValidationOptions
  79. )
  80. };
  81. const parserValidationOptions = {
  82. name: "Css Modules Plugin",
  83. baseDataPath: "parser"
  84. };
  85. const validateParserOptions = {
  86. css: createSchemaValidation(
  87. require("../../schemas/plugins/css/CssParserOptions.check.js"),
  88. () => getSchema("CssParserOptions"),
  89. parserValidationOptions
  90. ),
  91. "css/auto": createSchemaValidation(
  92. require("../../schemas/plugins/css/CssAutoParserOptions.check.js"),
  93. () => getSchema("CssAutoParserOptions"),
  94. parserValidationOptions
  95. ),
  96. "css/module": createSchemaValidation(
  97. require("../../schemas/plugins/css/CssModuleParserOptions.check.js"),
  98. () => getSchema("CssModuleParserOptions"),
  99. parserValidationOptions
  100. ),
  101. "css/global": createSchemaValidation(
  102. require("../../schemas/plugins/css/CssGlobalParserOptions.check.js"),
  103. () => getSchema("CssGlobalParserOptions"),
  104. parserValidationOptions
  105. )
  106. };
  107. /**
  108. * @param {string} str string
  109. * @param {boolean=} omitOptionalUnderscore if true, optional underscore is not added
  110. * @returns {string} escaped string
  111. */
  112. const escapeCss = (str, omitOptionalUnderscore) => {
  113. const escaped = `${str}`.replace(
  114. // cspell:word uffff
  115. /[^a-zA-Z0-9_\u0081-\uffff-]/g,
  116. s => `\\${s}`
  117. );
  118. return !omitOptionalUnderscore && /^(?!--)[0-9_-]/.test(escaped)
  119. ? `_${escaped}`
  120. : escaped;
  121. };
  122. /**
  123. * @param {string} str string
  124. * @returns {string} encoded string
  125. */
  126. const LZWEncode = str => {
  127. /** @type {Map<string, string>} */
  128. const map = new Map();
  129. let encoded = "";
  130. let phrase = str[0];
  131. let code = 256;
  132. let maxCode = "\uffff".charCodeAt(0);
  133. for (let i = 1; i < str.length; i++) {
  134. const c = str[i];
  135. if (map.has(phrase + c)) {
  136. phrase += c;
  137. } else {
  138. encoded += phrase.length > 1 ? map.get(phrase) : phrase;
  139. map.set(phrase + c, String.fromCharCode(code));
  140. phrase = c;
  141. if (++code > maxCode) {
  142. code = 256;
  143. map.clear();
  144. }
  145. }
  146. }
  147. encoded += phrase.length > 1 ? map.get(phrase) : phrase;
  148. return encoded;
  149. };
  150. const plugin = "CssModulesPlugin";
  151. class CssModulesPlugin {
  152. /**
  153. * Apply the plugin
  154. * @param {Compiler} compiler the compiler instance
  155. * @returns {void}
  156. */
  157. apply(compiler) {
  158. compiler.hooks.compilation.tap(
  159. plugin,
  160. (compilation, { normalModuleFactory }) => {
  161. const selfFactory = new SelfModuleFactory(compilation.moduleGraph);
  162. compilation.dependencyFactories.set(
  163. CssUrlDependency,
  164. normalModuleFactory
  165. );
  166. compilation.dependencyTemplates.set(
  167. CssUrlDependency,
  168. new CssUrlDependency.Template()
  169. );
  170. compilation.dependencyTemplates.set(
  171. CssLocalIdentifierDependency,
  172. new CssLocalIdentifierDependency.Template()
  173. );
  174. compilation.dependencyFactories.set(
  175. CssSelfLocalIdentifierDependency,
  176. selfFactory
  177. );
  178. compilation.dependencyTemplates.set(
  179. CssSelfLocalIdentifierDependency,
  180. new CssSelfLocalIdentifierDependency.Template()
  181. );
  182. compilation.dependencyTemplates.set(
  183. CssExportDependency,
  184. new CssExportDependency.Template()
  185. );
  186. compilation.dependencyFactories.set(
  187. CssImportDependency,
  188. normalModuleFactory
  189. );
  190. compilation.dependencyTemplates.set(
  191. CssImportDependency,
  192. new CssImportDependency.Template()
  193. );
  194. compilation.dependencyTemplates.set(
  195. StaticExportsDependency,
  196. new StaticExportsDependency.Template()
  197. );
  198. for (const type of [
  199. CSS_MODULE_TYPE,
  200. CSS_MODULE_TYPE_GLOBAL,
  201. CSS_MODULE_TYPE_MODULE,
  202. CSS_MODULE_TYPE_AUTO
  203. ]) {
  204. normalModuleFactory.hooks.createParser
  205. .for(type)
  206. .tap(plugin, parserOptions => {
  207. validateParserOptions[type](parserOptions);
  208. const { namedExports } = parserOptions;
  209. switch (type) {
  210. case CSS_MODULE_TYPE:
  211. case CSS_MODULE_TYPE_AUTO:
  212. return new CssParser({
  213. namedExports
  214. });
  215. case CSS_MODULE_TYPE_GLOBAL:
  216. return new CssParser({
  217. allowModeSwitch: false,
  218. namedExports
  219. });
  220. case CSS_MODULE_TYPE_MODULE:
  221. return new CssParser({
  222. defaultMode: "local",
  223. namedExports
  224. });
  225. }
  226. });
  227. normalModuleFactory.hooks.createGenerator
  228. .for(type)
  229. .tap(plugin, generatorOptions => {
  230. validateGeneratorOptions[type](generatorOptions);
  231. return generatorOptions.exportsOnly
  232. ? new CssExportsGenerator(
  233. generatorOptions.exportsConvention,
  234. generatorOptions.localIdentName
  235. )
  236. : new CssGenerator(
  237. generatorOptions.exportsConvention,
  238. generatorOptions.localIdentName
  239. );
  240. });
  241. normalModuleFactory.hooks.createModuleClass
  242. .for(type)
  243. .tap(plugin, (createData, resolveData) => {
  244. if (resolveData.dependencies.length > 0) {
  245. // When CSS is imported from CSS there is only one dependency
  246. const dependency = resolveData.dependencies[0];
  247. if (dependency instanceof CssImportDependency) {
  248. const parent =
  249. /** @type {CssModule} */
  250. (compilation.moduleGraph.getParentModule(dependency));
  251. if (parent instanceof CssModule) {
  252. /** @type {import("../CssModule").Inheritance | undefined} */
  253. let inheritance;
  254. if (
  255. (parent.cssLayer !== null &&
  256. parent.cssLayer !== undefined) ||
  257. parent.supports ||
  258. parent.media
  259. ) {
  260. if (!inheritance) {
  261. inheritance = [];
  262. }
  263. inheritance.push([
  264. parent.cssLayer,
  265. parent.supports,
  266. parent.media
  267. ]);
  268. }
  269. if (parent.inheritance) {
  270. if (!inheritance) {
  271. inheritance = [];
  272. }
  273. inheritance.push(...parent.inheritance);
  274. }
  275. return new CssModule({
  276. ...createData,
  277. cssLayer: dependency.layer,
  278. supports: dependency.supports,
  279. media: dependency.media,
  280. inheritance
  281. });
  282. }
  283. return new CssModule({
  284. ...createData,
  285. cssLayer: dependency.layer,
  286. supports: dependency.supports,
  287. media: dependency.media
  288. });
  289. }
  290. }
  291. return new CssModule(createData);
  292. });
  293. }
  294. const orderedCssModulesPerChunk = new WeakMap();
  295. compilation.hooks.afterCodeGeneration.tap("CssModulesPlugin", () => {
  296. const { chunkGraph } = compilation;
  297. for (const chunk of compilation.chunks) {
  298. if (CssModulesPlugin.chunkHasCss(chunk, chunkGraph)) {
  299. orderedCssModulesPerChunk.set(
  300. chunk,
  301. this.getOrderedChunkCssModules(chunk, chunkGraph, compilation)
  302. );
  303. }
  304. }
  305. });
  306. compilation.hooks.contentHash.tap("CssModulesPlugin", chunk => {
  307. const {
  308. chunkGraph,
  309. outputOptions: {
  310. hashSalt,
  311. hashDigest,
  312. hashDigestLength,
  313. hashFunction
  314. }
  315. } = compilation;
  316. const modules = orderedCssModulesPerChunk.get(chunk);
  317. if (modules === undefined) return;
  318. const hash = createHash(hashFunction);
  319. if (hashSalt) hash.update(hashSalt);
  320. for (const module of modules) {
  321. hash.update(chunkGraph.getModuleHash(module, chunk.runtime));
  322. }
  323. const digest = /** @type {string} */ (hash.digest(hashDigest));
  324. chunk.contentHash.css = nonNumericOnlyHash(digest, hashDigestLength);
  325. });
  326. compilation.hooks.renderManifest.tap(plugin, (result, options) => {
  327. const { chunkGraph } = compilation;
  328. const { hash, chunk, codeGenerationResults } = options;
  329. if (chunk instanceof HotUpdateChunk) return result;
  330. /** @type {CssModule[] | undefined} */
  331. const modules = orderedCssModulesPerChunk.get(chunk);
  332. if (modules !== undefined) {
  333. result.push({
  334. render: () =>
  335. this.renderChunk({
  336. chunk,
  337. chunkGraph,
  338. codeGenerationResults,
  339. uniqueName: compilation.outputOptions.uniqueName,
  340. cssHeadDataCompression:
  341. compilation.outputOptions.cssHeadDataCompression,
  342. modules
  343. }),
  344. filenameTemplate: CssModulesPlugin.getChunkFilenameTemplate(
  345. chunk,
  346. compilation.outputOptions
  347. ),
  348. pathOptions: {
  349. hash,
  350. runtime: chunk.runtime,
  351. chunk,
  352. contentHashType: "css"
  353. },
  354. identifier: `css${chunk.id}`,
  355. hash: chunk.contentHash.css
  356. });
  357. }
  358. return result;
  359. });
  360. const globalChunkLoading = compilation.outputOptions.chunkLoading;
  361. /**
  362. * @param {Chunk} chunk the chunk
  363. * @returns {boolean} true, when enabled
  364. */
  365. const isEnabledForChunk = chunk => {
  366. const options = chunk.getEntryOptions();
  367. const chunkLoading =
  368. options && options.chunkLoading !== undefined
  369. ? options.chunkLoading
  370. : globalChunkLoading;
  371. return chunkLoading === "jsonp";
  372. };
  373. const onceForChunkSet = new WeakSet();
  374. /**
  375. * @param {Chunk} chunk chunk to check
  376. * @param {Set<string>} set runtime requirements
  377. */
  378. const handler = (chunk, set) => {
  379. if (onceForChunkSet.has(chunk)) return;
  380. onceForChunkSet.add(chunk);
  381. if (!isEnabledForChunk(chunk)) return;
  382. set.add(RuntimeGlobals.publicPath);
  383. set.add(RuntimeGlobals.getChunkCssFilename);
  384. set.add(RuntimeGlobals.hasOwnProperty);
  385. set.add(RuntimeGlobals.moduleFactoriesAddOnly);
  386. set.add(RuntimeGlobals.makeNamespaceObject);
  387. const CssLoadingRuntimeModule = getCssLoadingRuntimeModule();
  388. compilation.addRuntimeModule(chunk, new CssLoadingRuntimeModule(set));
  389. };
  390. compilation.hooks.runtimeRequirementInTree
  391. .for(RuntimeGlobals.hasCssModules)
  392. .tap(plugin, handler);
  393. compilation.hooks.runtimeRequirementInTree
  394. .for(RuntimeGlobals.ensureChunkHandlers)
  395. .tap(plugin, handler);
  396. compilation.hooks.runtimeRequirementInTree
  397. .for(RuntimeGlobals.hmrDownloadUpdateHandlers)
  398. .tap(plugin, handler);
  399. }
  400. );
  401. }
  402. /**
  403. * @param {Chunk} chunk chunk
  404. * @param {Iterable<Module>} modules unordered modules
  405. * @param {Compilation} compilation compilation
  406. * @returns {Module[]} ordered modules
  407. */
  408. getModulesInOrder(chunk, modules, compilation) {
  409. if (!modules) return [];
  410. /** @type {Module[]} */
  411. const modulesList = [...modules];
  412. // Get ordered list of modules per chunk group
  413. // Lists are in reverse order to allow to use Array.pop()
  414. const modulesByChunkGroup = Array.from(chunk.groupsIterable, chunkGroup => {
  415. const sortedModules = modulesList
  416. .map(module => {
  417. return {
  418. module,
  419. index: chunkGroup.getModulePostOrderIndex(module)
  420. };
  421. })
  422. .filter(item => item.index !== undefined)
  423. .sort(
  424. (a, b) =>
  425. /** @type {number} */ (b.index) - /** @type {number} */ (a.index)
  426. )
  427. .map(item => item.module);
  428. return { list: sortedModules, set: new Set(sortedModules) };
  429. });
  430. if (modulesByChunkGroup.length === 1)
  431. return modulesByChunkGroup[0].list.reverse();
  432. const compareModuleLists = ({ list: a }, { list: b }) => {
  433. if (a.length === 0) {
  434. return b.length === 0 ? 0 : 1;
  435. } else {
  436. if (b.length === 0) return -1;
  437. return compareModulesByIdentifier(a[a.length - 1], b[b.length - 1]);
  438. }
  439. };
  440. modulesByChunkGroup.sort(compareModuleLists);
  441. /** @type {Module[]} */
  442. const finalModules = [];
  443. for (;;) {
  444. const failedModules = new Set();
  445. const list = modulesByChunkGroup[0].list;
  446. if (list.length === 0) {
  447. // done, everything empty
  448. break;
  449. }
  450. /** @type {Module} */
  451. let selectedModule = list[list.length - 1];
  452. let hasFailed = undefined;
  453. outer: for (;;) {
  454. for (const { list, set } of modulesByChunkGroup) {
  455. if (list.length === 0) continue;
  456. const lastModule = list[list.length - 1];
  457. if (lastModule === selectedModule) continue;
  458. if (!set.has(selectedModule)) continue;
  459. failedModules.add(selectedModule);
  460. if (failedModules.has(lastModule)) {
  461. // There is a conflict, try other alternatives
  462. hasFailed = lastModule;
  463. continue;
  464. }
  465. selectedModule = lastModule;
  466. hasFailed = false;
  467. continue outer; // restart
  468. }
  469. break;
  470. }
  471. if (hasFailed) {
  472. // There is a not resolve-able conflict with the selectedModule
  473. if (compilation) {
  474. // TODO print better warning
  475. compilation.warnings.push(
  476. new WebpackError(
  477. `chunk ${chunk.name || chunk.id}\nConflicting order between ${
  478. /** @type {Module} */
  479. (hasFailed).readableIdentifier(compilation.requestShortener)
  480. } and ${selectedModule.readableIdentifier(
  481. compilation.requestShortener
  482. )}`
  483. )
  484. );
  485. }
  486. selectedModule = /** @type {Module} */ (hasFailed);
  487. }
  488. // Insert the selected module into the final modules list
  489. finalModules.push(selectedModule);
  490. // Remove the selected module from all lists
  491. for (const { list, set } of modulesByChunkGroup) {
  492. const lastModule = list[list.length - 1];
  493. if (lastModule === selectedModule) list.pop();
  494. else if (hasFailed && set.has(selectedModule)) {
  495. const idx = list.indexOf(selectedModule);
  496. if (idx >= 0) list.splice(idx, 1);
  497. }
  498. }
  499. modulesByChunkGroup.sort(compareModuleLists);
  500. }
  501. return finalModules;
  502. }
  503. /**
  504. * @param {Chunk} chunk chunk
  505. * @param {ChunkGraph} chunkGraph chunk graph
  506. * @param {Compilation} compilation compilation
  507. * @returns {Module[]} ordered css modules
  508. */
  509. getOrderedChunkCssModules(chunk, chunkGraph, compilation) {
  510. return [
  511. ...this.getModulesInOrder(
  512. chunk,
  513. /** @type {Iterable<Module>} */
  514. (
  515. chunkGraph.getOrderedChunkModulesIterableBySourceType(
  516. chunk,
  517. "css-import",
  518. compareModulesByIdentifier
  519. )
  520. ),
  521. compilation
  522. ),
  523. ...this.getModulesInOrder(
  524. chunk,
  525. /** @type {Iterable<Module>} */
  526. (
  527. chunkGraph.getOrderedChunkModulesIterableBySourceType(
  528. chunk,
  529. "css",
  530. compareModulesByIdentifier
  531. )
  532. ),
  533. compilation
  534. )
  535. ];
  536. }
  537. /**
  538. * @param {Object} options options
  539. * @param {string | undefined} options.uniqueName unique name
  540. * @param {boolean | undefined} options.cssHeadDataCompression compress css head data
  541. * @param {Chunk} options.chunk chunk
  542. * @param {ChunkGraph} options.chunkGraph chunk graph
  543. * @param {CodeGenerationResults} options.codeGenerationResults code generation results
  544. * @param {CssModule[]} options.modules ordered css modules
  545. * @returns {Source} generated source
  546. */
  547. renderChunk({
  548. uniqueName,
  549. cssHeadDataCompression,
  550. chunk,
  551. chunkGraph,
  552. codeGenerationResults,
  553. modules
  554. }) {
  555. const source = new ConcatSource();
  556. /** @type {string[]} */
  557. const metaData = [];
  558. for (const module of modules) {
  559. try {
  560. const codeGenResult = codeGenerationResults.get(module, chunk.runtime);
  561. let moduleSource =
  562. /** @type {Source} */
  563. (
  564. codeGenResult.sources.get("css") ||
  565. codeGenResult.sources.get("css-import")
  566. );
  567. let inheritance = [[module.cssLayer, module.supports, module.media]];
  568. if (module.inheritance) {
  569. inheritance.push(...module.inheritance);
  570. }
  571. for (let i = 0; i < inheritance.length; i++) {
  572. const layer = inheritance[i][0];
  573. const supports = inheritance[i][1];
  574. const media = inheritance[i][2];
  575. if (media) {
  576. moduleSource = new ConcatSource(
  577. `@media ${media} {\n`,
  578. new PrefixSource("\t", moduleSource),
  579. "}\n"
  580. );
  581. }
  582. if (supports) {
  583. moduleSource = new ConcatSource(
  584. `@supports (${supports}) {\n`,
  585. new PrefixSource("\t", moduleSource),
  586. "}\n"
  587. );
  588. }
  589. // Layer can be anonymous
  590. if (layer !== undefined && layer !== null) {
  591. moduleSource = new ConcatSource(
  592. `@layer${layer ? ` ${layer}` : ""} {\n`,
  593. new PrefixSource("\t", moduleSource),
  594. "}\n"
  595. );
  596. }
  597. }
  598. if (moduleSource) {
  599. source.add(moduleSource);
  600. source.add("\n");
  601. }
  602. /** @type {Map<string, string> | undefined} */
  603. const exports =
  604. codeGenResult.data && codeGenResult.data.get("css-exports");
  605. let moduleId = chunkGraph.getModuleId(module) + "";
  606. // When `optimization.moduleIds` is `named` the module id is a path, so we need to normalize it between platforms
  607. if (typeof moduleId === "string") {
  608. moduleId = moduleId.replace(/\\/g, "/");
  609. }
  610. metaData.push(
  611. `${
  612. exports
  613. ? Array.from(
  614. exports,
  615. ([n, v]) => `${escapeCss(n)}:${escapeCss(v)}/`
  616. ).join("")
  617. : ""
  618. }${escapeCss(moduleId)}`
  619. );
  620. } catch (e) {
  621. /** @type {Error} */
  622. (e).message += `\nduring rendering of css ${module.identifier()}`;
  623. throw e;
  624. }
  625. }
  626. const metaDataStr = metaData.join(",");
  627. source.add(
  628. `head{--webpack-${escapeCss(
  629. (uniqueName ? uniqueName + "-" : "") + chunk.id,
  630. true
  631. )}:${cssHeadDataCompression ? LZWEncode(metaDataStr) : metaDataStr};}`
  632. );
  633. return source;
  634. }
  635. /**
  636. * @param {Chunk} chunk chunk
  637. * @param {OutputOptions} outputOptions output options
  638. * @returns {Chunk["cssFilenameTemplate"] | OutputOptions["cssFilename"] | OutputOptions["cssChunkFilename"]} used filename template
  639. */
  640. static getChunkFilenameTemplate(chunk, outputOptions) {
  641. if (chunk.cssFilenameTemplate) {
  642. return chunk.cssFilenameTemplate;
  643. } else if (chunk.canBeInitial()) {
  644. return outputOptions.cssFilename;
  645. } else {
  646. return outputOptions.cssChunkFilename;
  647. }
  648. }
  649. /**
  650. * @param {Chunk} chunk chunk
  651. * @param {ChunkGraph} chunkGraph chunk graph
  652. * @returns {boolean} true, when the chunk has css
  653. */
  654. static chunkHasCss(chunk, chunkGraph) {
  655. return (
  656. !!chunkGraph.getChunkModulesIterableBySourceType(chunk, "css") ||
  657. !!chunkGraph.getChunkModulesIterableBySourceType(chunk, "css-import")
  658. );
  659. }
  660. }
  661. module.exports = CssModulesPlugin;