UNPKG

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