UNPKG

20.7 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const util = require("util");
9const ExportsInfo = require("./ExportsInfo");
10const ModuleGraphConnection = require("./ModuleGraphConnection");
11
12/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
13/** @typedef {import("./Dependency")} Dependency */
14/** @typedef {import("./ExportsInfo").ExportInfo} ExportInfo */
15/** @typedef {import("./Module")} Module */
16/** @typedef {import("./ModuleProfile")} ModuleProfile */
17/** @typedef {import("./RequestShortener")} RequestShortener */
18/** @template T @typedef {import("./util/SortableSet")<T>} SortableSet<t> */
19/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
20
21/**
22 * @callback OptimizationBailoutFunction
23 * @param {RequestShortener} requestShortener
24 * @returns {string}
25 */
26
27const EMPTY_ARRAY = [];
28
29class ModuleGraphModule {
30 constructor() {
31 /** @type {Set<ModuleGraphConnection>} */
32 this.incomingConnections = new Set();
33 /** @type {Set<ModuleGraphConnection> | undefined} */
34 this.outgoingConnections = undefined;
35 /** @type {Module | null} */
36 this.issuer = undefined;
37 /** @type {(string | OptimizationBailoutFunction)[]} */
38 this.optimizationBailout = [];
39 /** @type {ExportsInfo} */
40 this.exports = new ExportsInfo();
41 /** @type {number} */
42 this.preOrderIndex = null;
43 /** @type {number} */
44 this.postOrderIndex = null;
45 /** @type {number} */
46 this.depth = null;
47 /** @type {ModuleProfile} */
48 this.profile = undefined;
49 /** @type {boolean} */
50 this.async = false;
51 }
52}
53
54class ModuleGraphDependency {
55 constructor() {
56 /** @type {ModuleGraphConnection} */
57 this.connection = undefined;
58 /** @type {Module} */
59 this.parentModule = undefined;
60 /** @type {DependenciesBlock} */
61 this.parentBlock = undefined;
62 }
63}
64
65class ModuleGraph {
66 constructor() {
67 /** @type {Map<Dependency, ModuleGraphDependency>} */
68 this._dependencyMap = new Map();
69 /** @type {Map<Module, ModuleGraphModule>} */
70 this._moduleMap = new Map();
71 /** @type {Map<Module, Set<ModuleGraphConnection>>} */
72 this._originMap = new Map();
73 /** @type {Map<any, Object>} */
74 this._metaMap = new Map();
75
76 // Caching
77 this._cacheModuleGraphModuleKey1 = undefined;
78 this._cacheModuleGraphModuleValue1 = undefined;
79 this._cacheModuleGraphModuleKey2 = undefined;
80 this._cacheModuleGraphModuleValue2 = undefined;
81 this._cacheModuleGraphDependencyKey = undefined;
82 this._cacheModuleGraphDependencyValue = undefined;
83 }
84
85 /**
86 * @param {Module} module the module
87 * @returns {ModuleGraphModule} the internal module
88 */
89 _getModuleGraphModule(module) {
90 if (this._cacheModuleGraphModuleKey1 === module)
91 return this._cacheModuleGraphModuleValue1;
92 if (this._cacheModuleGraphModuleKey2 === module)
93 return this._cacheModuleGraphModuleValue2;
94 let mgm = this._moduleMap.get(module);
95 if (mgm === undefined) {
96 mgm = new ModuleGraphModule();
97 this._moduleMap.set(module, mgm);
98 }
99 this._cacheModuleGraphModuleKey2 = this._cacheModuleGraphModuleKey1;
100 this._cacheModuleGraphModuleValue2 = this._cacheModuleGraphModuleValue1;
101 this._cacheModuleGraphModuleKey1 = module;
102 this._cacheModuleGraphModuleValue1 = mgm;
103 return mgm;
104 }
105
106 /**
107 * @param {Dependency} dependency the dependency
108 * @returns {ModuleGraphDependency} the internal dependency
109 */
110 _getModuleGraphDependency(dependency) {
111 if (this._cacheModuleGraphDependencyKey === dependency)
112 return this._cacheModuleGraphDependencyValue;
113 let mgd = this._dependencyMap.get(dependency);
114 if (mgd === undefined) {
115 mgd = new ModuleGraphDependency();
116 this._dependencyMap.set(dependency, mgd);
117 }
118 this._cacheModuleGraphDependencyKey = dependency;
119 this._cacheModuleGraphDependencyValue = mgd;
120 return mgd;
121 }
122
123 /**
124 * @param {Dependency} dependency the dependency
125 * @param {DependenciesBlock} block parent block
126 * @param {Module} module parent module
127 * @returns {void}
128 */
129 setParents(dependency, block, module) {
130 const mgd = this._getModuleGraphDependency(dependency);
131 mgd.parentBlock = block;
132 mgd.parentModule = module;
133 }
134
135 /**
136 * @param {Dependency} dependency the dependency
137 * @returns {Module} parent module
138 */
139 getParentModule(dependency) {
140 const mgd = this._getModuleGraphDependency(dependency);
141 return mgd.parentModule;
142 }
143
144 /**
145 * @param {Dependency} dependency the dependency
146 * @returns {DependenciesBlock} parent block
147 */
148 getParentBlock(dependency) {
149 const mgd = this._getModuleGraphDependency(dependency);
150 return mgd.parentBlock;
151 }
152
153 /**
154 * @param {Module} originModule the referencing module
155 * @param {Dependency} dependency the referencing dependency
156 * @param {Module} module the referenced module
157 * @returns {void}
158 */
159 setResolvedModule(originModule, dependency, module) {
160 const connection = new ModuleGraphConnection(
161 originModule,
162 dependency,
163 module,
164 undefined,
165 dependency.weak,
166 dependency.getCondition(this)
167 );
168 const mgd = this._getModuleGraphDependency(dependency);
169 mgd.connection = connection;
170 const connections = this._getModuleGraphModule(module).incomingConnections;
171 connections.add(connection);
172 const mgm = this._getModuleGraphModule(originModule);
173 if (mgm.outgoingConnections === undefined) {
174 mgm.outgoingConnections = new Set();
175 }
176 mgm.outgoingConnections.add(connection);
177 }
178
179 /**
180 * @param {Dependency} dependency the referencing dependency
181 * @param {Module} module the referenced module
182 * @returns {void}
183 */
184 updateModule(dependency, module) {
185 const mgd = this._getModuleGraphDependency(dependency);
186 if (mgd.connection.module === module) return;
187 const { connection } = mgd;
188 const newConnection = connection.clone();
189 newConnection.module = module;
190 mgd.connection = newConnection;
191 connection.setActive(false);
192 const originMgm = this._getModuleGraphModule(connection.originModule);
193 originMgm.outgoingConnections.add(newConnection);
194 const targetMgm = this._getModuleGraphModule(module);
195 targetMgm.incomingConnections.add(newConnection);
196 }
197
198 /**
199 * @param {Dependency} dependency the referencing dependency
200 * @returns {void}
201 */
202 removeConnection(dependency) {
203 const mgd = this._getModuleGraphDependency(dependency);
204 const { connection } = mgd;
205 const targetMgm = this._getModuleGraphModule(connection.module);
206 targetMgm.incomingConnections.delete(connection);
207 const originMgm = this._getModuleGraphModule(connection.originModule);
208 originMgm.outgoingConnections.delete(connection);
209 mgd.connection = undefined;
210 }
211
212 /**
213 * @param {Dependency} dependency the referencing dependency
214 * @param {string} explanation an explanation
215 * @returns {void}
216 */
217 addExplanation(dependency, explanation) {
218 const { connection } = this._getModuleGraphDependency(dependency);
219 connection.addExplanation(explanation);
220 }
221
222 /**
223 * @param {Module} sourceModule the source module
224 * @param {Module} targetModule the target module
225 * @returns {void}
226 */
227 cloneModuleAttributes(sourceModule, targetModule) {
228 const oldMgm = this._getModuleGraphModule(sourceModule);
229 const newMgm = this._getModuleGraphModule(targetModule);
230 newMgm.postOrderIndex = oldMgm.postOrderIndex;
231 newMgm.preOrderIndex = oldMgm.preOrderIndex;
232 newMgm.depth = oldMgm.depth;
233 newMgm.exports = oldMgm.exports;
234 newMgm.async = oldMgm.async;
235 }
236
237 /**
238 * @param {Module} module the module
239 * @returns {void}
240 */
241 removeModuleAttributes(module) {
242 const mgm = this._getModuleGraphModule(module);
243 mgm.postOrderIndex = null;
244 mgm.preOrderIndex = null;
245 mgm.depth = null;
246 mgm.async = false;
247 }
248
249 /**
250 * @returns {void}
251 */
252 removeAllModuleAttributes() {
253 for (const mgm of this._moduleMap.values()) {
254 mgm.postOrderIndex = null;
255 mgm.preOrderIndex = null;
256 mgm.depth = null;
257 mgm.async = false;
258 }
259 }
260
261 /**
262 * @param {Module} oldModule the old referencing module
263 * @param {Module} newModule the new referencing module
264 * @param {function(ModuleGraphConnection): boolean} filterConnection filter predicate for replacement
265 * @returns {void}
266 */
267 moveModuleConnections(oldModule, newModule, filterConnection) {
268 if (oldModule === newModule) return;
269 const oldMgm = this._getModuleGraphModule(oldModule);
270 const newMgm = this._getModuleGraphModule(newModule);
271 // Outgoing connections
272 const oldConnections = oldMgm.outgoingConnections;
273 if (oldConnections !== undefined) {
274 if (newMgm.outgoingConnections === undefined) {
275 newMgm.outgoingConnections = new Set();
276 }
277 const newConnections = newMgm.outgoingConnections;
278 for (const connection of oldConnections) {
279 if (filterConnection(connection)) {
280 connection.originModule = newModule;
281 newConnections.add(connection);
282 oldConnections.delete(connection);
283 }
284 }
285 }
286 // Incoming connections
287 const oldConnections2 = oldMgm.incomingConnections;
288 const newConnections2 = newMgm.incomingConnections;
289 for (const connection of oldConnections2) {
290 if (filterConnection(connection)) {
291 connection.module = newModule;
292 newConnections2.add(connection);
293 oldConnections2.delete(connection);
294 }
295 }
296 }
297
298 /**
299 * @param {Module} oldModule the old referencing module
300 * @param {Module} newModule the new referencing module
301 * @param {function(ModuleGraphConnection): boolean} filterConnection filter predicate for replacement
302 * @returns {void}
303 */
304 copyOutgoingModuleConnections(oldModule, newModule, filterConnection) {
305 if (oldModule === newModule) return;
306 const oldMgm = this._getModuleGraphModule(oldModule);
307 const newMgm = this._getModuleGraphModule(newModule);
308 // Outgoing connections
309 const oldConnections = oldMgm.outgoingConnections;
310 if (oldConnections !== undefined) {
311 if (newMgm.outgoingConnections === undefined) {
312 newMgm.outgoingConnections = new Set();
313 }
314 const newConnections = newMgm.outgoingConnections;
315 for (const connection of oldConnections) {
316 if (filterConnection(connection)) {
317 const newConnection = connection.clone();
318 newConnection.originModule = newModule;
319 newConnections.add(newConnection);
320 if (newConnection.module !== undefined) {
321 const otherMgm = this._getModuleGraphModule(newConnection.module);
322 if (otherMgm.incomingConnections === undefined) {
323 otherMgm.incomingConnections = new Set();
324 }
325 otherMgm.incomingConnections.add(newConnection);
326 }
327 }
328 }
329 }
330 }
331
332 /**
333 * @param {Module} module the referenced module
334 * @param {string} explanation an explanation why it's referenced
335 * @returns {void}
336 */
337 addExtraReason(module, explanation) {
338 const connections = this._getModuleGraphModule(module).incomingConnections;
339 connections.add(new ModuleGraphConnection(null, null, module, explanation));
340 }
341
342 /**
343 * @param {Dependency} dependency the dependency to look for a referenced module
344 * @returns {Module} the referenced module
345 */
346 getResolvedModule(dependency) {
347 const { connection } = this._getModuleGraphDependency(dependency);
348 return connection !== undefined ? connection.resolvedModule : null;
349 }
350
351 /**
352 * @param {Dependency} dependency the dependency to look for a referenced module
353 * @returns {ModuleGraphConnection | undefined} the connection
354 */
355 getConnection(dependency) {
356 const { connection } = this._getModuleGraphDependency(dependency);
357 return connection;
358 }
359
360 /**
361 * @param {Dependency} dependency the dependency to look for a referenced module
362 * @returns {Module} the referenced module
363 */
364 getModule(dependency) {
365 const { connection } = this._getModuleGraphDependency(dependency);
366 return connection !== undefined ? connection.module : null;
367 }
368
369 /**
370 * @param {Dependency} dependency the dependency to look for a referencing module
371 * @returns {Module} the referencing module
372 */
373 getOrigin(dependency) {
374 const { connection } = this._getModuleGraphDependency(dependency);
375 return connection !== undefined ? connection.originModule : null;
376 }
377
378 /**
379 * @param {Dependency} dependency the dependency to look for a referencing module
380 * @returns {Module} the original referencing module
381 */
382 getResolvedOrigin(dependency) {
383 const { connection } = this._getModuleGraphDependency(dependency);
384 return connection !== undefined ? connection.resolvedOriginModule : null;
385 }
386
387 /**
388 * @param {Module} module the module
389 * @returns {Iterable<ModuleGraphConnection>} reasons why a module is included
390 */
391 getIncomingConnections(module) {
392 const connections = this._getModuleGraphModule(module).incomingConnections;
393 return connections;
394 }
395
396 /**
397 * @param {Module} module the module
398 * @returns {Iterable<ModuleGraphConnection>} list of outgoing connections
399 */
400 getOutgoingConnections(module) {
401 const connections = this._getModuleGraphModule(module).outgoingConnections;
402 return connections === undefined ? EMPTY_ARRAY : connections;
403 }
404
405 /**
406 * @param {Module} module the module
407 * @returns {ModuleProfile | null} the module profile
408 */
409 getProfile(module) {
410 const mgm = this._getModuleGraphModule(module);
411 return mgm.profile;
412 }
413
414 /**
415 * @param {Module} module the module
416 * @param {ModuleProfile | null} profile the module profile
417 * @returns {void}
418 */
419 setProfile(module, profile) {
420 const mgm = this._getModuleGraphModule(module);
421 mgm.profile = profile;
422 }
423
424 /**
425 * @param {Module} module the module
426 * @returns {Module | null} the issuer module
427 */
428 getIssuer(module) {
429 const mgm = this._getModuleGraphModule(module);
430 return mgm.issuer;
431 }
432
433 /**
434 * @param {Module} module the module
435 * @param {Module | null} issuer the issuer module
436 * @returns {void}
437 */
438 setIssuer(module, issuer) {
439 const mgm = this._getModuleGraphModule(module);
440 mgm.issuer = issuer;
441 }
442
443 /**
444 * @param {Module} module the module
445 * @param {Module | null} issuer the issuer module
446 * @returns {void}
447 */
448 setIssuerIfUnset(module, issuer) {
449 const mgm = this._getModuleGraphModule(module);
450 if (mgm.issuer === undefined) mgm.issuer = issuer;
451 }
452
453 /**
454 * @param {Module} module the module
455 * @returns {(string | OptimizationBailoutFunction)[]} optimization bailouts
456 */
457 getOptimizationBailout(module) {
458 const mgm = this._getModuleGraphModule(module);
459 return mgm.optimizationBailout;
460 }
461
462 /**
463 * @param {Module} module the module
464 * @returns {true | string[] | null} the provided exports
465 */
466 getProvidedExports(module) {
467 const mgm = this._getModuleGraphModule(module);
468 return mgm.exports.getProvidedExports();
469 }
470
471 /**
472 * @param {Module} module the module
473 * @param {string | string[]} exportName a name of an export
474 * @returns {boolean | null} true, if the export is provided by the module.
475 * null, if it's unknown.
476 * false, if it's not provided.
477 */
478 isExportProvided(module, exportName) {
479 const mgm = this._getModuleGraphModule(module);
480 const result = mgm.exports.isExportProvided(exportName);
481 return result === undefined ? null : result;
482 }
483
484 /**
485 * @param {Module} module the module
486 * @returns {ExportsInfo} info about the exports
487 */
488 getExportsInfo(module) {
489 const mgm = this._getModuleGraphModule(module);
490 return mgm.exports;
491 }
492
493 /**
494 * @param {Module} module the module
495 * @param {string} exportName the export
496 * @returns {ExportInfo} info about the export
497 */
498 getExportInfo(module, exportName) {
499 const mgm = this._getModuleGraphModule(module);
500 return mgm.exports.getExportInfo(exportName);
501 }
502
503 /**
504 * @param {Module} module the module
505 * @param {string} exportName the export
506 * @returns {ExportInfo} info about the export (do not modify)
507 */
508 getReadOnlyExportInfo(module, exportName) {
509 const mgm = this._getModuleGraphModule(module);
510 return mgm.exports.getReadOnlyExportInfo(exportName);
511 }
512
513 /**
514 * @param {Module} module the module
515 * @param {RuntimeSpec} runtime the runtime
516 * @returns {false | true | SortableSet<string> | null} the used exports
517 * false: module is not used at all.
518 * true: the module namespace/object export is used.
519 * SortableSet<string>: these export names are used.
520 * empty SortableSet<string>: module is used but no export.
521 * null: unknown, worst case should be assumed.
522 */
523 getUsedExports(module, runtime) {
524 const mgm = this._getModuleGraphModule(module);
525 return mgm.exports.getUsedExports(runtime);
526 }
527
528 /**
529 * @param {Module} module the module
530 * @returns {number} the index of the module
531 */
532 getPreOrderIndex(module) {
533 const mgm = this._getModuleGraphModule(module);
534 return mgm.preOrderIndex;
535 }
536
537 /**
538 * @param {Module} module the module
539 * @returns {number} the index of the module
540 */
541 getPostOrderIndex(module) {
542 const mgm = this._getModuleGraphModule(module);
543 return mgm.postOrderIndex;
544 }
545
546 /**
547 * @param {Module} module the module
548 * @param {number} index the index of the module
549 * @returns {void}
550 */
551 setPreOrderIndex(module, index) {
552 const mgm = this._getModuleGraphModule(module);
553 mgm.preOrderIndex = index;
554 }
555
556 /**
557 * @param {Module} module the module
558 * @param {number} index the index of the module
559 * @returns {boolean} true, if the index was set
560 */
561 setPreOrderIndexIfUnset(module, index) {
562 const mgm = this._getModuleGraphModule(module);
563 if (mgm.preOrderIndex === null) {
564 mgm.preOrderIndex = index;
565 return true;
566 }
567 return false;
568 }
569
570 /**
571 * @param {Module} module the module
572 * @param {number} index the index of the module
573 * @returns {void}
574 */
575 setPostOrderIndex(module, index) {
576 const mgm = this._getModuleGraphModule(module);
577 mgm.postOrderIndex = index;
578 }
579
580 /**
581 * @param {Module} module the module
582 * @param {number} index the index of the module
583 * @returns {boolean} true, if the index was set
584 */
585 setPostOrderIndexIfUnset(module, index) {
586 const mgm = this._getModuleGraphModule(module);
587 if (mgm.postOrderIndex === null) {
588 mgm.postOrderIndex = index;
589 return true;
590 }
591 return false;
592 }
593
594 /**
595 * @param {Module} module the module
596 * @returns {number} the depth of the module
597 */
598 getDepth(module) {
599 const mgm = this._getModuleGraphModule(module);
600 return mgm.depth;
601 }
602
603 /**
604 * @param {Module} module the module
605 * @param {number} depth the depth of the module
606 * @returns {void}
607 */
608 setDepth(module, depth) {
609 const mgm = this._getModuleGraphModule(module);
610 mgm.depth = depth;
611 }
612
613 /**
614 * @param {Module} module the module
615 * @param {number} depth the depth of the module
616 * @returns {boolean} true, if the depth was set
617 */
618 setDepthIfLower(module, depth) {
619 const mgm = this._getModuleGraphModule(module);
620 if (mgm.depth === null || mgm.depth > depth) {
621 mgm.depth = depth;
622 return true;
623 }
624 return false;
625 }
626
627 /**
628 * @param {Module} module the module
629 * @returns {boolean} true, if the module is async
630 */
631 isAsync(module) {
632 const mgm = this._getModuleGraphModule(module);
633 return mgm.async;
634 }
635
636 /**
637 * @param {Module} module the module
638 * @returns {void}
639 */
640 setAsync(module) {
641 const mgm = this._getModuleGraphModule(module);
642 mgm.async = true;
643 }
644
645 /**
646 * @param {any} thing any thing
647 * @returns {Object} metadata
648 */
649 getMeta(thing) {
650 let meta = this._metaMap.get(thing);
651 if (meta === undefined) {
652 meta = Object.create(null);
653 this._metaMap.set(thing, meta);
654 }
655 return meta;
656 }
657
658 // TODO remove in webpack 6
659 /**
660 * @param {Module} module the module
661 * @param {string} deprecateMessage message for the deprecation message
662 * @param {string} deprecationCode code for the deprecation
663 * @returns {ModuleGraph} the module graph
664 */
665 static getModuleGraphForModule(module, deprecateMessage, deprecationCode) {
666 const fn = deprecateMap.get(deprecateMessage);
667 if (fn) return fn(module);
668 const newFn = util.deprecate(
669 /**
670 * @param {Module} module the module
671 * @returns {ModuleGraph} the module graph
672 */
673 module => {
674 const moduleGraph = moduleGraphForModuleMap.get(module);
675 if (!moduleGraph)
676 throw new Error(
677 deprecateMessage +
678 "There was no ModuleGraph assigned to the Module for backward-compat (Use the new API)"
679 );
680 return moduleGraph;
681 },
682 deprecateMessage + ": Use new ModuleGraph API",
683 deprecationCode
684 );
685 deprecateMap.set(deprecateMessage, newFn);
686 return newFn(module);
687 }
688
689 // TODO remove in webpack 6
690 /**
691 * @param {Module} module the module
692 * @param {ModuleGraph} moduleGraph the module graph
693 * @returns {void}
694 */
695 static setModuleGraphForModule(module, moduleGraph) {
696 moduleGraphForModuleMap.set(module, moduleGraph);
697 }
698}
699
700// TODO remove in webpack 6
701/** @type {WeakMap<Module, ModuleGraph>} */
702const moduleGraphForModuleMap = new WeakMap();
703
704// TODO remove in webpack 6
705/** @type {Map<string, (module: Module) => ModuleGraph>} */
706const deprecateMap = new Map();
707
708module.exports = ModuleGraph;
709module.exports.ModuleGraphConnection = ModuleGraphConnection;