source: trip-planner-front/node_modules/webpack/lib/optimize/ModuleConcatenationPlugin.js

Last change on this file was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 25.9 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const asyncLib = require("neo-async");
9const ChunkGraph = require("../ChunkGraph");
10const ModuleGraph = require("../ModuleGraph");
11const { STAGE_DEFAULT } = require("../OptimizationStages");
12const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
13const { compareModulesByIdentifier } = require("../util/comparators");
14const {
15 intersectRuntime,
16 mergeRuntimeOwned,
17 filterRuntime,
18 runtimeToString,
19 mergeRuntime
20} = require("../util/runtime");
21const ConcatenatedModule = require("./ConcatenatedModule");
22
23/** @typedef {import("../Compilation")} Compilation */
24/** @typedef {import("../Compiler")} Compiler */
25/** @typedef {import("../Module")} Module */
26/** @typedef {import("../RequestShortener")} RequestShortener */
27/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
28
29/**
30 * @typedef {Object} Statistics
31 * @property {number} cached
32 * @property {number} alreadyInConfig
33 * @property {number} invalidModule
34 * @property {number} incorrectChunks
35 * @property {number} incorrectDependency
36 * @property {number} incorrectModuleDependency
37 * @property {number} incorrectChunksOfImporter
38 * @property {number} incorrectRuntimeCondition
39 * @property {number} importerFailed
40 * @property {number} added
41 */
42
43const formatBailoutReason = msg => {
44 return "ModuleConcatenation bailout: " + msg;
45};
46
47class ModuleConcatenationPlugin {
48 constructor(options) {
49 if (typeof options !== "object") options = {};
50 this.options = options;
51 }
52
53 /**
54 * Apply the plugin
55 * @param {Compiler} compiler the compiler instance
56 * @returns {void}
57 */
58 apply(compiler) {
59 compiler.hooks.compilation.tap("ModuleConcatenationPlugin", compilation => {
60 const moduleGraph = compilation.moduleGraph;
61 const bailoutReasonMap = new Map();
62
63 const setBailoutReason = (module, reason) => {
64 setInnerBailoutReason(module, reason);
65 moduleGraph
66 .getOptimizationBailout(module)
67 .push(
68 typeof reason === "function"
69 ? rs => formatBailoutReason(reason(rs))
70 : formatBailoutReason(reason)
71 );
72 };
73
74 const setInnerBailoutReason = (module, reason) => {
75 bailoutReasonMap.set(module, reason);
76 };
77
78 const getInnerBailoutReason = (module, requestShortener) => {
79 const reason = bailoutReasonMap.get(module);
80 if (typeof reason === "function") return reason(requestShortener);
81 return reason;
82 };
83
84 const formatBailoutWarning = (module, problem) => requestShortener => {
85 if (typeof problem === "function") {
86 return formatBailoutReason(
87 `Cannot concat with ${module.readableIdentifier(
88 requestShortener
89 )}: ${problem(requestShortener)}`
90 );
91 }
92 const reason = getInnerBailoutReason(module, requestShortener);
93 const reasonWithPrefix = reason ? `: ${reason}` : "";
94 if (module === problem) {
95 return formatBailoutReason(
96 `Cannot concat with ${module.readableIdentifier(
97 requestShortener
98 )}${reasonWithPrefix}`
99 );
100 } else {
101 return formatBailoutReason(
102 `Cannot concat with ${module.readableIdentifier(
103 requestShortener
104 )} because of ${problem.readableIdentifier(
105 requestShortener
106 )}${reasonWithPrefix}`
107 );
108 }
109 };
110
111 compilation.hooks.optimizeChunkModules.tapAsync(
112 {
113 name: "ModuleConcatenationPlugin",
114 stage: STAGE_DEFAULT
115 },
116 (allChunks, modules, callback) => {
117 const logger = compilation.getLogger(
118 "webpack.ModuleConcatenationPlugin"
119 );
120 const { chunkGraph, moduleGraph } = compilation;
121 const relevantModules = [];
122 const possibleInners = new Set();
123 const context = {
124 chunkGraph,
125 moduleGraph
126 };
127 logger.time("select relevant modules");
128 for (const module of modules) {
129 let canBeRoot = true;
130 let canBeInner = true;
131
132 const bailoutReason = module.getConcatenationBailoutReason(context);
133 if (bailoutReason) {
134 setBailoutReason(module, bailoutReason);
135 continue;
136 }
137
138 // Must not be an async module
139 if (moduleGraph.isAsync(module)) {
140 setBailoutReason(module, `Module is async`);
141 continue;
142 }
143
144 // Must be in strict mode
145 if (!module.buildInfo.strict) {
146 setBailoutReason(module, `Module is not in strict mode`);
147 continue;
148 }
149
150 // Module must be in any chunk (we don't want to do useless work)
151 if (chunkGraph.getNumberOfModuleChunks(module) === 0) {
152 setBailoutReason(module, "Module is not in any chunk");
153 continue;
154 }
155
156 // Exports must be known (and not dynamic)
157 const exportsInfo = moduleGraph.getExportsInfo(module);
158 const relevantExports = exportsInfo.getRelevantExports(undefined);
159 const unknownReexports = relevantExports.filter(exportInfo => {
160 return (
161 exportInfo.isReexport() && !exportInfo.getTarget(moduleGraph)
162 );
163 });
164 if (unknownReexports.length > 0) {
165 setBailoutReason(
166 module,
167 `Reexports in this module do not have a static target (${Array.from(
168 unknownReexports,
169 exportInfo =>
170 `${
171 exportInfo.name || "other exports"
172 }: ${exportInfo.getUsedInfo()}`
173 ).join(", ")})`
174 );
175 continue;
176 }
177
178 // Root modules must have a static list of exports
179 const unknownProvidedExports = relevantExports.filter(
180 exportInfo => {
181 return exportInfo.provided !== true;
182 }
183 );
184 if (unknownProvidedExports.length > 0) {
185 setBailoutReason(
186 module,
187 `List of module exports is dynamic (${Array.from(
188 unknownProvidedExports,
189 exportInfo =>
190 `${
191 exportInfo.name || "other exports"
192 }: ${exportInfo.getProvidedInfo()} and ${exportInfo.getUsedInfo()}`
193 ).join(", ")})`
194 );
195 canBeRoot = false;
196 }
197
198 // Module must not be an entry point
199 if (chunkGraph.isEntryModule(module)) {
200 setInnerBailoutReason(module, "Module is an entry point");
201 canBeInner = false;
202 }
203
204 if (canBeRoot) relevantModules.push(module);
205 if (canBeInner) possibleInners.add(module);
206 }
207 logger.timeEnd("select relevant modules");
208 logger.debug(
209 `${relevantModules.length} potential root modules, ${possibleInners.size} potential inner modules`
210 );
211 // sort by depth
212 // modules with lower depth are more likely suited as roots
213 // this improves performance, because modules already selected as inner are skipped
214 logger.time("sort relevant modules");
215 relevantModules.sort((a, b) => {
216 return moduleGraph.getDepth(a) - moduleGraph.getDepth(b);
217 });
218 logger.timeEnd("sort relevant modules");
219
220 /** @type {Statistics} */
221 const stats = {
222 cached: 0,
223 alreadyInConfig: 0,
224 invalidModule: 0,
225 incorrectChunks: 0,
226 incorrectDependency: 0,
227 incorrectModuleDependency: 0,
228 incorrectChunksOfImporter: 0,
229 incorrectRuntimeCondition: 0,
230 importerFailed: 0,
231 added: 0
232 };
233 let statsCandidates = 0;
234 let statsSizeSum = 0;
235 let statsEmptyConfigurations = 0;
236
237 logger.time("find modules to concatenate");
238 const concatConfigurations = [];
239 const usedAsInner = new Set();
240 for (const currentRoot of relevantModules) {
241 // when used by another configuration as inner:
242 // the other configuration is better and we can skip this one
243 // TODO reconsider that when it's only used in a different runtime
244 if (usedAsInner.has(currentRoot)) continue;
245
246 let chunkRuntime = undefined;
247 for (const r of chunkGraph.getModuleRuntimes(currentRoot)) {
248 chunkRuntime = mergeRuntimeOwned(chunkRuntime, r);
249 }
250 const exportsInfo = moduleGraph.getExportsInfo(currentRoot);
251 const filteredRuntime = filterRuntime(chunkRuntime, r =>
252 exportsInfo.isModuleUsed(r)
253 );
254 const activeRuntime =
255 filteredRuntime === true
256 ? chunkRuntime
257 : filteredRuntime === false
258 ? undefined
259 : filteredRuntime;
260
261 // create a configuration with the root
262 const currentConfiguration = new ConcatConfiguration(
263 currentRoot,
264 activeRuntime
265 );
266
267 // cache failures to add modules
268 const failureCache = new Map();
269
270 // potential optional import candidates
271 /** @type {Set<Module>} */
272 const candidates = new Set();
273
274 // try to add all imports
275 for (const imp of this._getImports(
276 compilation,
277 currentRoot,
278 activeRuntime
279 )) {
280 candidates.add(imp);
281 }
282
283 for (const imp of candidates) {
284 const impCandidates = new Set();
285 const problem = this._tryToAdd(
286 compilation,
287 currentConfiguration,
288 imp,
289 chunkRuntime,
290 activeRuntime,
291 possibleInners,
292 impCandidates,
293 failureCache,
294 chunkGraph,
295 true,
296 stats
297 );
298 if (problem) {
299 failureCache.set(imp, problem);
300 currentConfiguration.addWarning(imp, problem);
301 } else {
302 for (const c of impCandidates) {
303 candidates.add(c);
304 }
305 }
306 }
307 statsCandidates += candidates.size;
308 if (!currentConfiguration.isEmpty()) {
309 const modules = currentConfiguration.getModules();
310 statsSizeSum += modules.size;
311 concatConfigurations.push(currentConfiguration);
312 for (const module of modules) {
313 if (module !== currentConfiguration.rootModule) {
314 usedAsInner.add(module);
315 }
316 }
317 } else {
318 statsEmptyConfigurations++;
319 const optimizationBailouts =
320 moduleGraph.getOptimizationBailout(currentRoot);
321 for (const warning of currentConfiguration.getWarningsSorted()) {
322 optimizationBailouts.push(
323 formatBailoutWarning(warning[0], warning[1])
324 );
325 }
326 }
327 }
328 logger.timeEnd("find modules to concatenate");
329 logger.debug(
330 `${
331 concatConfigurations.length
332 } successful concat configurations (avg size: ${
333 statsSizeSum / concatConfigurations.length
334 }), ${statsEmptyConfigurations} bailed out completely`
335 );
336 logger.debug(
337 `${statsCandidates} candidates were considered for adding (${stats.cached} cached failure, ${stats.alreadyInConfig} already in config, ${stats.invalidModule} invalid module, ${stats.incorrectChunks} incorrect chunks, ${stats.incorrectDependency} incorrect dependency, ${stats.incorrectChunksOfImporter} incorrect chunks of importer, ${stats.incorrectModuleDependency} incorrect module dependency, ${stats.incorrectRuntimeCondition} incorrect runtime condition, ${stats.importerFailed} importer failed, ${stats.added} added)`
338 );
339 // HACK: Sort configurations by length and start with the longest one
340 // to get the biggest groups possible. Used modules are marked with usedModules
341 // TODO: Allow to reuse existing configuration while trying to add dependencies.
342 // This would improve performance. O(n^2) -> O(n)
343 logger.time(`sort concat configurations`);
344 concatConfigurations.sort((a, b) => {
345 return b.modules.size - a.modules.size;
346 });
347 logger.timeEnd(`sort concat configurations`);
348 const usedModules = new Set();
349
350 logger.time("create concatenated modules");
351 asyncLib.each(
352 concatConfigurations,
353 (concatConfiguration, callback) => {
354 const rootModule = concatConfiguration.rootModule;
355
356 // Avoid overlapping configurations
357 // TODO: remove this when todo above is fixed
358 if (usedModules.has(rootModule)) return callback();
359 const modules = concatConfiguration.getModules();
360 for (const m of modules) {
361 usedModules.add(m);
362 }
363
364 // Create a new ConcatenatedModule
365 let newModule = ConcatenatedModule.create(
366 rootModule,
367 modules,
368 concatConfiguration.runtime,
369 compiler.root
370 );
371
372 const build = () => {
373 newModule.build(
374 compiler.options,
375 compilation,
376 null,
377 null,
378 err => {
379 if (err) {
380 if (!err.module) {
381 err.module = newModule;
382 }
383 return callback(err);
384 }
385 integrate();
386 }
387 );
388 };
389
390 const integrate = () => {
391 ChunkGraph.setChunkGraphForModule(newModule, chunkGraph);
392 ModuleGraph.setModuleGraphForModule(newModule, moduleGraph);
393
394 for (const warning of concatConfiguration.getWarningsSorted()) {
395 moduleGraph
396 .getOptimizationBailout(newModule)
397 .push(formatBailoutWarning(warning[0], warning[1]));
398 }
399 moduleGraph.cloneModuleAttributes(rootModule, newModule);
400 for (const m of modules) {
401 // add to builtModules when one of the included modules was built
402 if (compilation.builtModules.has(m)) {
403 compilation.builtModules.add(newModule);
404 }
405 if (m !== rootModule) {
406 // attach external references to the concatenated module too
407 moduleGraph.copyOutgoingModuleConnections(
408 m,
409 newModule,
410 c => {
411 return (
412 c.originModule === m &&
413 !(
414 c.dependency instanceof HarmonyImportDependency &&
415 modules.has(c.module)
416 )
417 );
418 }
419 );
420 // remove module from chunk
421 for (const chunk of chunkGraph.getModuleChunksIterable(
422 rootModule
423 )) {
424 chunkGraph.disconnectChunkAndModule(chunk, m);
425 }
426 }
427 }
428 compilation.modules.delete(rootModule);
429 ChunkGraph.clearChunkGraphForModule(rootModule);
430 ModuleGraph.clearModuleGraphForModule(rootModule);
431
432 // remove module from chunk
433 chunkGraph.replaceModule(rootModule, newModule);
434 // replace module references with the concatenated module
435 moduleGraph.moveModuleConnections(rootModule, newModule, c => {
436 const otherModule =
437 c.module === rootModule ? c.originModule : c.module;
438 const innerConnection =
439 c.dependency instanceof HarmonyImportDependency &&
440 modules.has(otherModule);
441 return !innerConnection;
442 });
443 // add concatenated module to the compilation
444 compilation.modules.add(newModule);
445
446 callback();
447 };
448
449 build();
450 },
451 err => {
452 logger.timeEnd("create concatenated modules");
453 process.nextTick(callback.bind(null, err));
454 }
455 );
456 }
457 );
458 });
459 }
460
461 /**
462 * @param {Compilation} compilation the compilation
463 * @param {Module} module the module to be added
464 * @param {RuntimeSpec} runtime the runtime scope
465 * @returns {Set<Module>} the imported modules
466 */
467 _getImports(compilation, module, runtime) {
468 const moduleGraph = compilation.moduleGraph;
469 const set = new Set();
470 for (const dep of module.dependencies) {
471 // Get reference info only for harmony Dependencies
472 if (!(dep instanceof HarmonyImportDependency)) continue;
473
474 const connection = moduleGraph.getConnection(dep);
475 // Reference is valid and has a module
476 if (
477 !connection ||
478 !connection.module ||
479 !connection.isTargetActive(runtime)
480 ) {
481 continue;
482 }
483
484 const importedNames = compilation.getDependencyReferencedExports(
485 dep,
486 undefined
487 );
488
489 if (
490 importedNames.every(i =>
491 Array.isArray(i) ? i.length > 0 : i.name.length > 0
492 ) ||
493 Array.isArray(moduleGraph.getProvidedExports(module))
494 ) {
495 set.add(connection.module);
496 }
497 }
498 return set;
499 }
500
501 /**
502 * @param {Compilation} compilation webpack compilation
503 * @param {ConcatConfiguration} config concat configuration (will be modified when added)
504 * @param {Module} module the module to be added
505 * @param {RuntimeSpec} runtime the runtime scope of the generated code
506 * @param {RuntimeSpec} activeRuntime the runtime scope of the root module
507 * @param {Set<Module>} possibleModules modules that are candidates
508 * @param {Set<Module>} candidates list of potential candidates (will be added to)
509 * @param {Map<Module, Module | function(RequestShortener): string>} failureCache cache for problematic modules to be more performant
510 * @param {ChunkGraph} chunkGraph the chunk graph
511 * @param {boolean} avoidMutateOnFailure avoid mutating the config when adding fails
512 * @param {Statistics} statistics gathering metrics
513 * @returns {Module | function(RequestShortener): string} the problematic module
514 */
515 _tryToAdd(
516 compilation,
517 config,
518 module,
519 runtime,
520 activeRuntime,
521 possibleModules,
522 candidates,
523 failureCache,
524 chunkGraph,
525 avoidMutateOnFailure,
526 statistics
527 ) {
528 const cacheEntry = failureCache.get(module);
529 if (cacheEntry) {
530 statistics.cached++;
531 return cacheEntry;
532 }
533
534 // Already added?
535 if (config.has(module)) {
536 statistics.alreadyInConfig++;
537 return null;
538 }
539
540 // Not possible to add?
541 if (!possibleModules.has(module)) {
542 statistics.invalidModule++;
543 failureCache.set(module, module); // cache failures for performance
544 return module;
545 }
546
547 // Module must be in the correct chunks
548 const missingChunks = Array.from(
549 chunkGraph.getModuleChunksIterable(config.rootModule)
550 ).filter(chunk => !chunkGraph.isModuleInChunk(module, chunk));
551 if (missingChunks.length > 0) {
552 const problem = requestShortener => {
553 const missingChunksList = Array.from(
554 new Set(missingChunks.map(chunk => chunk.name || "unnamed chunk(s)"))
555 ).sort();
556 const chunks = Array.from(
557 new Set(
558 Array.from(chunkGraph.getModuleChunksIterable(module)).map(
559 chunk => chunk.name || "unnamed chunk(s)"
560 )
561 )
562 ).sort();
563 return `Module ${module.readableIdentifier(
564 requestShortener
565 )} is not in the same chunk(s) (expected in chunk(s) ${missingChunksList.join(
566 ", "
567 )}, module is in chunk(s) ${chunks.join(", ")})`;
568 };
569 statistics.incorrectChunks++;
570 failureCache.set(module, problem); // cache failures for performance
571 return problem;
572 }
573
574 const moduleGraph = compilation.moduleGraph;
575
576 const incomingConnections =
577 moduleGraph.getIncomingConnectionsByOriginModule(module);
578
579 const incomingConnectionsFromNonModules =
580 incomingConnections.get(null) || incomingConnections.get(undefined);
581 if (incomingConnectionsFromNonModules) {
582 const activeNonModulesConnections =
583 incomingConnectionsFromNonModules.filter(connection => {
584 // We are not interested in inactive connections
585 // or connections without dependency
586 return connection.isActive(runtime) || connection.dependency;
587 });
588 if (activeNonModulesConnections.length > 0) {
589 const problem = requestShortener => {
590 const importingExplanations = new Set(
591 activeNonModulesConnections.map(c => c.explanation).filter(Boolean)
592 );
593 const explanations = Array.from(importingExplanations).sort();
594 return `Module ${module.readableIdentifier(
595 requestShortener
596 )} is referenced ${
597 explanations.length > 0
598 ? `by: ${explanations.join(", ")}`
599 : "in an unsupported way"
600 }`;
601 };
602 statistics.incorrectDependency++;
603 failureCache.set(module, problem); // cache failures for performance
604 return problem;
605 }
606 }
607
608 /** @type {Map<Module, readonly ModuleGraph.ModuleGraphConnection[]>} */
609 const incomingConnectionsFromModules = new Map();
610 for (const [originModule, connections] of incomingConnections) {
611 if (originModule) {
612 // Ignore connection from orphan modules
613 if (chunkGraph.getNumberOfModuleChunks(originModule) === 0) continue;
614
615 // We don't care for connections from other runtimes
616 let originRuntime = undefined;
617 for (const r of chunkGraph.getModuleRuntimes(originModule)) {
618 originRuntime = mergeRuntimeOwned(originRuntime, r);
619 }
620
621 if (!intersectRuntime(runtime, originRuntime)) continue;
622
623 // We are not interested in inactive connections
624 const activeConnections = connections.filter(connection =>
625 connection.isActive(runtime)
626 );
627 if (activeConnections.length > 0)
628 incomingConnectionsFromModules.set(originModule, activeConnections);
629 }
630 }
631
632 const incomingModules = Array.from(incomingConnectionsFromModules.keys());
633
634 // Module must be in the same chunks like the referencing module
635 const otherChunkModules = incomingModules.filter(originModule => {
636 for (const chunk of chunkGraph.getModuleChunksIterable(
637 config.rootModule
638 )) {
639 if (!chunkGraph.isModuleInChunk(originModule, chunk)) {
640 return true;
641 }
642 }
643 return false;
644 });
645 if (otherChunkModules.length > 0) {
646 const problem = requestShortener => {
647 const names = otherChunkModules
648 .map(m => m.readableIdentifier(requestShortener))
649 .sort();
650 return `Module ${module.readableIdentifier(
651 requestShortener
652 )} is referenced from different chunks by these modules: ${names.join(
653 ", "
654 )}`;
655 };
656 statistics.incorrectChunksOfImporter++;
657 failureCache.set(module, problem); // cache failures for performance
658 return problem;
659 }
660
661 /** @type {Map<Module, readonly ModuleGraph.ModuleGraphConnection[]>} */
662 const nonHarmonyConnections = new Map();
663 for (const [originModule, connections] of incomingConnectionsFromModules) {
664 const selected = connections.filter(
665 connection =>
666 !connection.dependency ||
667 !(connection.dependency instanceof HarmonyImportDependency)
668 );
669 if (selected.length > 0)
670 nonHarmonyConnections.set(originModule, connections);
671 }
672 if (nonHarmonyConnections.size > 0) {
673 const problem = requestShortener => {
674 const names = Array.from(nonHarmonyConnections)
675 .map(([originModule, connections]) => {
676 return `${originModule.readableIdentifier(
677 requestShortener
678 )} (referenced with ${Array.from(
679 new Set(
680 connections
681 .map(c => c.dependency && c.dependency.type)
682 .filter(Boolean)
683 )
684 )
685 .sort()
686 .join(", ")})`;
687 })
688 .sort();
689 return `Module ${module.readableIdentifier(
690 requestShortener
691 )} is referenced from these modules with unsupported syntax: ${names.join(
692 ", "
693 )}`;
694 };
695 statistics.incorrectModuleDependency++;
696 failureCache.set(module, problem); // cache failures for performance
697 return problem;
698 }
699
700 if (runtime !== undefined && typeof runtime !== "string") {
701 // Module must be consistently referenced in the same runtimes
702 /** @type {{ originModule: Module, runtimeCondition: RuntimeSpec }[]} */
703 const otherRuntimeConnections = [];
704 outer: for (const [
705 originModule,
706 connections
707 ] of incomingConnectionsFromModules) {
708 /** @type {false | RuntimeSpec} */
709 let currentRuntimeCondition = false;
710 for (const connection of connections) {
711 const runtimeCondition = filterRuntime(runtime, runtime => {
712 return connection.isTargetActive(runtime);
713 });
714 if (runtimeCondition === false) continue;
715 if (runtimeCondition === true) continue outer;
716 if (currentRuntimeCondition !== false) {
717 currentRuntimeCondition = mergeRuntime(
718 currentRuntimeCondition,
719 runtimeCondition
720 );
721 } else {
722 currentRuntimeCondition = runtimeCondition;
723 }
724 }
725 if (currentRuntimeCondition !== false) {
726 otherRuntimeConnections.push({
727 originModule,
728 runtimeCondition: currentRuntimeCondition
729 });
730 }
731 }
732 if (otherRuntimeConnections.length > 0) {
733 const problem = requestShortener => {
734 return `Module ${module.readableIdentifier(
735 requestShortener
736 )} is runtime-dependent referenced by these modules: ${Array.from(
737 otherRuntimeConnections,
738 ({ originModule, runtimeCondition }) =>
739 `${originModule.readableIdentifier(
740 requestShortener
741 )} (expected runtime ${runtimeToString(
742 runtime
743 )}, module is only referenced in ${runtimeToString(
744 /** @type {RuntimeSpec} */ (runtimeCondition)
745 )})`
746 ).join(", ")}`;
747 };
748 statistics.incorrectRuntimeCondition++;
749 failureCache.set(module, problem); // cache failures for performance
750 return problem;
751 }
752 }
753
754 let backup;
755 if (avoidMutateOnFailure) {
756 backup = config.snapshot();
757 }
758
759 // Add the module
760 config.add(module);
761
762 incomingModules.sort(compareModulesByIdentifier);
763
764 // Every module which depends on the added module must be in the configuration too.
765 for (const originModule of incomingModules) {
766 const problem = this._tryToAdd(
767 compilation,
768 config,
769 originModule,
770 runtime,
771 activeRuntime,
772 possibleModules,
773 candidates,
774 failureCache,
775 chunkGraph,
776 false,
777 statistics
778 );
779 if (problem) {
780 if (backup !== undefined) config.rollback(backup);
781 statistics.importerFailed++;
782 failureCache.set(module, problem); // cache failures for performance
783 return problem;
784 }
785 }
786
787 // Add imports to possible candidates list
788 for (const imp of this._getImports(compilation, module, runtime)) {
789 candidates.add(imp);
790 }
791 statistics.added++;
792 return null;
793 }
794}
795
796class ConcatConfiguration {
797 /**
798 * @param {Module} rootModule the root module
799 * @param {RuntimeSpec} runtime the runtime
800 */
801 constructor(rootModule, runtime) {
802 this.rootModule = rootModule;
803 this.runtime = runtime;
804 /** @type {Set<Module>} */
805 this.modules = new Set();
806 this.modules.add(rootModule);
807 /** @type {Map<Module, Module | function(RequestShortener): string>} */
808 this.warnings = new Map();
809 }
810
811 add(module) {
812 this.modules.add(module);
813 }
814
815 has(module) {
816 return this.modules.has(module);
817 }
818
819 isEmpty() {
820 return this.modules.size === 1;
821 }
822
823 addWarning(module, problem) {
824 this.warnings.set(module, problem);
825 }
826
827 getWarningsSorted() {
828 return new Map(
829 Array.from(this.warnings).sort((a, b) => {
830 const ai = a[0].identifier();
831 const bi = b[0].identifier();
832 if (ai < bi) return -1;
833 if (ai > bi) return 1;
834 return 0;
835 })
836 );
837 }
838
839 /**
840 * @returns {Set<Module>} modules as set
841 */
842 getModules() {
843 return this.modules;
844 }
845
846 snapshot() {
847 return this.modules.size;
848 }
849
850 rollback(snapshot) {
851 const modules = this.modules;
852 for (const m of modules) {
853 if (snapshot === 0) {
854 modules.delete(m);
855 } else {
856 snapshot--;
857 }
858 }
859 }
860}
861
862module.exports = ModuleConcatenationPlugin;
Note: See TracBrowser for help on using the repository browser.