10
0

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