| /* |
| MIT License http://www.opensource.org/licenses/mit-license.php |
| Author Tobias Koppers @sokra |
| */ |
| "use strict"; |
| |
| const util = require("util"); |
| |
| const DependenciesBlock = require("./DependenciesBlock"); |
| const ModuleReason = require("./ModuleReason"); |
| const SortableSet = require("./util/SortableSet"); |
| const Template = require("./Template"); |
| |
| /** @typedef {import("./Chunk")} Chunk */ |
| /** @typedef {import("./RequestShortener")} RequestShortener */ |
| /** @typedef {import("./WebpackError")} WebpackError */ |
| /** @typedef {import("./util/createHash").Hash} Hash */ |
| |
| const EMPTY_RESOLVE_OPTIONS = {}; |
| |
| let debugId = 1000; |
| |
| const sortById = (a, b) => { |
| return a.id - b.id; |
| }; |
| |
| const sortByDebugId = (a, b) => { |
| return a.debugId - b.debugId; |
| }; |
| |
| /** @typedef {(requestShortener: RequestShortener) => string} OptimizationBailoutFunction */ |
| |
| class Module extends DependenciesBlock { |
| constructor(type, context = null) { |
| super(); |
| /** @type {string} */ |
| this.type = type; |
| /** @type {string} */ |
| this.context = context; |
| |
| // Unique Id |
| /** @type {number} */ |
| this.debugId = debugId++; |
| |
| // Hash |
| /** @type {string} */ |
| this.hash = undefined; |
| /** @type {string} */ |
| this.renderedHash = undefined; |
| |
| // Info from Factory |
| /** @type {TODO} */ |
| this.resolveOptions = EMPTY_RESOLVE_OPTIONS; |
| /** @type {object} */ |
| this.factoryMeta = {}; |
| |
| // Info from Build |
| /** @type {WebpackError[]} */ |
| this.warnings = []; |
| /** @type {WebpackError[]} */ |
| this.errors = []; |
| /** @type {object} */ |
| this.buildMeta = undefined; |
| /** @type {object} */ |
| this.buildInfo = undefined; |
| |
| // Graph (per Compilation) |
| /** @type {ModuleReason[]} */ |
| this.reasons = []; |
| /** @type {SortableSet<Chunk>} */ |
| this._chunks = new SortableSet(undefined, sortById); |
| |
| // Info from Compilation (per Compilation) |
| /** @type {number|string} */ |
| this.id = null; |
| /** @type {number} */ |
| this.index = null; |
| /** @type {number} */ |
| this.index2 = null; |
| /** @type {number} */ |
| this.depth = null; |
| /** @type {Module} */ |
| this.issuer = null; |
| /** @type {undefined | object} */ |
| this.profile = undefined; |
| /** @type {boolean} */ |
| this.prefetched = false; |
| /** @type {boolean} */ |
| this.built = false; |
| |
| // Info from Optimization (per Compilation) |
| /** @type {null | boolean} */ |
| this.used = null; |
| /** @type {false | true | string[]} */ |
| this.usedExports = null; |
| /** @type {(string | OptimizationBailoutFunction)[]} */ |
| this.optimizationBailout = []; |
| |
| // delayed operations |
| /** @type {undefined | {oldChunk: Chunk, newChunks: Chunk[]}[] } */ |
| this._rewriteChunkInReasons = undefined; |
| |
| /** @type {boolean} */ |
| this.useSourceMap = false; |
| |
| // info from build |
| this._source = null; |
| } |
| |
| get exportsArgument() { |
| return (this.buildInfo && this.buildInfo.exportsArgument) || "exports"; |
| } |
| |
| get moduleArgument() { |
| return (this.buildInfo && this.buildInfo.moduleArgument) || "module"; |
| } |
| |
| disconnect() { |
| this.hash = undefined; |
| this.renderedHash = undefined; |
| |
| this.reasons.length = 0; |
| this._rewriteChunkInReasons = undefined; |
| this._chunks.clear(); |
| |
| this.id = null; |
| this.index = null; |
| this.index2 = null; |
| this.depth = null; |
| this.issuer = null; |
| this.profile = undefined; |
| this.prefetched = false; |
| this.built = false; |
| |
| this.used = null; |
| this.usedExports = null; |
| this.optimizationBailout.length = 0; |
| super.disconnect(); |
| } |
| |
| unseal() { |
| this.id = null; |
| this.index = null; |
| this.index2 = null; |
| this.depth = null; |
| this._chunks.clear(); |
| super.unseal(); |
| } |
| |
| setChunks(chunks) { |
| this._chunks = new SortableSet(chunks, sortById); |
| } |
| |
| addChunk(chunk) { |
| if (this._chunks.has(chunk)) return false; |
| this._chunks.add(chunk); |
| return true; |
| } |
| |
| removeChunk(chunk) { |
| if (this._chunks.delete(chunk)) { |
| chunk.removeModule(this); |
| return true; |
| } |
| return false; |
| } |
| |
| isInChunk(chunk) { |
| return this._chunks.has(chunk); |
| } |
| |
| isEntryModule() { |
| for (const chunk of this._chunks) { |
| if (chunk.entryModule === this) return true; |
| } |
| return false; |
| } |
| |
| get optional() { |
| return ( |
| this.reasons.length > 0 && |
| this.reasons.every(r => r.dependency && r.dependency.optional) |
| ); |
| } |
| |
| /** |
| * @returns {Chunk[]} all chunks which contain the module |
| */ |
| getChunks() { |
| return Array.from(this._chunks); |
| } |
| |
| getNumberOfChunks() { |
| return this._chunks.size; |
| } |
| |
| get chunksIterable() { |
| return this._chunks; |
| } |
| |
| hasEqualsChunks(otherModule) { |
| if (this._chunks.size !== otherModule._chunks.size) return false; |
| this._chunks.sortWith(sortByDebugId); |
| otherModule._chunks.sortWith(sortByDebugId); |
| const a = this._chunks[Symbol.iterator](); |
| const b = otherModule._chunks[Symbol.iterator](); |
| // eslint-disable-next-line no-constant-condition |
| while (true) { |
| const aItem = a.next(); |
| const bItem = b.next(); |
| if (aItem.done) return true; |
| if (aItem.value !== bItem.value) return false; |
| } |
| } |
| |
| addReason(module, dependency, explanation) { |
| this.reasons.push(new ModuleReason(module, dependency, explanation)); |
| } |
| |
| removeReason(module, dependency) { |
| for (let i = 0; i < this.reasons.length; i++) { |
| let r = this.reasons[i]; |
| if (r.module === module && r.dependency === dependency) { |
| this.reasons.splice(i, 1); |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| hasReasonForChunk(chunk) { |
| if (this._rewriteChunkInReasons) { |
| for (const operation of this._rewriteChunkInReasons) { |
| this._doRewriteChunkInReasons(operation.oldChunk, operation.newChunks); |
| } |
| this._rewriteChunkInReasons = undefined; |
| } |
| for (let i = 0; i < this.reasons.length; i++) { |
| if (this.reasons[i].hasChunk(chunk)) return true; |
| } |
| return false; |
| } |
| |
| hasReasons() { |
| return this.reasons.length > 0; |
| } |
| |
| rewriteChunkInReasons(oldChunk, newChunks) { |
| // This is expensive. Delay operation until we really need the data |
| if (this._rewriteChunkInReasons === undefined) { |
| this._rewriteChunkInReasons = []; |
| } |
| this._rewriteChunkInReasons.push({ |
| oldChunk, |
| newChunks |
| }); |
| } |
| |
| _doRewriteChunkInReasons(oldChunk, newChunks) { |
| for (let i = 0; i < this.reasons.length; i++) { |
| this.reasons[i].rewriteChunks(oldChunk, newChunks); |
| } |
| } |
| |
| /** |
| * @param {string=} exportName the name of the export |
| * @returns {boolean|string} false if the export isn't used, true if no exportName is provided and the module is used, or the name to access it if the export is used |
| */ |
| isUsed(exportName) { |
| if (!exportName) return this.used !== false; |
| if (this.used === null || this.usedExports === null) return exportName; |
| if (!this.used) return false; |
| if (!this.usedExports) return false; |
| if (this.usedExports === true) return exportName; |
| let idx = this.usedExports.indexOf(exportName); |
| if (idx < 0) return false; |
| |
| // Mangle export name if possible |
| if (this.isProvided(exportName)) { |
| if (this.buildMeta.exportsType === "namespace") { |
| return Template.numberToIdentifer(idx); |
| } |
| if ( |
| this.buildMeta.exportsType === "named" && |
| !this.usedExports.includes("default") |
| ) { |
| return Template.numberToIdentifer(idx); |
| } |
| } |
| return exportName; |
| } |
| |
| isProvided(exportName) { |
| if (!Array.isArray(this.buildMeta.providedExports)) return null; |
| return this.buildMeta.providedExports.includes(exportName); |
| } |
| |
| toString() { |
| return `Module[${this.id || this.debugId}]`; |
| } |
| |
| needRebuild(fileTimestamps, contextTimestamps) { |
| return true; |
| } |
| |
| /** |
| * @param {Hash} hash the hash used to track dependencies |
| * @returns {void} |
| */ |
| updateHash(hash) { |
| hash.update(`${this.id}`); |
| hash.update(JSON.stringify(this.usedExports)); |
| super.updateHash(hash); |
| } |
| |
| sortItems(sortChunks) { |
| super.sortItems(); |
| if (sortChunks) this._chunks.sort(); |
| this.reasons.sort((a, b) => { |
| if (a.module === b.module) return 0; |
| if (!a.module) return -1; |
| if (!b.module) return 1; |
| return sortById(a.module, b.module); |
| }); |
| if (Array.isArray(this.usedExports)) { |
| this.usedExports.sort(); |
| } |
| } |
| |
| unbuild() { |
| this.dependencies.length = 0; |
| this.blocks.length = 0; |
| this.variables.length = 0; |
| this.buildMeta = undefined; |
| this.buildInfo = undefined; |
| this.disconnect(); |
| } |
| |
| get arguments() { |
| throw new Error("Module.arguments was removed, there is no replacement."); |
| } |
| |
| set arguments(value) { |
| throw new Error("Module.arguments was removed, there is no replacement."); |
| } |
| } |
| |
| // TODO remove in webpack 5 |
| Object.defineProperty(Module.prototype, "forEachChunk", { |
| configurable: false, |
| value: util.deprecate( |
| /** |
| * @deprecated |
| * @param {function(any, any, Set<any>): void} fn callback function |
| * @returns {void} |
| * @this {Module} |
| */ |
| function(fn) { |
| this._chunks.forEach(fn); |
| }, |
| "Module.forEachChunk: Use for(const chunk of module.chunksIterable) instead" |
| ) |
| }); |
| |
| // TODO remove in webpack 5 |
| Object.defineProperty(Module.prototype, "mapChunks", { |
| configurable: false, |
| value: util.deprecate( |
| /** |
| * @deprecated |
| * @param {function(any, any): void} fn Mapper function |
| * @returns {Array<TODO>} Array of chunks mapped |
| * @this {Module} |
| */ |
| function(fn) { |
| return Array.from(this._chunks, fn); |
| }, |
| "Module.mapChunks: Use Array.from(module.chunksIterable, fn) instead" |
| ) |
| }); |
| |
| // TODO remove in webpack 5 |
| Object.defineProperty(Module.prototype, "entry", { |
| configurable: false, |
| get() { |
| throw new Error("Module.entry was removed. Use Chunk.entryModule"); |
| }, |
| set() { |
| throw new Error("Module.entry was removed. Use Chunk.entryModule"); |
| } |
| }); |
| |
| // TODO remove in webpack 5 |
| Object.defineProperty(Module.prototype, "meta", { |
| configurable: false, |
| get: util.deprecate( |
| /** |
| * @deprecated |
| * @returns {void} |
| * @this {Module} |
| */ |
| function() { |
| return this.buildMeta; |
| }, |
| "Module.meta was renamed to Module.buildMeta" |
| ), |
| set: util.deprecate( |
| /** |
| * @deprecated |
| * @param {TODO} value Value |
| * @returns {void} |
| * @this {Module} |
| */ |
| function(value) { |
| this.buildMeta = value; |
| }, |
| "Module.meta was renamed to Module.buildMeta" |
| ) |
| }); |
| |
| /** @type {function(): string} */ |
| Module.prototype.identifier = null; |
| |
| /** @type {function(RequestShortener): string} */ |
| Module.prototype.readableIdentifier = null; |
| |
| Module.prototype.build = null; |
| Module.prototype.source = null; |
| Module.prototype.size = null; |
| Module.prototype.nameForCondition = null; |
| /** @type {null | function(Chunk): boolean} */ |
| Module.prototype.chunkCondition = null; |
| Module.prototype.updateCacheModule = null; |
| |
| module.exports = Module; |