source: frontend/node_modules/webpack/lib/asset/AssetGenerator.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

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