AsyncWebAssemblyJavascriptGenerator.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { RawSource } = require("webpack-sources");
  7. const Generator = require("../Generator");
  8. const InitFragment = require("../InitFragment");
  9. const { WEBASSEMBLY_TYPES } = require("../ModuleSourceTypesConstants");
  10. const RuntimeGlobals = require("../RuntimeGlobals");
  11. const Template = require("../Template");
  12. const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
  13. /** @typedef {import("webpack-sources").Source} Source */
  14. /** @typedef {import("../../declarations/WebpackOptions").OutputNormalized} OutputOptions */
  15. /** @typedef {import("../DependencyTemplates")} DependencyTemplates */
  16. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  17. /** @typedef {import("../Module")} Module */
  18. /** @typedef {import("../Module").SourceTypes} SourceTypes */
  19. /** @typedef {import("../NormalModule")} NormalModule */
  20. /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
  21. /**
  22. * @typedef {{ request: string, importVar: string }} ImportObjRequestItem
  23. */
  24. class AsyncWebAssemblyJavascriptGenerator extends Generator {
  25. /**
  26. * @param {OutputOptions["webassemblyModuleFilename"]} filenameTemplate template for the WebAssembly module filename
  27. */
  28. constructor(filenameTemplate) {
  29. super();
  30. this.filenameTemplate = filenameTemplate;
  31. }
  32. /**
  33. * @param {NormalModule} module fresh module
  34. * @returns {SourceTypes} available types (do not mutate)
  35. */
  36. getTypes(module) {
  37. return WEBASSEMBLY_TYPES;
  38. }
  39. /**
  40. * @param {NormalModule} module the module
  41. * @param {string=} type source type
  42. * @returns {number} estimate size of the module
  43. */
  44. getSize(module, type) {
  45. return 40 + module.dependencies.length * 10;
  46. }
  47. /**
  48. * @param {NormalModule} module module for which the code should be generated
  49. * @param {GenerateContext} generateContext context for generate
  50. * @returns {Source | null} generated code
  51. */
  52. generate(module, generateContext) {
  53. const {
  54. runtimeTemplate,
  55. chunkGraph,
  56. moduleGraph,
  57. runtimeRequirements,
  58. runtime
  59. } = generateContext;
  60. runtimeRequirements.add(RuntimeGlobals.module);
  61. runtimeRequirements.add(RuntimeGlobals.moduleId);
  62. runtimeRequirements.add(RuntimeGlobals.exports);
  63. runtimeRequirements.add(RuntimeGlobals.instantiateWasm);
  64. /** @type {InitFragment<InitFragment<string>>[]} */
  65. const initFragments = [];
  66. /** @type {Map<Module, ImportObjRequestItem>} */
  67. const depModules = new Map();
  68. /** @type {Map<string, WebAssemblyImportDependency[]>} */
  69. const wasmDepsByRequest = new Map();
  70. for (const dep of module.dependencies) {
  71. if (dep instanceof WebAssemblyImportDependency) {
  72. const module = /** @type {Module} */ (moduleGraph.getModule(dep));
  73. if (!depModules.has(module)) {
  74. depModules.set(module, {
  75. request: dep.request,
  76. importVar: `WEBPACK_IMPORTED_MODULE_${depModules.size}`
  77. });
  78. }
  79. let list = wasmDepsByRequest.get(dep.request);
  80. if (list === undefined) {
  81. list = [];
  82. wasmDepsByRequest.set(dep.request, list);
  83. }
  84. list.push(dep);
  85. }
  86. }
  87. /** @type {Array<string>} */
  88. const promises = [];
  89. const importStatements = Array.from(
  90. depModules,
  91. ([importedModule, { request, importVar }]) => {
  92. if (moduleGraph.isAsync(importedModule)) {
  93. promises.push(importVar);
  94. }
  95. return runtimeTemplate.importStatement({
  96. update: false,
  97. module: importedModule,
  98. chunkGraph,
  99. request,
  100. originModule: module,
  101. importVar,
  102. runtimeRequirements
  103. });
  104. }
  105. );
  106. const importsCode = importStatements.map(([x]) => x).join("");
  107. const importsCompatCode = importStatements.map(([_, x]) => x).join("");
  108. const importObjRequestItems = Array.from(
  109. wasmDepsByRequest,
  110. ([request, deps]) => {
  111. const exportItems = deps.map(dep => {
  112. const importedModule =
  113. /** @type {Module} */
  114. (moduleGraph.getModule(dep));
  115. const importVar =
  116. /** @type {ImportObjRequestItem} */
  117. (depModules.get(importedModule)).importVar;
  118. return `${JSON.stringify(
  119. dep.name
  120. )}: ${runtimeTemplate.exportFromImport({
  121. moduleGraph,
  122. module: importedModule,
  123. request,
  124. exportName: dep.name,
  125. originModule: module,
  126. asiSafe: true,
  127. isCall: false,
  128. callContext: false,
  129. defaultInterop: true,
  130. importVar,
  131. initFragments,
  132. runtime,
  133. runtimeRequirements
  134. })}`;
  135. });
  136. return Template.asString([
  137. `${JSON.stringify(request)}: {`,
  138. Template.indent(exportItems.join(",\n")),
  139. "}"
  140. ]);
  141. }
  142. );
  143. const importsObj =
  144. importObjRequestItems.length > 0
  145. ? Template.asString([
  146. "{",
  147. Template.indent(importObjRequestItems.join(",\n")),
  148. "}"
  149. ])
  150. : undefined;
  151. const instantiateCall = `${RuntimeGlobals.instantiateWasm}(${module.exportsArgument}, ${
  152. module.moduleArgument
  153. }.id, ${JSON.stringify(
  154. chunkGraph.getRenderedModuleHash(module, runtime)
  155. )}${importsObj ? `, ${importsObj})` : ")"}`;
  156. if (promises.length > 0)
  157. runtimeRequirements.add(RuntimeGlobals.asyncModule);
  158. const source = new RawSource(
  159. promises.length > 0
  160. ? Template.asString([
  161. `var __webpack_instantiate__ = ${runtimeTemplate.basicFunction(
  162. `[${promises.join(", ")}]`,
  163. `${importsCompatCode}return ${instantiateCall};`
  164. )}`,
  165. `${RuntimeGlobals.asyncModule}(${
  166. module.moduleArgument
  167. }, async ${runtimeTemplate.basicFunction(
  168. "__webpack_handle_async_dependencies__, __webpack_async_result__",
  169. [
  170. "try {",
  171. importsCode,
  172. `var __webpack_async_dependencies__ = __webpack_handle_async_dependencies__([${promises.join(
  173. ", "
  174. )}]);`,
  175. `var [${promises.join(
  176. ", "
  177. )}] = __webpack_async_dependencies__.then ? (await __webpack_async_dependencies__)() : __webpack_async_dependencies__;`,
  178. `${importsCompatCode}await ${instantiateCall};`,
  179. "__webpack_async_result__();",
  180. "} catch(e) { __webpack_async_result__(e); }"
  181. ]
  182. )}, 1);`
  183. ])
  184. : `${importsCode}${importsCompatCode}module.exports = ${instantiateCall};`
  185. );
  186. return InitFragment.addToSource(source, initFragments, generateContext);
  187. }
  188. }
  189. module.exports = AsyncWebAssemblyJavascriptGenerator;