AsyncWasmCompileRuntimeModule.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const RuntimeGlobals = require("../RuntimeGlobals");
  7. const RuntimeModule = require("../RuntimeModule");
  8. const Template = require("../Template");
  9. /** @typedef {import("../Chunk")} Chunk */
  10. /** @typedef {import("../Compilation")} Compilation */
  11. /** @typedef {(wasmModuleSrcPath: string) => string} GenerateBeforeLoadBinaryCode */
  12. /** @typedef {(wasmModuleSrcPath: string) => string} GenerateLoadBinaryCode */
  13. /** @typedef {() => string} GenerateBeforeCompileStreaming */
  14. /**
  15. * @typedef {object} AsyncWasmCompileRuntimeModuleOptions
  16. * @property {GenerateLoadBinaryCode} generateLoadBinaryCode
  17. * @property {GenerateBeforeLoadBinaryCode=} generateBeforeLoadBinaryCode
  18. * @property {GenerateBeforeCompileStreaming=} generateBeforeCompileStreaming
  19. * @property {boolean} supportsStreaming
  20. */
  21. class AsyncWasmCompileRuntimeModule extends RuntimeModule {
  22. /**
  23. * @param {AsyncWasmCompileRuntimeModuleOptions} options options
  24. */
  25. constructor({
  26. generateLoadBinaryCode,
  27. generateBeforeLoadBinaryCode,
  28. generateBeforeCompileStreaming,
  29. supportsStreaming
  30. }) {
  31. super("wasm compile", RuntimeModule.STAGE_NORMAL);
  32. /** @type {GenerateLoadBinaryCode} */
  33. this.generateLoadBinaryCode = generateLoadBinaryCode;
  34. /** @type {GenerateBeforeLoadBinaryCode | undefined} */
  35. this.generateBeforeLoadBinaryCode = generateBeforeLoadBinaryCode;
  36. /** @type {GenerateBeforeCompileStreaming | undefined} */
  37. this.generateBeforeCompileStreaming = generateBeforeCompileStreaming;
  38. /** @type {boolean} */
  39. this.supportsStreaming = supportsStreaming;
  40. }
  41. /**
  42. * Generates runtime code for this runtime module.
  43. * @returns {string | null} runtime code
  44. */
  45. generate() {
  46. const compilation = /** @type {Compilation} */ (this.compilation);
  47. const chunk = /** @type {Chunk} */ (this.chunk);
  48. const { outputOptions, runtimeTemplate } = compilation;
  49. const fn = RuntimeGlobals.compileWasm;
  50. const wasmModuleSrcPath = compilation.getPath(
  51. JSON.stringify(outputOptions.webassemblyModuleFilename),
  52. {
  53. hash: `" + ${RuntimeGlobals.getFullHash}() + "`,
  54. hashWithLength: (length) =>
  55. `" + ${RuntimeGlobals.getFullHash}}().slice(0, ${length}) + "`,
  56. module: {
  57. id: '" + wasmModuleId + "',
  58. hash: '" + wasmModuleHash + "',
  59. hashWithLength(length) {
  60. return `" + wasmModuleHash.slice(0, ${length}) + "`;
  61. }
  62. },
  63. runtime: chunk.runtime
  64. }
  65. );
  66. const loader = this.generateLoadBinaryCode(wasmModuleSrcPath);
  67. // Fallback path: fetch -> arrayBuffer -> WebAssembly.compile
  68. const fallback = [
  69. `.then(${runtimeTemplate.returningFunction("x.arrayBuffer()", "x")})`,
  70. `.then(${runtimeTemplate.returningFunction(
  71. "WebAssembly.compile(bytes)",
  72. "bytes"
  73. )})`
  74. ];
  75. const getStreaming = () => {
  76. /**
  77. * @param {string[]} text text
  78. * @returns {string} merged text
  79. */
  80. const concat = (...text) => text.join("");
  81. return [
  82. this.generateBeforeLoadBinaryCode
  83. ? this.generateBeforeLoadBinaryCode(wasmModuleSrcPath)
  84. : "",
  85. `var req = ${loader};`,
  86. `var fallback = ${runtimeTemplate.returningFunction(
  87. Template.asString(["req", Template.indent(fallback)])
  88. )};`,
  89. concat(
  90. "return req.then(",
  91. runtimeTemplate.basicFunction("res", [
  92. 'if (typeof WebAssembly.compileStreaming === "function") {',
  93. Template.indent(
  94. this.generateBeforeCompileStreaming
  95. ? this.generateBeforeCompileStreaming()
  96. : ""
  97. ),
  98. Template.indent([
  99. "return WebAssembly.compileStreaming(res)",
  100. Template.indent([
  101. ".catch(",
  102. Template.indent([
  103. runtimeTemplate.basicFunction("e", [
  104. 'if(res.headers.get("Content-Type") !== "application/wasm") {',
  105. Template.indent([
  106. 'console.warn("`WebAssembly.compileStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.compile` which is slower. Original error:\\n", e);',
  107. "return fallback();"
  108. ]),
  109. "}",
  110. "throw e;"
  111. ])
  112. ]),
  113. ");"
  114. ])
  115. ]),
  116. "}",
  117. "return fallback();"
  118. ]),
  119. ");"
  120. )
  121. ];
  122. };
  123. return `${fn} = ${runtimeTemplate.basicFunction(
  124. "wasmModuleId, wasmModuleHash",
  125. this.supportsStreaming
  126. ? getStreaming()
  127. : [
  128. this.generateBeforeLoadBinaryCode
  129. ? this.generateBeforeLoadBinaryCode(wasmModuleSrcPath)
  130. : "",
  131. `return ${loader}`,
  132. `${Template.indent(fallback)};`
  133. ]
  134. )};`;
  135. }
  136. }
  137. module.exports = AsyncWasmCompileRuntimeModule;