AssetGenerator.js 22 KB

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