AsyncWebAssemblyModulesPlugin.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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 { compareModulesByIdOrIdentifier } = 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(PLUGIN_NAME, (result, options) => {
  121. const { moduleGraph, chunkGraph, runtimeTemplate } = compilation;
  122. const {
  123. chunk,
  124. outputOptions,
  125. dependencyTemplates,
  126. codeGenerationResults
  127. } = options;
  128. for (const module of chunkGraph.getOrderedChunkModulesIterable(
  129. chunk,
  130. compareModulesByIdOrIdentifier(chunkGraph)
  131. )) {
  132. if (module.type === WEBASSEMBLY_MODULE_TYPE_ASYNC) {
  133. const filenameTemplate =
  134. /** @type {NonNullable<OutputOptions["webassemblyModuleFilename"]>} */
  135. (outputOptions.webassemblyModuleFilename);
  136. result.push({
  137. render: () =>
  138. this.renderModule(
  139. module,
  140. {
  141. chunk,
  142. dependencyTemplates,
  143. runtimeTemplate,
  144. moduleGraph,
  145. chunkGraph,
  146. codeGenerationResults
  147. },
  148. hooks
  149. ),
  150. filenameTemplate,
  151. pathOptions: {
  152. module,
  153. runtime: chunk.runtime,
  154. chunkGraph
  155. },
  156. auxiliary: true,
  157. identifier: `webassemblyAsyncModule${chunkGraph.getModuleId(
  158. module
  159. )}`,
  160. hash: chunkGraph.getModuleHash(module, chunk.runtime)
  161. });
  162. }
  163. }
  164. return result;
  165. });
  166. }
  167. );
  168. }
  169. /**
  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. module.exports = AsyncWebAssemblyModulesPlugin;