source: frontend/node_modules/webpack/lib/ids/IdHelpers.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: 14.1 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const createHash = require("../util/createHash");
9const { makePathsRelative } = require("../util/identifier");
10const numberHash = require("../util/numberHash");
11
12/** @typedef {import("../Chunk")} Chunk */
13/** @typedef {import("../ChunkGraph")} ChunkGraph */
14/** @typedef {import("../Compilation")} Compilation */
15/** @typedef {import("../Module")} Module */
16/** @typedef {import("../util/Hash").HashFunction} HashFunction */
17/** @typedef {import("../util/identifier").AssociatedObjectForCache} AssociatedObjectForCache */
18/** @typedef {import("../Module").BuildMeta} BuildMeta */
19
20/**
21 * Returns hash.
22 * @param {string} str string to hash
23 * @param {number} len max length of the hash
24 * @param {HashFunction} hashFunction hash function to use
25 * @returns {string} hash
26 */
27const getHash = (str, len, hashFunction) => {
28 const hash = createHash(hashFunction);
29 hash.update(str);
30 const digest = hash.digest("hex");
31 return digest.slice(0, len);
32};
33
34/**
35 * Returns string prefixed by an underscore if it is a number.
36 * @param {string} str the string
37 * @returns {string} string prefixed by an underscore if it is a number
38 */
39const avoidNumber = (str) => {
40 // max length of a number is 21 chars, bigger numbers a written as "...e+xx"
41 if (str.length > 21) return str;
42 const firstChar = str.charCodeAt(0);
43 // skip everything that doesn't look like a number
44 // charCodes: "-": 45, "1": 49, "9": 57
45 if (firstChar < 49) {
46 if (firstChar !== 45) return str;
47 } else if (firstChar > 57) {
48 return str;
49 }
50 if (str === String(Number(str))) {
51 return `_${str}`;
52 }
53 return str;
54};
55
56/**
57 * Returns id representation.
58 * @param {string} request the request
59 * @returns {string} id representation
60 */
61const requestToId = (request) =>
62 request.replace(/^(\.\.?\/)+/, "").replace(/(^[.-]|[^a-z0-9_-])+/gi, "_");
63
64/**
65 * Shorten long string.
66 * @param {string} string the string
67 * @param {string} delimiter separator for string and hash
68 * @param {HashFunction} hashFunction hash function to use
69 * @returns {string} string with limited max length to 100 chars
70 */
71const shortenLongString = (string, delimiter, hashFunction) => {
72 if (string.length < 100) return string;
73 return (
74 string.slice(0, 100 - 6 - delimiter.length) +
75 delimiter +
76 getHash(string, 6, hashFunction)
77 );
78};
79
80/**
81 * Gets short module name.
82 * @param {Module} module the module
83 * @param {string} context context directory
84 * @param {AssociatedObjectForCache=} associatedObjectForCache an object to which the cache will be attached
85 * @returns {string} short module name
86 */
87const getShortModuleName = (module, context, associatedObjectForCache) => {
88 const libIdent = module.libIdent({ context, associatedObjectForCache });
89 if (libIdent) return avoidNumber(libIdent);
90 const nameForCondition = module.nameForCondition();
91 if (nameForCondition) {
92 return avoidNumber(
93 makePathsRelative(context, nameForCondition, associatedObjectForCache)
94 );
95 }
96 return "";
97};
98
99/**
100 * Gets long module name.
101 * @param {string} shortName the short name
102 * @param {Module} module the module
103 * @param {string} context context directory
104 * @param {HashFunction} hashFunction hash function to use
105 * @param {AssociatedObjectForCache=} associatedObjectForCache an object to which the cache will be attached
106 * @returns {string} long module name
107 */
108const getLongModuleName = (
109 shortName,
110 module,
111 context,
112 hashFunction,
113 associatedObjectForCache
114) => {
115 const fullName = getFullModuleName(module, context, associatedObjectForCache);
116 return `${shortName}?${getHash(fullName, 4, hashFunction)}`;
117};
118
119/**
120 * Gets full module name.
121 * @param {Module} module the module
122 * @param {string} context context directory
123 * @param {AssociatedObjectForCache=} associatedObjectForCache an object to which the cache will be attached
124 * @returns {string} full module name
125 */
126const getFullModuleName = (module, context, associatedObjectForCache) =>
127 makePathsRelative(context, module.identifier(), associatedObjectForCache);
128
129/**
130 * Gets short chunk name.
131 * @param {Chunk} chunk the chunk
132 * @param {ChunkGraph} chunkGraph the chunk graph
133 * @param {string} context context directory
134 * @param {string} delimiter delimiter for names
135 * @param {HashFunction} hashFunction hash function to use
136 * @param {AssociatedObjectForCache=} associatedObjectForCache an object to which the cache will be attached
137 * @returns {string} short chunk name
138 */
139const getShortChunkName = (
140 chunk,
141 chunkGraph,
142 context,
143 delimiter,
144 hashFunction,
145 associatedObjectForCache
146) => {
147 const modules = chunkGraph.getChunkRootModules(chunk);
148 const shortModuleNames = modules.map((m) =>
149 requestToId(getShortModuleName(m, context, associatedObjectForCache))
150 );
151 chunk.idNameHints.sort();
152 const chunkName = [...chunk.idNameHints, ...shortModuleNames]
153 .filter(Boolean)
154 .join(delimiter);
155 return shortenLongString(chunkName, delimiter, hashFunction);
156};
157
158/**
159 * Gets long chunk name.
160 * @param {Chunk} chunk the chunk
161 * @param {ChunkGraph} chunkGraph the chunk graph
162 * @param {string} context context directory
163 * @param {string} delimiter delimiter for names
164 * @param {HashFunction} hashFunction hash function to use
165 * @param {AssociatedObjectForCache=} associatedObjectForCache an object to which the cache will be attached
166 * @returns {string} short chunk name
167 */
168const getLongChunkName = (
169 chunk,
170 chunkGraph,
171 context,
172 delimiter,
173 hashFunction,
174 associatedObjectForCache
175) => {
176 const modules = chunkGraph.getChunkRootModules(chunk);
177 const shortModuleNames = modules.map((m) =>
178 requestToId(getShortModuleName(m, context, associatedObjectForCache))
179 );
180 const longModuleNames = modules.map((m) =>
181 requestToId(
182 getLongModuleName("", m, context, hashFunction, associatedObjectForCache)
183 )
184 );
185 chunk.idNameHints.sort();
186 const chunkName = [
187 ...chunk.idNameHints,
188 ...shortModuleNames,
189 ...longModuleNames
190 ]
191 .filter(Boolean)
192 .join(delimiter);
193 return shortenLongString(chunkName, delimiter, hashFunction);
194};
195
196/**
197 * Gets full chunk name.
198 * @param {Chunk} chunk the chunk
199 * @param {ChunkGraph} chunkGraph the chunk graph
200 * @param {string} context context directory
201 * @param {AssociatedObjectForCache=} associatedObjectForCache an object to which the cache will be attached
202 * @returns {string} full chunk name
203 */
204const getFullChunkName = (
205 chunk,
206 chunkGraph,
207 context,
208 associatedObjectForCache
209) => {
210 if (chunk.name) return chunk.name;
211 const modules = chunkGraph.getChunkRootModules(chunk);
212 const fullModuleNames = modules.map((m) =>
213 makePathsRelative(context, m.identifier(), associatedObjectForCache)
214 );
215 return fullModuleNames.join();
216};
217
218/**
219 * Adds to map of items.
220 * @template K
221 * @template V
222 * @param {Map<K, V[]>} map a map from key to values
223 * @param {K} key key
224 * @param {V} value value
225 * @returns {void}
226 */
227const addToMapOfItems = (map, key, value) => {
228 let array = map.get(key);
229 if (array === undefined) {
230 array = [];
231 map.set(key, array);
232 }
233 array.push(value);
234};
235
236/** @typedef {Set<string>} UsedModuleIds */
237
238/**
239 * Gets used module ids and modules.
240 * @param {Compilation} compilation the compilation
241 * @param {((module: Module) => boolean)=} filter filter modules
242 * @returns {[UsedModuleIds, Module[]]} used module ids as strings and modules without id matching the filter
243 */
244const getUsedModuleIdsAndModules = (compilation, filter) => {
245 const chunkGraph = compilation.chunkGraph;
246 /** @type {Module[]} */
247 const modules = [];
248
249 /** @type {UsedModuleIds} */
250 const usedIds = new Set();
251 if (compilation.usedModuleIds) {
252 for (const id of compilation.usedModuleIds) {
253 usedIds.add(String(id));
254 }
255 }
256
257 for (const module of compilation.modules) {
258 if (!module.needId) continue;
259 const moduleId = chunkGraph.getModuleId(module);
260 if (moduleId !== null) {
261 usedIds.add(String(moduleId));
262 } else if (
263 (!filter || filter(module)) &&
264 (chunkGraph.getNumberOfModuleChunks(module) !== 0 ||
265 // CSS modules need IDs even when not in chunks, for generating CSS class names(i.e. [id]-[local])
266 /** @type {BuildMeta} */ (module.buildMeta).isCssModule ||
267 /** @type {BuildMeta} */ (module.buildMeta).needIdInConcatenation)
268 ) {
269 modules.push(module);
270 }
271 }
272
273 return [usedIds, modules];
274};
275
276/** @typedef {Set<string>} UsedChunkIds */
277
278/**
279 * Gets used chunk ids.
280 * @param {Compilation} compilation the compilation
281 * @returns {UsedChunkIds} used chunk ids as strings
282 */
283const getUsedChunkIds = (compilation) => {
284 /** @type {UsedChunkIds} */
285 const usedIds = new Set();
286 if (compilation.usedChunkIds) {
287 for (const id of compilation.usedChunkIds) {
288 usedIds.add(String(id));
289 }
290 }
291
292 for (const chunk of compilation.chunks) {
293 const chunkId = chunk.id;
294 if (chunkId !== null) {
295 usedIds.add(String(chunkId));
296 }
297 }
298
299 return usedIds;
300};
301
302/**
303 * Returns list of items without a name.
304 * @template T
305 * @param {Iterable<T>} items list of items to be named
306 * @param {(item: T) => string} getShortName get a short name for an item
307 * @param {(item: T, name: string) => string} getLongName get a long name for an item
308 * @param {(a: T, b: T) => -1 | 0 | 1} comparator order of items
309 * @param {Set<string>} usedIds already used ids, will not be assigned
310 * @param {(item: T, name: string) => void} assignName assign a name to an item
311 * @returns {T[]} list of items without a name
312 */
313const assignNames = (
314 items,
315 getShortName,
316 getLongName,
317 comparator,
318 usedIds,
319 assignName
320) => {
321 /**
322 * Defines the map to item type used by this module.
323 * @template T
324 * @typedef {Map<string, T[]>} MapToItem
325 */
326
327 /** @type {MapToItem<T>} */
328 const nameToItems = new Map();
329
330 for (const item of items) {
331 const name = getShortName(item);
332 addToMapOfItems(nameToItems, name, item);
333 }
334
335 /** @type {MapToItem<T>} */
336 const nameToItems2 = new Map();
337
338 for (const [name, items] of nameToItems) {
339 if (items.length > 1 || !name) {
340 for (const item of items) {
341 const longName = getLongName(item, name);
342 addToMapOfItems(nameToItems2, longName, item);
343 }
344 } else {
345 addToMapOfItems(nameToItems2, name, items[0]);
346 }
347 }
348
349 /** @type {T[]} */
350 const unnamedItems = [];
351
352 for (const [name, items] of nameToItems2) {
353 if (!name) {
354 for (const item of items) {
355 unnamedItems.push(item);
356 }
357 } else if (items.length === 1 && !usedIds.has(name)) {
358 assignName(items[0], name);
359 usedIds.add(name);
360 } else {
361 items.sort(comparator);
362 let i = 0;
363 for (const item of items) {
364 while (nameToItems2.has(name + i) && usedIds.has(name + i)) i++;
365 assignName(item, name + i);
366 usedIds.add(name + i);
367 i++;
368 }
369 }
370 }
371
372 unnamedItems.sort(comparator);
373 return unnamedItems;
374};
375
376/**
377 * Assign deterministic ids.
378 * @template T
379 * @param {T[]} items list of items to be named
380 * @param {(item: T) => string} getName get a name for an item
381 * @param {(a: T, n: T) => -1 | 0 | 1} comparator order of items
382 * @param {(item: T, id: number) => boolean} assignId assign an id to an item
383 * @param {number[]} ranges usable ranges for ids
384 * @param {number} expandFactor factor to create more ranges
385 * @param {number} extraSpace extra space to allocate, i. e. when some ids are already used
386 * @param {number} salt salting number to initialize hashing
387 * @returns {void}
388 */
389const assignDeterministicIds = (
390 items,
391 getName,
392 comparator,
393 assignId,
394 ranges = [10],
395 expandFactor = 10,
396 extraSpace = 0,
397 salt = 0
398) => {
399 items.sort(comparator);
400
401 // max 5% fill rate
402 const optimalRange = Math.min(
403 items.length * 20 + extraSpace,
404 Number.MAX_SAFE_INTEGER
405 );
406
407 let i = 0;
408 let range = ranges[i];
409 while (range < optimalRange) {
410 i++;
411 if (i < ranges.length) {
412 range = Math.min(ranges[i], Number.MAX_SAFE_INTEGER);
413 } else if (expandFactor) {
414 range = Math.min(range * expandFactor, Number.MAX_SAFE_INTEGER);
415 } else {
416 break;
417 }
418 }
419
420 for (const item of items) {
421 const ident = getName(item);
422 /** @type {number} */
423 let id;
424 let i = salt;
425 do {
426 id = numberHash(ident + i++, range);
427 } while (!assignId(item, id));
428 }
429};
430
431/**
432 * Assign ascending module ids.
433 * @param {UsedModuleIds} usedIds used ids
434 * @param {Iterable<Module>} modules the modules
435 * @param {Compilation} compilation the compilation
436 * @returns {void}
437 */
438const assignAscendingModuleIds = (usedIds, modules, compilation) => {
439 const chunkGraph = compilation.chunkGraph;
440
441 let nextId = 0;
442 /** @type {(mod: Module) => void} */
443 let assignId;
444 if (usedIds.size > 0) {
445 /**
446 * Processes the provided module.
447 * @param {Module} module the module
448 */
449 assignId = (module) => {
450 if (chunkGraph.getModuleId(module) === null) {
451 while (usedIds.has(String(nextId))) nextId++;
452 chunkGraph.setModuleId(module, nextId++);
453 }
454 };
455 } else {
456 /**
457 * Processes the provided module.
458 * @param {Module} module the module
459 */
460 assignId = (module) => {
461 if (chunkGraph.getModuleId(module) === null) {
462 chunkGraph.setModuleId(module, nextId++);
463 }
464 };
465 }
466 for (const module of modules) {
467 assignId(module);
468 }
469};
470
471/**
472 * Assign ascending chunk ids.
473 * @param {Iterable<Chunk>} chunks the chunks
474 * @param {Compilation} compilation the compilation
475 * @returns {void}
476 */
477const assignAscendingChunkIds = (chunks, compilation) => {
478 const usedIds = getUsedChunkIds(compilation);
479
480 let nextId = 0;
481 if (usedIds.size > 0) {
482 for (const chunk of chunks) {
483 if (chunk.id === null) {
484 while (usedIds.has(String(nextId))) nextId++;
485 chunk.id = nextId;
486 chunk.ids = [nextId];
487 nextId++;
488 }
489 }
490 } else {
491 for (const chunk of chunks) {
492 if (chunk.id === null) {
493 chunk.id = nextId;
494 chunk.ids = [nextId];
495 nextId++;
496 }
497 }
498 }
499};
500
501module.exports.assignAscendingChunkIds = assignAscendingChunkIds;
502module.exports.assignAscendingModuleIds = assignAscendingModuleIds;
503module.exports.assignDeterministicIds = assignDeterministicIds;
504module.exports.assignNames = assignNames;
505module.exports.getFullChunkName = getFullChunkName;
506module.exports.getFullModuleName = getFullModuleName;
507module.exports.getLongChunkName = getLongChunkName;
508module.exports.getLongModuleName = getLongModuleName;
509module.exports.getShortChunkName = getShortChunkName;
510module.exports.getShortModuleName = getShortModuleName;
511module.exports.getUsedChunkIds = getUsedChunkIds;
512module.exports.getUsedModuleIdsAndModules = getUsedModuleIdsAndModules;
513module.exports.requestToId = requestToId;
Note: See TracBrowser for help on using the repository browser.