AsyncWebAssemblyJavascriptGenerator.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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 RuntimeGlobals = require("../RuntimeGlobals");
  10. const Template = require("../Template");
  11. const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
  12. /** @typedef {import("webpack-sources").Source} Source */
  13. /** @typedef {import("../../declarations/WebpackOptions").OutputNormalized} OutputOptions */
  14. /** @typedef {import("../DependencyTemplates")} DependencyTemplates */
  15. /** @typedef {import("../Generator").GenerateContext} GenerateContext */
  16. /** @typedef {import("../Module")} Module */
  17. /** @typedef {import("../NormalModule")} NormalModule */
  18. /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
  19. const TYPES = new Set(["webassembly"]);
  20. /**
  21. * @typedef {{ request: string, importVar: string }} ImportObjRequestItem
  22. */
  23. class AsyncWebAssemblyJavascriptGenerator extends Generator {
  24. /**
  25. * @param {OutputOptions["webassemblyModuleFilename"]} filenameTemplate template for the WebAssembly module filename
  26. */
  27. constructor(filenameTemplate) {
  28. super();
  29. this.filenameTemplate = filenameTemplate;
  30. }
  31. /**
  32. * @param {NormalModule} module fresh module
  33. * @returns {Set<string>} available types (do not mutate)
  34. */
  35. getTypes(module) {
  36. return TYPES;
  37. }
  38. /**
  39. * @param {NormalModule} module the module
  40. * @param {string=} type source type
  41. * @returns {number} estimate size of the module
  42. */
  43. getSize(module, type) {
  44. return 40 + module.dependencies.length * 10;
  45. }
  46. /**
  47. * @param {NormalModule} module module for which the code should be generated
  48. * @param {GenerateContext} generateContext context for generate
  49. * @returns {Source} generated code
  50. */
  51. generate(module, generateContext) {
  52. const {
  53. runtimeTemplate,
  54. chunkGraph,
  55. moduleGraph,
  56. runtimeRequirements,
  57. runtime
  58. } = generateContext;
  59. runtimeRequirements.add(RuntimeGlobals.module);
  60. runtimeRequirements.add(RuntimeGlobals.moduleId);
  61. runtimeRequirements.add(RuntimeGlobals.exports);
  62. runtimeRequirements.add(RuntimeGlobals.instantiateWasm);
  63. /** @type {InitFragment<InitFragment<string>>[]} */
  64. const initFragments = [];
  65. /** @type {Map<Module, ImportObjRequestItem>} */
  66. const depModules = new Map();
  67. /** @type {Map<string, WebAssemblyImportDependency[]>} */
  68. const wasmDepsByRequest = new Map();
  69. for (const dep of module.dependencies) {
  70. if (dep instanceof WebAssemblyImportDependency) {
  71. const module = /** @type {Module} */ (moduleGraph.getModule(dep));
  72. if (!depModules.has(module)) {
  73. depModules.set(module, {
  74. request: dep.request,
  75. importVar: `WEBPACK_IMPORTED_MODULE_${depModules.size}`
  76. });
  77. }
  78. let list = wasmDepsByRequest.get(dep.request);
  79. if (list === undefined) {
  80. list = [];
  81. wasmDepsByRequest.set(dep.request, list);
  82. }
  83. list.push(dep);
  84. }
  85. }
  86. /** @type {Array<string>} */
  87. const promises = [];
  88. const importStatements = Array.from(
  89. depModules,
  90. ([importedModule, { request, importVar }]) => {
  91. if (moduleGraph.isAsync(importedModule)) {
  92. promises.push(importVar);
  93. }
  94. return runtimeTemplate.importStatement({
  95. update: false,
  96. module: importedModule,
  97. chunkGraph,
  98. request,
  99. originModule: module,
  100. importVar,
  101. runtimeRequirements
  102. });
  103. }
  104. );
  105. const importsCode = importStatements.map(([x]) => x).join("");
  106. const importsCompatCode = importStatements.map(([_, x]) => x).join("");
  107. const importObjRequestItems = Array.from(
  108. wasmDepsByRequest,
  109. ([request, deps]) => {
  110. const exportItems = deps.map(dep => {
  111. const importedModule =
  112. /** @type {Module} */
  113. (moduleGraph.getModule(dep));
  114. const importVar =
  115. /** @type {ImportObjRequestItem} */
  116. (depModules.get(importedModule)).importVar;
  117. return `${JSON.stringify(
  118. dep.name
  119. )}: ${runtimeTemplate.exportFromImport({
  120. moduleGraph,
  121. module: importedModule,
  122. request,
  123. exportName: dep.name,
  124. originModule: module,
  125. asiSafe: true,
  126. isCall: false,
  127. callContext: false,
  128. defaultInterop: true,
  129. importVar,
  130. initFragments,
  131. runtime,
  132. runtimeRequirements
  133. })}`;
  134. });
  135. return Template.asString([
  136. `${JSON.stringify(request)}: {`,
  137. Template.indent(exportItems.join(",\n")),
  138. "}"
  139. ]);
  140. }
  141. );
  142. const importsObj =
  143. importObjRequestItems.length > 0
  144. ? Template.asString([
  145. "{",
  146. Template.indent(importObjRequestItems.join(",\n")),
  147. "}"
  148. ])
  149. : undefined;
  150. const instantiateCall =
  151. `${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;