source: frontend/node_modules/webpack/lib/RuntimeTemplate.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: 40.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 InitFragment = require("./InitFragment");
9const RuntimeGlobals = require("./RuntimeGlobals");
10const Template = require("./Template");
11const {
12 getOutgoingAsyncModules
13} = require("./async-modules/AsyncModuleHelpers");
14const { ImportPhaseUtils } = require("./dependencies/ImportPhase");
15const {
16 getMakeDeferredNamespaceModeFromExportsType,
17 getOptimizedDeferredModule
18} = require("./runtime/MakeDeferredNamespaceObjectRuntime");
19const { equals } = require("./util/ArrayHelpers");
20const compileBooleanMatcher = require("./util/compileBooleanMatcher");
21const memoize = require("./util/memoize");
22const { propertyAccess } = require("./util/property");
23const { forEachRuntime, subtractRuntime } = require("./util/runtime");
24
25const getHarmonyImportDependency = memoize(() =>
26 require("./dependencies/HarmonyImportDependency")
27);
28const getImportDependency = memoize(() =>
29 require("./dependencies/ImportDependency")
30);
31
32/** @typedef {import("./config/defaults").OutputNormalizedWithDefaults} OutputOptions */
33/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
34/** @typedef {import("./Chunk")} Chunk */
35/** @typedef {import("./ChunkGraph")} ChunkGraph */
36/** @typedef {import("./Compilation")} Compilation */
37/** @typedef {import("./Dependency")} Dependency */
38/** @typedef {import("./Module")} Module */
39/** @typedef {import("./Module").BuildMeta} BuildMeta */
40/** @typedef {import("./Module").RuntimeRequirements} RuntimeRequirements */
41/** @typedef {import("./ModuleGraph")} ModuleGraph */
42/** @typedef {import("./RequestShortener")} RequestShortener */
43/** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
44/** @typedef {import("./dependencies/ImportPhase").ImportPhaseType} ImportPhaseType */
45/** @typedef {import("./NormalModuleFactory").ModuleDependency} ModuleDependency */
46
47/**
48 * No module id error message.
49 * @param {Module} module the module
50 * @param {ChunkGraph} chunkGraph the chunk graph
51 * @returns {string} error message
52 */
53const noModuleIdErrorMessage = (
54 module,
55 chunkGraph
56) => `Module ${module.identifier()} has no id assigned.
57This should not happen.
58It's in these chunks: ${
59 Array.from(
60 chunkGraph.getModuleChunksIterable(module),
61 (c) => c.name || c.id || c.debugId
62 ).join(", ") || "none"
63} (If module is in no chunk this indicates a bug in some chunk/module optimization logic)
64Module has these incoming connections: ${Array.from(
65 chunkGraph.moduleGraph.getIncomingConnections(module),
66 (connection) =>
67 `\n - ${
68 connection.originModule && connection.originModule.identifier()
69 } ${connection.dependency && connection.dependency.type} ${
70 (connection.explanations && [...connection.explanations].join(", ")) || ""
71 }`
72).join("")}`;
73
74/**
75 * Gets global object.
76 * @param {string | undefined} definition global object definition
77 * @returns {string | undefined} save to use global object
78 */
79function getGlobalObject(definition) {
80 if (!definition) return definition;
81 const trimmed = definition.trim();
82
83 if (
84 // identifier, we do not need real identifier regarding ECMAScript/Unicode
85 /^[_\p{L}][_0-9\p{L}]*$/iu.test(trimmed) ||
86 // iife
87 // call expression
88 // expression in parentheses
89 /^(?:[_\p{L}][_0-9\p{L}]*)?\(.*\)$/iu.test(trimmed)
90 ) {
91 return trimmed;
92 }
93
94 return `Object(${trimmed})`;
95}
96
97class RuntimeTemplate {
98 /**
99 * Creates an instance of RuntimeTemplate.
100 * @param {Compilation} compilation the compilation
101 * @param {OutputOptions} outputOptions the compilation output options
102 * @param {RequestShortener} requestShortener the request shortener
103 */
104 constructor(compilation, outputOptions, requestShortener) {
105 this.compilation = compilation;
106 this.outputOptions = /** @type {OutputOptions} */ (outputOptions || {});
107 this.requestShortener = requestShortener;
108 this.globalObject =
109 /** @type {string} */
110 (getGlobalObject(outputOptions.globalObject));
111 this.contentHashReplacement = "X".repeat(outputOptions.hashDigestLength);
112 }
113
114 isIIFE() {
115 return this.outputOptions.iife;
116 }
117
118 isModule() {
119 return this.outputOptions.module;
120 }
121
122 isNeutralPlatform() {
123 return (
124 !this.compilation.compiler.platform.web &&
125 !this.compilation.compiler.platform.node
126 );
127 }
128
129 supportsConst() {
130 return this.outputOptions.environment.const;
131 }
132
133 supportsMethodShorthand() {
134 return this.outputOptions.environment.methodShorthand;
135 }
136
137 supportsArrowFunction() {
138 return this.outputOptions.environment.arrowFunction;
139 }
140
141 supportsAsyncFunction() {
142 return this.outputOptions.environment.asyncFunction;
143 }
144
145 supportsOptionalChaining() {
146 return this.outputOptions.environment.optionalChaining;
147 }
148
149 supportsForOf() {
150 return this.outputOptions.environment.forOf;
151 }
152
153 supportsDestructuring() {
154 return this.outputOptions.environment.destructuring;
155 }
156
157 supportsBigIntLiteral() {
158 return this.outputOptions.environment.bigIntLiteral;
159 }
160
161 supportsDynamicImport() {
162 return this.outputOptions.environment.dynamicImport;
163 }
164
165 supportsEcmaScriptModuleSyntax() {
166 return this.outputOptions.environment.module;
167 }
168
169 supportTemplateLiteral() {
170 return this.outputOptions.environment.templateLiteral;
171 }
172
173 supportNodePrefixForCoreModules() {
174 return this.outputOptions.environment.nodePrefixForCoreModules;
175 }
176
177 /**
178 * Renders node prefix for core module.
179 * @param {string} mod a module
180 * @returns {string} a module with `node:` prefix when supported, otherwise an original name
181 */
182 renderNodePrefixForCoreModule(mod) {
183 return this.outputOptions.environment.nodePrefixForCoreModules
184 ? `"node:${mod}"`
185 : `"${mod}"`;
186 }
187
188 /**
189 * Renders return const when it is supported, otherwise var.
190 * @returns {"const" | "var"} return `const` when it is supported, otherwise `var`
191 */
192 renderConst() {
193 return this.supportsConst() ? "const" : "var";
194 }
195
196 /**
197 * Returning function.
198 * @param {string} returnValue return value
199 * @param {string} args arguments
200 * @returns {string} returning function
201 */
202 returningFunction(returnValue, args = "") {
203 return this.supportsArrowFunction()
204 ? `(${args}) => (${returnValue})`
205 : `function(${args}) { return ${returnValue}; }`;
206 }
207
208 /**
209 * Returns basic function.
210 * @param {string} args arguments
211 * @param {string | string[]} body body
212 * @returns {string} basic function
213 */
214 basicFunction(args, body) {
215 return this.supportsArrowFunction()
216 ? `(${args}) => {\n${Template.indent(body)}\n}`
217 : `function(${args}) {\n${Template.indent(body)}\n}`;
218 }
219
220 /**
221 * Returns result expression.
222 * @param {(string | { expr: string })[]} args args
223 * @returns {string} result expression
224 */
225 concatenation(...args) {
226 const len = args.length;
227
228 if (len === 2) return this._es5Concatenation(args);
229 if (len === 0) return '""';
230 if (len === 1) {
231 return typeof args[0] === "string"
232 ? JSON.stringify(args[0])
233 : `"" + ${args[0].expr}`;
234 }
235 if (!this.supportTemplateLiteral()) return this._es5Concatenation(args);
236
237 // cost comparison between template literal and concatenation:
238 // both need equal surroundings: `xxx` vs "xxx"
239 // template literal has constant cost of 3 chars for each expression
240 // es5 concatenation has cost of 3 + n chars for n expressions in row
241 // when a es5 concatenation ends with an expression it reduces cost by 3
242 // when a es5 concatenation starts with an single expression it reduces cost by 3
243 // e. g. `${a}${b}${c}` (3*3 = 9) is longer than ""+a+b+c ((3+3)-3 = 3)
244 // e. g. `x${a}x${b}x${c}x` (3*3 = 9) is shorter than "x"+a+"x"+b+"x"+c+"x" (4+4+4 = 12)
245
246 let templateCost = 0;
247 let concatenationCost = 0;
248
249 let lastWasExpr = false;
250 for (const arg of args) {
251 const isExpr = typeof arg !== "string";
252 if (isExpr) {
253 templateCost += 3;
254 concatenationCost += lastWasExpr ? 1 : 4;
255 }
256 lastWasExpr = isExpr;
257 }
258 if (lastWasExpr) concatenationCost -= 3;
259 if (typeof args[0] !== "string" && typeof args[1] === "string") {
260 concatenationCost -= 3;
261 }
262
263 if (concatenationCost <= templateCost) return this._es5Concatenation(args);
264
265 return `\`${args
266 .map((arg) => (typeof arg === "string" ? arg : `\${${arg.expr}}`))
267 .join("")}\``;
268 }
269
270 /**
271 * Returns result expression.
272 * @param {(string | { expr: string })[]} args args (len >= 2)
273 * @returns {string} result expression
274 * @private
275 */
276 _es5Concatenation(args) {
277 const str = args
278 .map((arg) => (typeof arg === "string" ? JSON.stringify(arg) : arg.expr))
279 .join(" + ");
280
281 // when the first two args are expression, we need to prepend "" + to force string
282 // concatenation instead of number addition.
283 return typeof args[0] !== "string" && typeof args[1] !== "string"
284 ? `"" + ${str}`
285 : str;
286 }
287
288 /**
289 * Expression function.
290 * @param {string} expression expression
291 * @param {string} args arguments
292 * @returns {string} expression function code
293 */
294 expressionFunction(expression, args = "") {
295 return this.supportsArrowFunction()
296 ? `(${args}) => (${expression})`
297 : `function(${args}) { ${expression}; }`;
298 }
299
300 /**
301 * Returns empty function code.
302 * @returns {string} empty function code
303 */
304 emptyFunction() {
305 return this.supportsArrowFunction() ? "x => {}" : "function() {}";
306 }
307
308 /**
309 * Returns destructure array code.
310 * @param {string[]} items items
311 * @param {string} value value
312 * @returns {string} destructure array code
313 */
314 destructureArray(items, value) {
315 return this.supportsDestructuring()
316 ? `var [${items.join(", ")}] = ${value};`
317 : Template.asString(
318 items.map((item, i) => `var ${item} = ${value}[${i}];`)
319 );
320 }
321
322 /**
323 * Destructure object.
324 * @param {string[]} items items
325 * @param {string} value value
326 * @returns {string} destructure object code
327 */
328 destructureObject(items, value) {
329 return this.supportsDestructuring()
330 ? `var {${items.join(", ")}} = ${value};`
331 : Template.asString(
332 items.map(
333 (item) => `var ${item} = ${value}${propertyAccess([item])};`
334 )
335 );
336 }
337
338 /**
339 * Returns iIFE code.
340 * @param {string} args arguments
341 * @param {string} body body
342 * @returns {string} IIFE code
343 */
344 iife(args, body) {
345 return `(${this.basicFunction(args, body)})()`;
346 }
347
348 /**
349 * Returns for each code.
350 * @param {string} variable variable
351 * @param {string} array array
352 * @param {string | string[]} body body
353 * @returns {string} for each code
354 */
355 forEach(variable, array, body) {
356 return this.supportsForOf()
357 ? `for(const ${variable} of ${array}) {\n${Template.indent(body)}\n}`
358 : `${array}.forEach(function(${variable}) {\n${Template.indent(
359 body
360 )}\n});`;
361 }
362
363 /**
364 * Returns comment.
365 * @param {object} options Information content of the comment
366 * @param {string=} options.request request string used originally
367 * @param {(string | null)=} options.chunkName name of the chunk referenced
368 * @param {string=} options.chunkReason reason information of the chunk
369 * @param {string=} options.message additional message
370 * @param {string=} options.exportName name of the export
371 * @returns {string} comment
372 */
373 comment({ request, chunkName, chunkReason, message, exportName }) {
374 /** @type {string} */
375 let content;
376 if (this.outputOptions.pathinfo) {
377 content = [message, request, chunkName, chunkReason]
378 .filter(Boolean)
379 .map((item) => this.requestShortener.shorten(item))
380 .join(" | ");
381 } else {
382 content = [message, chunkName, chunkReason]
383 .filter(Boolean)
384 .map((item) => this.requestShortener.shorten(item))
385 .join(" | ");
386 }
387 if (!content) return "";
388 if (this.outputOptions.pathinfo) {
389 return `${Template.toComment(content)} `;
390 }
391 return `${Template.toNormalComment(content)} `;
392 }
393
394 /**
395 * Throw missing module error block.
396 * @param {object} options generation options
397 * @param {string=} options.request request string used originally
398 * @returns {string} generated error block
399 */
400 throwMissingModuleErrorBlock({ request }) {
401 const err = `Cannot find module '${request}'`;
402 return `var e = new Error(${JSON.stringify(
403 err
404 )}); e.code = 'MODULE_NOT_FOUND'; throw e;`;
405 }
406
407 /**
408 * Throw missing module error function.
409 * @param {object} options generation options
410 * @param {string=} options.request request string used originally
411 * @returns {string} generated error function
412 */
413 throwMissingModuleErrorFunction({ request }) {
414 return `function webpackMissingModule() { ${this.throwMissingModuleErrorBlock(
415 { request }
416 )} }`;
417 }
418
419 /**
420 * Returns generated error IIFE.
421 * @param {object} options generation options
422 * @param {string=} options.request request string used originally
423 * @returns {string} generated error IIFE
424 */
425 missingModule({ request }) {
426 return `Object(${this.throwMissingModuleErrorFunction({ request })}())`;
427 }
428
429 /**
430 * Missing module statement.
431 * @param {object} options generation options
432 * @param {string=} options.request request string used originally
433 * @returns {string} generated error statement
434 */
435 missingModuleStatement({ request }) {
436 return `${this.missingModule({ request })};\n`;
437 }
438
439 /**
440 * Missing module promise.
441 * @param {object} options generation options
442 * @param {string=} options.request request string used originally
443 * @returns {string} generated error code
444 */
445 missingModulePromise({ request }) {
446 return `Promise.resolve().then(${this.throwMissingModuleErrorFunction({
447 request
448 })})`;
449 }
450
451 /**
452 * Returns the code.
453 * @param {object} options options object
454 * @param {ChunkGraph} options.chunkGraph the chunk graph
455 * @param {Module} options.module the module
456 * @param {string=} options.request the request that should be printed as comment
457 * @param {string=} options.idExpr expression to use as id expression
458 * @param {"expression" | "promise" | "statements"} options.type which kind of code should be returned
459 * @returns {string} the code
460 */
461 weakError({ module, chunkGraph, request, idExpr, type }) {
462 const moduleId = chunkGraph.getModuleId(module);
463 const errorMessage =
464 moduleId === null
465 ? JSON.stringify("Module is not available (weak dependency)")
466 : idExpr
467 ? `"Module '" + ${idExpr} + "' is not available (weak dependency)"`
468 : JSON.stringify(
469 `Module '${moduleId}' is not available (weak dependency)`
470 );
471 const comment = request ? `${Template.toNormalComment(request)} ` : "";
472 const errorStatements = `var e = new Error(${errorMessage}); ${
473 comment
474 }e.code = 'MODULE_NOT_FOUND'; throw e;`;
475 switch (type) {
476 case "statements":
477 return errorStatements;
478 case "promise":
479 return `Promise.resolve().then(${this.basicFunction(
480 "",
481 errorStatements
482 )})`;
483 case "expression":
484 return this.iife("", errorStatements);
485 }
486 }
487
488 /**
489 * Returns the expression.
490 * @param {object} options options object
491 * @param {Module} options.module the module
492 * @param {ChunkGraph} options.chunkGraph the chunk graph
493 * @param {string=} options.request the request that should be printed as comment
494 * @param {boolean=} options.weak if the dependency is weak (will create a nice error message)
495 * @returns {string} the expression
496 */
497 moduleId({ module, chunkGraph, request, weak }) {
498 if (!module) {
499 return this.missingModule({
500 request
501 });
502 }
503 const moduleId = chunkGraph.getModuleId(module);
504 if (moduleId === null) {
505 if (weak) {
506 return "null /* weak dependency, without id */";
507 }
508 throw new Error(
509 `RuntimeTemplate.moduleId(): ${noModuleIdErrorMessage(
510 module,
511 chunkGraph
512 )}`
513 );
514 }
515 return `${this.comment({ request })}${JSON.stringify(moduleId)}`;
516 }
517
518 /**
519 * Returns the expression.
520 * @param {object} options options object
521 * @param {Module | null} options.module the module
522 * @param {ChunkGraph} options.chunkGraph the chunk graph
523 * @param {string=} options.request the request that should be printed as comment
524 * @param {boolean=} options.weak if the dependency is weak (will create a nice error message)
525 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
526 * @returns {string} the expression
527 */
528 moduleRaw({ module, chunkGraph, request, weak, runtimeRequirements }) {
529 if (!module) {
530 return this.missingModule({
531 request
532 });
533 }
534 const moduleId = chunkGraph.getModuleId(module);
535 if (moduleId === null) {
536 if (weak) {
537 // only weak referenced modules don't get an id
538 // we can always emit an error emitting code here
539 return this.weakError({
540 module,
541 chunkGraph,
542 request,
543 type: "expression"
544 });
545 }
546 throw new Error(
547 `RuntimeTemplate.moduleId(): ${noModuleIdErrorMessage(
548 module,
549 chunkGraph
550 )}`
551 );
552 }
553 runtimeRequirements.add(RuntimeGlobals.require);
554 return `${RuntimeGlobals.require}(${this.moduleId({
555 module,
556 chunkGraph,
557 request,
558 weak
559 })})`;
560 }
561
562 /**
563 * Returns the expression.
564 * @param {object} options options object
565 * @param {Module | null} options.module the module
566 * @param {ChunkGraph} options.chunkGraph the chunk graph
567 * @param {string} options.request the request that should be printed as comment
568 * @param {boolean=} options.weak if the dependency is weak (will create a nice error message)
569 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
570 * @returns {string} the expression
571 */
572 moduleExports({ module, chunkGraph, request, weak, runtimeRequirements }) {
573 return this.moduleRaw({
574 module,
575 chunkGraph,
576 request,
577 weak,
578 runtimeRequirements
579 });
580 }
581
582 /**
583 * Returns the expression.
584 * @param {object} options options object
585 * @param {Module} options.module the module
586 * @param {ChunkGraph} options.chunkGraph the chunk graph
587 * @param {string} options.request the request that should be printed as comment
588 * @param {boolean=} options.strict if the current module is in strict esm mode
589 * @param {boolean=} options.weak if the dependency is weak (will create a nice error message)
590 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
591 * @returns {string} the expression
592 */
593 moduleNamespace({
594 module,
595 chunkGraph,
596 request,
597 strict,
598 weak,
599 runtimeRequirements
600 }) {
601 if (!module) {
602 return this.missingModule({
603 request
604 });
605 }
606 if (chunkGraph.getModuleId(module) === null) {
607 if (weak) {
608 // only weak referenced modules don't get an id
609 // we can always emit an error emitting code here
610 return this.weakError({
611 module,
612 chunkGraph,
613 request,
614 type: "expression"
615 });
616 }
617 throw new Error(
618 `RuntimeTemplate.moduleNamespace(): ${noModuleIdErrorMessage(
619 module,
620 chunkGraph
621 )}`
622 );
623 }
624 const moduleId = this.moduleId({
625 module,
626 chunkGraph,
627 request,
628 weak
629 });
630 const exportsType = module.getExportsType(chunkGraph.moduleGraph, strict);
631 switch (exportsType) {
632 case "namespace":
633 return this.moduleRaw({
634 module,
635 chunkGraph,
636 request,
637 weak,
638 runtimeRequirements
639 });
640 case "default-with-named":
641 runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
642 return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 3)`;
643 case "default-only":
644 runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
645 return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 1)`;
646 case "dynamic":
647 runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
648 return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 7)`;
649 }
650 }
651
652 /**
653 * Module namespace promise.
654 * @param {object} options options object
655 * @param {ChunkGraph} options.chunkGraph the chunk graph
656 * @param {AsyncDependenciesBlock=} options.block the current dependencies block
657 * @param {Module} options.module the module
658 * @param {string} options.request the request that should be printed as comment
659 * @param {string} options.message a message for the comment
660 * @param {boolean=} options.strict if the current module is in strict esm mode
661 * @param {boolean=} options.weak if the dependency is weak (will create a nice error message)
662 * @param {Dependency} options.dependency dependency
663 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
664 * @returns {string} the promise expression
665 */
666 moduleNamespacePromise({
667 chunkGraph,
668 block,
669 module,
670 request,
671 message,
672 strict,
673 weak,
674 dependency,
675 runtimeRequirements
676 }) {
677 if (!module) {
678 return this.missingModulePromise({
679 request
680 });
681 }
682 const moduleId = chunkGraph.getModuleId(module);
683 if (moduleId === null) {
684 if (weak) {
685 // only weak referenced modules don't get an id
686 // we can always emit an error emitting code here
687 return this.weakError({
688 module,
689 chunkGraph,
690 request,
691 type: "promise"
692 });
693 }
694 throw new Error(
695 `RuntimeTemplate.moduleNamespacePromise(): ${noModuleIdErrorMessage(
696 module,
697 chunkGraph
698 )}`
699 );
700 }
701 const promise = this.blockPromise({
702 chunkGraph,
703 block,
704 message,
705 runtimeRequirements
706 });
707
708 /** @type {string} */
709 let appending;
710 let idExpr = JSON.stringify(chunkGraph.getModuleId(module));
711 const comment = this.comment({
712 request
713 });
714 let header = "";
715 if (weak) {
716 if (idExpr.length > 8) {
717 // 'var x="nnnnnn";x,"+x+",x' vs '"nnnnnn",nnnnnn,"nnnnnn"'
718 header += `var id = ${idExpr}; `;
719 idExpr = "id";
720 }
721 runtimeRequirements.add(RuntimeGlobals.moduleFactories);
722 header += `if(!${
723 RuntimeGlobals.moduleFactories
724 }[${idExpr}]) { ${this.weakError({
725 module,
726 chunkGraph,
727 request,
728 idExpr,
729 type: "statements"
730 })} } `;
731 }
732 const exportsType = module.getExportsType(chunkGraph.moduleGraph, strict);
733
734 const isModuleDeferred =
735 (dependency instanceof getHarmonyImportDependency() ||
736 dependency instanceof getImportDependency()) &&
737 ImportPhaseUtils.isDefer(dependency.phase) &&
738 !(/** @type {BuildMeta} */ (module.buildMeta).async);
739
740 if (isModuleDeferred) {
741 runtimeRequirements.add(RuntimeGlobals.makeDeferredNamespaceObject);
742
743 let mode = getMakeDeferredNamespaceModeFromExportsType(exportsType);
744 if (mode) mode = `${mode} | 16`;
745
746 const asyncDeps = Array.from(
747 getOutgoingAsyncModules(chunkGraph.moduleGraph, module),
748 (m) => chunkGraph.getModuleId(m)
749 ).filter((id) => id !== null);
750 if (asyncDeps.length) {
751 if (header) {
752 appending = `.then(${this.basicFunction(
753 "",
754 `${header}return ${RuntimeGlobals.deferredModuleAsyncTransitiveDependencies}(${JSON.stringify(asyncDeps)});`
755 )})`;
756 } else {
757 runtimeRequirements.add(RuntimeGlobals.require);
758 appending = `.then(${this.returningFunction(`${RuntimeGlobals.deferredModuleAsyncTransitiveDependencies}(${JSON.stringify(asyncDeps)})`)})`;
759 }
760 appending += `.then(${RuntimeGlobals.makeDeferredNamespaceObject}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}, ${mode}))`;
761 } else if (header) {
762 appending = `.then(${this.basicFunction(
763 "",
764 `${header}return ${RuntimeGlobals.makeDeferredNamespaceObject}(${comment}${idExpr}, ${mode});`
765 )})`;
766 } else {
767 runtimeRequirements.add(RuntimeGlobals.require);
768 appending = `.then(${RuntimeGlobals.makeDeferredNamespaceObject}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}, ${mode}))`;
769 }
770 } else {
771 let fakeType = 16;
772 switch (exportsType) {
773 case "namespace":
774 if (header) {
775 const rawModule = this.moduleRaw({
776 module,
777 chunkGraph,
778 request,
779 weak,
780 runtimeRequirements
781 });
782 appending = `.then(${this.basicFunction(
783 "",
784 `${header}return ${rawModule};`
785 )})`;
786 } else {
787 runtimeRequirements.add(RuntimeGlobals.require);
788 appending = `.then(${RuntimeGlobals.require}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}))`;
789 }
790 break;
791 case "dynamic":
792 fakeType |= 4;
793 /* fall through */
794 case "default-with-named":
795 fakeType |= 2;
796 /* fall through */
797 case "default-only":
798 runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
799 if (chunkGraph.moduleGraph.isAsync(module)) {
800 if (header) {
801 const rawModule = this.moduleRaw({
802 module,
803 chunkGraph,
804 request,
805 weak,
806 runtimeRequirements
807 });
808 appending = `.then(${this.basicFunction(
809 "",
810 `${header}return ${rawModule};`
811 )})`;
812 } else {
813 runtimeRequirements.add(RuntimeGlobals.require);
814 appending = `.then(${RuntimeGlobals.require}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}))`;
815 }
816 appending += `.then(${this.returningFunction(
817 `${RuntimeGlobals.createFakeNamespaceObject}(m, ${fakeType})`,
818 "m"
819 )})`;
820 } else {
821 fakeType |= 1;
822 if (header) {
823 const moduleIdExpr = this.moduleId({
824 module,
825 chunkGraph,
826 request,
827 weak
828 });
829 const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, ${fakeType})`;
830 appending = `.then(${this.basicFunction(
831 "",
832 `${header}return ${returnExpression};`
833 )})`;
834 } else {
835 appending = `.then(${RuntimeGlobals.createFakeNamespaceObject}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}, ${fakeType}))`;
836 }
837 }
838 break;
839 }
840 }
841
842 return `${promise || "Promise.resolve()"}${appending}`;
843 }
844
845 /**
846 * Runtime condition expression.
847 * @param {object} options options object
848 * @param {ChunkGraph} options.chunkGraph the chunk graph
849 * @param {RuntimeSpec=} options.runtime runtime for which this code will be generated
850 * @param {RuntimeSpec | boolean=} options.runtimeCondition only execute the statement in some runtimes
851 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
852 * @returns {string} expression
853 */
854 runtimeConditionExpression({
855 chunkGraph,
856 runtimeCondition,
857 runtime,
858 runtimeRequirements
859 }) {
860 if (runtimeCondition === undefined) return "true";
861 if (typeof runtimeCondition === "boolean") return `${runtimeCondition}`;
862 /** @type {Set<string>} */
863 const positiveRuntimeIds = new Set();
864 forEachRuntime(runtimeCondition, (runtime) =>
865 positiveRuntimeIds.add(
866 `${chunkGraph.getRuntimeId(/** @type {string} */ (runtime))}`
867 )
868 );
869 /** @type {Set<string>} */
870 const negativeRuntimeIds = new Set();
871 forEachRuntime(subtractRuntime(runtime, runtimeCondition), (runtime) =>
872 negativeRuntimeIds.add(
873 `${chunkGraph.getRuntimeId(/** @type {string} */ (runtime))}`
874 )
875 );
876 runtimeRequirements.add(RuntimeGlobals.runtimeId);
877 return compileBooleanMatcher.fromLists(
878 [...positiveRuntimeIds],
879 [...negativeRuntimeIds]
880 )(RuntimeGlobals.runtimeId);
881 }
882
883 /**
884 * Returns the import statement and the compat statement.
885 * @param {object} options options object
886 * @param {boolean=} options.update whether a new variable should be created or the existing one updated
887 * @param {Module} options.module the module
888 * @param {Module} options.originModule module in which the statement is emitted
889 * @param {ModuleGraph} options.moduleGraph the module graph
890 * @param {ChunkGraph} options.chunkGraph the chunk graph
891 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
892 * @param {string} options.importVar name of the import variable
893 * @param {string=} options.request the request that should be printed as comment
894 * @param {boolean=} options.weak true, if this is a weak dependency
895 * @param {ModuleDependency=} options.dependency module dependency
896 * @returns {[string, string]} the import statement and the compat statement
897 */
898 importStatement({
899 update,
900 module,
901 moduleGraph,
902 chunkGraph,
903 request,
904 importVar,
905 originModule,
906 weak,
907 dependency,
908 runtimeRequirements
909 }) {
910 if (!module) {
911 return [
912 this.missingModuleStatement({
913 request
914 }),
915 ""
916 ];
917 }
918
919 if (chunkGraph.getModuleId(module) === null) {
920 if (weak) {
921 // only weak referenced modules don't get an id
922 // we can always emit an error emitting code here
923 return [
924 this.weakError({
925 module,
926 chunkGraph,
927 request,
928 type: "statements"
929 }),
930 ""
931 ];
932 }
933 throw new Error(
934 `RuntimeTemplate.importStatement(): ${noModuleIdErrorMessage(
935 module,
936 chunkGraph
937 )}`
938 );
939 }
940 const moduleId = this.moduleId({
941 module,
942 chunkGraph,
943 request,
944 weak
945 });
946 const optDeclaration = update ? "" : "var ";
947
948 const exportsType = module.getExportsType(
949 chunkGraph.moduleGraph,
950 /** @type {BuildMeta} */
951 (originModule.buildMeta).strictHarmonyModule
952 );
953 runtimeRequirements.add(RuntimeGlobals.require);
954
955 /** @type {string} */
956 let importContent;
957
958 const isModuleDeferred =
959 (dependency instanceof getHarmonyImportDependency() ||
960 dependency instanceof getImportDependency()) &&
961 ImportPhaseUtils.isDefer(dependency.phase) &&
962 !(/** @type {BuildMeta} */ (module.buildMeta).async);
963
964 if (isModuleDeferred) {
965 /** @type {Set<Module>} */
966 const outgoingAsyncModules = getOutgoingAsyncModules(moduleGraph, module);
967
968 importContent = `/* deferred harmony import */ ${optDeclaration}${importVar} = ${getOptimizedDeferredModule(
969 moduleId,
970 exportsType,
971 Array.from(outgoingAsyncModules, (mod) => chunkGraph.getModuleId(mod)),
972 runtimeRequirements
973 )};\n`;
974
975 return [importContent, ""];
976 }
977 importContent = `/* harmony import */ ${optDeclaration}${importVar} = ${RuntimeGlobals.require}(${moduleId});\n`;
978
979 if (exportsType === "dynamic") {
980 runtimeRequirements.add(RuntimeGlobals.compatGetDefaultExport);
981 return [
982 importContent,
983 `/* harmony import */ ${optDeclaration}${importVar}_default = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${importVar});\n`
984 ];
985 }
986 return [importContent, ""];
987 }
988
989 /**
990 * Export from import.
991 * @template GenerateContext
992 * @param {object} options options
993 * @param {ModuleGraph} options.moduleGraph the module graph
994 * @param {ChunkGraph} options.chunkGraph the chunk graph
995 * @param {Module} options.module the module
996 * @param {string} options.request the request
997 * @param {string | string[]} options.exportName the export name
998 * @param {Module} options.originModule the origin module
999 * @param {boolean | undefined} options.asiSafe true, if location is safe for ASI, a bracket can be emitted
1000 * @param {boolean | undefined} options.isCall true, if expression will be called
1001 * @param {boolean | null} options.callContext when false, call context will not be preserved
1002 * @param {boolean} options.defaultInterop when true and accessing the default exports, interop code will be generated
1003 * @param {string} options.importVar the identifier name of the import variable
1004 * @param {InitFragment<GenerateContext>[]} options.initFragments init fragments will be added here
1005 * @param {RuntimeSpec} options.runtime runtime for which this code will be generated
1006 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
1007 * @param {ModuleDependency} options.dependency module dependency
1008 * @returns {string} expression
1009 */
1010 exportFromImport({
1011 moduleGraph,
1012 chunkGraph,
1013 module,
1014 request,
1015 exportName,
1016 originModule,
1017 asiSafe,
1018 isCall,
1019 callContext,
1020 defaultInterop,
1021 importVar,
1022 initFragments,
1023 runtime,
1024 runtimeRequirements,
1025 dependency
1026 }) {
1027 if (!module) {
1028 return this.missingModule({
1029 request
1030 });
1031 }
1032 if (!Array.isArray(exportName)) {
1033 exportName = exportName ? [exportName] : [];
1034 }
1035 const exportsType = module.getExportsType(
1036 moduleGraph,
1037 /** @type {BuildMeta} */
1038 (originModule.buildMeta).strictHarmonyModule
1039 );
1040
1041 const isModuleDeferred =
1042 (dependency instanceof getHarmonyImportDependency() ||
1043 dependency instanceof getImportDependency()) &&
1044 ImportPhaseUtils.isDefer(dependency.phase) &&
1045 !(/** @type {BuildMeta} */ (module.buildMeta).async);
1046
1047 if (defaultInterop) {
1048 // when the defaultInterop is used (when a ESM imports a CJS module),
1049 if (exportName.length > 0 && exportName[0] === "default") {
1050 if (isModuleDeferred && exportsType !== "namespace") {
1051 const exportsInfo = moduleGraph.getExportsInfo(module);
1052 const name = exportName.slice(1);
1053 const used = exportsInfo.getUsedName(name, runtime);
1054 if (!used) {
1055 const comment = Template.toNormalComment(
1056 `unused export ${propertyAccess(exportName)}`
1057 );
1058 return `${comment} undefined`;
1059 }
1060 const access = `${importVar}.a${propertyAccess(used)}`;
1061 if (isCall || asiSafe === undefined) {
1062 return access;
1063 }
1064 return asiSafe ? `(${access})` : `;(${access})`;
1065 }
1066 // accessing the .default property is same thing as `require()` the module.
1067
1068 // For example:
1069 // import mod from "cjs"; mod.default.x;
1070 // is translated to
1071 // var mod = require("cjs"); mod.x;
1072 switch (exportsType) {
1073 case "dynamic":
1074 if (isCall) {
1075 return `${importVar}_default()${propertyAccess(exportName, 1)}`;
1076 }
1077 return asiSafe
1078 ? `(${importVar}_default()${propertyAccess(exportName, 1)})`
1079 : asiSafe === false
1080 ? `;(${importVar}_default()${propertyAccess(exportName, 1)})`
1081 : `${importVar}_default.a${propertyAccess(exportName, 1)}`;
1082
1083 case "default-only":
1084 case "default-with-named":
1085 exportName = exportName.slice(1);
1086 break;
1087 }
1088 } else if (exportName.length > 0) {
1089 // the property used is not .default.
1090 // For example:
1091 // import * as ns from "cjs"; cjs.prop;
1092 if (exportsType === "default-only") {
1093 // in the strictest case, it is a runtime error (e.g. NodeJS behavior of CJS-ESM interop).
1094 return `/* non-default import from non-esm module */undefined${propertyAccess(
1095 exportName,
1096 1
1097 )}`;
1098 } else if (
1099 exportsType !== "namespace" &&
1100 exportName[0] === "__esModule"
1101 ) {
1102 return "/* __esModule */true";
1103 }
1104 } else if (isModuleDeferred) {
1105 // now exportName.length is 0
1106 // fall through to the end of this function, create the namespace there.
1107 } else if (
1108 exportsType === "default-only" ||
1109 exportsType === "default-with-named"
1110 ) {
1111 // now exportName.length is 0, which means the namespace object is used in an unknown way
1112 // for example:
1113 // import * as ns from "cjs"; console.log(ns);
1114 // we will need to createFakeNamespaceObject that simulates ES Module namespace object
1115 runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
1116 initFragments.push(
1117 new InitFragment(
1118 `var ${importVar}_namespace_cache;\n`,
1119 InitFragment.STAGE_CONSTANTS,
1120 -1,
1121 `${importVar}_namespace_cache`
1122 )
1123 );
1124 return `/*#__PURE__*/ ${
1125 asiSafe ? "" : asiSafe === false ? ";" : "Object"
1126 }(${importVar}_namespace_cache || (${importVar}_namespace_cache = ${
1127 RuntimeGlobals.createFakeNamespaceObject
1128 }(${importVar}${exportsType === "default-only" ? "" : ", 2"})))`;
1129 }
1130 }
1131
1132 if (exportName.length > 0) {
1133 const exportsInfo = moduleGraph.getExportsInfo(module);
1134 // in some case the exported item is renamed (get this by getUsedName). for example,
1135 // x.default might be emitted as x.Z (default is renamed to Z)
1136 const used = exportsInfo.getUsedName(exportName, runtime);
1137 if (!used) {
1138 const comment = Template.toNormalComment(
1139 `unused export ${propertyAccess(exportName)}`
1140 );
1141 return `${comment} undefined`;
1142 }
1143 const comment = equals(used, exportName)
1144 ? ""
1145 : `${Template.toNormalComment(propertyAccess(exportName))} `;
1146 const access = `${importVar}${
1147 isModuleDeferred ? ".a" : ""
1148 }${comment}${propertyAccess(used)}`;
1149 if (isCall && callContext === false) {
1150 return asiSafe
1151 ? `(0,${access})`
1152 : asiSafe === false
1153 ? `;(0,${access})`
1154 : `/*#__PURE__*/Object(${access})`;
1155 }
1156 return access;
1157 }
1158 if (isModuleDeferred) {
1159 initFragments.push(
1160 new InitFragment(
1161 `var ${importVar}_deferred_namespace_cache;\n`,
1162 InitFragment.STAGE_CONSTANTS,
1163 -1,
1164 `${importVar}_deferred_namespace_cache`
1165 )
1166 );
1167
1168 runtimeRequirements.add(RuntimeGlobals.makeDeferredNamespaceObject);
1169 const id = chunkGraph.getModuleId(module);
1170 const type = getMakeDeferredNamespaceModeFromExportsType(exportsType);
1171 const init = `${
1172 RuntimeGlobals.makeDeferredNamespaceObject
1173 }(${JSON.stringify(id)}, ${type})`;
1174
1175 return `/*#__PURE__*/ ${
1176 asiSafe ? "" : asiSafe === false ? ";" : "Object"
1177 }(${importVar}_deferred_namespace_cache || (${importVar}_deferred_namespace_cache = ${init}))`;
1178 }
1179 // if we hit here, the importVar is either
1180 // - already a ES module namespace object
1181 // - or imported by a way that does not need interop.
1182 return importVar;
1183 }
1184
1185 /**
1186 * Returns expression.
1187 * @param {object} options options
1188 * @param {AsyncDependenciesBlock | undefined} options.block the async block
1189 * @param {string} options.message the message
1190 * @param {ChunkGraph} options.chunkGraph the chunk graph
1191 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
1192 * @returns {string} expression
1193 */
1194 blockPromise({ block, message, chunkGraph, runtimeRequirements }) {
1195 if (!block) {
1196 const comment = this.comment({
1197 message
1198 });
1199 return `Promise.resolve(${comment.trim()})`;
1200 }
1201 const chunkGroup = chunkGraph.getBlockChunkGroup(block);
1202 if (!chunkGroup || chunkGroup.chunks.length === 0) {
1203 const comment = this.comment({
1204 message
1205 });
1206 return `Promise.resolve(${comment.trim()})`;
1207 }
1208 const chunks = chunkGroup.chunks.filter(
1209 (chunk) => !chunk.hasRuntime() && chunk.id !== null
1210 );
1211 const comment = this.comment({
1212 message,
1213 chunkName: block.chunkName
1214 });
1215 if (chunks.length === 1) {
1216 const chunkId = JSON.stringify(chunks[0].id);
1217 runtimeRequirements.add(RuntimeGlobals.ensureChunk);
1218
1219 const fetchPriority = chunkGroup.options.fetchPriority;
1220
1221 if (fetchPriority) {
1222 runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
1223 }
1224
1225 return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId}${
1226 fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
1227 })`;
1228 } else if (chunks.length > 0) {
1229 runtimeRequirements.add(RuntimeGlobals.ensureChunk);
1230
1231 const fetchPriority = chunkGroup.options.fetchPriority;
1232
1233 if (fetchPriority) {
1234 runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
1235 }
1236
1237 /**
1238 * Returns require chunk id code.
1239 * @param {Chunk} chunk chunk
1240 * @returns {string} require chunk id code
1241 */
1242 const requireChunkId = (chunk) =>
1243 `${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)}${
1244 fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
1245 })`;
1246 return `Promise.all(${comment.trim()}[${chunks
1247 .map(requireChunkId)
1248 .join(", ")}])`;
1249 }
1250 return `Promise.resolve(${comment.trim()})`;
1251 }
1252
1253 /**
1254 * Async module factory.
1255 * @param {object} options options
1256 * @param {AsyncDependenciesBlock} options.block the async block
1257 * @param {ChunkGraph} options.chunkGraph the chunk graph
1258 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
1259 * @param {string=} options.request request string used originally
1260 * @returns {string} expression
1261 */
1262 asyncModuleFactory({ block, chunkGraph, runtimeRequirements, request }) {
1263 const dep = block.dependencies[0];
1264 const module = chunkGraph.moduleGraph.getModule(dep);
1265 const ensureChunk = this.blockPromise({
1266 block,
1267 message: "",
1268 chunkGraph,
1269 runtimeRequirements
1270 });
1271 const factory = this.returningFunction(
1272 this.moduleRaw({
1273 module,
1274 chunkGraph,
1275 request,
1276 runtimeRequirements
1277 })
1278 );
1279 return this.returningFunction(
1280 ensureChunk.startsWith("Promise.resolve(")
1281 ? `${factory}`
1282 : `${ensureChunk}.then(${this.returningFunction(factory)})`
1283 );
1284 }
1285
1286 /**
1287 * Sync module factory.
1288 * @param {object} options options
1289 * @param {Dependency} options.dependency the dependency
1290 * @param {ChunkGraph} options.chunkGraph the chunk graph
1291 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
1292 * @param {string=} options.request request string used originally
1293 * @returns {string} expression
1294 */
1295 syncModuleFactory({ dependency, chunkGraph, runtimeRequirements, request }) {
1296 const module = chunkGraph.moduleGraph.getModule(dependency);
1297 const factory = this.returningFunction(
1298 this.moduleRaw({
1299 module,
1300 chunkGraph,
1301 request,
1302 runtimeRequirements
1303 })
1304 );
1305 return this.returningFunction(factory);
1306 }
1307
1308 /**
1309 * Define es module flag statement.
1310 * @param {object} options options
1311 * @param {string} options.exportsArgument the name of the exports object
1312 * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
1313 * @returns {string} statement
1314 */
1315 defineEsModuleFlagStatement({ exportsArgument, runtimeRequirements }) {
1316 runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
1317 runtimeRequirements.add(RuntimeGlobals.exports);
1318 return `${RuntimeGlobals.makeNamespaceObject}(${exportsArgument});\n`;
1319 }
1320}
1321
1322module.exports = RuntimeTemplate;
Note: See TracBrowser for help on using the repository browser.