AssetGenerator.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Sergey Melyukov @smelukov
  4. */
  5. "use strict";
  6. const path = require("path");
  7. const { RawSource } = require("webpack-sources");
  8. const ConcatenationScope = require("../ConcatenationScope");
  9. const Generator = require("../Generator");
  10. const {
  11. ASSET_AND_CSS_URL_TYPES,
  12. ASSET_AND_JAVASCRIPT_AND_CSS_URL_TYPES,
  13. ASSET_AND_JAVASCRIPT_TYPES,
  14. ASSET_TYPES,
  15. CSS_TYPE,
  16. CSS_URL_TYPE,
  17. CSS_URL_TYPES,
  18. JAVASCRIPT_AND_CSS_URL_TYPES,
  19. JAVASCRIPT_TYPE,
  20. JAVASCRIPT_TYPES,
  21. NO_TYPES
  22. } = require("../ModuleSourceTypeConstants");
  23. const { ASSET_MODULE_TYPE } = require("../ModuleTypeConstants");
  24. const RuntimeGlobals = require("../RuntimeGlobals");
  25. const CssUrlDependency = require("../dependencies/CssUrlDependency");
  26. const createHash = require("../util/createHash");
  27. const { makePathsRelative } = require("../util/identifier");
  28. const memoize = require("../util/memoize");
  29. const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
  30. const getMimeTypes = memoize(() => require("mime-types"));
  31. /** @typedef {import("webpack-sources").Source} Source */
  32. /** @typedef {import("../../declarations/WebpackOptions").AssetGeneratorDataUrlOptions} AssetGeneratorDataUrlOptions */
  33. /** @typedef {import("../../declarations/WebpackOptions").AssetGeneratorOptions} AssetGeneratorOptions */
  34. /** @typedef {import("../../declarations/WebpackOptions").AssetModuleFilename} AssetModuleFilename */
  35. /** @typedef {import("../../declarations/WebpackOptions").AssetModuleOutputPath} AssetModuleOutputPath */
  36. /** @typedef {import("../../declarations/WebpackOptions").AssetResourceGeneratorOptions} AssetResourceGeneratorOptions */
  37. /** @typedef {import("../../declarations/WebpackOptions").RawPublicPath} RawPublicPath */
  38. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  39. /** @typedef {import("../Compilation").AssetInfo} AssetInfo */
  40. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  41. /** @typedef {import("../Generator").UpdateHashContext} UpdateHashContext */
  42. /** @typedef {import("../Module").NameForCondition} NameForCondition */
  43. /** @typedef {import("../Module").BuildInfo} BuildInfo */
  44. /** @typedef {import("../Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
  45. /** @typedef {import("../Module").SourceType} SourceType */
  46. /** @typedef {import("../Module").SourceTypes} SourceTypes */
  47. /** @typedef {import("../ModuleGraph")} ModuleGraph */
  48. /** @typedef {import("../NormalModule")} NormalModule */
  49. /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
  50. /** @typedef {import("../util/Hash")} Hash */
  51. /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
  52. /**
  53. * @template T
  54. * @template U
  55. * @param {null | string | T[] | Set<T> | undefined} a a
  56. * @param {null | string | U[] | Set<U> | undefined} b b
  57. * @returns {T[] & U[]} array
  58. */
  59. const mergeMaybeArrays = (a, b) => {
  60. const set = new Set();
  61. if (Array.isArray(a)) for (const item of a) set.add(item);
  62. else set.add(a);
  63. if (Array.isArray(b)) for (const item of b) set.add(item);
  64. else set.add(b);
  65. return [...set];
  66. };
  67. /**
  68. * @param {AssetInfo} a a
  69. * @param {AssetInfo} b b
  70. * @returns {AssetInfo} object
  71. */
  72. const mergeAssetInfo = (a, b) => {
  73. /** @type {AssetInfo} */
  74. const result = { ...a, ...b };
  75. for (const key of Object.keys(a)) {
  76. if (key in b) {
  77. if (a[key] === b[key]) continue;
  78. switch (key) {
  79. case "fullhash":
  80. case "chunkhash":
  81. case "modulehash":
  82. case "contenthash":
  83. result[key] = mergeMaybeArrays(a[key], b[key]);
  84. break;
  85. case "immutable":
  86. case "development":
  87. case "hotModuleReplacement":
  88. case "javascriptModule":
  89. result[key] = a[key] || b[key];
  90. break;
  91. case "related":
  92. result[key] = mergeRelatedInfo(
  93. /** @type {NonNullable<AssetInfo["related"]>} */
  94. (a[key]),
  95. /** @type {NonNullable<AssetInfo["related"]>} */
  96. (b[key])
  97. );
  98. break;
  99. default:
  100. throw new Error(`Can't handle conflicting asset info for ${key}`);
  101. }
  102. }
  103. }
  104. return result;
  105. };
  106. /**
  107. * @param {NonNullable<AssetInfo["related"]>} a a
  108. * @param {NonNullable<AssetInfo["related"]>} b b
  109. * @returns {NonNullable<AssetInfo["related"]>} object
  110. */
  111. const mergeRelatedInfo = (a, b) => {
  112. const result = { ...a, ...b };
  113. for (const key of Object.keys(a)) {
  114. if (key in b) {
  115. if (a[key] === b[key]) continue;
  116. result[key] = mergeMaybeArrays(a[key], b[key]);
  117. }
  118. }
  119. return result;
  120. };
  121. /**
  122. * @param {"base64" | false} encoding encoding
  123. * @param {Source} source source
  124. * @returns {string} encoded data
  125. */
  126. const encodeDataUri = (encoding, source) => {
  127. /** @type {string | undefined} */
  128. let encodedContent;
  129. switch (encoding) {
  130. case "base64": {
  131. encodedContent = source.buffer().toString("base64");
  132. break;
  133. }
  134. case false: {
  135. const content = source.source();
  136. if (typeof content !== "string") {
  137. encodedContent = content.toString("utf8");
  138. }
  139. encodedContent = encodeURIComponent(
  140. /** @type {string} */
  141. (encodedContent)
  142. ).replace(
  143. /[!'()*]/g,
  144. (character) =>
  145. `%${/** @type {number} */ (character.codePointAt(0)).toString(16)}`
  146. );
  147. break;
  148. }
  149. default:
  150. throw new Error(`Unsupported encoding '${encoding}'`);
  151. }
  152. return encodedContent;
  153. };
  154. /**
  155. * @param {"base64" | false} encoding encoding
  156. * @param {string} content content
  157. * @returns {Buffer} decoded content
  158. */
  159. const decodeDataUriContent = (encoding, content) => {
  160. const isBase64 = encoding === "base64";
  161. if (isBase64) {
  162. return Buffer.from(content, "base64");
  163. }
  164. // If we can't decode return the original body
  165. try {
  166. return Buffer.from(decodeURIComponent(content), "ascii");
  167. } catch (_) {
  168. return Buffer.from(content, "ascii");
  169. }
  170. };
  171. const DEFAULT_ENCODING = "base64";
  172. class AssetGenerator extends Generator {
  173. /**
  174. * @param {ModuleGraph} moduleGraph the module graph
  175. * @param {AssetGeneratorOptions["dataUrl"]=} dataUrlOptions the options for the data url
  176. * @param {AssetModuleFilename=} filename override for output.assetModuleFilename
  177. * @param {RawPublicPath=} publicPath override for output.assetModulePublicPath
  178. * @param {AssetModuleOutputPath=} outputPath the output path for the emitted file which is not included in the runtime import
  179. * @param {boolean=} emit generate output asset
  180. */
  181. constructor(
  182. moduleGraph,
  183. dataUrlOptions,
  184. filename,
  185. publicPath,
  186. outputPath,
  187. emit
  188. ) {
  189. super();
  190. this.dataUrlOptions = dataUrlOptions;
  191. this.filename = filename;
  192. this.publicPath = publicPath;
  193. this.outputPath = outputPath;
  194. this.emit = emit;
  195. this._moduleGraph = moduleGraph;
  196. }
  197. /**
  198. * @param {NormalModule} module module
  199. * @param {RuntimeTemplate} runtimeTemplate runtime template
  200. * @returns {string} source file name
  201. */
  202. static getSourceFileName(module, runtimeTemplate) {
  203. return makePathsRelative(
  204. runtimeTemplate.compilation.compiler.context,
  205. /** @type {string} */
  206. (module.getResource()),
  207. runtimeTemplate.compilation.compiler.root
  208. ).replace(/^\.\//, "");
  209. }
  210. /**
  211. * @param {NormalModule} module module
  212. * @param {RuntimeTemplate} runtimeTemplate runtime template
  213. * @returns {[string, string]} return full hash and non-numeric full hash
  214. */
  215. static getFullContentHash(module, runtimeTemplate) {
  216. const hash = createHash(runtimeTemplate.outputOptions.hashFunction);
  217. if (runtimeTemplate.outputOptions.hashSalt) {
  218. hash.update(runtimeTemplate.outputOptions.hashSalt);
  219. }
  220. const source = module.originalSource();
  221. if (source) {
  222. hash.update(source.buffer());
  223. }
  224. if (module.error) {
  225. hash.update(module.error.toString());
  226. }
  227. const fullContentHash = hash.digest(
  228. runtimeTemplate.outputOptions.hashDigest
  229. );
  230. const contentHash = nonNumericOnlyHash(
  231. fullContentHash,
  232. runtimeTemplate.outputOptions.hashDigestLength
  233. );
  234. return [fullContentHash, contentHash];
  235. }
  236. /**
  237. * @param {NormalModule} module module for which the code should be generated
  238. * @param {Pick<AssetResourceGeneratorOptions, "filename" | "outputPath">} generatorOptions generator options
  239. * @param {{ runtime: RuntimeSpec, runtimeTemplate: RuntimeTemplate, chunkGraph: ChunkGraph }} generateContext context for generate
  240. * @param {string} contentHash the content hash
  241. * @returns {{ filename: string, originalFilename: string, assetInfo: AssetInfo }} info
  242. */
  243. static getFilenameWithInfo(
  244. module,
  245. generatorOptions,
  246. { runtime, runtimeTemplate, chunkGraph },
  247. contentHash
  248. ) {
  249. const assetModuleFilename =
  250. generatorOptions.filename ||
  251. runtimeTemplate.outputOptions.assetModuleFilename;
  252. const sourceFilename = AssetGenerator.getSourceFileName(
  253. module,
  254. runtimeTemplate
  255. );
  256. let { path: filename, info: assetInfo } =
  257. runtimeTemplate.compilation.getAssetPathWithInfo(assetModuleFilename, {
  258. module,
  259. runtime,
  260. filename: sourceFilename,
  261. chunkGraph,
  262. contentHash
  263. });
  264. const originalFilename = filename;
  265. if (generatorOptions.outputPath) {
  266. const { path: outputPath, info } =
  267. runtimeTemplate.compilation.getAssetPathWithInfo(
  268. generatorOptions.outputPath,
  269. {
  270. module,
  271. runtime,
  272. filename: sourceFilename,
  273. chunkGraph,
  274. contentHash
  275. }
  276. );
  277. filename = path.posix.join(outputPath, filename);
  278. assetInfo = mergeAssetInfo(assetInfo, info);
  279. }
  280. return { originalFilename, filename, assetInfo };
  281. }
  282. /**
  283. * @param {NormalModule} module module for which the code should be generated
  284. * @param {Pick<AssetResourceGeneratorOptions, "publicPath">} generatorOptions generator options
  285. * @param {GenerateContext} generateContext context for generate
  286. * @param {string} filename the filename
  287. * @param {AssetInfo} assetInfo the asset info
  288. * @param {string} contentHash the content hash
  289. * @returns {{ assetPath: string, assetInfo: AssetInfo }} asset path and info
  290. */
  291. static getAssetPathWithInfo(
  292. module,
  293. generatorOptions,
  294. { runtime, runtimeTemplate, type, chunkGraph, runtimeRequirements },
  295. filename,
  296. assetInfo,
  297. contentHash
  298. ) {
  299. const sourceFilename = AssetGenerator.getSourceFileName(
  300. module,
  301. runtimeTemplate
  302. );
  303. let assetPath;
  304. if (generatorOptions.publicPath !== undefined && type === JAVASCRIPT_TYPE) {
  305. const { path, info } = runtimeTemplate.compilation.getAssetPathWithInfo(
  306. generatorOptions.publicPath,
  307. {
  308. module,
  309. runtime,
  310. filename: sourceFilename,
  311. chunkGraph,
  312. contentHash
  313. }
  314. );
  315. assetInfo = mergeAssetInfo(assetInfo, info);
  316. assetPath = JSON.stringify(path + filename);
  317. } else if (
  318. generatorOptions.publicPath !== undefined &&
  319. type === CSS_URL_TYPE
  320. ) {
  321. const { path, info } = runtimeTemplate.compilation.getAssetPathWithInfo(
  322. generatorOptions.publicPath,
  323. {
  324. module,
  325. runtime,
  326. filename: sourceFilename,
  327. chunkGraph,
  328. contentHash
  329. }
  330. );
  331. assetInfo = mergeAssetInfo(assetInfo, info);
  332. assetPath = path + filename;
  333. } else if (type === JAVASCRIPT_TYPE) {
  334. // add __webpack_require__.p
  335. runtimeRequirements.add(RuntimeGlobals.publicPath);
  336. assetPath = runtimeTemplate.concatenation(
  337. { expr: RuntimeGlobals.publicPath },
  338. filename
  339. );
  340. } else if (type === CSS_URL_TYPE) {
  341. const compilation = runtimeTemplate.compilation;
  342. const path =
  343. compilation.outputOptions.publicPath === "auto"
  344. ? CssUrlDependency.PUBLIC_PATH_AUTO
  345. : compilation.getAssetPath(compilation.outputOptions.publicPath, {
  346. hash: compilation.hash
  347. });
  348. assetPath = path + filename;
  349. }
  350. return {
  351. assetPath: /** @type {string} */ (assetPath),
  352. assetInfo: { sourceFilename, ...assetInfo }
  353. };
  354. }
  355. /**
  356. * @param {NormalModule} module module for which the bailout reason should be determined
  357. * @param {ConcatenationBailoutReasonContext} context context
  358. * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
  359. */
  360. getConcatenationBailoutReason(module, context) {
  361. return undefined;
  362. }
  363. /**
  364. * @param {NormalModule} module module
  365. * @returns {string} mime type
  366. */
  367. getMimeType(module) {
  368. if (typeof this.dataUrlOptions === "function") {
  369. throw new Error(
  370. "This method must not be called when dataUrlOptions is a function"
  371. );
  372. }
  373. /** @type {string | boolean | undefined} */
  374. let mimeType =
  375. /** @type {AssetGeneratorDataUrlOptions} */
  376. (this.dataUrlOptions).mimetype;
  377. if (mimeType === undefined) {
  378. const ext = path.extname(
  379. /** @type {NameForCondition} */
  380. (module.nameForCondition())
  381. );
  382. if (
  383. module.resourceResolveData &&
  384. module.resourceResolveData.mimetype !== undefined
  385. ) {
  386. mimeType =
  387. module.resourceResolveData.mimetype +
  388. module.resourceResolveData.parameters;
  389. } else if (ext) {
  390. mimeType = getMimeTypes().lookup(ext);
  391. if (typeof mimeType !== "string") {
  392. throw new Error(
  393. "DataUrl can't be generated automatically, " +
  394. `because there is no mimetype for "${ext}" in mimetype database. ` +
  395. 'Either pass a mimetype via "generator.mimetype" or ' +
  396. 'use type: "asset/resource" to create a resource file instead of a DataUrl'
  397. );
  398. }
  399. }
  400. }
  401. if (typeof mimeType !== "string") {
  402. throw new Error(
  403. "DataUrl can't be generated automatically. " +
  404. 'Either pass a mimetype via "generator.mimetype" or ' +
  405. 'use type: "asset/resource" to create a resource file instead of a DataUrl'
  406. );
  407. }
  408. return /** @type {string} */ (mimeType);
  409. }
  410. /**
  411. * @param {NormalModule} module module for which the code should be generated
  412. * @returns {string} DataURI
  413. */
  414. generateDataUri(module) {
  415. const source = /** @type {Source} */ (module.originalSource());
  416. let encodedSource;
  417. if (typeof this.dataUrlOptions === "function") {
  418. encodedSource = this.dataUrlOptions.call(null, source.source(), {
  419. filename: /** @type {string} */ (module.getResource()),
  420. module
  421. });
  422. } else {
  423. let encoding =
  424. /** @type {AssetGeneratorDataUrlOptions} */
  425. (this.dataUrlOptions).encoding;
  426. if (
  427. encoding === undefined &&
  428. module.resourceResolveData &&
  429. module.resourceResolveData.encoding !== undefined
  430. ) {
  431. encoding = module.resourceResolveData.encoding;
  432. }
  433. if (encoding === undefined) {
  434. encoding = DEFAULT_ENCODING;
  435. }
  436. const mimeType = this.getMimeType(module);
  437. let encodedContent;
  438. if (
  439. module.resourceResolveData &&
  440. module.resourceResolveData.encoding === encoding &&
  441. decodeDataUriContent(
  442. module.resourceResolveData.encoding,
  443. /** @type {string} */ (module.resourceResolveData.encodedContent)
  444. ).equals(source.buffer())
  445. ) {
  446. encodedContent = module.resourceResolveData.encodedContent;
  447. } else {
  448. encodedContent = encodeDataUri(
  449. /** @type {"base64" | false} */ (encoding),
  450. source
  451. );
  452. }
  453. encodedSource = `data:${mimeType}${
  454. encoding ? `;${encoding}` : ""
  455. },${encodedContent}`;
  456. }
  457. return encodedSource;
  458. }
  459. /**
  460. * @param {NormalModule} module module for which the code should be generated
  461. * @param {GenerateContext} generateContext context for generate
  462. * @returns {Source | null} generated code
  463. */
  464. generate(module, generateContext) {
  465. const {
  466. type,
  467. getData,
  468. runtimeTemplate,
  469. runtimeRequirements,
  470. concatenationScope
  471. } = generateContext;
  472. let content;
  473. const needContent = type === JAVASCRIPT_TYPE || type === CSS_URL_TYPE;
  474. const data = getData ? getData() : undefined;
  475. if (
  476. /** @type {BuildInfo} */
  477. (module.buildInfo).dataUrl &&
  478. needContent
  479. ) {
  480. const encodedSource = this.generateDataUri(module);
  481. content =
  482. type === JAVASCRIPT_TYPE
  483. ? JSON.stringify(encodedSource)
  484. : encodedSource;
  485. if (data) {
  486. data.set("url", { [type]: content, ...data.get("url") });
  487. }
  488. } else {
  489. const [fullContentHash, contentHash] = AssetGenerator.getFullContentHash(
  490. module,
  491. runtimeTemplate
  492. );
  493. if (data) {
  494. data.set("fullContentHash", fullContentHash);
  495. data.set("contentHash", contentHash);
  496. }
  497. /** @type {BuildInfo} */
  498. (module.buildInfo).fullContentHash = fullContentHash;
  499. const { originalFilename, filename, assetInfo } =
  500. AssetGenerator.getFilenameWithInfo(
  501. module,
  502. { filename: this.filename, outputPath: this.outputPath },
  503. generateContext,
  504. contentHash
  505. );
  506. if (data) {
  507. data.set("filename", filename);
  508. }
  509. let { assetPath, assetInfo: newAssetInfo } =
  510. AssetGenerator.getAssetPathWithInfo(
  511. module,
  512. { publicPath: this.publicPath },
  513. generateContext,
  514. originalFilename,
  515. assetInfo,
  516. contentHash
  517. );
  518. if (data && (type === JAVASCRIPT_TYPE || type === CSS_URL_TYPE)) {
  519. data.set("url", { [type]: assetPath, ...data.get("url") });
  520. }
  521. if (data) {
  522. const oldAssetInfo = data.get("assetInfo");
  523. if (oldAssetInfo) {
  524. newAssetInfo = mergeAssetInfo(oldAssetInfo, newAssetInfo);
  525. }
  526. }
  527. if (data) {
  528. data.set("assetInfo", newAssetInfo);
  529. }
  530. // Due to code generation caching module.buildInfo.XXX can't used to store such information
  531. // It need to be stored in the code generation results instead, where it's cached too
  532. // TODO webpack 6 For back-compat reasons we also store in on module.buildInfo
  533. /** @type {BuildInfo} */
  534. (module.buildInfo).filename = filename;
  535. /** @type {BuildInfo} */
  536. (module.buildInfo).assetInfo = newAssetInfo;
  537. content = assetPath;
  538. }
  539. if (type === JAVASCRIPT_TYPE) {
  540. if (concatenationScope) {
  541. concatenationScope.registerNamespaceExport(
  542. ConcatenationScope.NAMESPACE_OBJECT_EXPORT
  543. );
  544. return new RawSource(
  545. `${runtimeTemplate.renderConst()} ${
  546. ConcatenationScope.NAMESPACE_OBJECT_EXPORT
  547. } = ${content};`
  548. );
  549. }
  550. runtimeRequirements.add(RuntimeGlobals.module);
  551. return new RawSource(`${module.moduleArgument}.exports = ${content};`);
  552. } else if (type === CSS_URL_TYPE) {
  553. return null;
  554. }
  555. return /** @type {Source} */ (module.originalSource());
  556. }
  557. /**
  558. * @param {Error} error the error
  559. * @param {NormalModule} module module for which the code should be generated
  560. * @param {GenerateContext} generateContext context for generate
  561. * @returns {Source | null} generated code
  562. */
  563. generateError(error, module, generateContext) {
  564. switch (generateContext.type) {
  565. case "asset": {
  566. return new RawSource(error.message);
  567. }
  568. case JAVASCRIPT_TYPE: {
  569. return new RawSource(
  570. `throw new Error(${JSON.stringify(error.message)});`
  571. );
  572. }
  573. default:
  574. return null;
  575. }
  576. }
  577. /**
  578. * @param {NormalModule} module fresh module
  579. * @returns {SourceTypes} available types (do not mutate)
  580. */
  581. getTypes(module) {
  582. /** @type {Set<string>} */
  583. const sourceTypes = new Set();
  584. const connections = this._moduleGraph.getIncomingConnections(module);
  585. for (const connection of connections) {
  586. if (!connection.originModule) {
  587. continue;
  588. }
  589. sourceTypes.add(connection.originModule.type.split("/")[0]);
  590. }
  591. if ((module.buildInfo && module.buildInfo.dataUrl) || this.emit === false) {
  592. if (sourceTypes.size > 0) {
  593. if (sourceTypes.has(JAVASCRIPT_TYPE) && sourceTypes.has(CSS_TYPE)) {
  594. return JAVASCRIPT_AND_CSS_URL_TYPES;
  595. } else if (sourceTypes.has(CSS_TYPE)) {
  596. return CSS_URL_TYPES;
  597. }
  598. return JAVASCRIPT_TYPES;
  599. }
  600. return NO_TYPES;
  601. }
  602. if (sourceTypes.size > 0) {
  603. if (sourceTypes.has(JAVASCRIPT_TYPE) && sourceTypes.has(CSS_TYPE)) {
  604. return ASSET_AND_JAVASCRIPT_AND_CSS_URL_TYPES;
  605. } else if (sourceTypes.has(CSS_TYPE)) {
  606. return ASSET_AND_CSS_URL_TYPES;
  607. }
  608. return ASSET_AND_JAVASCRIPT_TYPES;
  609. }
  610. return ASSET_TYPES;
  611. }
  612. /**
  613. * @param {NormalModule} module the module
  614. * @param {SourceType=} type source type
  615. * @returns {number} estimate size of the module
  616. */
  617. getSize(module, type) {
  618. switch (type) {
  619. case ASSET_MODULE_TYPE: {
  620. const originalSource = module.originalSource();
  621. if (!originalSource) {
  622. return 0;
  623. }
  624. return originalSource.size();
  625. }
  626. default:
  627. if (module.buildInfo && module.buildInfo.dataUrl) {
  628. const originalSource = module.originalSource();
  629. if (!originalSource) {
  630. return 0;
  631. }
  632. // roughly for data url
  633. // Example: m.exports="data:image/png;base64,ag82/f+2=="
  634. // 4/3 = base64 encoding
  635. // 34 = ~ data url header + footer + rounding
  636. return originalSource.size() * 1.34 + 36;
  637. }
  638. // it's only estimated so this number is probably fine
  639. // Example: m.exports=r.p+"0123456789012345678901.ext"
  640. return 42;
  641. }
  642. }
  643. /**
  644. * @param {Hash} hash hash that will be modified
  645. * @param {UpdateHashContext} updateHashContext context for updating hash
  646. */
  647. updateHash(hash, updateHashContext) {
  648. const { module } = updateHashContext;
  649. if (
  650. /** @type {BuildInfo} */
  651. (module.buildInfo).dataUrl
  652. ) {
  653. hash.update("data-url");
  654. // this.dataUrlOptions as function should be pure and only depend on input source and filename
  655. // therefore it doesn't need to be hashed
  656. if (typeof this.dataUrlOptions === "function") {
  657. const ident = /** @type {{ ident?: string }} */ (this.dataUrlOptions)
  658. .ident;
  659. if (ident) hash.update(ident);
  660. } else {
  661. const dataUrlOptions =
  662. /** @type {AssetGeneratorDataUrlOptions} */
  663. (this.dataUrlOptions);
  664. if (
  665. dataUrlOptions.encoding &&
  666. dataUrlOptions.encoding !== DEFAULT_ENCODING
  667. ) {
  668. hash.update(dataUrlOptions.encoding);
  669. }
  670. if (dataUrlOptions.mimetype) hash.update(dataUrlOptions.mimetype);
  671. // computed mimetype depends only on module filename which is already part of the hash
  672. }
  673. } else {
  674. hash.update("resource");
  675. const { module, chunkGraph, runtime } = updateHashContext;
  676. const runtimeTemplate =
  677. /** @type {NonNullable<UpdateHashContext["runtimeTemplate"]>} */
  678. (updateHashContext.runtimeTemplate);
  679. const pathData = {
  680. module,
  681. runtime,
  682. filename: AssetGenerator.getSourceFileName(module, runtimeTemplate),
  683. chunkGraph,
  684. contentHash: runtimeTemplate.contentHashReplacement
  685. };
  686. if (typeof this.publicPath === "function") {
  687. hash.update("path");
  688. const assetInfo = {};
  689. hash.update(this.publicPath(pathData, assetInfo));
  690. hash.update(JSON.stringify(assetInfo));
  691. } else if (this.publicPath) {
  692. hash.update("path");
  693. hash.update(this.publicPath);
  694. } else {
  695. hash.update("no-path");
  696. }
  697. const assetModuleFilename =
  698. this.filename || runtimeTemplate.outputOptions.assetModuleFilename;
  699. const { path: filename, info } =
  700. runtimeTemplate.compilation.getAssetPathWithInfo(
  701. assetModuleFilename,
  702. pathData
  703. );
  704. hash.update(filename);
  705. hash.update(JSON.stringify(info));
  706. }
  707. }
  708. }
  709. module.exports = AssetGenerator;