RuntimeTemplate.js 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208
  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("../declarations/WebpackOptions").Environment} Environment */
  21. /** @typedef {import("../declarations/WebpackOptions").OutputNormalized} OutputOptions */
  22. /** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
  23. /** @typedef {import("./Chunk")} Chunk */
  24. /** @typedef {import("./ChunkGraph")} ChunkGraph */
  25. /** @typedef {import("./CodeGenerationResults")} CodeGenerationResults */
  26. /** @typedef {import("./CodeGenerationResults").CodeGenerationResult} CodeGenerationResult */
  27. /** @typedef {import("./Compilation")} Compilation */
  28. /** @typedef {import("./Dependency")} Dependency */
  29. /** @typedef {import("./Module")} Module */
  30. /** @typedef {import("./Module").BuildMeta} BuildMeta */
  31. /** @typedef {import("./Module").RuntimeRequirements} RuntimeRequirements */
  32. /** @typedef {import("./ModuleGraph")} ModuleGraph */
  33. /** @typedef {import("./RequestShortener")} RequestShortener */
  34. /** @typedef {import("./util/runtime").RuntimeSpec} RuntimeSpec */
  35. /**
  36. * @param {Module} module the module
  37. * @param {ChunkGraph} chunkGraph the chunk graph
  38. * @returns {string} error message
  39. */
  40. const noModuleIdErrorMessage = (
  41. module,
  42. chunkGraph
  43. ) => `Module ${module.identifier()} has no id assigned.
  44. This should not happen.
  45. It's in these chunks: ${
  46. Array.from(
  47. chunkGraph.getModuleChunksIterable(module),
  48. (c) => c.name || c.id || c.debugId
  49. ).join(", ") || "none"
  50. } (If module is in no chunk this indicates a bug in some chunk/module optimization logic)
  51. Module has these incoming connections: ${Array.from(
  52. chunkGraph.moduleGraph.getIncomingConnections(module),
  53. (connection) =>
  54. `\n - ${
  55. connection.originModule && connection.originModule.identifier()
  56. } ${connection.dependency && connection.dependency.type} ${
  57. (connection.explanations && [...connection.explanations].join(", ")) || ""
  58. }`
  59. ).join("")}`;
  60. /**
  61. * @param {string | undefined} definition global object definition
  62. * @returns {string | undefined} save to use global object
  63. */
  64. function getGlobalObject(definition) {
  65. if (!definition) return definition;
  66. const trimmed = definition.trim();
  67. if (
  68. // identifier, we do not need real identifier regarding ECMAScript/Unicode
  69. /^[_\p{L}][_0-9\p{L}]*$/iu.test(trimmed) ||
  70. // iife
  71. // call expression
  72. // expression in parentheses
  73. /^([_\p{L}][_0-9\p{L}]*)?\(.*\)$/iu.test(trimmed)
  74. ) {
  75. return trimmed;
  76. }
  77. return `Object(${trimmed})`;
  78. }
  79. class RuntimeTemplate {
  80. /**
  81. * @param {Compilation} compilation the compilation
  82. * @param {OutputOptions} outputOptions the compilation output options
  83. * @param {RequestShortener} requestShortener the request shortener
  84. */
  85. constructor(compilation, outputOptions, requestShortener) {
  86. this.compilation = compilation;
  87. this.outputOptions = /** @type {OutputOptions} */ (outputOptions || {});
  88. this.requestShortener = requestShortener;
  89. this.globalObject =
  90. /** @type {string} */
  91. (getGlobalObject(outputOptions.globalObject));
  92. this.contentHashReplacement = "X".repeat(
  93. /** @type {NonNullable<OutputOptions["hashDigestLength"]>} */
  94. (outputOptions.hashDigestLength)
  95. );
  96. }
  97. isIIFE() {
  98. return this.outputOptions.iife;
  99. }
  100. isModule() {
  101. return this.outputOptions.module;
  102. }
  103. isNeutralPlatform() {
  104. return (
  105. !this.outputOptions.environment.document &&
  106. !this.compilation.compiler.platform.node
  107. );
  108. }
  109. supportsConst() {
  110. return this.outputOptions.environment.const;
  111. }
  112. supportsArrowFunction() {
  113. return this.outputOptions.environment.arrowFunction;
  114. }
  115. supportsAsyncFunction() {
  116. return this.outputOptions.environment.asyncFunction;
  117. }
  118. supportsOptionalChaining() {
  119. return this.outputOptions.environment.optionalChaining;
  120. }
  121. supportsForOf() {
  122. return this.outputOptions.environment.forOf;
  123. }
  124. supportsDestructuring() {
  125. return this.outputOptions.environment.destructuring;
  126. }
  127. supportsBigIntLiteral() {
  128. return this.outputOptions.environment.bigIntLiteral;
  129. }
  130. supportsDynamicImport() {
  131. return this.outputOptions.environment.dynamicImport;
  132. }
  133. supportsEcmaScriptModuleSyntax() {
  134. return this.outputOptions.environment.module;
  135. }
  136. supportTemplateLiteral() {
  137. return this.outputOptions.environment.templateLiteral;
  138. }
  139. supportNodePrefixForCoreModules() {
  140. return this.outputOptions.environment.nodePrefixForCoreModules;
  141. }
  142. /**
  143. * @param {string} mod a module
  144. * @returns {string} a module with `node:` prefix when supported, otherwise an original name
  145. */
  146. renderNodePrefixForCoreModule(mod) {
  147. return this.outputOptions.environment.nodePrefixForCoreModules
  148. ? `"node:${mod}"`
  149. : `"${mod}"`;
  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 {Array<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 {Array<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 access = `${importVar}.a${propertyAccess(exportName, 1)}`;
  888. if (isCall || asiSafe === undefined) {
  889. return access;
  890. }
  891. return asiSafe ? `(${access})` : `;(${access})`;
  892. }
  893. // accessing the .default property is same thing as `require()` the module.
  894. // For example:
  895. // import mod from "cjs"; mod.default.x;
  896. // is translated to
  897. // var mod = require("cjs"); mod.x;
  898. switch (exportsType) {
  899. case "dynamic":
  900. if (isCall) {
  901. return `${importVar}_default()${propertyAccess(exportName, 1)}`;
  902. }
  903. return asiSafe
  904. ? `(${importVar}_default()${propertyAccess(exportName, 1)})`
  905. : asiSafe === false
  906. ? `;(${importVar}_default()${propertyAccess(exportName, 1)})`
  907. : `${importVar}_default.a${propertyAccess(exportName, 1)}`;
  908. case "default-only":
  909. case "default-with-named":
  910. exportName = exportName.slice(1);
  911. break;
  912. }
  913. } else if (exportName.length > 0) {
  914. // the property used is not .default.
  915. // For example:
  916. // import * as ns from "cjs"; cjs.prop;
  917. if (exportsType === "default-only") {
  918. // in the strictest case, it is a runtime error (e.g. NodeJS behavior of CJS-ESM interop).
  919. return `/* non-default import from non-esm module */undefined${propertyAccess(
  920. exportName,
  921. 1
  922. )}`;
  923. } else if (
  924. exportsType !== "namespace" &&
  925. exportName[0] === "__esModule"
  926. ) {
  927. return "/* __esModule */true";
  928. }
  929. } else if (isDeferred) {
  930. // now exportName.length is 0
  931. // fall through to the end of this function, create the namespace there.
  932. } else if (
  933. exportsType === "default-only" ||
  934. exportsType === "default-with-named"
  935. ) {
  936. // now exportName.length is 0, which means the namespace object is used in an unknown way
  937. // for example:
  938. // import * as ns from "cjs"; console.log(ns);
  939. // we will need to createFakeNamespaceObject that simulates ES Module namespace object
  940. runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
  941. initFragments.push(
  942. new InitFragment(
  943. `var ${importVar}_namespace_cache;\n`,
  944. InitFragment.STAGE_CONSTANTS,
  945. -1,
  946. `${importVar}_namespace_cache`
  947. )
  948. );
  949. return `/*#__PURE__*/ ${
  950. asiSafe ? "" : asiSafe === false ? ";" : "Object"
  951. }(${importVar}_namespace_cache || (${importVar}_namespace_cache = ${
  952. RuntimeGlobals.createFakeNamespaceObject
  953. }(${importVar}${exportsType === "default-only" ? "" : ", 2"})))`;
  954. }
  955. }
  956. if (exportName.length > 0) {
  957. const exportsInfo = moduleGraph.getExportsInfo(module);
  958. // in some case the exported item is renamed (get this by getUsedName). for example,
  959. // x.default might be emitted as x.Z (default is renamed to Z)
  960. const used = exportsInfo.getUsedName(exportName, runtime);
  961. if (!used) {
  962. const comment = Template.toNormalComment(
  963. `unused export ${propertyAccess(exportName)}`
  964. );
  965. return `${comment} undefined`;
  966. }
  967. const comment = equals(used, exportName)
  968. ? ""
  969. : `${Template.toNormalComment(propertyAccess(exportName))} `;
  970. const access = `${importVar}${
  971. isDeferred ? ".a" : ""
  972. }${comment}${propertyAccess(used)}`;
  973. if (isCall && callContext === false) {
  974. return asiSafe
  975. ? `(0,${access})`
  976. : asiSafe === false
  977. ? `;(0,${access})`
  978. : `/*#__PURE__*/Object(${access})`;
  979. }
  980. return access;
  981. }
  982. if (isDeferred) {
  983. initFragments.push(
  984. new InitFragment(
  985. `var ${importVar}_deferred_namespace_cache;\n`,
  986. InitFragment.STAGE_CONSTANTS,
  987. -1,
  988. `${importVar}_deferred_namespace_cache`
  989. )
  990. );
  991. runtimeRequirements.add(RuntimeGlobals.makeDeferredNamespaceObject);
  992. const id = chunkGraph.getModuleId(module);
  993. const type = getMakeDeferredNamespaceModeFromExportsType(exportsType);
  994. const init = `${
  995. RuntimeGlobals.makeDeferredNamespaceObject
  996. }(${JSON.stringify(id)}, ${type})`;
  997. return `/*#__PURE__*/ ${
  998. asiSafe ? "" : asiSafe === false ? ";" : "Object"
  999. }(${importVar}_deferred_namespace_cache || (${importVar}_deferred_namespace_cache = ${init}))`;
  1000. }
  1001. // if we hit here, the importVar is either
  1002. // - already a ES module namespace object
  1003. // - or imported by a way that does not need interop.
  1004. return importVar;
  1005. }
  1006. /**
  1007. * @param {object} options options
  1008. * @param {AsyncDependenciesBlock | undefined} options.block the async block
  1009. * @param {string} options.message the message
  1010. * @param {ChunkGraph} options.chunkGraph the chunk graph
  1011. * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
  1012. * @returns {string} expression
  1013. */
  1014. blockPromise({ block, message, chunkGraph, runtimeRequirements }) {
  1015. if (!block) {
  1016. const comment = this.comment({
  1017. message
  1018. });
  1019. return `Promise.resolve(${comment.trim()})`;
  1020. }
  1021. const chunkGroup = chunkGraph.getBlockChunkGroup(block);
  1022. if (!chunkGroup || chunkGroup.chunks.length === 0) {
  1023. const comment = this.comment({
  1024. message
  1025. });
  1026. return `Promise.resolve(${comment.trim()})`;
  1027. }
  1028. const chunks = chunkGroup.chunks.filter(
  1029. (chunk) => !chunk.hasRuntime() && chunk.id !== null
  1030. );
  1031. const comment = this.comment({
  1032. message,
  1033. chunkName: block.chunkName
  1034. });
  1035. if (chunks.length === 1) {
  1036. const chunkId = JSON.stringify(chunks[0].id);
  1037. runtimeRequirements.add(RuntimeGlobals.ensureChunk);
  1038. const fetchPriority = chunkGroup.options.fetchPriority;
  1039. if (fetchPriority) {
  1040. runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
  1041. }
  1042. return `${RuntimeGlobals.ensureChunk}(${comment}${chunkId}${
  1043. fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
  1044. })`;
  1045. } else if (chunks.length > 0) {
  1046. runtimeRequirements.add(RuntimeGlobals.ensureChunk);
  1047. const fetchPriority = chunkGroup.options.fetchPriority;
  1048. if (fetchPriority) {
  1049. runtimeRequirements.add(RuntimeGlobals.hasFetchPriority);
  1050. }
  1051. /**
  1052. * @param {Chunk} chunk chunk
  1053. * @returns {string} require chunk id code
  1054. */
  1055. const requireChunkId = (chunk) =>
  1056. `${RuntimeGlobals.ensureChunk}(${JSON.stringify(chunk.id)}${
  1057. fetchPriority ? `, ${JSON.stringify(fetchPriority)}` : ""
  1058. })`;
  1059. return `Promise.all(${comment.trim()}[${chunks
  1060. .map(requireChunkId)
  1061. .join(", ")}])`;
  1062. }
  1063. return `Promise.resolve(${comment.trim()})`;
  1064. }
  1065. /**
  1066. * @param {object} options options
  1067. * @param {AsyncDependenciesBlock} options.block the async block
  1068. * @param {ChunkGraph} options.chunkGraph the chunk graph
  1069. * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
  1070. * @param {string=} options.request request string used originally
  1071. * @returns {string} expression
  1072. */
  1073. asyncModuleFactory({ block, chunkGraph, runtimeRequirements, request }) {
  1074. const dep = block.dependencies[0];
  1075. const module = chunkGraph.moduleGraph.getModule(dep);
  1076. const ensureChunk = this.blockPromise({
  1077. block,
  1078. message: "",
  1079. chunkGraph,
  1080. runtimeRequirements
  1081. });
  1082. const factory = this.returningFunction(
  1083. this.moduleRaw({
  1084. module,
  1085. chunkGraph,
  1086. request,
  1087. runtimeRequirements
  1088. })
  1089. );
  1090. return this.returningFunction(
  1091. ensureChunk.startsWith("Promise.resolve(")
  1092. ? `${factory}`
  1093. : `${ensureChunk}.then(${this.returningFunction(factory)})`
  1094. );
  1095. }
  1096. /**
  1097. * @param {object} options options
  1098. * @param {Dependency} options.dependency the dependency
  1099. * @param {ChunkGraph} options.chunkGraph the chunk graph
  1100. * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
  1101. * @param {string=} options.request request string used originally
  1102. * @returns {string} expression
  1103. */
  1104. syncModuleFactory({ dependency, chunkGraph, runtimeRequirements, request }) {
  1105. const module = chunkGraph.moduleGraph.getModule(dependency);
  1106. const factory = this.returningFunction(
  1107. this.moduleRaw({
  1108. module,
  1109. chunkGraph,
  1110. request,
  1111. runtimeRequirements
  1112. })
  1113. );
  1114. return this.returningFunction(factory);
  1115. }
  1116. /**
  1117. * @param {object} options options
  1118. * @param {string} options.exportsArgument the name of the exports object
  1119. * @param {RuntimeRequirements} options.runtimeRequirements if set, will be filled with runtime requirements
  1120. * @returns {string} statement
  1121. */
  1122. defineEsModuleFlagStatement({ exportsArgument, runtimeRequirements }) {
  1123. runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
  1124. runtimeRequirements.add(RuntimeGlobals.exports);
  1125. return `${RuntimeGlobals.makeNamespaceObject}(${exportsArgument});\n`;
  1126. }
  1127. }
  1128. module.exports = RuntimeTemplate;