提交学习笔记专用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1218 lines
37 KiB

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