diff options
Diffstat (limited to 'node_modules/webpack/lib/ids/IdHelpers.js')
| -rw-r--r-- | node_modules/webpack/lib/ids/IdHelpers.js | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/ids/IdHelpers.js b/node_modules/webpack/lib/ids/IdHelpers.js new file mode 100644 index 00000000..1ca7824c --- /dev/null +++ b/node_modules/webpack/lib/ids/IdHelpers.js @@ -0,0 +1,483 @@ +/* + 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; |
