/* MIT License http://www.opensource.org/licenses/mit-license.php Author Tobias Koppers @sokra */ "use strict"; const RuntimeGlobals = require("../RuntimeGlobals"); const RuntimeModule = require("../RuntimeModule"); const Template = require("../Template"); /** @typedef {import("../Chunk")} Chunk */ /** @typedef {import("../Compilation")} Compilation */ /** @typedef {(wasmModuleSrcPath: string) => string} GenerateBeforeLoadBinaryCode */ /** @typedef {(wasmModuleSrcPath: string) => string} GenerateLoadBinaryCode */ /** @typedef {() => string} GenerateBeforeCompileStreaming */ /** * @typedef {object} AsyncWasmCompileRuntimeModuleOptions * @property {GenerateLoadBinaryCode} generateLoadBinaryCode * @property {GenerateBeforeLoadBinaryCode=} generateBeforeLoadBinaryCode * @property {GenerateBeforeCompileStreaming=} generateBeforeCompileStreaming * @property {boolean} supportsStreaming */ class AsyncWasmCompileRuntimeModule extends RuntimeModule { /** * @param {AsyncWasmCompileRuntimeModuleOptions} options options */ constructor({ generateLoadBinaryCode, generateBeforeLoadBinaryCode, generateBeforeCompileStreaming, supportsStreaming }) { super("wasm compile", RuntimeModule.STAGE_NORMAL); /** @type {GenerateLoadBinaryCode} */ this.generateLoadBinaryCode = generateLoadBinaryCode; /** @type {GenerateBeforeLoadBinaryCode | undefined} */ this.generateBeforeLoadBinaryCode = generateBeforeLoadBinaryCode; /** @type {GenerateBeforeCompileStreaming | undefined} */ this.generateBeforeCompileStreaming = generateBeforeCompileStreaming; /** @type {boolean} */ this.supportsStreaming = supportsStreaming; } /** * Generates runtime code for this runtime module. * @returns {string | null} runtime code */ generate() { const compilation = /** @type {Compilation} */ (this.compilation); const chunk = /** @type {Chunk} */ (this.chunk); const { outputOptions, runtimeTemplate } = compilation; const fn = RuntimeGlobals.compileWasm; const wasmModuleSrcPath = compilation.getPath( JSON.stringify(outputOptions.webassemblyModuleFilename), { hash: `" + ${RuntimeGlobals.getFullHash}() + "`, hashWithLength: (length) => `" + ${RuntimeGlobals.getFullHash}}().slice(0, ${length}) + "`, module: { id: '" + wasmModuleId + "', hash: '" + wasmModuleHash + "', hashWithLength(length) { return `" + wasmModuleHash.slice(0, ${length}) + "`; } }, runtime: chunk.runtime } ); const loader = this.generateLoadBinaryCode(wasmModuleSrcPath); // Fallback path: fetch -> arrayBuffer -> WebAssembly.compile const fallback = [ `.then(${runtimeTemplate.returningFunction("x.arrayBuffer()", "x")})`, `.then(${runtimeTemplate.returningFunction( "WebAssembly.compile(bytes)", "bytes" )})` ]; const getStreaming = () => { /** * @param {string[]} text text * @returns {string} merged text */ const concat = (...text) => text.join(""); return [ this.generateBeforeLoadBinaryCode ? this.generateBeforeLoadBinaryCode(wasmModuleSrcPath) : "", `var req = ${loader};`, `var fallback = ${runtimeTemplate.returningFunction( Template.asString(["req", Template.indent(fallback)]) )};`, concat( "return req.then(", runtimeTemplate.basicFunction("res", [ 'if (typeof WebAssembly.compileStreaming === "function") {', Template.indent( this.generateBeforeCompileStreaming ? this.generateBeforeCompileStreaming() : "" ), Template.indent([ "return WebAssembly.compileStreaming(res)", Template.indent([ ".catch(", Template.indent([ runtimeTemplate.basicFunction("e", [ 'if(res.headers.get("Content-Type") !== "application/wasm") {', Template.indent([ '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);', "return fallback();" ]), "}", "throw e;" ]) ]), ");" ]) ]), "}", "return fallback();" ]), ");" ) ]; }; return `${fn} = ${runtimeTemplate.basicFunction( "wasmModuleId, wasmModuleHash", this.supportsStreaming ? getStreaming() : [ this.generateBeforeLoadBinaryCode ? this.generateBeforeLoadBinaryCode(wasmModuleSrcPath) : "", `return ${loader}`, `${Template.indent(fallback)};` ] )};`; } } module.exports = AsyncWasmCompileRuntimeModule;