WebAssemblyModulesPlugin.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const Generator = require("../Generator");
  7. const {
  8. JAVASCRIPT_TYPE,
  9. WEBASSEMBLY_TYPE
  10. } = require("../ModuleSourceTypeConstants");
  11. const { WEBASSEMBLY_MODULE_TYPE_SYNC } = require("../ModuleTypeConstants");
  12. const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
  13. const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
  14. const { compareModulesByFullName } = require("../util/comparators");
  15. const memoize = require("../util/memoize");
  16. const WebAssemblyInInitialChunkError = require("./WebAssemblyInInitialChunkError");
  17. /** @typedef {import("../Compiler")} Compiler */
  18. /** @typedef {import("../Module")} Module */
  19. const getWebAssemblyGenerator = memoize(() =>
  20. require("./WebAssemblyGenerator")
  21. );
  22. const getWebAssemblyJavascriptGenerator = memoize(() =>
  23. require("./WebAssemblyJavascriptGenerator")
  24. );
  25. const getWebAssemblyParser = memoize(() => require("./WebAssemblyParser"));
  26. const PLUGIN_NAME = "WebAssemblyModulesPlugin";
  27. /**
  28. * Options that influence how synchronous WebAssembly modules are transformed
  29. * and emitted.
  30. * @typedef {object} WebAssemblyModulesPluginOptions
  31. * @property {boolean=} mangleImports mangle imports
  32. */
  33. /**
  34. * Adds parser, generator, manifest, and validation support for synchronous
  35. * WebAssembly modules in the compilation pipeline.
  36. */
  37. class WebAssemblyModulesPlugin {
  38. /**
  39. * Stores options that affect generated synchronous WebAssembly output.
  40. * @param {WebAssemblyModulesPluginOptions} options options
  41. */
  42. constructor(options) {
  43. this.options = options;
  44. }
  45. /**
  46. * Registers compilation hooks that parse and generate sync WebAssembly
  47. * modules, emit their binary assets, and report invalid placement in initial
  48. * chunks.
  49. * @param {Compiler} compiler the compiler instance
  50. * @returns {void}
  51. */
  52. apply(compiler) {
  53. compiler.hooks.compilation.tap(
  54. PLUGIN_NAME,
  55. (compilation, { normalModuleFactory }) => {
  56. compilation.dependencyFactories.set(
  57. WebAssemblyImportDependency,
  58. normalModuleFactory
  59. );
  60. compilation.dependencyFactories.set(
  61. WebAssemblyExportImportedDependency,
  62. normalModuleFactory
  63. );
  64. normalModuleFactory.hooks.createParser
  65. .for(WEBASSEMBLY_MODULE_TYPE_SYNC)
  66. .tap(PLUGIN_NAME, () => {
  67. const WebAssemblyParser = getWebAssemblyParser();
  68. return new WebAssemblyParser();
  69. });
  70. normalModuleFactory.hooks.createGenerator
  71. .for(WEBASSEMBLY_MODULE_TYPE_SYNC)
  72. .tap(PLUGIN_NAME, () => {
  73. const WebAssemblyJavascriptGenerator =
  74. getWebAssemblyJavascriptGenerator();
  75. const WebAssemblyGenerator = getWebAssemblyGenerator();
  76. return Generator.byType({
  77. [JAVASCRIPT_TYPE]: new WebAssemblyJavascriptGenerator(),
  78. [WEBASSEMBLY_TYPE]: new WebAssemblyGenerator(this.options)
  79. });
  80. });
  81. compilation.hooks.renderManifest.tap(PLUGIN_NAME, (result, options) => {
  82. const { chunkGraph } = compilation;
  83. const { chunk, outputOptions, codeGenerationResults } = options;
  84. for (const module of chunkGraph.getOrderedChunkModulesIterable(
  85. chunk,
  86. compareModulesByFullName(compiler)
  87. )) {
  88. if (module.type === WEBASSEMBLY_MODULE_TYPE_SYNC) {
  89. const filenameTemplate = outputOptions.webassemblyModuleFilename;
  90. result.push({
  91. render: () =>
  92. codeGenerationResults.getSource(
  93. module,
  94. chunk.runtime,
  95. "webassembly"
  96. ),
  97. filenameTemplate,
  98. pathOptions: {
  99. module,
  100. runtime: chunk.runtime,
  101. chunkGraph
  102. },
  103. auxiliary: true,
  104. identifier: `webassemblyModule${chunkGraph.getModuleId(
  105. module
  106. )}`,
  107. hash: chunkGraph.getModuleHash(module, chunk.runtime)
  108. });
  109. }
  110. }
  111. return result;
  112. });
  113. compilation.hooks.afterChunks.tap(PLUGIN_NAME, () => {
  114. const chunkGraph = compilation.chunkGraph;
  115. /** @type {Set<Module>} */
  116. const initialWasmModules = new Set();
  117. for (const chunk of compilation.chunks) {
  118. if (chunk.canBeInitial()) {
  119. for (const module of chunkGraph.getChunkModulesIterable(chunk)) {
  120. if (module.type === WEBASSEMBLY_MODULE_TYPE_SYNC) {
  121. initialWasmModules.add(module);
  122. }
  123. }
  124. }
  125. }
  126. for (const module of initialWasmModules) {
  127. compilation.errors.push(
  128. new WebAssemblyInInitialChunkError(
  129. module,
  130. compilation.moduleGraph,
  131. compilation.chunkGraph,
  132. compilation.requestShortener
  133. )
  134. );
  135. }
  136. });
  137. }
  138. );
  139. }
  140. }
  141. module.exports = WebAssemblyModulesPlugin;