source: frontend/node_modules/webpack/lib/runtime/GetChunkFilenameRuntimeModule.js

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

Fix frontend appearance

  • Property mode set to 100644
File size: 9.3 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3*/
4
5"use strict";
6
7const RuntimeGlobals = require("../RuntimeGlobals");
8const RuntimeModule = require("../RuntimeModule");
9const Template = require("../Template");
10const { first } = require("../util/SetHelpers");
11
12/** @typedef {import("../Chunk")} Chunk */
13/** @typedef {import("../Chunk").ChunkId} ChunkId */
14/** @typedef {import("../ChunkGraph")} ChunkGraph */
15/** @typedef {import("../Compilation")} Compilation */
16/** @typedef {import("../Compilation").HashWithLengthFunction} HashWithLengthFunction */
17/** @typedef {import("../Chunk").ChunkFilenameTemplate} ChunkFilenameTemplate */
18
19class GetChunkFilenameRuntimeModule extends RuntimeModule {
20 /**
21 * @param {string} contentType the contentType to use the content hash for
22 * @param {string} name kind of filename
23 * @param {string} global function name to be assigned
24 * @param {(chunk: Chunk) => ChunkFilenameTemplate | false} getFilenameForChunk functor to get the filename or function
25 * @param {boolean} allChunks when false, only async chunks are included
26 */
27 constructor(contentType, name, global, getFilenameForChunk, allChunks) {
28 super(`get ${name} chunk filename`);
29 /** @type {string} */
30 this.contentType = contentType;
31 /** @type {string} */
32 this.global = global;
33 /** @type {(chunk: Chunk) => ChunkFilenameTemplate | false} */
34 this.getFilenameForChunk = getFilenameForChunk;
35 /** @type {boolean} */
36 this.allChunks = allChunks;
37 /** @type {boolean} */
38 this.dependentHash = true;
39 }
40
41 /**
42 * Generates runtime code for this runtime module.
43 * @returns {string | null} runtime code
44 */
45 generate() {
46 const { global, contentType, getFilenameForChunk, allChunks } = this;
47 const compilation = /** @type {Compilation} */ (this.compilation);
48 const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph);
49 const chunk = /** @type {Chunk} */ (this.chunk);
50 const { runtimeTemplate } = compilation;
51
52 /** @type {Map<ChunkFilenameTemplate, Set<Chunk>>} */
53 const chunkFilenames = new Map();
54 let maxChunks = 0;
55 /** @type {string | undefined} */
56 let dynamicFilename;
57
58 /**
59 * @param {Chunk} c the chunk
60 * @returns {void}
61 */
62 const addChunk = (c) => {
63 const chunkFilename = getFilenameForChunk(c);
64 if (chunkFilename) {
65 let set = chunkFilenames.get(chunkFilename);
66 if (set === undefined) {
67 chunkFilenames.set(chunkFilename, (set = new Set()));
68 }
69 set.add(c);
70 if (typeof chunkFilename === "string") {
71 if (set.size < maxChunks) return;
72 if (set.size === maxChunks) {
73 if (
74 chunkFilename.length <
75 /** @type {string} */ (dynamicFilename).length
76 ) {
77 return;
78 }
79
80 if (
81 chunkFilename.length ===
82 /** @type {string} */ (dynamicFilename).length &&
83 chunkFilename < /** @type {string} */ (dynamicFilename)
84 ) {
85 return;
86 }
87 }
88 maxChunks = set.size;
89 dynamicFilename = chunkFilename;
90 }
91 }
92 };
93
94 /** @type {string[]} */
95 const includedChunksMessages = [];
96 if (allChunks) {
97 includedChunksMessages.push("all chunks");
98 for (const c of chunk.getAllReferencedChunks()) {
99 addChunk(c);
100 }
101 } else {
102 includedChunksMessages.push("async chunks");
103 for (const c of chunk.getAllAsyncChunks()) {
104 addChunk(c);
105 }
106 const includeEntries = chunkGraph
107 .getTreeRuntimeRequirements(chunk)
108 .has(RuntimeGlobals.ensureChunkIncludeEntries);
109 if (includeEntries) {
110 includedChunksMessages.push("chunks that the entrypoint depends on");
111 for (const c of chunkGraph.getRuntimeChunkDependentChunksIterable(
112 chunk
113 )) {
114 addChunk(c);
115 }
116 }
117 }
118 for (const entrypoint of chunk.getAllReferencedAsyncEntrypoints()) {
119 addChunk(entrypoint.chunks[entrypoint.chunks.length - 1]);
120 }
121
122 /** @type {Map<string, Set<string | number | null>>} */
123 const staticUrls = new Map();
124 /** @type {Set<Chunk>} */
125 const dynamicUrlChunks = new Set();
126
127 /**
128 * @param {Chunk} c the chunk
129 * @param {ChunkFilenameTemplate} chunkFilename the filename template for the chunk
130 * @returns {void}
131 */
132 const addStaticUrl = (c, chunkFilename) => {
133 /**
134 * @param {ChunkId} value a value
135 * @returns {string} string to put in quotes
136 */
137 const unquotedStringify = (value) => {
138 const str = `${value}`;
139 if (str.length >= 5 && str === `${c.id}`) {
140 // This is shorter and generates the same result
141 return '" + chunkId + "';
142 }
143 const s = JSON.stringify(str);
144 return s.slice(1, -1);
145 };
146 /**
147 * @param {string} value string
148 * @returns {HashWithLengthFunction} string to put in quotes with length
149 */
150 const unquotedStringifyWithLength = (value) => (length) =>
151 unquotedStringify(`${value}`.slice(0, length));
152 const chunkFilenameValue =
153 typeof chunkFilename === "function"
154 ? JSON.stringify(
155 chunkFilename({
156 chunk: c,
157 contentHashType: contentType
158 })
159 )
160 : JSON.stringify(chunkFilename);
161 const staticChunkFilename = compilation.getPath(chunkFilenameValue, {
162 hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
163 hashWithLength: (length) =>
164 `" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`,
165 chunk: {
166 id: unquotedStringify(/** @type {ChunkId} */ (c.id)),
167 hash: unquotedStringify(/** @type {string} */ (c.renderedHash)),
168 hashWithLength: unquotedStringifyWithLength(
169 /** @type {string} */ (c.renderedHash)
170 ),
171 name: unquotedStringify(c.name || /** @type {ChunkId} */ (c.id)),
172 contentHash: {
173 [contentType]: unquotedStringify(c.contentHash[contentType])
174 },
175 contentHashWithLength: {
176 [contentType]: unquotedStringifyWithLength(
177 c.contentHash[contentType]
178 )
179 }
180 },
181 contentHashType: contentType
182 });
183 let set = staticUrls.get(staticChunkFilename);
184 if (set === undefined) {
185 staticUrls.set(staticChunkFilename, (set = new Set()));
186 }
187 set.add(c.id);
188 };
189
190 for (const [filename, chunks] of chunkFilenames) {
191 if (filename !== dynamicFilename) {
192 for (const c of chunks) addStaticUrl(c, filename);
193 } else {
194 for (const c of chunks) dynamicUrlChunks.add(c);
195 }
196 }
197
198 /**
199 * @param {(chunk: Chunk) => string | number} fn function from chunk to value
200 * @returns {string} code with static mapping of results of fn
201 */
202 const createMap = (fn) => {
203 /** @type {Record<ChunkId, ChunkId>} */
204 const obj = {};
205 let useId = false;
206 /** @type {ChunkId | undefined} */
207 let lastKey;
208 let entries = 0;
209 for (const c of dynamicUrlChunks) {
210 const value = fn(c);
211 if (value === c.id) {
212 useId = true;
213 } else {
214 obj[/** @type {ChunkId} */ (c.id)] = value;
215 lastKey = /** @type {ChunkId} */ (c.id);
216 entries++;
217 }
218 }
219 if (entries === 0) return "chunkId";
220 if (entries === 1) {
221 return useId
222 ? `(chunkId === ${JSON.stringify(lastKey)} ? ${JSON.stringify(
223 obj[/** @type {ChunkId} */ (lastKey)]
224 )} : chunkId)`
225 : JSON.stringify(obj[/** @type {ChunkId} */ (lastKey)]);
226 }
227 return useId
228 ? `(${JSON.stringify(obj)}[chunkId] || chunkId)`
229 : `${JSON.stringify(obj)}[chunkId]`;
230 };
231
232 /**
233 * @param {(chunk: Chunk) => string | number} fn function from chunk to value
234 * @returns {string} code with static mapping of results of fn for including in quoted string
235 */
236 const mapExpr = (fn) => `" + ${createMap(fn)} + "`;
237
238 /**
239 * @param {(chunk: Chunk) => string | number} fn function from chunk to value
240 * @returns {HashWithLengthFunction} function which generates code with static mapping of results of fn for including in quoted string for specific length
241 */
242 const mapExprWithLength = (fn) => (length) =>
243 `" + ${createMap((c) => `${fn(c)}`.slice(0, length))} + "`;
244
245 const url =
246 dynamicFilename &&
247 compilation.getPath(JSON.stringify(dynamicFilename), {
248 hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
249 hashWithLength: (length) =>
250 `" + ${RuntimeGlobals.getFullHash}().slice(0, ${length}) + "`,
251 chunk: {
252 id: '" + chunkId + "',
253 hash: mapExpr((c) => /** @type {string} */ (c.renderedHash)),
254 hashWithLength: mapExprWithLength(
255 (c) => /** @type {string} */ (c.renderedHash)
256 ),
257 name: mapExpr((c) => c.name || /** @type {ChunkId} */ (c.id)),
258 contentHash: {
259 [contentType]: mapExpr((c) => c.contentHash[contentType])
260 },
261 contentHashWithLength: {
262 [contentType]: mapExprWithLength((c) => c.contentHash[contentType])
263 }
264 },
265 contentHashType: contentType
266 });
267
268 return Template.asString([
269 `// This function allow to reference ${includedChunksMessages.join(
270 " and "
271 )}`,
272 `${global} = ${runtimeTemplate.basicFunction(
273 "chunkId",
274
275 staticUrls.size > 0
276 ? [
277 "// return url for filenames not based on template",
278 // it minimizes to `x===1?"...":x===2?"...":"..."`
279 Template.asString(
280 Array.from(staticUrls, ([url, ids]) => {
281 const condition =
282 ids.size === 1
283 ? `chunkId === ${JSON.stringify(first(ids))}`
284 : `{${Array.from(
285 ids,
286 (id) => `${JSON.stringify(id)}:1`
287 ).join(",")}}[chunkId]`;
288 return `if (${condition}) return ${url};`;
289 })
290 ),
291 "// return url for filenames based on template",
292 `return ${url};`
293 ]
294 : ["// return url for filenames based on template", `return ${url};`]
295 )};`
296 ]);
297 }
298}
299
300module.exports = GetChunkFilenameRuntimeModule;
Note: See TracBrowser for help on using the repository browser.