RuntimeTemplate.js 34 KB

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