AsyncWebAssemblyModulesPlugin.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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 Compilation = require("../Compilation");
  8. const Generator = require("../Generator");
  9. const { tryRunOrWebpackError } = require("../HookWebpackError");
  10. const { WEBASSEMBLY_MODULE_TYPE_ASYNC } = require("../ModuleTypeConstants");
  11. const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
  12. const { compareModulesByIdentifier } = require("../util/comparators");
  13. const memoize = require("../util/memoize");
  14. /** @typedef {import("webpack-sources").Source} Source */
  15. /** @typedef {import("../../declarations/WebpackOptions").OutputNormalized} OutputOptions */
  16. /** @typedef {import("../Chunk")} Chunk */
  17. /** @typedef {import("../ChunkGraph")} ChunkGraph */
  18. /** @typedef {import("../CodeGenerationResults")} CodeGenerationResults */
  19. /** @typedef {import("../Compiler")} Compiler */
  20. /** @typedef {import("../DependencyTemplates")} DependencyTemplates */
  21. /** @typedef {import("../Module")} Module */
  22. /** @typedef {import("../ModuleGraph")} ModuleGraph */
  23. /** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */
  24. /** @typedef {import("../Template").RenderManifestEntry} RenderManifestEntry */
  25. /** @typedef {import("../Template").RenderManifestOptions} RenderManifestOptions */
  26. /** @typedef {import("../WebpackError")} WebpackError */
  27. const getAsyncWebAssemblyGenerator = memoize(() =>
  28. require("./AsyncWebAssemblyGenerator")
  29. );
  30. const getAsyncWebAssemblyJavascriptGenerator = memoize(() =>
  31. require("./AsyncWebAssemblyJavascriptGenerator")
  32. );
  33. const getAsyncWebAssemblyParser = memoize(() =>
  34. require("./AsyncWebAssemblyParser")
  35. );
  36. /**
  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. * @typedef {object} CompilationHooks
  47. * @property {SyncWaterfallHook<[Source, Module, WebAssemblyRenderContext]>} renderModuleContent
  48. */
  49. /**
  50. * @typedef {object} AsyncWebAssemblyModulesPluginOptions
  51. * @property {boolean} [mangleImports] mangle imports
  52. */
  53. /** @type {WeakMap<Compilation, CompilationHooks>} */
  54. const compilationHooksMap = new WeakMap();
  55. const PLUGIN_NAME = "AsyncWebAssemblyModulesPlugin";
  56. class AsyncWebAssemblyModulesPlugin {
  57. /**
  58. * @param {Compilation} compilation the compilation
  59. * @returns {CompilationHooks} the attached hooks
  60. */
  61. static getCompilationHooks(compilation) {
  62. if (!(compilation instanceof Compilation)) {
  63. throw new TypeError(
  64. "The 'compilation' argument must be an instance of Compilation"
  65. );
  66. }
  67. let hooks = compilationHooksMap.get(compilation);
  68. if (hooks === undefined) {
  69. hooks = {
  70. renderModuleContent: new SyncWaterfallHook([
  71. "source",
  72. "module",
  73. "renderContext"
  74. ])
  75. };
  76. compilationHooksMap.set(compilation, hooks);
  77. }
  78. return hooks;
  79. }
  80. /**
  81. * @param {AsyncWebAssemblyModulesPluginOptions} options options
  82. */
  83. constructor(options) {
  84. this.options = options;
  85. }
  86. /**
  87. * Apply the plugin
  88. * @param {Compiler} compiler the compiler instance
  89. * @returns {void}
  90. */
  91. apply(compiler) {
  92. compiler.hooks.compilation.tap(
  93. PLUGIN_NAME,
  94. (compilation, { normalModuleFactory }) => {
  95. const hooks =
  96. AsyncWebAssemblyModulesPlugin.getCompilationHooks(compilation);
  97. compilation.dependencyFactories.set(
  98. WebAssemblyImportDependency,
  99. normalModuleFactory
  100. );
  101. normalModuleFactory.hooks.createParser
  102. .for(WEBASSEMBLY_MODULE_TYPE_ASYNC)
  103. .tap(PLUGIN_NAME, () => {
  104. const AsyncWebAssemblyParser = getAsyncWebAssemblyParser();
  105. return new AsyncWebAssemblyParser();
  106. });
  107. normalModuleFactory.hooks.createGenerator
  108. .for(WEBASSEMBLY_MODULE_TYPE_ASYNC)
  109. .tap(PLUGIN_NAME, () => {
  110. const AsyncWebAssemblyJavascriptGenerator =
  111. getAsyncWebAssemblyJavascriptGenerator();
  112. const AsyncWebAssemblyGenerator = getAsyncWebAssemblyGenerator();
  113. return Generator.byType({
  114. javascript: new AsyncWebAssemblyJavascriptGenerator(
  115. compilation.outputOptions.webassemblyModuleFilename
  116. ),
  117. webassembly: new AsyncWebAssemblyGenerator(this.options)
  118. });
  119. });
  120. compilation.hooks.renderManifest.tap(
  121. "WebAssemblyModulesPlugin",
  122. (result, options) => {
  123. const { moduleGraph, chunkGraph, runtimeTemplate } = compilation;
  124. const {
  125. chunk,
  126. outputOptions,
  127. dependencyTemplates,
  128. codeGenerationResults
  129. } = options;
  130. for (const module of chunkGraph.getOrderedChunkModulesIterable(
  131. chunk,
  132. compareModulesByIdentifier
  133. )) {
  134. if (module.type === WEBASSEMBLY_MODULE_TYPE_ASYNC) {
  135. const filenameTemplate =
  136. /** @type {NonNullable<OutputOptions["webassemblyModuleFilename"]>} */
  137. (outputOptions.webassemblyModuleFilename);
  138. result.push({
  139. render: () =>
  140. this.renderModule(
  141. module,
  142. {
  143. chunk,
  144. dependencyTemplates,
  145. runtimeTemplate,
  146. moduleGraph,
  147. chunkGraph,
  148. codeGenerationResults
  149. },
  150. hooks
  151. ),
  152. filenameTemplate,
  153. pathOptions: {
  154. module,
  155. runtime: chunk.runtime,
  156. chunkGraph
  157. },
  158. auxiliary: true,
  159. identifier: `webassemblyAsyncModule${chunkGraph.getModuleId(
  160. module
  161. )}`,
  162. hash: chunkGraph.getModuleHash(module, chunk.runtime)
  163. });
  164. }
  165. }
  166. return result;
  167. }
  168. );
  169. }
  170. );
  171. }
  172. /**
  173. * @param {Module} module the rendered module
  174. * @param {WebAssemblyRenderContext} renderContext options object
  175. * @param {CompilationHooks} hooks hooks
  176. * @returns {Source} the newly generated source from rendering
  177. */
  178. renderModule(module, renderContext, hooks) {
  179. const { codeGenerationResults, chunk } = renderContext;
  180. try {
  181. const moduleSource = codeGenerationResults.getSource(
  182. module,
  183. chunk.runtime,
  184. "webassembly"
  185. );
  186. return tryRunOrWebpackError(
  187. () =>
  188. hooks.renderModuleContent.call(moduleSource, module, renderContext),
  189. "AsyncWebAssemblyModulesPlugin.getCompilationHooks().renderModuleContent"
  190. );
  191. } catch (e) {
  192. /** @type {WebpackError} */ (e).module = module;
  193. throw e;
  194. }
  195. }
  196. }
  197. module.exports = AsyncWebAssemblyModulesPlugin;