CssGenerator.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Sergey Melyukov @smelukov
  4. */
  5. "use strict";
  6. const {
  7. ConcatSource,
  8. RawSource,
  9. ReplaceSource,
  10. SourceMapSource
  11. } = require("webpack-sources");
  12. const { UsageState } = require("../ExportsInfo");
  13. const Generator = require("../Generator");
  14. const InitFragment = require("../InitFragment");
  15. const {
  16. CSS_TYPE,
  17. CSS_TYPES,
  18. JAVASCRIPT_AND_CSS_TYPES,
  19. JAVASCRIPT_TYPE,
  20. JAVASCRIPT_TYPES
  21. } = require("../ModuleSourceTypeConstants");
  22. const RuntimeGlobals = require("../RuntimeGlobals");
  23. const Template = require("../Template");
  24. const CssImportDependency = require("../dependencies/CssImportDependency");
  25. const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency");
  26. const memoize = require("../util/memoize");
  27. /** @typedef {import("webpack-sources").Source} Source */
  28. /** @typedef {import("../../declarations/WebpackOptions").CssModuleGeneratorOptions} CssModuleGeneratorOptions */
  29. /** @typedef {import("../Compilation").DependencyConstructor} DependencyConstructor */
  30. /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
  31. /** @typedef {import("../Dependency")} Dependency */
  32. /** @typedef {import("../DependencyTemplate").CssData} CssData */
  33. /** @typedef {import("../DependencyTemplate").CssDependencyTemplateContext} DependencyTemplateContext */
  34. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  35. /** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
  36. /** @typedef {import("../Module").BuildInfo} BuildInfo */
  37. /** @typedef {import("../Module").BuildMeta} BuildMeta */
  38. /** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
  39. /** @typedef {import("../Module").SourceType} SourceType */
  40. /** @typedef {import("../Module").SourceTypes} SourceTypes */
  41. /** @typedef {import("../ModuleGraph")} ModuleGraph */
  42. /** @typedef {import("../NormalModule")} NormalModule */
  43. /** @typedef {import("../util/Hash")} Hash */
  44. /** @typedef {import("./CssModulesPlugin").ModuleFactoryCacheEntry} ModuleFactoryCacheEntry */
  45. /** @typedef {import("../CssModule")} CssModule */
  46. /** @typedef {import("../Compilation")} Compilation */
  47. /** @typedef {import("../Module").RuntimeRequirements} RuntimeRequirements */
  48. /** @typedef {import("../../declarations/WebpackOptions").CssParserExportType} CssParserExportType */
  49. const getPropertyName = memoize(() => require("../util/property"));
  50. const getCssModulesPlugin = memoize(() => require("./CssModulesPlugin"));
  51. class CssGenerator extends Generator {
  52. /**
  53. * Creates an instance of CssGenerator.
  54. * @param {CssModuleGeneratorOptions} options options
  55. * @param {ModuleGraph} moduleGraph the module graph
  56. */
  57. constructor(options, moduleGraph) {
  58. super();
  59. this.options = options;
  60. this._exportsOnly = options.exportsOnly;
  61. this._esModule = options.esModule;
  62. this._moduleGraph = moduleGraph;
  63. /** @type {WeakMap<Source, ModuleFactoryCacheEntry>} */
  64. this._moduleFactoryCache = new WeakMap();
  65. }
  66. /**
  67. * Returns the reason this module cannot be concatenated, when one exists.
  68. * @param {NormalModule} module module for which the bailout reason should be determined
  69. * @param {ConcatenationBailoutReasonContext} context context
  70. * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
  71. */
  72. getConcatenationBailoutReason(module, context) {
  73. if (!this._esModule) {
  74. return "Module is not an ECMAScript module";
  75. }
  76. return undefined;
  77. }
  78. /**
  79. * Generate JavaScript code that requires and concatenates all CSS imports
  80. * @param {NormalModule} module the module to generate CSS text for
  81. * @param {GenerateContext} generateContext the generate context
  82. * @param {boolean} getDefaultExport whether to get the default export
  83. * @returns {{ expr: string, type: CssParserExportType }[]} JavaScript code that concatenates all imported CSS
  84. */
  85. _generateImportCode(module, generateContext, getDefaultExport = true) {
  86. const moduleGraph = generateContext.moduleGraph;
  87. /** @type {{ expr: string, type: CssParserExportType }[]} */
  88. const parts = [];
  89. // Iterate through module.dependencies to maintain source order
  90. for (const dep of module.dependencies) {
  91. if (dep instanceof CssImportDependency) {
  92. const depModule = /** @type {CssModule} */ (moduleGraph.getModule(dep));
  93. const importVar = generateContext.runtimeTemplate.moduleExports({
  94. module: depModule,
  95. chunkGraph: generateContext.chunkGraph,
  96. request: depModule.userRequest,
  97. weak: false,
  98. runtimeRequirements: generateContext.runtimeRequirements
  99. });
  100. if (getDefaultExport) {
  101. generateContext.runtimeRequirements.add(
  102. RuntimeGlobals.compatGetDefaultExport
  103. );
  104. parts.push({
  105. expr: `(${RuntimeGlobals.compatGetDefaultExport}(${importVar})() || "")`,
  106. type: /** @type {CssParserExportType} */ (depModule.exportType)
  107. });
  108. } else {
  109. parts.push({
  110. expr: importVar,
  111. type: /** @type {CssParserExportType} */ (depModule.exportType)
  112. });
  113. }
  114. }
  115. }
  116. return parts;
  117. }
  118. /**
  119. * Generate CSS source for the current module
  120. * @param {NormalModule} module the module to generate CSS source for
  121. * @param {GenerateContext} generateContext the generate context
  122. * @returns {Source | null} the CSS source
  123. */
  124. _generateContentSource(module, generateContext) {
  125. const moduleSourceContent = /** @type {Source} */ (
  126. this.generate(module, {
  127. ...generateContext,
  128. type: CSS_TYPE
  129. })
  130. );
  131. if (!moduleSourceContent) {
  132. return null;
  133. }
  134. const compilation = generateContext.runtimeTemplate.compilation;
  135. // For non-link exportTypes (style, text, css-style-sheet), url() in the CSS
  136. // is resolved relative to the document URL (for <style> tags and CSSStyleSheet),
  137. // not relative to any output file. Use empty undoPath so urls are relative to
  138. // the output root.
  139. const undoPath = "";
  140. const CssModulesPlugin = getCssModulesPlugin();
  141. const hooks = CssModulesPlugin.getCompilationHooks(compilation);
  142. return CssModulesPlugin.renderModule(
  143. /** @type {CssModule} */ (module),
  144. {
  145. undoPath,
  146. moduleSourceContent,
  147. moduleFactoryCache: this._moduleFactoryCache,
  148. runtimeTemplate: generateContext.runtimeTemplate
  149. },
  150. hooks
  151. );
  152. }
  153. /**
  154. * Convert a CSS Source to a JS string literal Source, preserving source map.
  155. * Wraps the CSS content with JSON.stringify so it can be embedded in JS code.
  156. * @param {Source} cssSource the CSS source
  157. * @param {NormalModule} module the module
  158. * @returns {Source} a Source representing a JS string literal
  159. */
  160. _cssSourceToJsStringLiteral(cssSource, module) {
  161. const { source, map } = cssSource.sourceAndMap();
  162. const content = /** @type {string} */ (source);
  163. const escaped = JSON.stringify(content);
  164. if (map) {
  165. return new SourceMapSource(escaped, module.identifier(), map, content);
  166. }
  167. return new RawSource(escaped);
  168. }
  169. /**
  170. * Processes the provided module.
  171. * @param {NormalModule} module the current module
  172. * @param {Dependency} dependency the dependency to generate
  173. * @param {InitFragment<GenerateContext>[]} initFragments mutable list of init fragments
  174. * @param {ReplaceSource} source the current replace source which can be modified
  175. * @param {GenerateContext & { cssData: CssData }} generateContext the render context
  176. * @returns {void}
  177. */
  178. sourceDependency(module, dependency, initFragments, source, generateContext) {
  179. const constructor =
  180. /** @type {DependencyConstructor} */
  181. (dependency.constructor);
  182. const template = generateContext.dependencyTemplates.get(constructor);
  183. if (!template) {
  184. throw new Error(
  185. `No template for dependency: ${dependency.constructor.name}`
  186. );
  187. }
  188. /** @type {DependencyTemplateContext} */
  189. /** @type {InitFragment<GenerateContext>[] | undefined} */
  190. let chunkInitFragments;
  191. /** @type {DependencyTemplateContext} */
  192. const templateContext = {
  193. runtimeTemplate: generateContext.runtimeTemplate,
  194. dependencyTemplates: generateContext.dependencyTemplates,
  195. moduleGraph: generateContext.moduleGraph,
  196. chunkGraph: generateContext.chunkGraph,
  197. module,
  198. runtime: generateContext.runtime,
  199. runtimeRequirements: generateContext.runtimeRequirements,
  200. concatenationScope: generateContext.concatenationScope,
  201. codeGenerationResults:
  202. /** @type {CodeGenerationResults} */
  203. (generateContext.codeGenerationResults),
  204. initFragments,
  205. cssData: generateContext.cssData,
  206. type: generateContext.type,
  207. get chunkInitFragments() {
  208. if (!chunkInitFragments) {
  209. const data =
  210. /** @type {NonNullable<GenerateContext["getData"]>} */
  211. (generateContext.getData)();
  212. chunkInitFragments = data.get("chunkInitFragments");
  213. if (!chunkInitFragments) {
  214. chunkInitFragments = [];
  215. data.set("chunkInitFragments", chunkInitFragments);
  216. }
  217. }
  218. return chunkInitFragments;
  219. }
  220. };
  221. template.apply(dependency, source, templateContext);
  222. }
  223. /**
  224. * Processes the provided module.
  225. * @param {NormalModule} module the module to generate
  226. * @param {InitFragment<GenerateContext>[]} initFragments mutable list of init fragments
  227. * @param {ReplaceSource} source the current replace source which can be modified
  228. * @param {GenerateContext & { cssData: CssData }} generateContext the generateContext
  229. * @returns {void}
  230. */
  231. sourceModule(module, initFragments, source, generateContext) {
  232. for (const dependency of module.dependencies) {
  233. this.sourceDependency(
  234. module,
  235. dependency,
  236. initFragments,
  237. source,
  238. generateContext
  239. );
  240. }
  241. if (module.presentationalDependencies !== undefined) {
  242. for (const dependency of module.presentationalDependencies) {
  243. this.sourceDependency(
  244. module,
  245. dependency,
  246. initFragments,
  247. source,
  248. generateContext
  249. );
  250. }
  251. }
  252. }
  253. /**
  254. * Generates generated code for this runtime module.
  255. * @param {NormalModule} module module for which the code should be generated
  256. * @param {GenerateContext} generateContext context for generate
  257. * @returns {Source | null} generated code
  258. */
  259. generate(module, generateContext) {
  260. const exportType = /** @type {CssModule} */ (module).exportType || "link";
  261. const source =
  262. generateContext.type === JAVASCRIPT_TYPE && exportType === "link"
  263. ? new ReplaceSource(new RawSource(""))
  264. : new ReplaceSource(/** @type {Source} */ (module.originalSource()));
  265. /** @type {InitFragment<GenerateContext>[]} */
  266. const initFragments = [];
  267. /** @type {CssData} */
  268. const cssData = {
  269. esModule: /** @type {boolean} */ (this._esModule),
  270. exports: new Map()
  271. };
  272. this.sourceModule(module, initFragments, source, {
  273. ...generateContext,
  274. cssData
  275. });
  276. switch (generateContext.type) {
  277. case JAVASCRIPT_TYPE: {
  278. const generateContentCode = () => {
  279. switch (exportType) {
  280. case "style": {
  281. const cssSource = this._generateContentSource(
  282. module,
  283. generateContext
  284. );
  285. if (!cssSource) return "";
  286. const moduleId = generateContext.chunkGraph.getModuleId(module);
  287. generateContext.runtimeRequirements.add(
  288. RuntimeGlobals.cssInjectStyle
  289. );
  290. return new ConcatSource(
  291. `${RuntimeGlobals.cssInjectStyle}(${JSON.stringify(moduleId || "")}, `,
  292. this._cssSourceToJsStringLiteral(cssSource, module),
  293. ");"
  294. );
  295. }
  296. default:
  297. return "";
  298. }
  299. };
  300. const generateImportCode = () => {
  301. switch (exportType) {
  302. case "style": {
  303. return this._generateImportCode(module, generateContext, false)
  304. .map((part) => `${part.expr};`)
  305. .join("\n");
  306. }
  307. default:
  308. return "";
  309. }
  310. };
  311. const generateExportCode = () => {
  312. /** @returns {Source} generated CSS text as JS expression */
  313. const generateCssText = () => {
  314. const importCode = this._generateImportCode(
  315. module,
  316. generateContext
  317. );
  318. const cssSource = this._generateContentSource(
  319. module,
  320. generateContext
  321. );
  322. let jsLiteral = cssSource
  323. ? this._cssSourceToJsStringLiteral(cssSource, module)
  324. : new RawSource('""');
  325. if (importCode.length > 0) {
  326. if (
  327. exportType === "css-style-sheet" ||
  328. importCode.some((part) => part.type !== exportType)
  329. ) {
  330. generateContext.runtimeRequirements.add(
  331. RuntimeGlobals.cssMergeStyleSheets
  332. );
  333. const args = importCode.map((part) => part.expr);
  334. jsLiteral = new ConcatSource(
  335. `${RuntimeGlobals.cssMergeStyleSheets}([${args.join(", ")}, `,
  336. jsLiteral,
  337. "])"
  338. );
  339. } else {
  340. jsLiteral = new ConcatSource(
  341. `${generateContext.runtimeTemplate.concatenation(
  342. ...importCode
  343. )} + `,
  344. jsLiteral
  345. );
  346. }
  347. }
  348. if (
  349. exportType === "css-style-sheet" &&
  350. typeof (/** @type {BuildInfo} */ (module.buildInfo).charset) !==
  351. "undefined"
  352. ) {
  353. jsLiteral = new ConcatSource(
  354. `'@charset "${/** @type {BuildInfo} */ (module.buildInfo).charset}";\\n' + `,
  355. jsLiteral
  356. );
  357. }
  358. return jsLiteral;
  359. };
  360. /**
  361. * Generates js default export.
  362. * @returns {Source | null} the default export
  363. */
  364. const generateJSDefaultExport = () => {
  365. switch (exportType) {
  366. case "text": {
  367. return generateCssText();
  368. }
  369. case "css-style-sheet": {
  370. const constOrVar =
  371. generateContext.runtimeTemplate.renderConst();
  372. const cssText = generateCssText();
  373. const fnPrefix =
  374. generateContext.runtimeTemplate.supportsArrowFunction()
  375. ? "() => {\n"
  376. : "function() {\n";
  377. const body =
  378. `${constOrVar} sheet = new CSSStyleSheet();\n` +
  379. "sheet.replaceSync(cssText);\n" +
  380. "return sheet;\n";
  381. return new ConcatSource(
  382. `(${fnPrefix}${constOrVar} cssText = `,
  383. cssText,
  384. `;\n${body}})()`
  385. );
  386. }
  387. default:
  388. return null;
  389. }
  390. };
  391. const isCSSModule = /** @type {BuildMeta} */ (module.buildMeta)
  392. .isCSSModule;
  393. /** @type {Source | null} */
  394. const defaultExport = generateJSDefaultExport();
  395. /** @type {BuildInfo} */
  396. (module.buildInfo).cssData = cssData;
  397. // Required for HMR
  398. if (module.hot) {
  399. generateContext.runtimeRequirements.add(RuntimeGlobals.module);
  400. }
  401. if (!defaultExport && cssData.exports.size === 0 && !isCSSModule) {
  402. return new RawSource("");
  403. }
  404. if (generateContext.concatenationScope) {
  405. const source = new ConcatSource();
  406. /** @type {Set<string>} */
  407. const usedIdentifiers = new Set();
  408. const { RESERVED_IDENTIFIER } = getPropertyName();
  409. if (defaultExport) {
  410. const usedName = generateContext.moduleGraph
  411. .getExportInfo(module, "default")
  412. .getUsedName("default", generateContext.runtime);
  413. if (usedName) {
  414. let identifier = Template.toIdentifier(usedName);
  415. if (RESERVED_IDENTIFIER.has(identifier)) {
  416. identifier = `_${identifier}`;
  417. }
  418. usedIdentifiers.add(identifier);
  419. generateContext.concatenationScope.registerExport(
  420. "default",
  421. identifier
  422. );
  423. source.add(
  424. `${generateContext.runtimeTemplate.renderConst()} ${identifier} = `
  425. );
  426. source.add(defaultExport);
  427. source.add(";\n");
  428. }
  429. }
  430. for (const [name, v] of cssData.exports) {
  431. const usedName = generateContext.moduleGraph
  432. .getExportInfo(module, name)
  433. .getUsedName(name, generateContext.runtime);
  434. if (!usedName) {
  435. continue;
  436. }
  437. let identifier = Template.toIdentifier(usedName);
  438. if (RESERVED_IDENTIFIER.has(identifier)) {
  439. identifier = `_${identifier}`;
  440. }
  441. let i = 0;
  442. while (usedIdentifiers.has(identifier)) {
  443. identifier = Template.toIdentifier(name + i);
  444. i += 1;
  445. }
  446. usedIdentifiers.add(identifier);
  447. generateContext.concatenationScope.registerExport(
  448. name,
  449. identifier
  450. );
  451. source.add(
  452. `${generateContext.runtimeTemplate.renderConst()} ${identifier} = ${JSON.stringify(v)};\n`
  453. );
  454. }
  455. return source;
  456. }
  457. const needNsObj =
  458. this._esModule &&
  459. generateContext.moduleGraph
  460. .getExportsInfo(module)
  461. .otherExportsInfo.getUsed(generateContext.runtime) !==
  462. UsageState.Unused;
  463. if (needNsObj) {
  464. generateContext.runtimeRequirements.add(
  465. RuntimeGlobals.makeNamespaceObject
  466. );
  467. }
  468. // Should be after `concatenationScope` to allow module inlining
  469. generateContext.runtimeRequirements.add(RuntimeGlobals.module);
  470. if (!isCSSModule && !needNsObj) {
  471. return new ConcatSource(
  472. `${module.moduleArgument}.exports = `,
  473. /** @type {Source} */ (defaultExport)
  474. );
  475. }
  476. const result = new ConcatSource();
  477. result.add(
  478. `${needNsObj ? `${RuntimeGlobals.makeNamespaceObject}(` : ""}${
  479. module.moduleArgument
  480. }.exports = {\n`
  481. );
  482. if (defaultExport) {
  483. result.add('\t"default": ');
  484. result.add(defaultExport);
  485. if (cssData.exports.size > 0) {
  486. result.add(",\n");
  487. }
  488. }
  489. /** @type {string[]} */
  490. const exportEntries = [];
  491. for (const [name, v] of cssData.exports) {
  492. exportEntries.push(
  493. `\t${JSON.stringify(name)}: ${JSON.stringify(v)}`
  494. );
  495. }
  496. if (exportEntries.length > 0) {
  497. result.add(exportEntries.join(",\n"));
  498. }
  499. result.add(`\n}${needNsObj ? ")" : ""};`);
  500. return result;
  501. };
  502. const codeParts = this._exportsOnly
  503. ? [generateExportCode()]
  504. : [generateImportCode(), generateContentCode(), generateExportCode()];
  505. const source = new ConcatSource();
  506. for (const part of codeParts) {
  507. if (part) {
  508. source.add(part);
  509. source.add("\n");
  510. }
  511. }
  512. return source;
  513. }
  514. case CSS_TYPE: {
  515. if (
  516. !(
  517. this._exportsOnly ||
  518. /** @type {boolean} */ (exportType && exportType !== "link")
  519. )
  520. ) {
  521. generateContext.runtimeRequirements.add(RuntimeGlobals.hasCssModules);
  522. }
  523. return InitFragment.addToSource(source, initFragments, generateContext);
  524. }
  525. default:
  526. return null;
  527. }
  528. }
  529. /**
  530. * Generates fallback output for the provided error condition.
  531. * @param {Error} error the error
  532. * @param {NormalModule} module module for which the code should be generated
  533. * @param {GenerateContext} generateContext context for generate
  534. * @returns {Source | null} generated code
  535. */
  536. generateError(error, module, generateContext) {
  537. switch (generateContext.type) {
  538. case JAVASCRIPT_TYPE: {
  539. return new RawSource(
  540. `throw new Error(${JSON.stringify(error.message)});`
  541. );
  542. }
  543. case CSS_TYPE: {
  544. return new RawSource(`/**\n ${error.message} \n**/`);
  545. }
  546. default:
  547. return null;
  548. }
  549. }
  550. /**
  551. * Returns the source types available for this module.
  552. * @param {NormalModule} module fresh module
  553. * @returns {SourceTypes} available types (do not mutate)
  554. */
  555. getTypes(module) {
  556. const exportType = /** @type {CssModule} */ (module).exportType || "link";
  557. if (exportType === "style") {
  558. return JAVASCRIPT_TYPES;
  559. }
  560. const sourceTypes = new Set();
  561. const connections = this._moduleGraph.getIncomingConnections(module);
  562. for (const connection of connections) {
  563. if (
  564. exportType === "link" &&
  565. connection.dependency instanceof CssImportDependency
  566. ) {
  567. continue;
  568. }
  569. // when no hmr required, css module js output contains no sideEffects at all
  570. // js sideeffect connection doesn't require js type output
  571. if (connection.dependency instanceof HarmonyImportSideEffectDependency) {
  572. continue;
  573. }
  574. if (!connection.originModule) {
  575. continue;
  576. }
  577. if (connection.originModule.type.split("/")[0] !== CSS_TYPE) {
  578. sourceTypes.add(JAVASCRIPT_TYPE);
  579. } else {
  580. const originModule = /** @type {CssModule} */ connection.originModule;
  581. const originExportType = /** @type {CssModule} */ (originModule)
  582. .exportType;
  583. if (
  584. /** @type {boolean} */ (
  585. originExportType && originExportType !== "link"
  586. )
  587. ) {
  588. sourceTypes.add(JAVASCRIPT_TYPE);
  589. }
  590. }
  591. }
  592. if (
  593. this._exportsOnly ||
  594. /** @type {boolean} */ (exportType && exportType !== "link")
  595. ) {
  596. if (sourceTypes.has(JAVASCRIPT_TYPE)) {
  597. return JAVASCRIPT_TYPES;
  598. }
  599. return new Set();
  600. }
  601. if (sourceTypes.has(JAVASCRIPT_TYPE)) {
  602. return JAVASCRIPT_AND_CSS_TYPES;
  603. }
  604. return CSS_TYPES;
  605. }
  606. /**
  607. * Returns the estimated size for the requested source type.
  608. * @param {NormalModule} module the module
  609. * @param {SourceType=} type source type
  610. * @returns {number} estimate size of the module
  611. */
  612. getSize(module, type) {
  613. switch (type) {
  614. case JAVASCRIPT_TYPE: {
  615. const cssData = /** @type {BuildInfo} */ (module.buildInfo).cssData;
  616. if (!cssData) {
  617. return 42;
  618. }
  619. if (cssData.exports.size === 0) {
  620. if (/** @type {BuildMeta} */ (module.buildMeta).isCSSModule) {
  621. return 42;
  622. }
  623. return 0;
  624. }
  625. const exports = cssData.exports;
  626. /** @type {Record<string, string>} */
  627. const exportsObj = {};
  628. for (const [key, value] of exports) {
  629. exportsObj[key] = value;
  630. }
  631. const stringifiedExports = JSON.stringify(exportsObj);
  632. return stringifiedExports.length + 42;
  633. }
  634. case CSS_TYPE: {
  635. const originalSource = module.originalSource();
  636. if (!originalSource) {
  637. return 0;
  638. }
  639. return originalSource.size();
  640. }
  641. default:
  642. return 0;
  643. }
  644. }
  645. /**
  646. * Updates the hash with the data contributed by this instance.
  647. * @param {Hash} hash hash that will be modified
  648. * @param {UpdateHashContext} updateHashContext context for updating hash
  649. */
  650. updateHash(hash, { module }) {
  651. hash.update(/** @type {boolean} */ (this._esModule).toString());
  652. hash.update(/** @type {boolean} */ (this._exportsOnly).toString());
  653. }
  654. }
  655. module.exports = CssGenerator;