RuntimeTemplate.js 36 KB

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