AsyncWebAssemblyJavascriptGenerator.js 5.7 KB

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