FetchCompileWasmPlugin.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { WEBASSEMBLY_MODULE_TYPE_SYNC } = require("../ModuleTypeConstants");
  7. const RuntimeGlobals = require("../RuntimeGlobals");
  8. const { usesFullHashDigest } = require("../TemplatedPathPlugin");
  9. const { needsRuntimeFullHash } = require("../wasm/wasmModuleFilename");
  10. const WasmChunkLoadingRuntimeModule = require("../wasm-sync/WasmChunkLoadingRuntimeModule");
  11. /** @import Chunk from "../Chunk" */
  12. /** @import Compiler from "../Compiler" */
  13. /**
  14. * Options that influence how synchronous WebAssembly modules are emitted for
  15. * the fetch-based wasm loading runtime.
  16. * @typedef {object} FetchCompileWasmPluginOptions
  17. * @property {boolean=} mangleImports mangle imports
  18. */
  19. const PLUGIN_NAME = "FetchCompileWasmPlugin";
  20. /**
  21. * Enables synchronous WebAssembly chunk loading that fetches `.wasm` files and
  22. * compiles them in browser-like environments.
  23. */
  24. class FetchCompileWasmPlugin {
  25. /**
  26. * Stores options that affect generated synchronous WebAssembly runtime code.
  27. * @param {FetchCompileWasmPluginOptions=} options options
  28. */
  29. constructor(options = {}) {
  30. /** @type {FetchCompileWasmPluginOptions} */
  31. this.options = options;
  32. }
  33. /**
  34. * Registers compilation hooks that attach the fetch-based synchronous wasm
  35. * runtime module to chunks containing sync WebAssembly modules.
  36. * @param {Compiler} compiler the compiler instance
  37. * @returns {void}
  38. */
  39. apply(compiler) {
  40. compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
  41. const globalWasmLoading = compilation.outputOptions.wasmLoading;
  42. /**
  43. * Determines whether the chunk should load synchronous WebAssembly
  44. * binaries through the `fetch` backend.
  45. * @param {Chunk} chunk chunk
  46. * @returns {boolean} true, if wasm loading is enabled for the chunk
  47. */
  48. const isEnabledForChunk = (chunk) => {
  49. const options = chunk.getEntryOptions();
  50. const wasmLoading =
  51. options && options.wasmLoading !== undefined
  52. ? options.wasmLoading
  53. : globalWasmLoading;
  54. return wasmLoading === "fetch";
  55. };
  56. /**
  57. * Downloads the emitted wasm binary through the runtime
  58. * `__webpack_require__.p` public path.
  59. * @param {string} path path to the wasm file
  60. * @returns {string} code to load the wasm file
  61. */
  62. const generateLoadBinaryCode = (path) =>
  63. `fetch(${RuntimeGlobals.publicPath} + ${path})`;
  64. /**
  65. * ESM-output variant: the analyzable `fetch(new URL(path, import.meta.url))`
  66. * form (relative to the chunk, no runtime public-path global) that other
  67. * bundlers and webpack itself can statically follow to the emitted wasm.
  68. * @param {string} path path to the wasm file
  69. * @returns {string} code to load the wasm file
  70. */
  71. const generateAnalyzableLoadBinaryCode = (path) =>
  72. `fetch(${compilation.runtimeTemplate.importMetaUrl(path)})`;
  73. /**
  74. * A public path that never changes is the same string the global would hold,
  75. * so it is inlined and the runtime module that sets it is not needed.
  76. * @param {string} constant the settled public path
  77. * @returns {(path: string) => string} code to load the wasm file
  78. */
  79. const generateConstantLoadBinaryCode = (constant) => (path) =>
  80. `fetch(${JSON.stringify(constant)} + ${path})`;
  81. compilation.hooks.runtimeRequirementInTree
  82. .for(RuntimeGlobals.ensureChunkHandlers)
  83. .tap(PLUGIN_NAME, (chunk, set, { chunkGraph }) => {
  84. if (!isEnabledForChunk(chunk)) return;
  85. if (
  86. !chunkGraph.hasModuleInGraph(
  87. chunk,
  88. (m) => m.type === WEBASSEMBLY_MODULE_TYPE_SYNC
  89. )
  90. ) {
  91. return;
  92. }
  93. set.add(RuntimeGlobals.moduleCache);
  94. const analyzable =
  95. compilation.runtimeTemplate.supportsAnalyzable("wasm-relative");
  96. const constant = analyzable
  97. ? undefined
  98. : compilation.runtimeTemplate.constantPublicPath();
  99. if (!analyzable && constant === undefined) {
  100. set.add(RuntimeGlobals.publicPath);
  101. }
  102. if (
  103. needsRuntimeFullHash(
  104. /** @type {string} */ (
  105. compilation.outputOptions.webassemblyModuleFilename
  106. )
  107. )
  108. ) {
  109. set.add(RuntimeGlobals.getFullHash);
  110. }
  111. compilation.addRuntimeModule(
  112. chunk,
  113. new WasmChunkLoadingRuntimeModule({
  114. fullHashDigest: usesFullHashDigest(
  115. /** @type {string} */ (
  116. compilation.outputOptions.webassemblyModuleFilename
  117. )
  118. ),
  119. generateLoadBinaryCode: analyzable
  120. ? generateAnalyzableLoadBinaryCode
  121. : constant === undefined
  122. ? generateLoadBinaryCode
  123. : generateConstantLoadBinaryCode(constant),
  124. supportsStreaming: true,
  125. mangleImports: this.options.mangleImports,
  126. runtimeRequirements: set
  127. })
  128. );
  129. });
  130. });
  131. }
  132. }
  133. module.exports = FetchCompileWasmPlugin;