RuntimeTemplate.js 32 KB

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