UNPKG

10.4 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const util = require("util");
8
9const DependenciesBlock = require("./DependenciesBlock");
10const ModuleReason = require("./ModuleReason");
11const SortableSet = require("./util/SortableSet");
12const Template = require("./Template");
13
14/** @typedef {import("./Chunk")} Chunk */
15/** @typedef {import("./RequestShortener")} RequestShortener */
16/** @typedef {import("./WebpackError")} WebpackError */
17/** @typedef {import("./util/createHash").Hash} Hash */
18
19const EMPTY_RESOLVE_OPTIONS = {};
20
21let debugId = 1000;
22
23const sortById = (a, b) => {
24 return a.id - b.id;
25};
26
27const sortByDebugId = (a, b) => {
28 return a.debugId - b.debugId;
29};
30
31/** @typedef {(requestShortener: RequestShortener) => string} OptimizationBailoutFunction */
32
33class Module extends DependenciesBlock {
34 constructor(type, context = null) {
35 super();
36 /** @type {string} */
37 this.type = type;
38 /** @type {string} */
39 this.context = context;
40
41 // Unique Id
42 /** @type {number} */
43 this.debugId = debugId++;
44
45 // Hash
46 /** @type {string} */
47 this.hash = undefined;
48 /** @type {string} */
49 this.renderedHash = undefined;
50
51 // Info from Factory
52 /** @type {TODO} */
53 this.resolveOptions = EMPTY_RESOLVE_OPTIONS;
54 /** @type {object} */
55 this.factoryMeta = {};
56
57 // Info from Build
58 /** @type {WebpackError[]} */
59 this.warnings = [];
60 /** @type {WebpackError[]} */
61 this.errors = [];
62 /** @type {object} */
63 this.buildMeta = undefined;
64 /** @type {object} */
65 this.buildInfo = undefined;
66
67 // Graph (per Compilation)
68 /** @type {ModuleReason[]} */
69 this.reasons = [];
70 /** @type {SortableSet<Chunk>} */
71 this._chunks = new SortableSet(undefined, sortById);
72
73 // Info from Compilation (per Compilation)
74 /** @type {number|string} */
75 this.id = null;
76 /** @type {number} */
77 this.index = null;
78 /** @type {number} */
79 this.index2 = null;
80 /** @type {number} */
81 this.depth = null;
82 /** @type {Module} */
83 this.issuer = null;
84 /** @type {undefined | object} */
85 this.profile = undefined;
86 /** @type {boolean} */
87 this.prefetched = false;
88 /** @type {boolean} */
89 this.built = false;
90
91 // Info from Optimization (per Compilation)
92 /** @type {null | boolean} */
93 this.used = null;
94 /** @type {false | true | string[]} */
95 this.usedExports = null;
96 /** @type {(string | OptimizationBailoutFunction)[]} */
97 this.optimizationBailout = [];
98
99 // delayed operations
100 /** @type {undefined | {oldChunk: Chunk, newChunks: Chunk[]}[] } */
101 this._rewriteChunkInReasons = undefined;
102
103 /** @type {boolean} */
104 this.useSourceMap = false;
105
106 // info from build
107 this._source = null;
108 }
109
110 get exportsArgument() {
111 return (this.buildInfo && this.buildInfo.exportsArgument) || "exports";
112 }
113
114 get moduleArgument() {
115 return (this.buildInfo && this.buildInfo.moduleArgument) || "module";
116 }
117
118 disconnect() {
119 this.hash = undefined;
120 this.renderedHash = undefined;
121
122 this.reasons.length = 0;
123 this._rewriteChunkInReasons = undefined;
124 this._chunks.clear();
125
126 this.id = null;
127 this.index = null;
128 this.index2 = null;
129 this.depth = null;
130 this.issuer = null;
131 this.profile = undefined;
132 this.prefetched = false;
133 this.built = false;
134
135 this.used = null;
136 this.usedExports = null;
137 this.optimizationBailout.length = 0;
138 super.disconnect();
139 }
140
141 unseal() {
142 this.id = null;
143 this.index = null;
144 this.index2 = null;
145 this.depth = null;
146 this._chunks.clear();
147 super.unseal();
148 }
149
150 setChunks(chunks) {
151 this._chunks = new SortableSet(chunks, sortById);
152 }
153
154 addChunk(chunk) {
155 if (this._chunks.has(chunk)) return false;
156 this._chunks.add(chunk);
157 return true;
158 }
159
160 removeChunk(chunk) {
161 if (this._chunks.delete(chunk)) {
162 chunk.removeModule(this);
163 return true;
164 }
165 return false;
166 }
167
168 isInChunk(chunk) {
169 return this._chunks.has(chunk);
170 }
171
172 isEntryModule() {
173 for (const chunk of this._chunks) {
174 if (chunk.entryModule === this) return true;
175 }
176 return false;
177 }
178
179 get optional() {
180 return (
181 this.reasons.length > 0 &&
182 this.reasons.every(r => r.dependency && r.dependency.optional)
183 );
184 }
185
186 /**
187 * @returns {Chunk[]} all chunks which contain the module
188 */
189 getChunks() {
190 return Array.from(this._chunks);
191 }
192
193 getNumberOfChunks() {
194 return this._chunks.size;
195 }
196
197 get chunksIterable() {
198 return this._chunks;
199 }
200
201 hasEqualsChunks(otherModule) {
202 if (this._chunks.size !== otherModule._chunks.size) return false;
203 this._chunks.sortWith(sortByDebugId);
204 otherModule._chunks.sortWith(sortByDebugId);
205 const a = this._chunks[Symbol.iterator]();
206 const b = otherModule._chunks[Symbol.iterator]();
207 // eslint-disable-next-line no-constant-condition
208 while (true) {
209 const aItem = a.next();
210 const bItem = b.next();
211 if (aItem.done) return true;
212 if (aItem.value !== bItem.value) return false;
213 }
214 }
215
216 addReason(module, dependency, explanation) {
217 this.reasons.push(new ModuleReason(module, dependency, explanation));
218 }
219
220 removeReason(module, dependency) {
221 for (let i = 0; i < this.reasons.length; i++) {
222 let r = this.reasons[i];
223 if (r.module === module && r.dependency === dependency) {
224 this.reasons.splice(i, 1);
225 return true;
226 }
227 }
228 return false;
229 }
230
231 hasReasonForChunk(chunk) {
232 if (this._rewriteChunkInReasons) {
233 for (const operation of this._rewriteChunkInReasons) {
234 this._doRewriteChunkInReasons(operation.oldChunk, operation.newChunks);
235 }
236 this._rewriteChunkInReasons = undefined;
237 }
238 for (let i = 0; i < this.reasons.length; i++) {
239 if (this.reasons[i].hasChunk(chunk)) return true;
240 }
241 return false;
242 }
243
244 hasReasons() {
245 return this.reasons.length > 0;
246 }
247
248 rewriteChunkInReasons(oldChunk, newChunks) {
249 // This is expensive. Delay operation until we really need the data
250 if (this._rewriteChunkInReasons === undefined) {
251 this._rewriteChunkInReasons = [];
252 }
253 this._rewriteChunkInReasons.push({
254 oldChunk,
255 newChunks
256 });
257 }
258
259 _doRewriteChunkInReasons(oldChunk, newChunks) {
260 for (let i = 0; i < this.reasons.length; i++) {
261 this.reasons[i].rewriteChunks(oldChunk, newChunks);
262 }
263 }
264
265 /**
266 * @param {string=} exportName the name of the export
267 * @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
268 */
269 isUsed(exportName) {
270 if (!exportName) return this.used !== false;
271 if (this.used === null || this.usedExports === null) return exportName;
272 if (!this.used) return false;
273 if (!this.usedExports) return false;
274 if (this.usedExports === true) return exportName;
275 let idx = this.usedExports.indexOf(exportName);
276 if (idx < 0) return false;
277
278 // Mangle export name if possible
279 if (this.isProvided(exportName)) {
280 if (this.buildMeta.exportsType === "namespace") {
281 return Template.numberToIdentifer(idx);
282 }
283 if (
284 this.buildMeta.exportsType === "named" &&
285 !this.usedExports.includes("default")
286 ) {
287 return Template.numberToIdentifer(idx);
288 }
289 }
290 return exportName;
291 }
292
293 isProvided(exportName) {
294 if (!Array.isArray(this.buildMeta.providedExports)) return null;
295 return this.buildMeta.providedExports.includes(exportName);
296 }
297
298 toString() {
299 return `Module[${this.id || this.debugId}]`;
300 }
301
302 needRebuild(fileTimestamps, contextTimestamps) {
303 return true;
304 }
305
306 /**
307 * @param {Hash} hash the hash used to track dependencies
308 * @returns {void}
309 */
310 updateHash(hash) {
311 hash.update(`${this.id}`);
312 hash.update(JSON.stringify(this.usedExports));
313 super.updateHash(hash);
314 }
315
316 sortItems(sortChunks) {
317 super.sortItems();
318 if (sortChunks) this._chunks.sort();
319 this.reasons.sort((a, b) => {
320 if (a.module === b.module) return 0;
321 if (!a.module) return -1;
322 if (!b.module) return 1;
323 return sortById(a.module, b.module);
324 });
325 if (Array.isArray(this.usedExports)) {
326 this.usedExports.sort();
327 }
328 }
329
330 unbuild() {
331 this.dependencies.length = 0;
332 this.blocks.length = 0;
333 this.variables.length = 0;
334 this.buildMeta = undefined;
335 this.buildInfo = undefined;
336 this.disconnect();
337 }
338
339 get arguments() {
340 throw new Error("Module.arguments was removed, there is no replacement.");
341 }
342
343 set arguments(value) {
344 throw new Error("Module.arguments was removed, there is no replacement.");
345 }
346}
347
348// TODO remove in webpack 5
349Object.defineProperty(Module.prototype, "forEachChunk", {
350 configurable: false,
351 value: util.deprecate(
352 /**
353 * @deprecated
354 * @param {function(any, any, Set<any>): void} fn callback function
355 * @returns {void}
356 * @this {Module}
357 */
358 function(fn) {
359 this._chunks.forEach(fn);
360 },
361 "Module.forEachChunk: Use for(const chunk of module.chunksIterable) instead"
362 )
363});
364
365// TODO remove in webpack 5
366Object.defineProperty(Module.prototype, "mapChunks", {
367 configurable: false,
368 value: util.deprecate(
369 /**
370 * @deprecated
371 * @param {function(any, any): void} fn Mapper function
372 * @returns {Array<TODO>} Array of chunks mapped
373 * @this {Module}
374 */
375 function(fn) {
376 return Array.from(this._chunks, fn);
377 },
378 "Module.mapChunks: Use Array.from(module.chunksIterable, fn) instead"
379 )
380});
381
382// TODO remove in webpack 5
383Object.defineProperty(Module.prototype, "entry", {
384 configurable: false,
385 get() {
386 throw new Error("Module.entry was removed. Use Chunk.entryModule");
387 },
388 set() {
389 throw new Error("Module.entry was removed. Use Chunk.entryModule");
390 }
391});
392
393// TODO remove in webpack 5
394Object.defineProperty(Module.prototype, "meta", {
395 configurable: false,
396 get: util.deprecate(
397 /**
398 * @deprecated
399 * @returns {void}
400 * @this {Module}
401 */
402 function() {
403 return this.buildMeta;
404 },
405 "Module.meta was renamed to Module.buildMeta"
406 ),
407 set: util.deprecate(
408 /**
409 * @deprecated
410 * @param {TODO} value Value
411 * @returns {void}
412 * @this {Module}
413 */
414 function(value) {
415 this.buildMeta = value;
416 },
417 "Module.meta was renamed to Module.buildMeta"
418 )
419});
420
421/** @type {function(): string} */
422Module.prototype.identifier = null;
423
424/** @type {function(RequestShortener): string} */
425Module.prototype.readableIdentifier = null;
426
427Module.prototype.build = null;
428Module.prototype.source = null;
429Module.prototype.size = null;
430Module.prototype.nameForCondition = null;
431/** @type {null | function(Chunk): boolean} */
432Module.prototype.chunkCondition = null;
433Module.prototype.updateCacheModule = null;
434
435module.exports = Module;