AsyncWebAssemblyModulesPlugin.js 6.1 KB

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