source: imaps-frontend/node_modules/webpack/lib/optimize/ModuleConcatenationPlugin.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 4 days ago

F4 Finalna Verzija

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