123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const createHash = require("../util/createHash");
- const { makePathsRelative } = require("../util/identifier");
- const numberHash = require("../util/numberHash");
- /** @typedef {import("../Chunk")} Chunk */
- /** @typedef {import("../ChunkGraph")} ChunkGraph */
- /** @typedef {import("../Compilation")} Compilation */
- /** @typedef {import("../Module")} Module */
- /** @typedef {typeof import("../util/Hash")} Hash */
- /**
- * @param {string} str string to hash
- * @param {number} len max length of the hash
- * @param {string | Hash} hashFunction hash function to use
- * @returns {string} hash
- */
- const getHash = (str, len, hashFunction) => {
- const hash = createHash(hashFunction);
- hash.update(str);
- const digest = /** @type {string} */ (hash.digest("hex"));
- return digest.slice(0, len);
- };
- /**
- * @param {string} str the string
- * @returns {string} string prefixed by an underscore if it is a number
- */
- const avoidNumber = str => {
- // max length of a number is 21 chars, bigger numbers a written as "...e+xx"
- if (str.length > 21) return str;
- const firstChar = str.charCodeAt(0);
- // skip everything that doesn't look like a number
- // charCodes: "-": 45, "1": 49, "9": 57
- if (firstChar < 49) {
- if (firstChar !== 45) return str;
- } else if (firstChar > 57) {
- return str;
- }
- if (str === +str + "") {
- return `_${str}`;
- }
- return str;
- };
- /**
- * @param {string} request the request
- * @returns {string} id representation
- */
- const requestToId = request => {
- return request
- .replace(/^(\.\.?\/)+/, "")
- .replace(/(^[.-]|[^a-zA-Z0-9_-])+/g, "_");
- };
- exports.requestToId = requestToId;
- /**
- * @param {string} string the string
- * @param {string} delimiter separator for string and hash
- * @param {string | Hash} hashFunction hash function to use
- * @returns {string} string with limited max length to 100 chars
- */
- const shortenLongString = (string, delimiter, hashFunction) => {
- if (string.length < 100) return string;
- return (
- string.slice(0, 100 - 6 - delimiter.length) +
- delimiter +
- getHash(string, 6, hashFunction)
- );
- };
- /**
- * @param {Module} module the module
- * @param {string} context context directory
- * @param {object=} associatedObjectForCache an object to which the cache will be attached
- * @returns {string} short module name
- */
- const getShortModuleName = (module, context, associatedObjectForCache) => {
- const libIdent = module.libIdent({ context, associatedObjectForCache });
- if (libIdent) return avoidNumber(libIdent);
- const nameForCondition = module.nameForCondition();
- if (nameForCondition)
- return avoidNumber(
- makePathsRelative(context, nameForCondition, associatedObjectForCache)
- );
- return "";
- };
- exports.getShortModuleName = getShortModuleName;
- /**
- * @param {string} shortName the short name
- * @param {Module} module the module
- * @param {string} context context directory
- * @param {string | Hash} hashFunction hash function to use
- * @param {object=} associatedObjectForCache an object to which the cache will be attached
- * @returns {string} long module name
- */
- const getLongModuleName = (
- shortName,
- module,
- context,
- hashFunction,
- associatedObjectForCache
- ) => {
- const fullName = getFullModuleName(module, context, associatedObjectForCache);
- return `${shortName}?${getHash(fullName, 4, hashFunction)}`;
- };
- exports.getLongModuleName = getLongModuleName;
- /**
- * @param {Module} module the module
- * @param {string} context context directory
- * @param {object=} associatedObjectForCache an object to which the cache will be attached
- * @returns {string} full module name
- */
- const getFullModuleName = (module, context, associatedObjectForCache) => {
- return makePathsRelative(
- context,
- module.identifier(),
- associatedObjectForCache
- );
- };
- exports.getFullModuleName = getFullModuleName;
- /**
- * @param {Chunk} chunk the chunk
- * @param {ChunkGraph} chunkGraph the chunk graph
- * @param {string} context context directory
- * @param {string} delimiter delimiter for names
- * @param {string | Hash} hashFunction hash function to use
- * @param {object=} associatedObjectForCache an object to which the cache will be attached
- * @returns {string} short chunk name
- */
- const getShortChunkName = (
- chunk,
- chunkGraph,
- context,
- delimiter,
- hashFunction,
- associatedObjectForCache
- ) => {
- const modules = chunkGraph.getChunkRootModules(chunk);
- const shortModuleNames = modules.map(m =>
- requestToId(getShortModuleName(m, context, associatedObjectForCache))
- );
- chunk.idNameHints.sort();
- const chunkName = Array.from(chunk.idNameHints)
- .concat(shortModuleNames)
- .filter(Boolean)
- .join(delimiter);
- return shortenLongString(chunkName, delimiter, hashFunction);
- };
- exports.getShortChunkName = getShortChunkName;
- /**
- * @param {Chunk} chunk the chunk
- * @param {ChunkGraph} chunkGraph the chunk graph
- * @param {string} context context directory
- * @param {string} delimiter delimiter for names
- * @param {string | Hash} hashFunction hash function to use
- * @param {object=} associatedObjectForCache an object to which the cache will be attached
- * @returns {string} short chunk name
- */
- const getLongChunkName = (
- chunk,
- chunkGraph,
- context,
- delimiter,
- hashFunction,
- associatedObjectForCache
- ) => {
- const modules = chunkGraph.getChunkRootModules(chunk);
- const shortModuleNames = modules.map(m =>
- requestToId(getShortModuleName(m, context, associatedObjectForCache))
- );
- const longModuleNames = modules.map(m =>
- requestToId(
- getLongModuleName("", m, context, hashFunction, associatedObjectForCache)
- )
- );
- chunk.idNameHints.sort();
- const chunkName = Array.from(chunk.idNameHints)
- .concat(shortModuleNames, longModuleNames)
- .filter(Boolean)
- .join(delimiter);
- return shortenLongString(chunkName, delimiter, hashFunction);
- };
- exports.getLongChunkName = getLongChunkName;
- /**
- * @param {Chunk} chunk the chunk
- * @param {ChunkGraph} chunkGraph the chunk graph
- * @param {string} context context directory
- * @param {object=} associatedObjectForCache an object to which the cache will be attached
- * @returns {string} full chunk name
- */
- const getFullChunkName = (
- chunk,
- chunkGraph,
- context,
- associatedObjectForCache
- ) => {
- if (chunk.name) return chunk.name;
- const modules = chunkGraph.getChunkRootModules(chunk);
- const fullModuleNames = modules.map(m =>
- makePathsRelative(context, m.identifier(), associatedObjectForCache)
- );
- return fullModuleNames.join();
- };
- exports.getFullChunkName = getFullChunkName;
- /**
- * @template K
- * @template V
- * @param {Map<K, V[]>} map a map from key to values
- * @param {K} key key
- * @param {V} value value
- * @returns {void}
- */
- const addToMapOfItems = (map, key, value) => {
- let array = map.get(key);
- if (array === undefined) {
- array = [];
- map.set(key, array);
- }
- array.push(value);
- };
- /**
- * @param {Compilation} compilation the compilation
- * @param {function(Module): boolean=} filter filter modules
- * @returns {[Set<string>, Module[]]} used module ids as strings and modules without id matching the filter
- */
- const getUsedModuleIdsAndModules = (compilation, filter) => {
- const chunkGraph = compilation.chunkGraph;
- const modules = [];
- /** @type {Set<string>} */
- const usedIds = new Set();
- if (compilation.usedModuleIds) {
- for (const id of compilation.usedModuleIds) {
- usedIds.add(id + "");
- }
- }
- for (const module of compilation.modules) {
- if (!module.needId) continue;
- const moduleId = chunkGraph.getModuleId(module);
- if (moduleId !== null) {
- usedIds.add(moduleId + "");
- } else {
- if (
- (!filter || filter(module)) &&
- chunkGraph.getNumberOfModuleChunks(module) !== 0
- ) {
- modules.push(module);
- }
- }
- }
- return [usedIds, modules];
- };
- exports.getUsedModuleIdsAndModules = getUsedModuleIdsAndModules;
- /**
- * @param {Compilation} compilation the compilation
- * @returns {Set<string>} used chunk ids as strings
- */
- const getUsedChunkIds = compilation => {
- /** @type {Set<string>} */
- const usedIds = new Set();
- if (compilation.usedChunkIds) {
- for (const id of compilation.usedChunkIds) {
- usedIds.add(id + "");
- }
- }
- for (const chunk of compilation.chunks) {
- const chunkId = chunk.id;
- if (chunkId !== null) {
- usedIds.add(chunkId + "");
- }
- }
- return usedIds;
- };
- exports.getUsedChunkIds = getUsedChunkIds;
- /**
- * @template T
- * @param {Iterable<T>} items list of items to be named
- * @param {function(T): string} getShortName get a short name for an item
- * @param {function(T, string): string} getLongName get a long name for an item
- * @param {function(T, T): -1|0|1} comparator order of items
- * @param {Set<string>} usedIds already used ids, will not be assigned
- * @param {function(T, string): void} assignName assign a name to an item
- * @returns {T[]} list of items without a name
- */
- const assignNames = (
- items,
- getShortName,
- getLongName,
- comparator,
- usedIds,
- assignName
- ) => {
- /** @type {Map<string, T[]>} */
- const nameToItems = new Map();
- for (const item of items) {
- const name = getShortName(item);
- addToMapOfItems(nameToItems, name, item);
- }
- /** @type {Map<string, T[]>} */
- const nameToItems2 = new Map();
- for (const [name, items] of nameToItems) {
- if (items.length > 1 || !name) {
- for (const item of items) {
- const longName = getLongName(item, name);
- addToMapOfItems(nameToItems2, longName, item);
- }
- } else {
- addToMapOfItems(nameToItems2, name, items[0]);
- }
- }
- /** @type {T[]} */
- const unnamedItems = [];
- for (const [name, items] of nameToItems2) {
- if (!name) {
- for (const item of items) {
- unnamedItems.push(item);
- }
- } else if (items.length === 1 && !usedIds.has(name)) {
- assignName(items[0], name);
- usedIds.add(name);
- } else {
- items.sort(comparator);
- let i = 0;
- for (const item of items) {
- while (nameToItems2.has(name + i) && usedIds.has(name + i)) i++;
- assignName(item, name + i);
- usedIds.add(name + i);
- i++;
- }
- }
- }
- unnamedItems.sort(comparator);
- return unnamedItems;
- };
- exports.assignNames = assignNames;
- /**
- * @template T
- * @param {T[]} items list of items to be named
- * @param {function(T): string} getName get a name for an item
- * @param {function(T, T): -1|0|1} comparator order of items
- * @param {function(T, number): boolean} assignId assign an id to an item
- * @param {number[]} ranges usable ranges for ids
- * @param {number} expandFactor factor to create more ranges
- * @param {number} extraSpace extra space to allocate, i. e. when some ids are already used
- * @param {number} salt salting number to initialize hashing
- * @returns {void}
- */
- const assignDeterministicIds = (
- items,
- getName,
- comparator,
- assignId,
- ranges = [10],
- expandFactor = 10,
- extraSpace = 0,
- salt = 0
- ) => {
- items.sort(comparator);
- // max 5% fill rate
- const optimalRange = Math.min(
- items.length * 20 + extraSpace,
- Number.MAX_SAFE_INTEGER
- );
- let i = 0;
- let range = ranges[i];
- while (range < optimalRange) {
- i++;
- if (i < ranges.length) {
- range = Math.min(ranges[i], Number.MAX_SAFE_INTEGER);
- } else if (expandFactor) {
- range = Math.min(range * expandFactor, Number.MAX_SAFE_INTEGER);
- } else {
- break;
- }
- }
- for (const item of items) {
- const ident = getName(item);
- let id;
- let i = salt;
- do {
- id = numberHash(ident + i++, range);
- } while (!assignId(item, id));
- }
- };
- exports.assignDeterministicIds = assignDeterministicIds;
- /**
- * @param {Set<string>} usedIds used ids
- * @param {Iterable<Module>} modules the modules
- * @param {Compilation} compilation the compilation
- * @returns {void}
- */
- const assignAscendingModuleIds = (usedIds, modules, compilation) => {
- const chunkGraph = compilation.chunkGraph;
- let nextId = 0;
- let assignId;
- if (usedIds.size > 0) {
- /**
- * @param {Module} module the module
- */
- assignId = module => {
- if (chunkGraph.getModuleId(module) === null) {
- while (usedIds.has(nextId + "")) nextId++;
- chunkGraph.setModuleId(module, nextId++);
- }
- };
- } else {
- /**
- * @param {Module} module the module
- */
- assignId = module => {
- if (chunkGraph.getModuleId(module) === null) {
- chunkGraph.setModuleId(module, nextId++);
- }
- };
- }
- for (const module of modules) {
- assignId(module);
- }
- };
- exports.assignAscendingModuleIds = assignAscendingModuleIds;
- /**
- * @param {Iterable<Chunk>} chunks the chunks
- * @param {Compilation} compilation the compilation
- * @returns {void}
- */
- const assignAscendingChunkIds = (chunks, compilation) => {
- const usedIds = getUsedChunkIds(compilation);
- let nextId = 0;
- if (usedIds.size > 0) {
- for (const chunk of chunks) {
- if (chunk.id === null) {
- while (usedIds.has(nextId + "")) nextId++;
- chunk.id = nextId;
- chunk.ids = [nextId];
- nextId++;
- }
- }
- } else {
- for (const chunk of chunks) {
- if (chunk.id === null) {
- chunk.id = nextId;
- chunk.ids = [nextId];
- nextId++;
- }
- }
- }
- };
- exports.assignAscendingChunkIds = assignAscendingChunkIds;
|