UNPKG

54.5 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 eslintScope = require("eslint-scope");
9const Referencer = require("eslint-scope/lib/referencer");
10const {
11 CachedSource,
12 ConcatSource,
13 ReplaceSource
14} = require("webpack-sources");
15const ConcatenationScope = require("../ConcatenationScope");
16const { UsageState } = require("../ExportsInfo");
17const Module = require("../Module");
18const RuntimeGlobals = require("../RuntimeGlobals");
19const Template = require("../Template");
20const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency");
21const JavascriptParser = require("../javascript/JavascriptParser");
22const { equals } = require("../util/ArrayHelpers");
23const LazySet = require("../util/LazySet");
24const { concatComparators, keepOriginalOrder } = require("../util/comparators");
25const createHash = require("../util/createHash");
26const { makePathsRelative } = require("../util/identifier");
27const makeSerializable = require("../util/makeSerializable");
28const propertyAccess = require("../util/propertyAccess");
29const {
30 filterRuntime,
31 intersectRuntime,
32 mergeRuntimeCondition,
33 mergeRuntimeConditionNonFalse,
34 runtimeConditionToString,
35 subtractRuntimeCondition
36} = require("../util/runtime");
37
38/** @typedef {import("eslint-scope").Scope} Scope */
39/** @typedef {import("webpack-sources").Source} Source */
40/** @typedef {import("../../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
41/** @typedef {import("../ChunkGraph")} ChunkGraph */
42/** @typedef {import("../Compilation")} Compilation */
43/** @typedef {import("../Dependency")} Dependency */
44/** @typedef {import("../Dependency").UpdateHashContext} UpdateHashContext */
45/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
46/** @typedef {import("../DependencyTemplates")} DependencyTemplates */
47/** @typedef {import("../ExportsInfo").ExportInfo} ExportInfo */
48/** @template T @typedef {import("../InitFragment")<T>} InitFragment */
49/** @typedef {import("../Module").CodeGenerationContext} CodeGenerationContext */
50/** @typedef {import("../Module").CodeGenerationResult} CodeGenerationResult */
51/** @typedef {import("../Module").LibIdentOptions} LibIdentOptions */
52/** @typedef {import("../ModuleGraph")} ModuleGraph */
53/** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */
54/** @typedef {import("../ModuleGraphConnection").ConnectionState} ConnectionState */
55/** @typedef {import("../RequestShortener")} RequestShortener */
56/** @typedef {import("../ResolverFactory").ResolverWithOptions} ResolverWithOptions */
57/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
58/** @typedef {import("../WebpackError")} WebpackError */
59/** @typedef {import("../javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */
60/** @typedef {import("../util/Hash")} Hash */
61/** @typedef {typeof import("../util/Hash")} HashConstructor */
62/** @typedef {import("../util/fs").InputFileSystem} InputFileSystem */
63/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
64
65// fix eslint-scope to support class properties correctly
66// cspell:word Referencer
67const ReferencerClass = Referencer;
68if (!ReferencerClass.prototype.PropertyDefinition) {
69 ReferencerClass.prototype.PropertyDefinition =
70 ReferencerClass.prototype.Property;
71}
72
73/**
74 * @typedef {Object} ReexportInfo
75 * @property {Module} module
76 * @property {string[]} export
77 */
78
79/** @typedef {RawBinding | SymbolBinding} Binding */
80
81/**
82 * @typedef {Object} RawBinding
83 * @property {ModuleInfo} info
84 * @property {string} rawName
85 * @property {string=} comment
86 * @property {string[]} ids
87 * @property {string[]} exportName
88 */
89
90/**
91 * @typedef {Object} SymbolBinding
92 * @property {ConcatenatedModuleInfo} info
93 * @property {string} name
94 * @property {string=} comment
95 * @property {string[]} ids
96 * @property {string[]} exportName
97 */
98
99/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo } ModuleInfo */
100/** @typedef {ConcatenatedModuleInfo | ExternalModuleInfo | ReferenceToModuleInfo } ModuleInfoOrReference */
101
102/**
103 * @typedef {Object} ConcatenatedModuleInfo
104 * @property {"concatenated"} type
105 * @property {Module} module
106 * @property {number} index
107 * @property {Object} ast
108 * @property {Source} internalSource
109 * @property {ReplaceSource} source
110 * @property {InitFragment<ChunkRenderContext>[]=} chunkInitFragments
111 * @property {Iterable<string>} runtimeRequirements
112 * @property {Scope} globalScope
113 * @property {Scope} moduleScope
114 * @property {Map<string, string>} internalNames
115 * @property {Map<string, string>} exportMap
116 * @property {Map<string, string>} rawExportMap
117 * @property {string=} namespaceExportSymbol
118 * @property {string} namespaceObjectName
119 * @property {boolean} interopNamespaceObjectUsed
120 * @property {string} interopNamespaceObjectName
121 * @property {boolean} interopNamespaceObject2Used
122 * @property {string} interopNamespaceObject2Name
123 * @property {boolean} interopDefaultAccessUsed
124 * @property {string} interopDefaultAccessName
125 */
126
127/**
128 * @typedef {Object} ExternalModuleInfo
129 * @property {"external"} type
130 * @property {Module} module
131 * @property {RuntimeSpec | boolean} runtimeCondition
132 * @property {number} index
133 * @property {string} name
134 * @property {boolean} interopNamespaceObjectUsed
135 * @property {string} interopNamespaceObjectName
136 * @property {boolean} interopNamespaceObject2Used
137 * @property {string} interopNamespaceObject2Name
138 * @property {boolean} interopDefaultAccessUsed
139 * @property {string} interopDefaultAccessName
140 */
141
142/**
143 * @typedef {Object} ReferenceToModuleInfo
144 * @property {"reference"} type
145 * @property {RuntimeSpec | boolean} runtimeCondition
146 * @property {ConcatenatedModuleInfo | ExternalModuleInfo} target
147 */
148
149const RESERVED_NAMES = new Set(
150 [
151 // internal names (should always be renamed)
152 ConcatenationScope.DEFAULT_EXPORT,
153 ConcatenationScope.NAMESPACE_OBJECT_EXPORT,
154
155 // keywords
156 "abstract,arguments,async,await,boolean,break,byte,case,catch,char,class,const,continue",
157 "debugger,default,delete,do,double,else,enum,eval,export,extends,false,final,finally,float",
158 "for,function,goto,if,implements,import,in,instanceof,int,interface,let,long,native,new,null",
159 "package,private,protected,public,return,short,static,super,switch,synchronized,this,throw",
160 "throws,transient,true,try,typeof,var,void,volatile,while,with,yield",
161
162 // commonjs/amd
163 "module,__dirname,__filename,exports,require,define",
164
165 // js globals
166 "Array,Date,eval,function,hasOwnProperty,Infinity,isFinite,isNaN,isPrototypeOf,length,Math",
167 "NaN,name,Number,Object,prototype,String,toString,undefined,valueOf",
168
169 // browser globals
170 "alert,all,anchor,anchors,area,assign,blur,button,checkbox,clearInterval,clearTimeout",
171 "clientInformation,close,closed,confirm,constructor,crypto,decodeURI,decodeURIComponent",
172 "defaultStatus,document,element,elements,embed,embeds,encodeURI,encodeURIComponent,escape",
173 "event,fileUpload,focus,form,forms,frame,innerHeight,innerWidth,layer,layers,link,location",
174 "mimeTypes,navigate,navigator,frames,frameRate,hidden,history,image,images,offscreenBuffering",
175 "open,opener,option,outerHeight,outerWidth,packages,pageXOffset,pageYOffset,parent,parseFloat",
176 "parseInt,password,pkcs11,plugin,prompt,propertyIsEnum,radio,reset,screenX,screenY,scroll",
177 "secure,select,self,setInterval,setTimeout,status,submit,taint,text,textarea,top,unescape",
178 "untaint,window",
179
180 // window events
181 "onblur,onclick,onerror,onfocus,onkeydown,onkeypress,onkeyup,onmouseover,onload,onmouseup,onmousedown,onsubmit"
182 ]
183 .join(",")
184 .split(",")
185);
186
187const bySourceOrder = (a, b) => {
188 const aOrder = a.sourceOrder;
189 const bOrder = b.sourceOrder;
190 if (isNaN(aOrder)) {
191 if (!isNaN(bOrder)) {
192 return 1;
193 }
194 } else {
195 if (isNaN(bOrder)) {
196 return -1;
197 }
198 if (aOrder !== bOrder) {
199 return aOrder < bOrder ? -1 : 1;
200 }
201 }
202 return 0;
203};
204
205const joinIterableWithComma = iterable => {
206 // This is more performant than Array.from().join(", ")
207 // as it doesn't create an array
208 let str = "";
209 let first = true;
210 for (const item of iterable) {
211 if (first) {
212 first = false;
213 } else {
214 str += ", ";
215 }
216 str += item;
217 }
218 return str;
219};
220
221/**
222 * @typedef {Object} ConcatenationEntry
223 * @property {"concatenated" | "external"} type
224 * @property {Module} module
225 * @property {RuntimeSpec | boolean} runtimeCondition
226 */
227
228/**
229 * @param {ModuleGraph} moduleGraph the module graph
230 * @param {ModuleInfo} info module info
231 * @param {string[]} exportName exportName
232 * @param {Map<Module, ModuleInfo>} moduleToInfoMap moduleToInfoMap
233 * @param {RuntimeSpec} runtime for which runtime
234 * @param {RequestShortener} requestShortener the request shortener
235 * @param {RuntimeTemplate} runtimeTemplate the runtime template
236 * @param {Set<ConcatenatedModuleInfo>} neededNamespaceObjects modules for which a namespace object should be generated
237 * @param {boolean} asCall asCall
238 * @param {boolean} strictHarmonyModule strictHarmonyModule
239 * @param {boolean | undefined} asiSafe asiSafe
240 * @param {Set<ExportInfo>} alreadyVisited alreadyVisited
241 * @returns {Binding} the final variable
242 */
243const getFinalBinding = (
244 moduleGraph,
245 info,
246 exportName,
247 moduleToInfoMap,
248 runtime,
249 requestShortener,
250 runtimeTemplate,
251 neededNamespaceObjects,
252 asCall,
253 strictHarmonyModule,
254 asiSafe,
255 alreadyVisited = new Set()
256) => {
257 const exportsType = info.module.getExportsType(
258 moduleGraph,
259 strictHarmonyModule
260 );
261 if (exportName.length === 0) {
262 switch (exportsType) {
263 case "default-only":
264 info.interopNamespaceObject2Used = true;
265 return {
266 info,
267 rawName: info.interopNamespaceObject2Name,
268 ids: exportName,
269 exportName
270 };
271 case "default-with-named":
272 info.interopNamespaceObjectUsed = true;
273 return {
274 info,
275 rawName: info.interopNamespaceObjectName,
276 ids: exportName,
277 exportName
278 };
279 case "namespace":
280 case "dynamic":
281 break;
282 default:
283 throw new Error(`Unexpected exportsType ${exportsType}`);
284 }
285 } else {
286 switch (exportsType) {
287 case "namespace":
288 break;
289 case "default-with-named":
290 switch (exportName[0]) {
291 case "default":
292 exportName = exportName.slice(1);
293 break;
294 case "__esModule":
295 return {
296 info,
297 rawName: "/* __esModule */true",
298 ids: exportName.slice(1),
299 exportName
300 };
301 }
302 break;
303 case "default-only": {
304 const exportId = exportName[0];
305 if (exportId === "__esModule") {
306 return {
307 info,
308 rawName: "/* __esModule */true",
309 ids: exportName.slice(1),
310 exportName
311 };
312 }
313 exportName = exportName.slice(1);
314 if (exportId !== "default") {
315 return {
316 info,
317 rawName:
318 "/* non-default import from default-exporting module */undefined",
319 ids: exportName,
320 exportName
321 };
322 }
323 break;
324 }
325 case "dynamic":
326 switch (exportName[0]) {
327 case "default": {
328 exportName = exportName.slice(1);
329 info.interopDefaultAccessUsed = true;
330 const defaultExport = asCall
331 ? `${info.interopDefaultAccessName}()`
332 : asiSafe
333 ? `(${info.interopDefaultAccessName}())`
334 : asiSafe === false
335 ? `;(${info.interopDefaultAccessName}())`
336 : `${info.interopDefaultAccessName}.a`;
337 return {
338 info,
339 rawName: defaultExport,
340 ids: exportName,
341 exportName
342 };
343 }
344 case "__esModule":
345 return {
346 info,
347 rawName: "/* __esModule */true",
348 ids: exportName.slice(1),
349 exportName
350 };
351 }
352 break;
353 default:
354 throw new Error(`Unexpected exportsType ${exportsType}`);
355 }
356 }
357 if (exportName.length === 0) {
358 switch (info.type) {
359 case "concatenated":
360 neededNamespaceObjects.add(info);
361 return {
362 info,
363 rawName: info.namespaceObjectName,
364 ids: exportName,
365 exportName
366 };
367 case "external":
368 return { info, rawName: info.name, ids: exportName, exportName };
369 }
370 }
371 const exportsInfo = moduleGraph.getExportsInfo(info.module);
372 const exportInfo = exportsInfo.getExportInfo(exportName[0]);
373 if (alreadyVisited.has(exportInfo)) {
374 return {
375 info,
376 rawName: "/* circular reexport */ Object(function x() { x() }())",
377 ids: [],
378 exportName
379 };
380 }
381 alreadyVisited.add(exportInfo);
382 switch (info.type) {
383 case "concatenated": {
384 const exportId = exportName[0];
385 if (exportInfo.provided === false) {
386 // It's not provided, but it could be on the prototype
387 neededNamespaceObjects.add(info);
388 return {
389 info,
390 rawName: info.namespaceObjectName,
391 ids: exportName,
392 exportName
393 };
394 }
395 const directExport = info.exportMap && info.exportMap.get(exportId);
396 if (directExport) {
397 const usedName = /** @type {string[]} */ (
398 exportsInfo.getUsedName(exportName, runtime)
399 );
400 if (!usedName) {
401 return {
402 info,
403 rawName: "/* unused export */ undefined",
404 ids: exportName.slice(1),
405 exportName
406 };
407 }
408 return {
409 info,
410 name: directExport,
411 ids: usedName.slice(1),
412 exportName
413 };
414 }
415 const rawExport = info.rawExportMap && info.rawExportMap.get(exportId);
416 if (rawExport) {
417 return {
418 info,
419 rawName: rawExport,
420 ids: exportName.slice(1),
421 exportName
422 };
423 }
424 const reexport = exportInfo.findTarget(moduleGraph, module =>
425 moduleToInfoMap.has(module)
426 );
427 if (reexport === false) {
428 throw new Error(
429 `Target module of reexport from '${info.module.readableIdentifier(
430 requestShortener
431 )}' is not part of the concatenation (export '${exportId}')\nModules in the concatenation:\n${Array.from(
432 moduleToInfoMap,
433 ([m, info]) =>
434 ` * ${info.type} ${m.readableIdentifier(requestShortener)}`
435 ).join("\n")}`
436 );
437 }
438 if (reexport) {
439 const refInfo = moduleToInfoMap.get(reexport.module);
440 return getFinalBinding(
441 moduleGraph,
442 refInfo,
443 reexport.export
444 ? [...reexport.export, ...exportName.slice(1)]
445 : exportName.slice(1),
446 moduleToInfoMap,
447 runtime,
448 requestShortener,
449 runtimeTemplate,
450 neededNamespaceObjects,
451 asCall,
452 info.module.buildMeta.strictHarmonyModule,
453 asiSafe,
454 alreadyVisited
455 );
456 }
457 if (info.namespaceExportSymbol) {
458 const usedName = /** @type {string[]} */ (
459 exportsInfo.getUsedName(exportName, runtime)
460 );
461 return {
462 info,
463 rawName: info.namespaceObjectName,
464 ids: usedName,
465 exportName
466 };
467 }
468 throw new Error(
469 `Cannot get final name for export '${exportName.join(
470 "."
471 )}' of ${info.module.readableIdentifier(requestShortener)}`
472 );
473 }
474
475 case "external": {
476 const used = /** @type {string[]} */ (
477 exportsInfo.getUsedName(exportName, runtime)
478 );
479 if (!used) {
480 return {
481 info,
482 rawName: "/* unused export */ undefined",
483 ids: exportName.slice(1),
484 exportName
485 };
486 }
487 const comment = equals(used, exportName)
488 ? ""
489 : Template.toNormalComment(`${exportName.join(".")}`);
490 return { info, rawName: info.name + comment, ids: used, exportName };
491 }
492 }
493};
494
495/**
496 * @param {ModuleGraph} moduleGraph the module graph
497 * @param {ModuleInfo} info module info
498 * @param {string[]} exportName exportName
499 * @param {Map<Module, ModuleInfo>} moduleToInfoMap moduleToInfoMap
500 * @param {RuntimeSpec} runtime for which runtime
501 * @param {RequestShortener} requestShortener the request shortener
502 * @param {RuntimeTemplate} runtimeTemplate the runtime template
503 * @param {Set<ConcatenatedModuleInfo>} neededNamespaceObjects modules for which a namespace object should be generated
504 * @param {boolean} asCall asCall
505 * @param {boolean} callContext callContext
506 * @param {boolean} strictHarmonyModule strictHarmonyModule
507 * @param {boolean | undefined} asiSafe asiSafe
508 * @returns {string} the final name
509 */
510const getFinalName = (
511 moduleGraph,
512 info,
513 exportName,
514 moduleToInfoMap,
515 runtime,
516 requestShortener,
517 runtimeTemplate,
518 neededNamespaceObjects,
519 asCall,
520 callContext,
521 strictHarmonyModule,
522 asiSafe
523) => {
524 const binding = getFinalBinding(
525 moduleGraph,
526 info,
527 exportName,
528 moduleToInfoMap,
529 runtime,
530 requestShortener,
531 runtimeTemplate,
532 neededNamespaceObjects,
533 asCall,
534 strictHarmonyModule,
535 asiSafe
536 );
537 {
538 const { ids, comment } = binding;
539 let reference;
540 let isPropertyAccess;
541 if ("rawName" in binding) {
542 reference = `${binding.rawName}${comment || ""}${propertyAccess(ids)}`;
543 isPropertyAccess = ids.length > 0;
544 } else {
545 const { info, name: exportId } = binding;
546 const name = info.internalNames.get(exportId);
547 if (!name) {
548 throw new Error(
549 `The export "${exportId}" in "${info.module.readableIdentifier(
550 requestShortener
551 )}" has no internal name (existing names: ${
552 Array.from(
553 info.internalNames,
554 ([name, symbol]) => `${name}: ${symbol}`
555 ).join(", ") || "none"
556 })`
557 );
558 }
559 reference = `${name}${comment || ""}${propertyAccess(ids)}`;
560 isPropertyAccess = ids.length > 1;
561 }
562 if (isPropertyAccess && asCall && callContext === false) {
563 return asiSafe
564 ? `(0,${reference})`
565 : asiSafe === false
566 ? `;(0,${reference})`
567 : `/*#__PURE__*/Object(${reference})`;
568 }
569 return reference;
570 }
571};
572
573const addScopeSymbols = (s, nameSet, scopeSet1, scopeSet2) => {
574 let scope = s;
575 while (scope) {
576 if (scopeSet1.has(scope)) break;
577 if (scopeSet2.has(scope)) break;
578 scopeSet1.add(scope);
579 for (const variable of scope.variables) {
580 nameSet.add(variable.name);
581 }
582 scope = scope.upper;
583 }
584};
585
586const getAllReferences = variable => {
587 let set = variable.references;
588 // Look for inner scope variables too (like in class Foo { t() { Foo } })
589 const identifiers = new Set(variable.identifiers);
590 for (const scope of variable.scope.childScopes) {
591 for (const innerVar of scope.variables) {
592 if (innerVar.identifiers.some(id => identifiers.has(id))) {
593 set = set.concat(innerVar.references);
594 break;
595 }
596 }
597 }
598 return set;
599};
600
601const getPathInAst = (ast, node) => {
602 if (ast === node) {
603 return [];
604 }
605
606 const nr = node.range;
607
608 const enterNode = n => {
609 if (!n) return undefined;
610 const r = n.range;
611 if (r) {
612 if (r[0] <= nr[0] && r[1] >= nr[1]) {
613 const path = getPathInAst(n, node);
614 if (path) {
615 path.push(n);
616 return path;
617 }
618 }
619 }
620 return undefined;
621 };
622
623 if (Array.isArray(ast)) {
624 for (let i = 0; i < ast.length; i++) {
625 const enterResult = enterNode(ast[i]);
626 if (enterResult !== undefined) return enterResult;
627 }
628 } else if (ast && typeof ast === "object") {
629 const keys = Object.keys(ast);
630 for (let i = 0; i < keys.length; i++) {
631 const value = ast[keys[i]];
632 if (Array.isArray(value)) {
633 const pathResult = getPathInAst(value, node);
634 if (pathResult !== undefined) return pathResult;
635 } else if (value && typeof value === "object") {
636 const enterResult = enterNode(value);
637 if (enterResult !== undefined) return enterResult;
638 }
639 }
640 }
641};
642
643const TYPES = new Set(["javascript"]);
644
645class ConcatenatedModule extends Module {
646 /**
647 * @param {Module} rootModule the root module of the concatenation
648 * @param {Set<Module>} modules all modules in the concatenation (including the root module)
649 * @param {RuntimeSpec} runtime the runtime
650 * @param {Object=} associatedObjectForCache object for caching
651 * @param {string | HashConstructor=} hashFunction hash function to use
652 * @returns {ConcatenatedModule} the module
653 */
654 static create(
655 rootModule,
656 modules,
657 runtime,
658 associatedObjectForCache,
659 hashFunction = "md4"
660 ) {
661 const identifier = ConcatenatedModule._createIdentifier(
662 rootModule,
663 modules,
664 associatedObjectForCache,
665 hashFunction
666 );
667 return new ConcatenatedModule({
668 identifier,
669 rootModule,
670 modules,
671 runtime
672 });
673 }
674
675 /**
676 * @param {Object} options options
677 * @param {string} options.identifier the identifier of the module
678 * @param {Module=} options.rootModule the root module of the concatenation
679 * @param {RuntimeSpec} options.runtime the selected runtime
680 * @param {Set<Module>=} options.modules all concatenated modules
681 */
682 constructor({ identifier, rootModule, modules, runtime }) {
683 super("javascript/esm", null, rootModule && rootModule.layer);
684
685 // Info from Factory
686 /** @type {string} */
687 this._identifier = identifier;
688 /** @type {Module} */
689 this.rootModule = rootModule;
690 /** @type {Set<Module>} */
691 this._modules = modules;
692 this._runtime = runtime;
693 this.factoryMeta = rootModule && rootModule.factoryMeta;
694 }
695
696 /**
697 * Assuming this module is in the cache. Update the (cached) module with
698 * the fresh module from the factory. Usually updates internal references
699 * and properties.
700 * @param {Module} module fresh module
701 * @returns {void}
702 */
703 updateCacheModule(module) {
704 throw new Error("Must not be called");
705 }
706
707 /**
708 * @returns {Set<string>} types available (do not mutate)
709 */
710 getSourceTypes() {
711 return TYPES;
712 }
713
714 get modules() {
715 return Array.from(this._modules);
716 }
717
718 /**
719 * @returns {string} a unique identifier of the module
720 */
721 identifier() {
722 return this._identifier;
723 }
724
725 /**
726 * @param {RequestShortener} requestShortener the request shortener
727 * @returns {string} a user readable identifier of the module
728 */
729 readableIdentifier(requestShortener) {
730 return (
731 this.rootModule.readableIdentifier(requestShortener) +
732 ` + ${this._modules.size - 1} modules`
733 );
734 }
735
736 /**
737 * @param {LibIdentOptions} options options
738 * @returns {string | null} an identifier for library inclusion
739 */
740 libIdent(options) {
741 return this.rootModule.libIdent(options);
742 }
743
744 /**
745 * @returns {string | null} absolute path which should be used for condition matching (usually the resource path)
746 */
747 nameForCondition() {
748 return this.rootModule.nameForCondition();
749 }
750
751 /**
752 * @param {ModuleGraph} moduleGraph the module graph
753 * @returns {ConnectionState} how this module should be connected to referencing modules when consumed for side-effects only
754 */
755 getSideEffectsConnectionState(moduleGraph) {
756 return this.rootModule.getSideEffectsConnectionState(moduleGraph);
757 }
758
759 /**
760 * @param {WebpackOptions} options webpack options
761 * @param {Compilation} compilation the compilation
762 * @param {ResolverWithOptions} resolver the resolver
763 * @param {InputFileSystem} fs the file system
764 * @param {function(WebpackError=): void} callback callback function
765 * @returns {void}
766 */
767 build(options, compilation, resolver, fs, callback) {
768 const { rootModule } = this;
769 this.buildInfo = {
770 strict: true,
771 cacheable: true,
772 moduleArgument: rootModule.buildInfo.moduleArgument,
773 exportsArgument: rootModule.buildInfo.exportsArgument,
774 fileDependencies: new LazySet(),
775 contextDependencies: new LazySet(),
776 missingDependencies: new LazySet(),
777 topLevelDeclarations: new Set(),
778 assets: undefined
779 };
780 this.buildMeta = rootModule.buildMeta;
781 this.clearDependenciesAndBlocks();
782 this.clearWarningsAndErrors();
783
784 for (const m of this._modules) {
785 // populate cacheable
786 if (!m.buildInfo.cacheable) {
787 this.buildInfo.cacheable = false;
788 }
789
790 // populate dependencies
791 for (const d of m.dependencies.filter(
792 dep =>
793 !(dep instanceof HarmonyImportDependency) ||
794 !this._modules.has(compilation.moduleGraph.getModule(dep))
795 )) {
796 this.dependencies.push(d);
797 }
798 // populate blocks
799 for (const d of m.blocks) {
800 this.blocks.push(d);
801 }
802
803 // populate warnings
804 const warnings = m.getWarnings();
805 if (warnings !== undefined) {
806 for (const warning of warnings) {
807 this.addWarning(warning);
808 }
809 }
810
811 // populate errors
812 const errors = m.getErrors();
813 if (errors !== undefined) {
814 for (const error of errors) {
815 this.addError(error);
816 }
817 }
818
819 // populate topLevelDeclarations
820 if (m.buildInfo.topLevelDeclarations) {
821 const topLevelDeclarations = this.buildInfo.topLevelDeclarations;
822 if (topLevelDeclarations !== undefined) {
823 for (const decl of m.buildInfo.topLevelDeclarations) {
824 // reserved names will always be renamed
825 if (RESERVED_NAMES.has(decl)) continue;
826 // TODO actually this is incorrect since with renaming there could be more
827 // We should do the renaming during build
828 topLevelDeclarations.add(decl);
829 }
830 }
831 } else {
832 this.buildInfo.topLevelDeclarations = undefined;
833 }
834
835 // populate assets
836 if (m.buildInfo.assets) {
837 if (this.buildInfo.assets === undefined) {
838 this.buildInfo.assets = Object.create(null);
839 }
840 Object.assign(this.buildInfo.assets, m.buildInfo.assets);
841 }
842 if (m.buildInfo.assetsInfo) {
843 if (this.buildInfo.assetsInfo === undefined) {
844 this.buildInfo.assetsInfo = new Map();
845 }
846 for (const [key, value] of m.buildInfo.assetsInfo) {
847 this.buildInfo.assetsInfo.set(key, value);
848 }
849 }
850 }
851 callback();
852 }
853
854 /**
855 * @param {string=} type the source type for which the size should be estimated
856 * @returns {number} the estimated size of the module (must be non-zero)
857 */
858 size(type) {
859 // Guess size from embedded modules
860 let size = 0;
861 for (const module of this._modules) {
862 size += module.size(type);
863 }
864 return size;
865 }
866
867 /**
868 * @private
869 * @param {Module} rootModule the root of the concatenation
870 * @param {Set<Module>} modulesSet a set of modules which should be concatenated
871 * @param {RuntimeSpec} runtime for this runtime
872 * @param {ModuleGraph} moduleGraph the module graph
873 * @returns {ConcatenationEntry[]} concatenation list
874 */
875 _createConcatenationList(rootModule, modulesSet, runtime, moduleGraph) {
876 /** @type {ConcatenationEntry[]} */
877 const list = [];
878 /** @type {Map<Module, RuntimeSpec | true>} */
879 const existingEntries = new Map();
880
881 /**
882 * @param {Module} module a module
883 * @returns {Iterable<{ connection: ModuleGraphConnection, runtimeCondition: RuntimeSpec | true }>} imported modules in order
884 */
885 const getConcatenatedImports = module => {
886 let connections = Array.from(moduleGraph.getOutgoingConnections(module));
887 if (module === rootModule) {
888 for (const c of moduleGraph.getOutgoingConnections(this))
889 connections.push(c);
890 }
891 const references = connections
892 .filter(connection => {
893 if (!(connection.dependency instanceof HarmonyImportDependency))
894 return false;
895 return (
896 connection &&
897 connection.resolvedOriginModule === module &&
898 connection.module &&
899 connection.isTargetActive(runtime)
900 );
901 })
902 .map(connection => ({
903 connection,
904 sourceOrder: /** @type {HarmonyImportDependency} */ (
905 connection.dependency
906 ).sourceOrder
907 }));
908 references.sort(
909 concatComparators(bySourceOrder, keepOriginalOrder(references))
910 );
911 /** @type {Map<Module, { connection: ModuleGraphConnection, runtimeCondition: RuntimeSpec | true }>} */
912 const referencesMap = new Map();
913 for (const { connection } of references) {
914 const runtimeCondition = filterRuntime(runtime, r =>
915 connection.isTargetActive(r)
916 );
917 if (runtimeCondition === false) continue;
918 const module = connection.module;
919 const entry = referencesMap.get(module);
920 if (entry === undefined) {
921 referencesMap.set(module, { connection, runtimeCondition });
922 continue;
923 }
924 entry.runtimeCondition = mergeRuntimeConditionNonFalse(
925 entry.runtimeCondition,
926 runtimeCondition,
927 runtime
928 );
929 }
930 return referencesMap.values();
931 };
932
933 /**
934 * @param {ModuleGraphConnection} connection graph connection
935 * @param {RuntimeSpec | true} runtimeCondition runtime condition
936 * @returns {void}
937 */
938 const enterModule = (connection, runtimeCondition) => {
939 const module = connection.module;
940 if (!module) return;
941 const existingEntry = existingEntries.get(module);
942 if (existingEntry === true) {
943 return;
944 }
945 if (modulesSet.has(module)) {
946 existingEntries.set(module, true);
947 if (runtimeCondition !== true) {
948 throw new Error(
949 `Cannot runtime-conditional concatenate a module (${module.identifier()} in ${this.rootModule.identifier()}, ${runtimeConditionToString(
950 runtimeCondition
951 )}). This should not happen.`
952 );
953 }
954 const imports = getConcatenatedImports(module);
955 for (const { connection, runtimeCondition } of imports)
956 enterModule(connection, runtimeCondition);
957 list.push({
958 type: "concatenated",
959 module: connection.module,
960 runtimeCondition
961 });
962 } else {
963 if (existingEntry !== undefined) {
964 const reducedRuntimeCondition = subtractRuntimeCondition(
965 runtimeCondition,
966 existingEntry,
967 runtime
968 );
969 if (reducedRuntimeCondition === false) return;
970 runtimeCondition = reducedRuntimeCondition;
971 existingEntries.set(
972 connection.module,
973 mergeRuntimeConditionNonFalse(
974 existingEntry,
975 runtimeCondition,
976 runtime
977 )
978 );
979 } else {
980 existingEntries.set(connection.module, runtimeCondition);
981 }
982 if (list.length > 0) {
983 const lastItem = list[list.length - 1];
984 if (
985 lastItem.type === "external" &&
986 lastItem.module === connection.module
987 ) {
988 lastItem.runtimeCondition = mergeRuntimeCondition(
989 lastItem.runtimeCondition,
990 runtimeCondition,
991 runtime
992 );
993 return;
994 }
995 }
996 list.push({
997 type: "external",
998 get module() {
999 // We need to use a getter here, because the module in the dependency
1000 // could be replaced by some other process (i. e. also replaced with a
1001 // concatenated module)
1002 return connection.module;
1003 },
1004 runtimeCondition
1005 });
1006 }
1007 };
1008
1009 existingEntries.set(rootModule, true);
1010 const imports = getConcatenatedImports(rootModule);
1011 for (const { connection, runtimeCondition } of imports)
1012 enterModule(connection, runtimeCondition);
1013 list.push({
1014 type: "concatenated",
1015 module: rootModule,
1016 runtimeCondition: true
1017 });
1018
1019 return list;
1020 }
1021
1022 /**
1023 * @param {Module} rootModule the root module of the concatenation
1024 * @param {Set<Module>} modules all modules in the concatenation (including the root module)
1025 * @param {Object=} associatedObjectForCache object for caching
1026 * @param {string | HashConstructor=} hashFunction hash function to use
1027 * @returns {string} the identifier
1028 */
1029 static _createIdentifier(
1030 rootModule,
1031 modules,
1032 associatedObjectForCache,
1033 hashFunction = "md4"
1034 ) {
1035 const cachedMakePathsRelative = makePathsRelative.bindContextCache(
1036 rootModule.context,
1037 associatedObjectForCache
1038 );
1039 let identifiers = [];
1040 for (const module of modules) {
1041 identifiers.push(cachedMakePathsRelative(module.identifier()));
1042 }
1043 identifiers.sort();
1044 const hash = createHash(hashFunction);
1045 hash.update(identifiers.join(" "));
1046 return rootModule.identifier() + "|" + hash.digest("hex");
1047 }
1048
1049 /**
1050 * @param {LazySet<string>} fileDependencies set where file dependencies are added to
1051 * @param {LazySet<string>} contextDependencies set where context dependencies are added to
1052 * @param {LazySet<string>} missingDependencies set where missing dependencies are added to
1053 * @param {LazySet<string>} buildDependencies set where build dependencies are added to
1054 */
1055 addCacheDependencies(
1056 fileDependencies,
1057 contextDependencies,
1058 missingDependencies,
1059 buildDependencies
1060 ) {
1061 for (const module of this._modules) {
1062 module.addCacheDependencies(
1063 fileDependencies,
1064 contextDependencies,
1065 missingDependencies,
1066 buildDependencies
1067 );
1068 }
1069 }
1070
1071 /**
1072 * @param {CodeGenerationContext} context context for code generation
1073 * @returns {CodeGenerationResult} result
1074 */
1075 codeGeneration({
1076 dependencyTemplates,
1077 runtimeTemplate,
1078 moduleGraph,
1079 chunkGraph,
1080 runtime: generationRuntime
1081 }) {
1082 /** @type {Set<string>} */
1083 const runtimeRequirements = new Set();
1084 const runtime = intersectRuntime(generationRuntime, this._runtime);
1085
1086 const requestShortener = runtimeTemplate.requestShortener;
1087 // Meta info for each module
1088 const [modulesWithInfo, moduleToInfoMap] = this._getModulesWithInfo(
1089 moduleGraph,
1090 runtime
1091 );
1092
1093 // Set with modules that need a generated namespace object
1094 /** @type {Set<ConcatenatedModuleInfo>} */
1095 const neededNamespaceObjects = new Set();
1096
1097 // Generate source code and analyse scopes
1098 // Prepare a ReplaceSource for the final source
1099 for (const info of moduleToInfoMap.values()) {
1100 this._analyseModule(
1101 moduleToInfoMap,
1102 info,
1103 dependencyTemplates,
1104 runtimeTemplate,
1105 moduleGraph,
1106 chunkGraph,
1107 runtime
1108 );
1109 }
1110
1111 // List of all used names to avoid conflicts
1112 const allUsedNames = new Set(RESERVED_NAMES);
1113
1114 // List of additional names in scope for module references
1115 /** @type {Map<string, { usedNames: Set<string>, alreadyCheckedScopes: Set<TODO> }>} */
1116 const usedNamesInScopeInfo = new Map();
1117 /**
1118 * @param {string} module module identifier
1119 * @param {string} id export id
1120 * @returns {{ usedNames: Set<string>, alreadyCheckedScopes: Set<TODO> }} info
1121 */
1122 const getUsedNamesInScopeInfo = (module, id) => {
1123 const key = `${module}-${id}`;
1124 let info = usedNamesInScopeInfo.get(key);
1125 if (info === undefined) {
1126 info = {
1127 usedNames: new Set(),
1128 alreadyCheckedScopes: new Set()
1129 };
1130 usedNamesInScopeInfo.set(key, info);
1131 }
1132 return info;
1133 };
1134
1135 // Set of already checked scopes
1136 const ignoredScopes = new Set();
1137
1138 // get all global names
1139 for (const info of modulesWithInfo) {
1140 if (info.type === "concatenated") {
1141 // ignore symbols from moduleScope
1142 if (info.moduleScope) {
1143 ignoredScopes.add(info.moduleScope);
1144 }
1145
1146 // The super class expression in class scopes behaves weird
1147 // We get ranges of all super class expressions to make
1148 // renaming to work correctly
1149 const superClassCache = new WeakMap();
1150 const getSuperClassExpressions = scope => {
1151 const cacheEntry = superClassCache.get(scope);
1152 if (cacheEntry !== undefined) return cacheEntry;
1153 const superClassExpressions = [];
1154 for (const childScope of scope.childScopes) {
1155 if (childScope.type !== "class") continue;
1156 const block = childScope.block;
1157 if (
1158 (block.type === "ClassDeclaration" ||
1159 block.type === "ClassExpression") &&
1160 block.superClass
1161 ) {
1162 superClassExpressions.push({
1163 range: block.superClass.range,
1164 variables: childScope.variables
1165 });
1166 }
1167 }
1168 superClassCache.set(scope, superClassExpressions);
1169 return superClassExpressions;
1170 };
1171
1172 // add global symbols
1173 if (info.globalScope) {
1174 for (const reference of info.globalScope.through) {
1175 const name = reference.identifier.name;
1176 if (ConcatenationScope.isModuleReference(name)) {
1177 const match = ConcatenationScope.matchModuleReference(name);
1178 if (!match) continue;
1179 const referencedInfo = modulesWithInfo[match.index];
1180 if (referencedInfo.type === "reference")
1181 throw new Error("Module reference can't point to a reference");
1182 const binding = getFinalBinding(
1183 moduleGraph,
1184 referencedInfo,
1185 match.ids,
1186 moduleToInfoMap,
1187 runtime,
1188 requestShortener,
1189 runtimeTemplate,
1190 neededNamespaceObjects,
1191 false,
1192 info.module.buildMeta.strictHarmonyModule,
1193 true
1194 );
1195 if (!binding.ids) continue;
1196 const { usedNames, alreadyCheckedScopes } =
1197 getUsedNamesInScopeInfo(
1198 binding.info.module.identifier(),
1199 "name" in binding ? binding.name : ""
1200 );
1201 for (const expr of getSuperClassExpressions(reference.from)) {
1202 if (
1203 expr.range[0] <= reference.identifier.range[0] &&
1204 expr.range[1] >= reference.identifier.range[1]
1205 ) {
1206 for (const variable of expr.variables) {
1207 usedNames.add(variable.name);
1208 }
1209 }
1210 }
1211 addScopeSymbols(
1212 reference.from,
1213 usedNames,
1214 alreadyCheckedScopes,
1215 ignoredScopes
1216 );
1217 } else {
1218 allUsedNames.add(name);
1219 }
1220 }
1221 }
1222 }
1223 }
1224
1225 // generate names for symbols
1226 for (const info of moduleToInfoMap.values()) {
1227 const { usedNames: namespaceObjectUsedNames } = getUsedNamesInScopeInfo(
1228 info.module.identifier(),
1229 ""
1230 );
1231 switch (info.type) {
1232 case "concatenated": {
1233 for (const variable of info.moduleScope.variables) {
1234 const name = variable.name;
1235 const { usedNames, alreadyCheckedScopes } = getUsedNamesInScopeInfo(
1236 info.module.identifier(),
1237 name
1238 );
1239 if (allUsedNames.has(name) || usedNames.has(name)) {
1240 const references = getAllReferences(variable);
1241 for (const ref of references) {
1242 addScopeSymbols(
1243 ref.from,
1244 usedNames,
1245 alreadyCheckedScopes,
1246 ignoredScopes
1247 );
1248 }
1249 const newName = this.findNewName(
1250 name,
1251 allUsedNames,
1252 usedNames,
1253 info.module.readableIdentifier(requestShortener)
1254 );
1255 allUsedNames.add(newName);
1256 info.internalNames.set(name, newName);
1257 const source = info.source;
1258 const allIdentifiers = new Set(
1259 references.map(r => r.identifier).concat(variable.identifiers)
1260 );
1261 for (const identifier of allIdentifiers) {
1262 const r = identifier.range;
1263 const path = getPathInAst(info.ast, identifier);
1264 if (path && path.length > 1) {
1265 const maybeProperty =
1266 path[1].type === "AssignmentPattern" &&
1267 path[1].left === path[0]
1268 ? path[2]
1269 : path[1];
1270 if (
1271 maybeProperty.type === "Property" &&
1272 maybeProperty.shorthand
1273 ) {
1274 source.insert(r[1], `: ${newName}`);
1275 continue;
1276 }
1277 }
1278 source.replace(r[0], r[1] - 1, newName);
1279 }
1280 } else {
1281 allUsedNames.add(name);
1282 info.internalNames.set(name, name);
1283 }
1284 }
1285 let namespaceObjectName;
1286 if (info.namespaceExportSymbol) {
1287 namespaceObjectName = info.internalNames.get(
1288 info.namespaceExportSymbol
1289 );
1290 } else {
1291 namespaceObjectName = this.findNewName(
1292 "namespaceObject",
1293 allUsedNames,
1294 namespaceObjectUsedNames,
1295 info.module.readableIdentifier(requestShortener)
1296 );
1297 allUsedNames.add(namespaceObjectName);
1298 }
1299 info.namespaceObjectName = namespaceObjectName;
1300 break;
1301 }
1302 case "external": {
1303 const externalName = this.findNewName(
1304 "",
1305 allUsedNames,
1306 namespaceObjectUsedNames,
1307 info.module.readableIdentifier(requestShortener)
1308 );
1309 allUsedNames.add(externalName);
1310 info.name = externalName;
1311 break;
1312 }
1313 }
1314 if (info.module.buildMeta.exportsType !== "namespace") {
1315 const externalNameInterop = this.findNewName(
1316 "namespaceObject",
1317 allUsedNames,
1318 namespaceObjectUsedNames,
1319 info.module.readableIdentifier(requestShortener)
1320 );
1321 allUsedNames.add(externalNameInterop);
1322 info.interopNamespaceObjectName = externalNameInterop;
1323 }
1324 if (
1325 info.module.buildMeta.exportsType === "default" &&
1326 info.module.buildMeta.defaultObject !== "redirect"
1327 ) {
1328 const externalNameInterop = this.findNewName(
1329 "namespaceObject2",
1330 allUsedNames,
1331 namespaceObjectUsedNames,
1332 info.module.readableIdentifier(requestShortener)
1333 );
1334 allUsedNames.add(externalNameInterop);
1335 info.interopNamespaceObject2Name = externalNameInterop;
1336 }
1337 if (
1338 info.module.buildMeta.exportsType === "dynamic" ||
1339 !info.module.buildMeta.exportsType
1340 ) {
1341 const externalNameInterop = this.findNewName(
1342 "default",
1343 allUsedNames,
1344 namespaceObjectUsedNames,
1345 info.module.readableIdentifier(requestShortener)
1346 );
1347 allUsedNames.add(externalNameInterop);
1348 info.interopDefaultAccessName = externalNameInterop;
1349 }
1350 }
1351
1352 // Find and replace references to modules
1353 for (const info of moduleToInfoMap.values()) {
1354 if (info.type === "concatenated") {
1355 for (const reference of info.globalScope.through) {
1356 const name = reference.identifier.name;
1357 const match = ConcatenationScope.matchModuleReference(name);
1358 if (match) {
1359 const referencedInfo = modulesWithInfo[match.index];
1360 if (referencedInfo.type === "reference")
1361 throw new Error("Module reference can't point to a reference");
1362 const finalName = getFinalName(
1363 moduleGraph,
1364 referencedInfo,
1365 match.ids,
1366 moduleToInfoMap,
1367 runtime,
1368 requestShortener,
1369 runtimeTemplate,
1370 neededNamespaceObjects,
1371 match.call,
1372 !match.directImport,
1373 info.module.buildMeta.strictHarmonyModule,
1374 match.asiSafe
1375 );
1376 const r = reference.identifier.range;
1377 const source = info.source;
1378 // range is extended by 2 chars to cover the appended "._"
1379 source.replace(r[0], r[1] + 1, finalName);
1380 }
1381 }
1382 }
1383 }
1384
1385 // Map with all root exposed used exports
1386 /** @type {Map<string, function(RequestShortener): string>} */
1387 const exportsMap = new Map();
1388
1389 // Set with all root exposed unused exports
1390 /** @type {Set<string>} */
1391 const unusedExports = new Set();
1392
1393 const rootInfo = /** @type {ConcatenatedModuleInfo} */ (
1394 moduleToInfoMap.get(this.rootModule)
1395 );
1396 const strictHarmonyModule = rootInfo.module.buildMeta.strictHarmonyModule;
1397 const exportsInfo = moduleGraph.getExportsInfo(rootInfo.module);
1398 for (const exportInfo of exportsInfo.orderedExports) {
1399 const name = exportInfo.name;
1400 if (exportInfo.provided === false) continue;
1401 const used = exportInfo.getUsedName(undefined, runtime);
1402 if (!used) {
1403 unusedExports.add(name);
1404 continue;
1405 }
1406 exportsMap.set(used, requestShortener => {
1407 try {
1408 const finalName = getFinalName(
1409 moduleGraph,
1410 rootInfo,
1411 [name],
1412 moduleToInfoMap,
1413 runtime,
1414 requestShortener,
1415 runtimeTemplate,
1416 neededNamespaceObjects,
1417 false,
1418 false,
1419 strictHarmonyModule,
1420 true
1421 );
1422 return `/* ${
1423 exportInfo.isReexport() ? "reexport" : "binding"
1424 } */ ${finalName}`;
1425 } catch (e) {
1426 e.message += `\nwhile generating the root export '${name}' (used name: '${used}')`;
1427 throw e;
1428 }
1429 });
1430 }
1431
1432 const result = new ConcatSource();
1433
1434 // add harmony compatibility flag (must be first because of possible circular dependencies)
1435 if (
1436 moduleGraph.getExportsInfo(this).otherExportsInfo.getUsed(runtime) !==
1437 UsageState.Unused
1438 ) {
1439 result.add(`// ESM COMPAT FLAG\n`);
1440 result.add(
1441 runtimeTemplate.defineEsModuleFlagStatement({
1442 exportsArgument: this.exportsArgument,
1443 runtimeRequirements
1444 })
1445 );
1446 }
1447
1448 // define exports
1449 if (exportsMap.size > 0) {
1450 runtimeRequirements.add(RuntimeGlobals.exports);
1451 runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
1452 const definitions = [];
1453 for (const [key, value] of exportsMap) {
1454 definitions.push(
1455 `\n ${JSON.stringify(key)}: ${runtimeTemplate.returningFunction(
1456 value(requestShortener)
1457 )}`
1458 );
1459 }
1460 result.add(`\n// EXPORTS\n`);
1461 result.add(
1462 `${RuntimeGlobals.definePropertyGetters}(${
1463 this.exportsArgument
1464 }, {${definitions.join(",")}\n});\n`
1465 );
1466 }
1467
1468 // list unused exports
1469 if (unusedExports.size > 0) {
1470 result.add(
1471 `\n// UNUSED EXPORTS: ${joinIterableWithComma(unusedExports)}\n`
1472 );
1473 }
1474
1475 // generate namespace objects
1476 const namespaceObjectSources = new Map();
1477 for (const info of neededNamespaceObjects) {
1478 if (info.namespaceExportSymbol) continue;
1479 const nsObj = [];
1480 const exportsInfo = moduleGraph.getExportsInfo(info.module);
1481 for (const exportInfo of exportsInfo.orderedExports) {
1482 if (exportInfo.provided === false) continue;
1483 const usedName = exportInfo.getUsedName(undefined, runtime);
1484 if (usedName) {
1485 const finalName = getFinalName(
1486 moduleGraph,
1487 info,
1488 [exportInfo.name],
1489 moduleToInfoMap,
1490 runtime,
1491 requestShortener,
1492 runtimeTemplate,
1493 neededNamespaceObjects,
1494 false,
1495 undefined,
1496 info.module.buildMeta.strictHarmonyModule,
1497 true
1498 );
1499 nsObj.push(
1500 `\n ${JSON.stringify(
1501 usedName
1502 )}: ${runtimeTemplate.returningFunction(finalName)}`
1503 );
1504 }
1505 }
1506 const name = info.namespaceObjectName;
1507 const defineGetters =
1508 nsObj.length > 0
1509 ? `${RuntimeGlobals.definePropertyGetters}(${name}, {${nsObj.join(
1510 ","
1511 )}\n});\n`
1512 : "";
1513 if (nsObj.length > 0)
1514 runtimeRequirements.add(RuntimeGlobals.definePropertyGetters);
1515 namespaceObjectSources.set(
1516 info,
1517 `
1518// NAMESPACE OBJECT: ${info.module.readableIdentifier(requestShortener)}
1519var ${name} = {};
1520${RuntimeGlobals.makeNamespaceObject}(${name});
1521${defineGetters}`
1522 );
1523 runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
1524 }
1525
1526 // define required namespace objects (must be before evaluation modules)
1527 for (const info of modulesWithInfo) {
1528 if (info.type === "concatenated") {
1529 const source = namespaceObjectSources.get(info);
1530 if (!source) continue;
1531 result.add(source);
1532 }
1533 }
1534
1535 const chunkInitFragments = [];
1536
1537 // evaluate modules in order
1538 for (const rawInfo of modulesWithInfo) {
1539 let name;
1540 let isConditional = false;
1541 const info = rawInfo.type === "reference" ? rawInfo.target : rawInfo;
1542 switch (info.type) {
1543 case "concatenated": {
1544 result.add(
1545 `\n;// CONCATENATED MODULE: ${info.module.readableIdentifier(
1546 requestShortener
1547 )}\n`
1548 );
1549 result.add(info.source);
1550 if (info.chunkInitFragments) {
1551 for (const f of info.chunkInitFragments) chunkInitFragments.push(f);
1552 }
1553 if (info.runtimeRequirements) {
1554 for (const r of info.runtimeRequirements) {
1555 runtimeRequirements.add(r);
1556 }
1557 }
1558 name = info.namespaceObjectName;
1559 break;
1560 }
1561 case "external": {
1562 result.add(
1563 `\n// EXTERNAL MODULE: ${info.module.readableIdentifier(
1564 requestShortener
1565 )}\n`
1566 );
1567 runtimeRequirements.add(RuntimeGlobals.require);
1568 const { runtimeCondition } =
1569 /** @type {ExternalModuleInfo | ReferenceToModuleInfo} */ (rawInfo);
1570 const condition = runtimeTemplate.runtimeConditionExpression({
1571 chunkGraph,
1572 runtimeCondition,
1573 runtime,
1574 runtimeRequirements
1575 });
1576 if (condition !== "true") {
1577 isConditional = true;
1578 result.add(`if (${condition}) {\n`);
1579 }
1580 result.add(
1581 `var ${info.name} = __webpack_require__(${JSON.stringify(
1582 chunkGraph.getModuleId(info.module)
1583 )});`
1584 );
1585 name = info.name;
1586 break;
1587 }
1588 default:
1589 // @ts-expect-error never is expected here
1590 throw new Error(`Unsupported concatenation entry type ${info.type}`);
1591 }
1592 if (info.interopNamespaceObjectUsed) {
1593 runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
1594 result.add(
1595 `\nvar ${info.interopNamespaceObjectName} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name}, 2);`
1596 );
1597 }
1598 if (info.interopNamespaceObject2Used) {
1599 runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
1600 result.add(
1601 `\nvar ${info.interopNamespaceObject2Name} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${name});`
1602 );
1603 }
1604 if (info.interopDefaultAccessUsed) {
1605 runtimeRequirements.add(RuntimeGlobals.compatGetDefaultExport);
1606 result.add(
1607 `\nvar ${info.interopDefaultAccessName} = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${name});`
1608 );
1609 }
1610 if (isConditional) {
1611 result.add("\n}");
1612 }
1613 }
1614
1615 const data = new Map();
1616 if (chunkInitFragments.length > 0)
1617 data.set("chunkInitFragments", chunkInitFragments);
1618
1619 /** @type {CodeGenerationResult} */
1620 const resultEntry = {
1621 sources: new Map([["javascript", new CachedSource(result)]]),
1622 data,
1623 runtimeRequirements
1624 };
1625
1626 return resultEntry;
1627 }
1628
1629 /**
1630 * @param {Map<Module, ModuleInfo>} modulesMap modulesMap
1631 * @param {ModuleInfo} info info
1632 * @param {DependencyTemplates} dependencyTemplates dependencyTemplates
1633 * @param {RuntimeTemplate} runtimeTemplate runtimeTemplate
1634 * @param {ModuleGraph} moduleGraph moduleGraph
1635 * @param {ChunkGraph} chunkGraph chunkGraph
1636 * @param {RuntimeSpec} runtime runtime
1637 */
1638 _analyseModule(
1639 modulesMap,
1640 info,
1641 dependencyTemplates,
1642 runtimeTemplate,
1643 moduleGraph,
1644 chunkGraph,
1645 runtime
1646 ) {
1647 if (info.type === "concatenated") {
1648 const m = info.module;
1649 try {
1650 // Create a concatenation scope to track and capture information
1651 const concatenationScope = new ConcatenationScope(modulesMap, info);
1652
1653 // TODO cache codeGeneration results
1654 const codeGenResult = m.codeGeneration({
1655 dependencyTemplates,
1656 runtimeTemplate,
1657 moduleGraph,
1658 chunkGraph,
1659 runtime,
1660 concatenationScope
1661 });
1662 const source = codeGenResult.sources.get("javascript");
1663 const data = codeGenResult.data;
1664 const chunkInitFragments = data && data.get("chunkInitFragments");
1665 const code = source.source().toString();
1666 let ast;
1667 try {
1668 ast = JavascriptParser._parse(code, {
1669 sourceType: "module"
1670 });
1671 } catch (err) {
1672 if (
1673 err.loc &&
1674 typeof err.loc === "object" &&
1675 typeof err.loc.line === "number"
1676 ) {
1677 const lineNumber = err.loc.line;
1678 const lines = code.split("\n");
1679 err.message +=
1680 "\n| " +
1681 lines
1682 .slice(Math.max(0, lineNumber - 3), lineNumber + 2)
1683 .join("\n| ");
1684 }
1685 throw err;
1686 }
1687 const scopeManager = eslintScope.analyze(ast, {
1688 ecmaVersion: 6,
1689 sourceType: "module",
1690 optimistic: true,
1691 ignoreEval: true,
1692 impliedStrict: true
1693 });
1694 const globalScope = scopeManager.acquire(ast);
1695 const moduleScope = globalScope.childScopes[0];
1696 const resultSource = new ReplaceSource(source);
1697 info.runtimeRequirements = codeGenResult.runtimeRequirements;
1698 info.ast = ast;
1699 info.internalSource = source;
1700 info.source = resultSource;
1701 info.chunkInitFragments = chunkInitFragments;
1702 info.globalScope = globalScope;
1703 info.moduleScope = moduleScope;
1704 } catch (err) {
1705 err.message += `\nwhile analysing module ${m.identifier()} for concatenation`;
1706 throw err;
1707 }
1708 }
1709 }
1710
1711 /**
1712 * @param {ModuleGraph} moduleGraph the module graph
1713 * @param {RuntimeSpec} runtime the runtime
1714 * @returns {[ModuleInfoOrReference[], Map<Module, ModuleInfo>]} module info items
1715 */
1716 _getModulesWithInfo(moduleGraph, runtime) {
1717 const orderedConcatenationList = this._createConcatenationList(
1718 this.rootModule,
1719 this._modules,
1720 runtime,
1721 moduleGraph
1722 );
1723 /** @type {Map<Module, ModuleInfo>} */
1724 const map = new Map();
1725 const list = orderedConcatenationList.map((info, index) => {
1726 let item = map.get(info.module);
1727 if (item === undefined) {
1728 switch (info.type) {
1729 case "concatenated":
1730 item = {
1731 type: "concatenated",
1732 module: info.module,
1733 index,
1734 ast: undefined,
1735 internalSource: undefined,
1736 runtimeRequirements: undefined,
1737 source: undefined,
1738 globalScope: undefined,
1739 moduleScope: undefined,
1740 internalNames: new Map(),
1741 exportMap: undefined,
1742 rawExportMap: undefined,
1743 namespaceExportSymbol: undefined,
1744 namespaceObjectName: undefined,
1745 interopNamespaceObjectUsed: false,
1746 interopNamespaceObjectName: undefined,
1747 interopNamespaceObject2Used: false,
1748 interopNamespaceObject2Name: undefined,
1749 interopDefaultAccessUsed: false,
1750 interopDefaultAccessName: undefined
1751 };
1752 break;
1753 case "external":
1754 item = {
1755 type: "external",
1756 module: info.module,
1757 runtimeCondition: info.runtimeCondition,
1758 index,
1759 name: undefined,
1760 interopNamespaceObjectUsed: false,
1761 interopNamespaceObjectName: undefined,
1762 interopNamespaceObject2Used: false,
1763 interopNamespaceObject2Name: undefined,
1764 interopDefaultAccessUsed: false,
1765 interopDefaultAccessName: undefined
1766 };
1767 break;
1768 default:
1769 throw new Error(
1770 `Unsupported concatenation entry type ${info.type}`
1771 );
1772 }
1773 map.set(item.module, item);
1774 return item;
1775 } else {
1776 /** @type {ReferenceToModuleInfo} */
1777 const ref = {
1778 type: "reference",
1779 runtimeCondition: info.runtimeCondition,
1780 target: item
1781 };
1782 return ref;
1783 }
1784 });
1785 return [list, map];
1786 }
1787
1788 findNewName(oldName, usedNamed1, usedNamed2, extraInfo) {
1789 let name = oldName;
1790
1791 if (name === ConcatenationScope.DEFAULT_EXPORT) {
1792 name = "";
1793 }
1794 if (name === ConcatenationScope.NAMESPACE_OBJECT_EXPORT) {
1795 name = "namespaceObject";
1796 }
1797
1798 // Remove uncool stuff
1799 extraInfo = extraInfo.replace(
1800 /\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g,
1801 ""
1802 );
1803
1804 const splittedInfo = extraInfo.split("/");
1805 while (splittedInfo.length) {
1806 name = splittedInfo.pop() + (name ? "_" + name : "");
1807 const nameIdent = Template.toIdentifier(name);
1808 if (
1809 !usedNamed1.has(nameIdent) &&
1810 (!usedNamed2 || !usedNamed2.has(nameIdent))
1811 )
1812 return nameIdent;
1813 }
1814
1815 let i = 0;
1816 let nameWithNumber = Template.toIdentifier(`${name}_${i}`);
1817 while (
1818 usedNamed1.has(nameWithNumber) ||
1819 (usedNamed2 && usedNamed2.has(nameWithNumber))
1820 ) {
1821 i++;
1822 nameWithNumber = Template.toIdentifier(`${name}_${i}`);
1823 }
1824 return nameWithNumber;
1825 }
1826
1827 /**
1828 * @param {Hash} hash the hash used to track dependencies
1829 * @param {UpdateHashContext} context context
1830 * @returns {void}
1831 */
1832 updateHash(hash, context) {
1833 const { chunkGraph, runtime } = context;
1834 for (const info of this._createConcatenationList(
1835 this.rootModule,
1836 this._modules,
1837 intersectRuntime(runtime, this._runtime),
1838 chunkGraph.moduleGraph
1839 )) {
1840 switch (info.type) {
1841 case "concatenated":
1842 info.module.updateHash(hash, context);
1843 break;
1844 case "external":
1845 hash.update(`${chunkGraph.getModuleId(info.module)}`);
1846 // TODO runtimeCondition
1847 break;
1848 }
1849 }
1850 super.updateHash(hash, context);
1851 }
1852
1853 static deserialize(context) {
1854 const obj = new ConcatenatedModule({
1855 identifier: undefined,
1856 rootModule: undefined,
1857 modules: undefined,
1858 runtime: undefined
1859 });
1860 obj.deserialize(context);
1861 return obj;
1862 }
1863}
1864
1865makeSerializable(ConcatenatedModule, "webpack/lib/optimize/ConcatenatedModule");
1866
1867module.exports = ConcatenatedModule;