AsyncWebAssemblyModulesPlugin.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { SyncWaterfallHook } = require("tapable");
  7. const Generator = require("../Generator");
  8. const { WEBASSEMBLY_MODULE_TYPE_ASYNC } = require("../ModuleTypeConstants");
  9. const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
  10. const { tryRunOrWebpackError } = require("../errors/HookWebpackError");
  11. const { compareModulesByFullName } = require("../util/comparators");
  12. const createHooksRegistry = require("../util/createHooksRegistry");
  13. const lazyModule = require("../util/lazyModule");
  14. const preloadModuleType = require("../util/preloadModuleType");
  15. const AsyncWasmModule = require("./AsyncWasmModule");
  16. /** @import { Source } from "webpack-sources" */
  17. /** @import Chunk from "../Chunk" */
  18. /** @import ChunkGraph from "../ChunkGraph" */
  19. /** @import CodeGenerationResults from "../CodeGenerationResults" */
  20. /** @import Compiler from "../Compiler" */
  21. /** @import DependencyTemplates from "../DependencyTemplates" */
  22. /** @import Module from "../Module" */
  23. /** @import ModuleGraph from "../ModuleGraph" */
  24. /** @import RuntimeTemplate from "../RuntimeTemplate" */
  25. /** @import WebpackError from "../errors/WebpackError" */
  26. const getAsyncWebAssemblyGenerator = lazyModule(() =>
  27. require("./AsyncWebAssemblyGenerator")
  28. );
  29. const getAsyncWebAssemblyJavascriptGenerator = lazyModule(() =>
  30. require("./AsyncWebAssemblyJavascriptGenerator")
  31. );
  32. const getAsyncWebAssemblyParser = lazyModule(() =>
  33. require("./AsyncWebAssemblyParser")
  34. );
  35. /**
  36. * Defines the web assembly render context type used by this module.
  37. * @typedef {object} WebAssemblyRenderContext
  38. * @property {Chunk} chunk the chunk
  39. * @property {DependencyTemplates} dependencyTemplates the dependency templates
  40. * @property {RuntimeTemplate} runtimeTemplate the runtime template
  41. * @property {ModuleGraph} moduleGraph the module graph
  42. * @property {ChunkGraph} chunkGraph the chunk graph
  43. * @property {CodeGenerationResults} codeGenerationResults results of code generation
  44. */
  45. /**
  46. * Defines the compilation hooks type used by this module.
  47. * @typedef {object} CompilationHooks
  48. * @property {SyncWaterfallHook<[Source, Module, WebAssemblyRenderContext]>} renderModuleContent
  49. */
  50. /**
  51. * Defines the async web assembly modules plugin options type used by this module.
  52. * @typedef {object} AsyncWebAssemblyModulesPluginOptions
  53. * @property {boolean=} mangleImports mangle imports
  54. */
  55. const PLUGIN_NAME = "AsyncWebAssemblyModulesPlugin";
  56. class AsyncWebAssemblyModulesPlugin {
  57. /**
  58. * Creates an instance of AsyncWebAssemblyModulesPlugin.
  59. * @param {AsyncWebAssemblyModulesPluginOptions} options options
  60. */
  61. constructor(options) {
  62. /** @type {AsyncWebAssemblyModulesPluginOptions} */
  63. this.options = options;
  64. }
  65. /**
  66. * Applies the plugin by registering its hooks on the compiler.
  67. * @param {Compiler} compiler the compiler instance
  68. * @returns {void}
  69. */
  70. apply(compiler) {
  71. // A binary's name may carry the compilation hash, which the analyzable call site
  72. // leaves as a stand-in — so whatever the chunk loading is, the pass that fills it
  73. // in has to be there. Applying it twice is a no-op.
  74. compiler.hooks.compilation.tap(
  75. PLUGIN_NAME,
  76. (compilation, { normalModuleFactory }) => {
  77. const hooks =
  78. AsyncWebAssemblyModulesPlugin.getCompilationHooks(compilation);
  79. compilation.dependencyFactories.set(
  80. WebAssemblyImportDependency,
  81. normalModuleFactory
  82. );
  83. preloadModuleType(normalModuleFactory, PLUGIN_NAME, [
  84. [
  85. WEBASSEMBLY_MODULE_TYPE_ASYNC,
  86. [
  87. getAsyncWebAssemblyParser,
  88. getAsyncWebAssemblyJavascriptGenerator,
  89. getAsyncWebAssemblyGenerator
  90. ]
  91. ]
  92. ]);
  93. normalModuleFactory.hooks.createModuleClass
  94. .for(WEBASSEMBLY_MODULE_TYPE_ASYNC)
  95. .tap(
  96. PLUGIN_NAME,
  97. (createData, resolveData) =>
  98. new AsyncWasmModule({
  99. ...createData,
  100. phase: resolveData.phase
  101. })
  102. );
  103. normalModuleFactory.hooks.createParser
  104. .for(WEBASSEMBLY_MODULE_TYPE_ASYNC)
  105. .tap(PLUGIN_NAME, () => {
  106. const AsyncWebAssemblyParser = getAsyncWebAssemblyParser.loaded();
  107. return new AsyncWebAssemblyParser();
  108. });
  109. normalModuleFactory.hooks.createGenerator
  110. .for(WEBASSEMBLY_MODULE_TYPE_ASYNC)
  111. .tap(PLUGIN_NAME, () => {
  112. const AsyncWebAssemblyJavascriptGenerator =
  113. getAsyncWebAssemblyJavascriptGenerator.loaded();
  114. const AsyncWebAssemblyGenerator =
  115. getAsyncWebAssemblyGenerator.loaded();
  116. return Generator.byType({
  117. javascript: new AsyncWebAssemblyJavascriptGenerator(),
  118. webassembly: new AsyncWebAssemblyGenerator(this.options)
  119. });
  120. });
  121. compilation.hooks.renderManifest.tap(PLUGIN_NAME, (result, options) => {
  122. const { moduleGraph, chunkGraph, runtimeTemplate } = compilation;
  123. const {
  124. chunk,
  125. outputOptions,
  126. dependencyTemplates,
  127. codeGenerationResults
  128. } = options;
  129. for (const module of chunkGraph.getOrderedChunkModulesIterable(
  130. chunk,
  131. compareModulesByFullName(compiler)
  132. )) {
  133. if (module.type === WEBASSEMBLY_MODULE_TYPE_ASYNC) {
  134. const filenameTemplate = outputOptions.webassemblyModuleFilename;
  135. result.push({
  136. render: () =>
  137. this.renderModule(
  138. module,
  139. {
  140. chunk,
  141. dependencyTemplates,
  142. runtimeTemplate,
  143. moduleGraph,
  144. chunkGraph,
  145. codeGenerationResults
  146. },
  147. hooks
  148. ),
  149. filenameTemplate,
  150. pathOptions: {
  151. module,
  152. runtime: chunk.runtime,
  153. chunkGraph
  154. },
  155. auxiliary: true,
  156. identifier: `webassemblyAsyncModule${chunkGraph.getModuleId(
  157. module
  158. )}`,
  159. hash: chunkGraph.getModuleHash(module, chunk.runtime)
  160. });
  161. }
  162. }
  163. return result;
  164. });
  165. }
  166. );
  167. }
  168. /**
  169. * Renders the newly generated source from rendering.
  170. * @param {Module} module the rendered module
  171. * @param {WebAssemblyRenderContext} renderContext options object
  172. * @param {CompilationHooks} hooks hooks
  173. * @returns {Source} the newly generated source from rendering
  174. */
  175. renderModule(module, renderContext, hooks) {
  176. const { codeGenerationResults, chunk } = renderContext;
  177. try {
  178. const moduleSource = codeGenerationResults.getSource(
  179. module,
  180. chunk.runtime,
  181. "webassembly"
  182. );
  183. return tryRunOrWebpackError(
  184. () =>
  185. hooks.renderModuleContent.call(moduleSource, module, renderContext),
  186. "AsyncWebAssemblyModulesPlugin.getCompilationHooks().renderModuleContent"
  187. );
  188. } catch (err) {
  189. /** @type {WebpackError} */ (err).module = module;
  190. throw err;
  191. }
  192. }
  193. }
  194. AsyncWebAssemblyModulesPlugin.getCompilationHooks = createHooksRegistry(
  195. () =>
  196. /** @type {CompilationHooks} */ ({
  197. renderModuleContent: new SyncWaterfallHook([
  198. "source",
  199. "module",
  200. "renderContext"
  201. ])
  202. })
  203. );
  204. module.exports = AsyncWebAssemblyModulesPlugin;