source: frontend/node_modules/webpack/lib/ExternalModule.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 11 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 39.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 { SyncBailHook } = require("tapable");
9const { OriginalSource, RawSource } = require("webpack-sources");
10const ConcatenationScope = require("./ConcatenationScope");
11const { UsageState } = require("./ExportsInfo");
12const InitFragment = require("./InitFragment");
13const Module = require("./Module");
14const {
15 ASSET_URL_TYPE,
16 ASSET_URL_TYPES,
17 CSS_IMPORT_TYPES,
18 JAVASCRIPT_TYPE,
19 JAVASCRIPT_TYPES
20} = require("./ModuleSourceTypeConstants");
21const { JAVASCRIPT_MODULE_TYPE_DYNAMIC } = require("./ModuleTypeConstants");
22const RuntimeGlobals = require("./RuntimeGlobals");
23const Template = require("./Template");
24const { DEFAULTS } = require("./config/defaults");
25const { ImportPhaseUtils } = require("./dependencies/ImportPhase");
26const StaticExportsDependency = require("./dependencies/StaticExportsDependency");
27const EnvironmentNotSupportAsyncWarning = require("./errors/EnvironmentNotSupportAsyncWarning");
28const createHash = require("./util/createHash");
29const extractUrlAndGlobal = require("./util/extractUrlAndGlobal");
30const makeSerializable = require("./util/makeSerializable");
31const { propertyAccess } = require("./util/property");
32const { register } = require("./util/serialization");
33
34/** @typedef {import("webpack-sources").Source} Source */
35/** @typedef {import("../declarations/WebpackOptions").ExternalsType} ExternalsType */
36/** @typedef {import("../declarations/WebpackOptions").HashFunction} HashFunction */
37/** @typedef {import("./config/defaults").WebpackOptionsNormalizedWithDefaults} WebpackOptions */
38/** @typedef {import("./Chunk")} Chunk */
39/** @typedef {import("./ChunkGraph")} ChunkGraph */
40/** @typedef {import("./Compilation")} Compilation */
41/** @typedef {import("./Compilation").UnsafeCacheData} UnsafeCacheData */
42/** @typedef {import("./Dependency").UpdateHashContext} UpdateHashContext */
43/** @typedef {import("./ExportsInfo")} ExportsInfo */
44/** @typedef {import("./Generator").GenerateContext} GenerateContext */
45/** @typedef {import("./Generator").SourceTypes} SourceTypes */
46/** @typedef {import("./Module").ModuleId} ModuleId */
47/** @typedef {import("./Module").BuildCallback} BuildCallback */
48/** @typedef {import("./Module").BuildInfo} BuildInfo */
49/** @typedef {import("./Module").CodeGenerationContext} CodeGenerationContext */
50/** @typedef {import("./Module").CodeGenerationResult} CodeGenerationResult */
51/** @typedef {import("./Module").CodeGenerationResultData} CodeGenerationResultData */
52/** @typedef {import("./Module").ConcatenationBailoutReasonContext} ConcatenationBailoutReasonContext */
53/** @typedef {import("./Module").LibIdentOptions} LibIdentOptions */
54/** @typedef {import("./Module").LibIdent} LibIdent */
55/** @typedef {import("./Module").NeedBuildCallback} NeedBuildCallback */
56/** @typedef {import("./Module").NeedBuildContext} NeedBuildContext */
57/** @typedef {import("./Module").RuntimeRequirements} RuntimeRequirements */
58/** @typedef {import("./Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
59/** @typedef {import("./Module").Sources} Sources */
60/** @typedef {import("./ModuleGraph")} ModuleGraph */
61/** @typedef {import("./NormalModuleFactory")} NormalModuleFactory */
62/** @typedef {import("./RequestShortener")} RequestShortener */
63/** @typedef {import("./ResolverFactory").ResolverWithOptions} ResolverWithOptions */
64/** @typedef {import("./RuntimeTemplate")} RuntimeTemplate */
65/** @typedef {import("./javascript/JavascriptModulesPlugin").ChunkRenderContext} ChunkRenderContext */
66/** @typedef {import("./javascript/JavascriptParser").ImportAttributes} ImportAttributes */
67/** @typedef {import("./dependencies/ImportPhase").ImportPhaseType} ImportPhaseType */
68/** @typedef {import("./serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
69/** @typedef {import("./serialization/ObjectMiddleware").ObjectSerializerContext} ObjectSerializerContext */
70/** @typedef {import("./util/Hash")} Hash */
71/** @typedef {import("./util/fs").InputFileSystem} InputFileSystem */
72/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
73
74/** @typedef {{ attributes?: ImportAttributes, phase?: ImportPhaseType, externalType: "import" | "module" | undefined }} ImportDependencyMeta */
75/** @typedef {{ layer?: string, supports?: string, media?: string }} CssImportDependencyMeta */
76/** @typedef {{ sourceType: "asset-url" | "css-url" }} AssetDependencyMeta */
77
78/** @typedef {ImportDependencyMeta | CssImportDependencyMeta | AssetDependencyMeta} DependencyMeta */
79
80/**
81 * Defines the source data type used by this module.
82 * @typedef {object} SourceData
83 * @property {boolean=} iife
84 * @property {string=} init
85 * @property {string} expression
86 * @property {InitFragment<ChunkRenderContext>[]=} chunkInitFragments
87 * @property {ReadOnlyRuntimeRequirements=} runtimeRequirements
88 * @property {[string, string][]=} specifiers
89 */
90
91/** @typedef {true | [string, string][]} Imported */
92
93/** @type {RuntimeRequirements} */
94const RUNTIME_REQUIREMENTS = new Set([RuntimeGlobals.module]);
95/** @type {RuntimeRequirements} */
96const RUNTIME_REQUIREMENTS_FOR_SCRIPT = new Set([RuntimeGlobals.loadScript]);
97/** @type {RuntimeRequirements} */
98const RUNTIME_REQUIREMENTS_FOR_MODULE = new Set([
99 RuntimeGlobals.definePropertyGetters
100]);
101/** @type {RuntimeRequirements} */
102const EMPTY_RUNTIME_REQUIREMENTS = new Set();
103
104/**
105 * Gets source for global variable external.
106 * @param {string | string[]} variableName the variable name or path
107 * @param {string} type the module system
108 * @returns {SourceData} the generated source
109 */
110const getSourceForGlobalVariableExternal = (variableName, type) => {
111 if (!Array.isArray(variableName)) {
112 // make it an array as the look up works the same basically
113 variableName = [variableName];
114 }
115
116 // needed for e.g. window["some"]["thing"]
117 const objectLookup = variableName
118 .map((r) => `[${JSON.stringify(r)}]`)
119 .join("");
120 return {
121 iife: type === "this",
122 expression: `${type}${objectLookup}`
123 };
124};
125
126/** @typedef {string | string[]} ModuleAndSpecifiers */
127
128/**
129 * Gets source for common js external.
130 * @param {ModuleAndSpecifiers} moduleAndSpecifiers the module request
131 * @returns {SourceData} the generated source
132 */
133const getSourceForCommonJsExternal = (moduleAndSpecifiers) => {
134 if (!Array.isArray(moduleAndSpecifiers)) {
135 return {
136 expression: `require(${JSON.stringify(moduleAndSpecifiers)})`
137 };
138 }
139 const moduleName = moduleAndSpecifiers[0];
140 return {
141 expression: `require(${JSON.stringify(moduleName)})${propertyAccess(
142 moduleAndSpecifiers,
143 1
144 )}`
145 };
146};
147
148/**
149 * Gets external module node commonjs init fragment.
150 * @param {RuntimeTemplate} runtimeTemplate the runtime template
151 * @returns {InitFragment<ChunkRenderContext>} code
152 */
153const getExternalModuleNodeCommonjsInitFragment = (runtimeTemplate) => {
154 const importMetaName = runtimeTemplate.outputOptions.importMetaName;
155
156 return new InitFragment(
157 `import { createRequire as __WEBPACK_EXTERNAL_createRequire } from ${runtimeTemplate.renderNodePrefixForCoreModule(
158 "module"
159 )};\n${runtimeTemplate.renderConst()} __WEBPACK_EXTERNAL_createRequire_require = __WEBPACK_EXTERNAL_createRequire(${importMetaName}.url);\n`,
160 InitFragment.STAGE_HARMONY_IMPORTS,
161 0,
162 "external module node-commonjs"
163 );
164};
165
166/**
167 * Gets source for common js external in node module.
168 * @param {ModuleAndSpecifiers} moduleAndSpecifiers the module request
169 * @param {RuntimeTemplate} runtimeTemplate the runtime template
170 * @returns {SourceData} the generated source
171 */
172const getSourceForCommonJsExternalInNodeModule = (
173 moduleAndSpecifiers,
174 runtimeTemplate
175) => {
176 const chunkInitFragments = [
177 getExternalModuleNodeCommonjsInitFragment(runtimeTemplate)
178 ];
179 if (!Array.isArray(moduleAndSpecifiers)) {
180 return {
181 chunkInitFragments,
182 expression: `__WEBPACK_EXTERNAL_createRequire_require(${JSON.stringify(
183 moduleAndSpecifiers
184 )})`
185 };
186 }
187 const moduleName = moduleAndSpecifiers[0];
188 return {
189 chunkInitFragments,
190 expression: `__WEBPACK_EXTERNAL_createRequire_require(${JSON.stringify(
191 moduleName
192 )})${propertyAccess(moduleAndSpecifiers, 1)}`
193 };
194};
195
196/**
197 * Gets source for import external.
198 * @param {ModuleAndSpecifiers} moduleAndSpecifiers the module request
199 * @param {RuntimeTemplate} runtimeTemplate the runtime template
200 * @param {ImportDependencyMeta=} dependencyMeta the dependency meta
201 * @returns {SourceData} the generated source
202 */
203const getSourceForImportExternal = (
204 moduleAndSpecifiers,
205 runtimeTemplate,
206 dependencyMeta
207) => {
208 const baseImportName = runtimeTemplate.outputOptions.importFunctionName;
209 if (
210 !runtimeTemplate.supportsDynamicImport() &&
211 (baseImportName === "import" || baseImportName === "module-import")
212 ) {
213 throw new Error(
214 "The target environment doesn't support 'import()' so it's not possible to use external type 'import'"
215 );
216 }
217 const phase = dependencyMeta && dependencyMeta.phase;
218 // `import.defer(…)` and `import.source(…)` are only valid forms of the
219 // native `import(…)` function, so we only emit the phase suffix when the
220 // importFunctionName is the default `"import"`.
221 const importName =
222 baseImportName === "import" && ImportPhaseUtils.isDefer(phase)
223 ? "import.defer"
224 : baseImportName === "import" && ImportPhaseUtils.isSource(phase)
225 ? "import.source"
226 : baseImportName;
227 const attributes =
228 dependencyMeta && dependencyMeta.attributes
229 ? dependencyMeta.attributes._isLegacyAssert
230 ? `, { assert: ${JSON.stringify(
231 dependencyMeta.attributes,
232 importAssertionReplacer
233 )} }`
234 : `, { with: ${JSON.stringify(dependencyMeta.attributes)} }`
235 : "";
236 if (!Array.isArray(moduleAndSpecifiers)) {
237 return {
238 expression: `${importName}(${JSON.stringify(
239 moduleAndSpecifiers
240 )}${attributes});`
241 };
242 }
243 if (moduleAndSpecifiers.length === 1) {
244 return {
245 expression: `${importName}(${JSON.stringify(
246 moduleAndSpecifiers[0]
247 )}${attributes});`
248 };
249 }
250 const moduleName = moduleAndSpecifiers[0];
251 return {
252 expression: `${importName}(${JSON.stringify(
253 moduleName
254 )}${attributes}).then(${runtimeTemplate.returningFunction(
255 `module${propertyAccess(moduleAndSpecifiers, 1)}`,
256 "module"
257 )});`
258 };
259};
260
261/**
262 * Import assertion replacer.
263 * @param {string} key key
264 * @param {ImportAttributes | string | boolean | undefined} value value
265 * @returns {ImportAttributes | string | boolean | undefined} replaced value
266 */
267const importAssertionReplacer = (key, value) => {
268 if (key === "_isLegacyAssert") {
269 return;
270 }
271
272 return value;
273};
274
275/**
276 * Represents ModuleExternalInitFragment.
277 * @extends {InitFragment<GenerateContext>}
278 */
279class ModuleExternalInitFragment extends InitFragment {
280 /**
281 * Creates an instance of ModuleExternalInitFragment.
282 * @param {string} request import source
283 * @param {Imported} imported the imported specifiers
284 * @param {string=} ident recomputed ident
285 * @param {ImportDependencyMeta=} dependencyMeta the dependency meta
286 * @param {HashFunction=} hashFunction the hash function to use
287 */
288 constructor(
289 request,
290 imported,
291 ident,
292 dependencyMeta,
293 hashFunction = DEFAULTS.HASH_FUNCTION
294 ) {
295 if (ident === undefined) {
296 ident = Template.toIdentifier(request);
297 if (ident !== request) {
298 ident += `_${createHash(hashFunction)
299 .update(request)
300 .digest("hex")
301 .slice(0, 8)}`;
302 }
303 }
304
305 super(
306 "",
307 InitFragment.STAGE_HARMONY_IMPORTS,
308 0,
309 `external module import ${ident} ${
310 imported === true ? imported : imported.join(" ")
311 }`
312 );
313 this._ident = ident;
314 this._request = request;
315 this._dependencyMeta = dependencyMeta;
316 this._identifier = this.buildIdentifier(ident);
317 this._imported = this.buildImported(imported);
318 }
319
320 /**
321 * Returns imported.
322 * @returns {Imported} imported
323 */
324 getImported() {
325 return this._imported;
326 }
327
328 /**
329 * Updates imported using the provided imported.
330 * @param {Imported} imported imported
331 */
332 setImported(imported) {
333 this._imported = imported;
334 }
335
336 /**
337 * Returns the source code that will be included as initialization code.
338 * @param {GenerateContext} context context
339 * @returns {string | Source | undefined} the source code that will be included as initialization code
340 */
341 getContent(context) {
342 const {
343 _dependencyMeta: dependencyMeta,
344 _imported: imported,
345 _request: request,
346 _identifier: identifier
347 } = this;
348 const attributes =
349 dependencyMeta && dependencyMeta.attributes
350 ? dependencyMeta.attributes._isLegacyAssert
351 ? ` assert ${JSON.stringify(
352 dependencyMeta.attributes,
353 importAssertionReplacer
354 )}`
355 : ` with ${JSON.stringify(dependencyMeta.attributes)}`
356 : "";
357 const phase = dependencyMeta && dependencyMeta.phase;
358 let content = "";
359 if (imported === true) {
360 // namespace
361 const phaseKeyword = ImportPhaseUtils.isDefer(phase) ? "defer " : "";
362 content = `import ${phaseKeyword}* as ${identifier} from ${JSON.stringify(
363 request
364 )}${attributes};\n`;
365 } else if (imported.length === 0) {
366 // just import, no use
367 content = `import ${JSON.stringify(request)}${attributes};\n`;
368 } else if (
369 ImportPhaseUtils.isSource(phase) &&
370 imported.length === 1 &&
371 imported[0][0] === "default"
372 ) {
373 // `import source x from "…"` — the source-phase form binds the source
374 // object directly to a single identifier (no namespace, no destructuring).
375 content = `import source ${imported[0][1]} from ${JSON.stringify(
376 request
377 )}${attributes};\n`;
378 } else {
379 content = `import { ${imported
380 .map(([name, finalName]) => {
381 if (name !== finalName) {
382 return `${name} as ${finalName}`;
383 }
384 return name;
385 })
386 .join(", ")} } from ${JSON.stringify(request)}${attributes};\n`;
387 }
388 return content;
389 }
390
391 getNamespaceIdentifier() {
392 return this._identifier;
393 }
394
395 /**
396 * Returns identifier.
397 * @param {string} ident ident
398 * @returns {string} identifier
399 */
400 buildIdentifier(ident) {
401 return `__WEBPACK_EXTERNAL_MODULE_${ident}__`;
402 }
403
404 /**
405 * Returns normalized imported.
406 * @param {Imported} imported imported
407 * @returns {Imported} normalized imported
408 */
409 buildImported(imported) {
410 if (Array.isArray(imported)) {
411 return imported.map(([name]) => {
412 const ident = `${this._ident}_${name}`;
413 return [name, this.buildIdentifier(ident)];
414 });
415 }
416 return imported;
417 }
418}
419
420register(
421 ModuleExternalInitFragment,
422 "webpack/lib/ExternalModule",
423 "ModuleExternalInitFragment",
424 {
425 serialize(obj, { write }) {
426 write(obj._request);
427 write(obj._imported);
428 write(obj._ident);
429 write(obj._dependencyMeta);
430 },
431 deserialize({ read }) {
432 return new ModuleExternalInitFragment(read(), read(), read(), read());
433 }
434 }
435);
436
437/**
438 * Generates module remapping.
439 * @param {string} input input
440 * @param {ExportsInfo} exportsInfo the exports info
441 * @param {RuntimeSpec=} runtime the runtime
442 * @param {RuntimeTemplate=} runtimeTemplate the runtime template
443 * @returns {string | undefined} the module remapping
444 */
445const generateModuleRemapping = (
446 input,
447 exportsInfo,
448 runtime,
449 runtimeTemplate
450) => {
451 if (exportsInfo.otherExportsInfo.getUsed(runtime) === UsageState.Unused) {
452 /** @type {string[]} */
453 const properties = [];
454 for (const exportInfo of exportsInfo.orderedExports) {
455 const used = exportInfo.getUsedName(exportInfo.name, runtime);
456 if (!used) continue;
457 const nestedInfo = exportInfo.getNestedExportsInfo();
458 if (nestedInfo) {
459 const nestedExpr = generateModuleRemapping(
460 `${input}${propertyAccess([exportInfo.name])}`,
461 nestedInfo
462 );
463 if (nestedExpr) {
464 properties.push(`[${JSON.stringify(used)}]: y(${nestedExpr})`);
465 continue;
466 }
467 }
468 properties.push(
469 `[${JSON.stringify(used)}]: ${
470 /** @type {RuntimeTemplate} */ (runtimeTemplate).returningFunction(
471 `${input}${propertyAccess([exportInfo.name])}`
472 )
473 }`
474 );
475 }
476 return `x({ ${properties.join(", ")} })`;
477 }
478};
479
480/**
481 * Gets source for module external.
482 * @param {ModuleAndSpecifiers} moduleAndSpecifiers the module request
483 * @param {ExportsInfo} exportsInfo exports info of this module
484 * @param {RuntimeSpec} runtime the runtime
485 * @param {RuntimeTemplate} runtimeTemplate the runtime template
486 * @param {ImportDependencyMeta} dependencyMeta the dependency meta
487 * @param {ConcatenationScope=} concatenationScope concatenationScope
488 * @returns {SourceData} the generated source
489 */
490const getSourceForModuleExternal = (
491 moduleAndSpecifiers,
492 exportsInfo,
493 runtime,
494 runtimeTemplate,
495 dependencyMeta,
496 concatenationScope
497) => {
498 const phase = dependencyMeta && dependencyMeta.phase;
499 /** @type {Imported} */
500 let imported = true;
501 if (concatenationScope) {
502 const usedExports = exportsInfo.getUsedExports(runtime);
503 switch (usedExports) {
504 case true:
505 case null:
506 // unknown exports
507 imported = true;
508 break;
509 case false:
510 // no used exports
511 imported = [];
512 break;
513 default:
514 imported = [...usedExports.entries()];
515 }
516 }
517
518 if (!Array.isArray(moduleAndSpecifiers)) {
519 moduleAndSpecifiers = [moduleAndSpecifiers];
520 }
521
522 // Return to `namespace` when the external request includes a specific export
523 if (moduleAndSpecifiers.length > 1) {
524 imported = true;
525 }
526
527 // `import defer …` is only valid as `import defer * as ns from "…"`, so
528 // keep the namespace form even if usage analysis would otherwise narrow
529 // the import down to specific names. Defer + concatenation is semantically
530 // at odds (lazy vs. eager), so we preserve the user-written shape here.
531 if (ImportPhaseUtils.isDefer(phase)) {
532 imported = true;
533 }
534
535 const initFragment = new ModuleExternalInitFragment(
536 moduleAndSpecifiers[0],
537 imported,
538 undefined,
539 dependencyMeta,
540 runtimeTemplate.outputOptions.hashFunction
541 );
542 const normalizedImported = initFragment.getImported();
543
544 const baseAccess = `${initFragment.getNamespaceIdentifier()}${propertyAccess(
545 moduleAndSpecifiers,
546 1
547 )}`;
548 let expression = baseAccess;
549
550 const useNamespace = imported === true;
551 /** @type {undefined | string} */
552 let moduleRemapping;
553 if (useNamespace) {
554 moduleRemapping = generateModuleRemapping(
555 baseAccess,
556 exportsInfo,
557 runtime,
558 runtimeTemplate
559 );
560 expression = moduleRemapping || baseAccess;
561 }
562 return {
563 expression,
564 init: moduleRemapping
565 ? `var x = ${runtimeTemplate.basicFunction(
566 "y",
567 `var x = {}; ${RuntimeGlobals.definePropertyGetters}(x, y); return x`
568 )} \nvar y = ${runtimeTemplate.returningFunction(
569 runtimeTemplate.returningFunction("x"),
570 "x"
571 )}`
572 : undefined,
573 specifiers: normalizedImported === true ? undefined : normalizedImported,
574 runtimeRequirements: moduleRemapping
575 ? RUNTIME_REQUIREMENTS_FOR_MODULE
576 : undefined,
577 chunkInitFragments: [
578 /** @type {InitFragment<EXPECTED_ANY>} */ (initFragment)
579 ]
580 };
581};
582
583/**
584 * Gets source for script external.
585 * @param {string | string[]} urlAndGlobal the script request
586 * @param {RuntimeTemplate} runtimeTemplate the runtime template
587 * @returns {SourceData} the generated source
588 */
589const getSourceForScriptExternal = (urlAndGlobal, runtimeTemplate) => {
590 if (typeof urlAndGlobal === "string") {
591 urlAndGlobal = extractUrlAndGlobal(urlAndGlobal);
592 }
593 const url = urlAndGlobal[0];
594 const globalName = urlAndGlobal[1];
595 return {
596 init: "var __webpack_error__ = new Error();",
597 expression: `new Promise(${runtimeTemplate.basicFunction(
598 "resolve, reject",
599 [
600 `if(typeof ${globalName} !== "undefined") return resolve();`,
601 `${RuntimeGlobals.loadScript}(${JSON.stringify(
602 url
603 )}, ${runtimeTemplate.basicFunction("event", [
604 `if(typeof ${globalName} !== "undefined") return resolve();`,
605 "var errorType = event && (event.type === 'load' ? 'missing' : event.type);",
606 "var realSrc = event && event.target && event.target.src;",
607 "__webpack_error__.message = 'Loading script failed.\\n(' + errorType + ': ' + realSrc + ')';",
608 "__webpack_error__.name = 'ScriptExternalLoadError';",
609 "__webpack_error__.type = errorType;",
610 "__webpack_error__.request = realSrc;",
611 "reject(__webpack_error__);"
612 ])}, ${JSON.stringify(globalName)});`
613 ]
614 )}).then(${runtimeTemplate.returningFunction(
615 `${globalName}${propertyAccess(urlAndGlobal, 2)}`
616 )})`,
617 runtimeRequirements: RUNTIME_REQUIREMENTS_FOR_SCRIPT
618 };
619};
620
621/**
622 * Checks external variable.
623 * @param {string} variableName the variable name to check
624 * @param {string} request the request path
625 * @param {RuntimeTemplate} runtimeTemplate the runtime template
626 * @returns {string} the generated source
627 */
628const checkExternalVariable = (variableName, request, runtimeTemplate) =>
629 `if(typeof ${variableName} === 'undefined') { ${runtimeTemplate.throwMissingModuleErrorBlock(
630 { request }
631 )} }\n`;
632
633/**
634 * Gets source for amd or umd external.
635 * @param {ModuleId | string} id the module id
636 * @param {boolean} optional true, if the module is optional
637 * @param {string | string[]} request the request path
638 * @param {RuntimeTemplate} runtimeTemplate the runtime template
639 * @returns {SourceData} the generated source
640 */
641const getSourceForAmdOrUmdExternal = (
642 id,
643 optional,
644 request,
645 runtimeTemplate
646) => {
647 const externalVariable = `__WEBPACK_EXTERNAL_MODULE_${Template.toIdentifier(
648 `${id}`
649 )}__`;
650 return {
651 init: optional
652 ? checkExternalVariable(
653 externalVariable,
654 Array.isArray(request) ? request.join(".") : request,
655 runtimeTemplate
656 )
657 : undefined,
658 expression: externalVariable
659 };
660};
661
662/**
663 * Gets source for default case.
664 * @param {boolean} optional true, if the module is optional
665 * @param {string | string[]} request the request path
666 * @param {RuntimeTemplate} runtimeTemplate the runtime template
667 * @returns {SourceData} the generated source
668 */
669const getSourceForDefaultCase = (optional, request, runtimeTemplate) => {
670 if (!Array.isArray(request)) {
671 // make it an array as the look up works the same basically
672 request = [request];
673 }
674
675 const variableName = request[0];
676 const objectLookup = propertyAccess(request, 1);
677 return {
678 init: optional
679 ? checkExternalVariable(variableName, request.join("."), runtimeTemplate)
680 : undefined,
681 expression: `${variableName}${objectLookup}`
682 };
683};
684
685/** @typedef {Record<string, string | string[]>} RequestRecord */
686/** @typedef {string | string[] | RequestRecord} ExternalModuleRequest */
687
688/**
689 * Defines the external module hooks type used by this module.
690 * @typedef {object} ExternalModuleHooks
691 * @property {SyncBailHook<[Chunk, Compilation], boolean>} chunkCondition
692 */
693
694/** @type {WeakMap<Compilation, ExternalModuleHooks>} */
695const compilationHooksMap = new WeakMap();
696
697class ExternalModule extends Module {
698 /**
699 * Creates an instance of ExternalModule.
700 * @param {ExternalModuleRequest} request request
701 * @param {ExternalsType} type type
702 * @param {string} userRequest user request
703 * @param {DependencyMeta=} dependencyMeta dependency meta
704 */
705 constructor(request, type, userRequest, dependencyMeta) {
706 super(JAVASCRIPT_MODULE_TYPE_DYNAMIC, null);
707
708 // Info from Factory
709 /** @type {ExternalModuleRequest} */
710 this.request = request;
711 /** @type {ExternalsType} */
712 this.externalType = type;
713 /** @type {string} */
714 this.userRequest = userRequest;
715 /** @type {DependencyMeta=} */
716 this.dependencyMeta = dependencyMeta;
717 }
718
719 /**
720 * Returns the attached hooks.
721 * @param {Compilation} compilation the compilation
722 * @returns {ExternalModuleHooks} the attached hooks
723 */
724 static getCompilationHooks(compilation) {
725 let hooks = compilationHooksMap.get(compilation);
726 if (hooks === undefined) {
727 hooks = {
728 chunkCondition: new SyncBailHook(["chunk", "compilation"])
729 };
730 compilationHooksMap.set(compilation, hooks);
731 }
732 return hooks;
733 }
734
735 /**
736 * Returns the source types this module can generate.
737 * @returns {SourceTypes} types available (do not mutate)
738 */
739 getSourceTypes() {
740 if (this.externalType === "asset" && this.dependencyMeta) {
741 const sourceType =
742 /** @type {AssetDependencyMeta} */
743 (this.dependencyMeta).sourceType;
744 // TODO webpack 6 drop "css-url" once the alias is removed
745 if (sourceType === ASSET_URL_TYPE || sourceType === "css-url") {
746 return ASSET_URL_TYPES;
747 }
748 } else if (this.externalType === "css-import") {
749 return CSS_IMPORT_TYPES;
750 }
751
752 return JAVASCRIPT_TYPES;
753 }
754
755 /**
756 * Gets the library identifier.
757 * @param {LibIdentOptions} options options
758 * @returns {LibIdent | null} an identifier for library inclusion
759 */
760 libIdent(options) {
761 return this.userRequest;
762 }
763
764 /**
765 * Returns true if the module can be placed in the chunk.
766 * @param {Chunk} chunk the chunk which condition should be checked
767 * @param {Compilation} compilation the compilation
768 * @returns {boolean} true if the module can be placed in the chunk
769 */
770 chunkCondition(chunk, compilation) {
771 const { chunkCondition } = ExternalModule.getCompilationHooks(compilation);
772 const condition = chunkCondition.call(chunk, compilation);
773 if (condition !== undefined) return condition;
774
775 const type = this._resolveExternalType(this.externalType);
776
777 // For `import()` externals, keep them in the initial chunk to avoid loading
778 // them asynchronously twice and to improve runtime performance.
779 if (["css-import", "module"].includes(type)) {
780 return true;
781 }
782 return compilation.chunkGraph.getNumberOfEntryModules(chunk) > 0;
783 }
784
785 /**
786 * Returns the unique identifier used to reference this module.
787 * @returns {string} a unique identifier of the module
788 */
789 identifier() {
790 let id = `external ${this._resolveExternalType(
791 this.externalType
792 )} ${JSON.stringify(this.request)}`;
793 const meta = /** @type {ImportDependencyMeta | undefined} */ (
794 this.dependencyMeta
795 );
796 if (meta) {
797 if (meta.phase) {
798 id += `|phase=${ImportPhaseUtils.stringify(meta.phase)}`;
799 }
800 if (meta.attributes) {
801 id += `|attributes=${JSON.stringify(meta.attributes)}`;
802 }
803 }
804 return id;
805 }
806
807 /**
808 * Returns a human-readable identifier for this module.
809 * @param {RequestShortener} requestShortener the request shortener
810 * @returns {string} a user readable identifier of the module
811 */
812 readableIdentifier(requestShortener) {
813 return `external ${JSON.stringify(this.request)}`;
814 }
815
816 /**
817 * Checks whether the module needs to be rebuilt for the current build state.
818 * @param {NeedBuildContext} context context info
819 * @param {NeedBuildCallback} callback callback function, returns true, if the module needs a rebuild
820 * @returns {void}
821 */
822 needBuild(context, callback) {
823 return callback(null, !this.buildMeta);
824 }
825
826 /**
827 * Builds the module using the provided compilation context.
828 * @param {WebpackOptions} options webpack options
829 * @param {Compilation} compilation the compilation
830 * @param {ResolverWithOptions} resolver the resolver
831 * @param {InputFileSystem} fs the file system
832 * @param {BuildCallback} callback callback function
833 * @returns {void}
834 */
835 build(options, compilation, resolver, fs, callback) {
836 this.buildMeta = {
837 async: false,
838 exportsType: undefined
839 };
840 this.buildInfo = {
841 strict: true,
842 topLevelDeclarations: new Set(),
843 javascriptModule: compilation.outputOptions.module
844 };
845 const { request, externalType } = this._getRequestAndExternalType();
846 this.buildMeta.exportsType = "dynamic";
847 let canMangle = false;
848 this.clearDependenciesAndBlocks();
849 switch (externalType) {
850 case "this":
851 this.buildInfo.strict = false;
852 break;
853 case "system":
854 if (!Array.isArray(request) || request.length === 1) {
855 this.buildMeta.exportsType = "namespace";
856 canMangle = true;
857 }
858 break;
859 case "module":
860 if (this.buildInfo.javascriptModule) {
861 if (!Array.isArray(request) || request.length === 1) {
862 this.buildMeta.exportsType = "namespace";
863 canMangle = true;
864 }
865 } else {
866 this.buildMeta.async = true;
867 EnvironmentNotSupportAsyncWarning.check(
868 this,
869 compilation.runtimeTemplate,
870 "external module"
871 );
872 if (!Array.isArray(request) || request.length === 1) {
873 this.buildMeta.exportsType = "namespace";
874 canMangle = false;
875 }
876 }
877 break;
878 case "script":
879 this.buildMeta.async = true;
880 EnvironmentNotSupportAsyncWarning.check(
881 this,
882 compilation.runtimeTemplate,
883 "external script"
884 );
885 break;
886 case "promise":
887 this.buildMeta.async = true;
888 EnvironmentNotSupportAsyncWarning.check(
889 this,
890 compilation.runtimeTemplate,
891 "external promise"
892 );
893 break;
894 case "import":
895 this.buildMeta.async = true;
896 EnvironmentNotSupportAsyncWarning.check(
897 this,
898 compilation.runtimeTemplate,
899 "external import"
900 );
901 if (!Array.isArray(request) || request.length === 1) {
902 this.buildMeta.exportsType = "namespace";
903 canMangle = false;
904 }
905 break;
906 }
907 this.addDependency(new StaticExportsDependency(true, canMangle));
908 callback();
909 }
910
911 /**
912 * restore unsafe cache data
913 * @param {UnsafeCacheData} unsafeCacheData data from getUnsafeCacheData
914 * @param {NormalModuleFactory} normalModuleFactory the normal module factory handling the unsafe caching
915 */
916 restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory) {
917 this._restoreFromUnsafeCache(unsafeCacheData, normalModuleFactory);
918 }
919
920 /**
921 * Returns the reason this module cannot be concatenated, when one exists.
922 * @param {ConcatenationBailoutReasonContext} context context
923 * @returns {string | undefined} reason why this module can't be concatenated, undefined when it can be concatenated
924 */
925 getConcatenationBailoutReason(context) {
926 switch (this.externalType) {
927 case "amd":
928 case "amd-require":
929 case "umd":
930 case "umd2":
931 case "system":
932 case "jsonp":
933 return `${this.externalType} externals can't be concatenated`;
934 }
935 return undefined;
936 }
937
938 /**
939 * Get request and external type.
940 * @private
941 * @returns {{ request: string | string[], externalType: ExternalsType }} the request and external type
942 */
943 _getRequestAndExternalType() {
944 let { request, externalType } = this;
945 if (typeof request === "object" && !Array.isArray(request)) {
946 request = request[externalType];
947 }
948 externalType = this._resolveExternalType(externalType);
949 return { request, externalType };
950 }
951
952 /**
953 * Resolve the detailed external type from the raw external type.
954 * e.g. resolve "module" or "import" from "module-import" type
955 * @param {ExternalsType} externalType raw external type
956 * @returns {ExternalsType} resolved external type
957 */
958 _resolveExternalType(externalType) {
959 if (externalType === "module-import") {
960 if (
961 this.dependencyMeta &&
962 /** @type {ImportDependencyMeta} */
963 (this.dependencyMeta).externalType
964 ) {
965 return /** @type {ImportDependencyMeta} */ (this.dependencyMeta)
966 .externalType;
967 }
968 return "module";
969 } else if (externalType === "asset") {
970 if (
971 this.dependencyMeta &&
972 /** @type {AssetDependencyMeta} */
973 (this.dependencyMeta).sourceType
974 ) {
975 return /** @type {AssetDependencyMeta} */ (this.dependencyMeta)
976 .sourceType;
977 }
978
979 return "asset";
980 }
981
982 return externalType;
983 }
984
985 /**
986 * Returns the source data.
987 * @private
988 * @param {string | string[]} request request
989 * @param {ExternalsType} externalType the external type
990 * @param {RuntimeTemplate} runtimeTemplate the runtime template
991 * @param {ModuleGraph} moduleGraph the module graph
992 * @param {ChunkGraph} chunkGraph the chunk graph
993 * @param {RuntimeSpec} runtime the runtime
994 * @param {DependencyMeta | undefined} dependencyMeta the dependency meta
995 * @param {ConcatenationScope=} concatenationScope concatenationScope
996 * @returns {SourceData} the source data
997 */
998 _getSourceData(
999 request,
1000 externalType,
1001 runtimeTemplate,
1002 moduleGraph,
1003 chunkGraph,
1004 runtime,
1005 dependencyMeta,
1006 concatenationScope
1007 ) {
1008 switch (externalType) {
1009 case "this":
1010 case "window":
1011 case "self":
1012 return getSourceForGlobalVariableExternal(request, this.externalType);
1013 case "global":
1014 return getSourceForGlobalVariableExternal(
1015 request,
1016 runtimeTemplate.globalObject
1017 );
1018 case "commonjs":
1019 case "commonjs2":
1020 case "commonjs-module":
1021 case "commonjs-static":
1022 return getSourceForCommonJsExternal(request);
1023 case "node-commonjs":
1024 return /** @type {BuildInfo} */ (this.buildInfo).javascriptModule
1025 ? getSourceForCommonJsExternalInNodeModule(request, runtimeTemplate)
1026 : getSourceForCommonJsExternal(request);
1027 case "amd":
1028 case "amd-require":
1029 case "umd":
1030 case "umd2":
1031 case "system":
1032 case "jsonp": {
1033 const id = chunkGraph.getModuleId(this);
1034 return getSourceForAmdOrUmdExternal(
1035 id !== null ? id : this.identifier(),
1036 this.isOptional(moduleGraph),
1037 request,
1038 runtimeTemplate
1039 );
1040 }
1041 case "import":
1042 return getSourceForImportExternal(
1043 request,
1044 runtimeTemplate,
1045 /** @type {ImportDependencyMeta} */ (dependencyMeta)
1046 );
1047 case "script":
1048 return getSourceForScriptExternal(request, runtimeTemplate);
1049 case "module": {
1050 if (!(/** @type {BuildInfo} */ (this.buildInfo).javascriptModule)) {
1051 if (!runtimeTemplate.supportsDynamicImport()) {
1052 throw new Error(
1053 `The target environment doesn't support dynamic import() syntax so it's not possible to use external type 'module' within a script${
1054 runtimeTemplate.supportsEcmaScriptModuleSyntax()
1055 ? "\nDid you mean to build a EcmaScript Module ('output.module: true')?"
1056 : ""
1057 }`
1058 );
1059 }
1060 return getSourceForImportExternal(
1061 request,
1062 runtimeTemplate,
1063 /** @type {ImportDependencyMeta} */ (dependencyMeta)
1064 );
1065 }
1066 if (!runtimeTemplate.supportsEcmaScriptModuleSyntax()) {
1067 throw new Error(
1068 "The target environment doesn't support EcmaScriptModule syntax so it's not possible to use external type 'module'"
1069 );
1070 }
1071 return getSourceForModuleExternal(
1072 request,
1073 moduleGraph.getExportsInfo(this),
1074 runtime,
1075 runtimeTemplate,
1076 /** @type {ImportDependencyMeta} */ (dependencyMeta),
1077 concatenationScope
1078 );
1079 }
1080 case "var":
1081 case "promise":
1082 case "assign":
1083 default:
1084 return getSourceForDefaultCase(
1085 this.isOptional(moduleGraph),
1086 request,
1087 runtimeTemplate
1088 );
1089 }
1090 }
1091
1092 /**
1093 * Generates code and runtime requirements for this module.
1094 * @param {CodeGenerationContext} context context for code generation
1095 * @returns {CodeGenerationResult} result
1096 */
1097 codeGeneration({
1098 runtimeTemplate,
1099 moduleGraph,
1100 chunkGraph,
1101 runtime,
1102 concatenationScope
1103 }) {
1104 const { request, externalType } = this._getRequestAndExternalType();
1105 switch (externalType) {
1106 case "asset": {
1107 /** @type {Sources} */
1108 const sources = new Map();
1109 sources.set(
1110 JAVASCRIPT_TYPE,
1111 new RawSource(`module.exports = ${JSON.stringify(request)};`)
1112 );
1113 /** @type {CodeGenerationResultData} */
1114 const data = new Map();
1115 data.set("url", { javascript: /** @type {string} */ (request) });
1116 return { sources, runtimeRequirements: RUNTIME_REQUIREMENTS, data };
1117 }
1118 // TODO webpack 6 remove "css-url" alias
1119 case "css-url":
1120 case "asset-url": {
1121 /** @type {Sources} */
1122 const sources = new Map();
1123 /** @type {CodeGenerationResultData} */
1124 const data = new Map();
1125 data.set("url", { [ASSET_URL_TYPE]: /** @type {string} */ (request) });
1126 return { sources, runtimeRequirements: RUNTIME_REQUIREMENTS, data };
1127 }
1128 case "css-import": {
1129 /** @type {Sources} */
1130 const sources = new Map();
1131 const dependencyMeta = /** @type {CssImportDependencyMeta} */ (
1132 this.dependencyMeta
1133 );
1134 const layer =
1135 dependencyMeta.layer !== undefined
1136 ? ` layer(${dependencyMeta.layer})`
1137 : "";
1138 const supports = dependencyMeta.supports
1139 ? ` supports(${dependencyMeta.supports})`
1140 : "";
1141 const media = dependencyMeta.media ? ` ${dependencyMeta.media}` : "";
1142 sources.set(
1143 "css-import",
1144 new RawSource(
1145 `@import url(${JSON.stringify(
1146 request
1147 )})${layer}${supports}${media};`
1148 )
1149 );
1150 return {
1151 sources,
1152 runtimeRequirements: EMPTY_RUNTIME_REQUIREMENTS
1153 };
1154 }
1155 default: {
1156 const sourceData = this._getSourceData(
1157 request,
1158 externalType,
1159 runtimeTemplate,
1160 moduleGraph,
1161 chunkGraph,
1162 runtime,
1163 this.dependencyMeta,
1164 concatenationScope
1165 );
1166
1167 // sourceString can be empty str only when there is concatenationScope
1168 let sourceString = sourceData.expression;
1169 if (sourceData.iife) {
1170 sourceString = `(function() { return ${sourceString}; }())`;
1171 }
1172
1173 const specifiers = sourceData.specifiers;
1174 if (specifiers) {
1175 sourceString = "";
1176 const scope = /** @type {ConcatenationScope} */ (concatenationScope);
1177 for (const [specifier, finalName] of specifiers) {
1178 scope.registerRawExport(specifier, finalName);
1179 }
1180 } else if (concatenationScope) {
1181 sourceString = `${runtimeTemplate.renderConst()} ${
1182 ConcatenationScope.NAMESPACE_OBJECT_EXPORT
1183 } = ${sourceString};`;
1184 concatenationScope.registerNamespaceExport(
1185 ConcatenationScope.NAMESPACE_OBJECT_EXPORT
1186 );
1187 } else {
1188 sourceString = `module.exports = ${sourceString};`;
1189 }
1190 if (sourceData.init) {
1191 sourceString = `${sourceData.init}\n${sourceString}`;
1192 }
1193
1194 /** @type {undefined | CodeGenerationResultData} */
1195 let data;
1196 if (sourceData.chunkInitFragments) {
1197 data = new Map();
1198 data.set("chunkInitFragments", sourceData.chunkInitFragments);
1199 }
1200
1201 /** @type {Sources} */
1202 const sources = new Map();
1203 if (this.useSourceMap || this.useSimpleSourceMap) {
1204 sources.set(
1205 JAVASCRIPT_TYPE,
1206 new OriginalSource(sourceString, this.identifier())
1207 );
1208 } else {
1209 sources.set(JAVASCRIPT_TYPE, new RawSource(sourceString));
1210 }
1211
1212 let runtimeRequirements = sourceData.runtimeRequirements;
1213 if (!concatenationScope) {
1214 if (!runtimeRequirements) {
1215 runtimeRequirements = RUNTIME_REQUIREMENTS;
1216 } else {
1217 const set = new Set(runtimeRequirements);
1218 set.add(RuntimeGlobals.module);
1219 runtimeRequirements = set;
1220 }
1221 }
1222
1223 return {
1224 sources,
1225 runtimeRequirements:
1226 runtimeRequirements || EMPTY_RUNTIME_REQUIREMENTS,
1227 data
1228 };
1229 }
1230 }
1231 }
1232
1233 /**
1234 * Returns the estimated size for the requested source type.
1235 * @param {string=} type the source type for which the size should be estimated
1236 * @returns {number} the estimated size of the module (must be non-zero)
1237 */
1238 size(type) {
1239 return 42;
1240 }
1241
1242 /**
1243 * Updates the hash with the data contributed by this instance.
1244 * @param {Hash} hash the hash used to track dependencies
1245 * @param {UpdateHashContext} context context
1246 * @returns {void}
1247 */
1248 updateHash(hash, context) {
1249 const { chunkGraph } = context;
1250 hash.update(
1251 `${this._resolveExternalType(this.externalType)}${JSON.stringify(
1252 this.request
1253 )}${this.isOptional(chunkGraph.moduleGraph)}`
1254 );
1255 const meta = /** @type {ImportDependencyMeta | undefined} */ (
1256 this.dependencyMeta
1257 );
1258 if (meta) {
1259 if (meta.phase) {
1260 hash.update(`|phase=${ImportPhaseUtils.stringify(meta.phase)}`);
1261 }
1262 if (meta.attributes) {
1263 hash.update(`|attributes=${JSON.stringify(meta.attributes)}`);
1264 }
1265 }
1266 super.updateHash(hash, context);
1267 }
1268
1269 /**
1270 * Serializes this instance into the provided serializer context.
1271 * @param {ObjectSerializerContext} context context
1272 */
1273 serialize(context) {
1274 const { write } = context;
1275
1276 write(this.request);
1277 write(this.externalType);
1278 write(this.userRequest);
1279 write(this.dependencyMeta);
1280
1281 super.serialize(context);
1282 }
1283
1284 /**
1285 * Restores this instance from the provided deserializer context.
1286 * @param {ObjectDeserializerContext} context context
1287 */
1288 deserialize(context) {
1289 const { read } = context;
1290
1291 this.request = read();
1292 this.externalType = read();
1293 this.userRequest = read();
1294 this.dependencyMeta = read();
1295
1296 super.deserialize(context);
1297 }
1298}
1299
1300makeSerializable(ExternalModule, "webpack/lib/ExternalModule");
1301
1302module.exports = ExternalModule;
1303module.exports.ModuleExternalInitFragment = ModuleExternalInitFragment;
1304module.exports.getExternalModuleNodeCommonjsInitFragment =
1305 getExternalModuleNodeCommonjsInitFragment;
Note: See TracBrowser for help on using the repository browser.