UNPKG

webpack

Version:

Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

191 lines (180 loc) 6.22 kB
/* 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"); const { fullHashPathData } = require("../wasm/wasmModuleFilename"); /** @import Chunk from "../Chunk" */ /** @import ChunkGraph from "../ChunkGraph" */ /** @import Compilation from "../Compilation" */ /** @import { RuntimeSpec } from "../util/runtime" */ /** @typedef {(wasmModuleSrcPath: string) => string} GenerateBeforeLoadBinaryCode */ /** @typedef {(wasmModuleSrcPath: string, runtime: RuntimeSpec, analyzable: boolean) => string} GenerateLoadBinaryCode */ /** @typedef {() => string} GenerateBeforeInstantiateStreaming */ /** * @typedef {object} AsyncWasmLoadingRuntimeModuleOptions * @property {GenerateLoadBinaryCode} generateLoadBinaryCode * @property {GenerateBeforeLoadBinaryCode=} generateBeforeLoadBinaryCode * @property {GenerateBeforeInstantiateStreaming=} generateBeforeInstantiateStreaming * @property {boolean} supportsStreaming * @property {boolean=} fullHashDigest the binary's name inlines a re-encoded compilation hash */ class AsyncWasmLoadingRuntimeModule extends RuntimeModule { /** * @param {AsyncWasmLoadingRuntimeModuleOptions} options options */ constructor({ generateLoadBinaryCode, generateBeforeLoadBinaryCode, generateBeforeInstantiateStreaming, supportsStreaming, fullHashDigest }) { super("wasm loading", RuntimeModule.STAGE_NORMAL); // A re-encoded digest is inlined from the settled hash, so this has to render // again once there is one. if (fullHashDigest) { /** @type {boolean} */ this.fullHash = true; } /** @type {GenerateLoadBinaryCode} */ this.generateLoadBinaryCode = generateLoadBinaryCode; /** @type {generateBeforeLoadBinaryCode | undefined} */ this.generateBeforeLoadBinaryCode = generateBeforeLoadBinaryCode; /** @type {generateBeforeInstantiateStreaming | undefined} */ this.generateBeforeInstantiateStreaming = generateBeforeInstantiateStreaming; /** @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; // Opt-in fallback to non-streaming when the server serves wasm with a wrong MIME type. const streamingFallback = outputOptions.wasmStreamingFallback; const fn = RuntimeGlobals.instantiateWasm; // Analyzable output bakes the binary's URL into the call site instead. const analyzable = runtimeTemplate.supportsAnalyzable( "wasm", /** @type {ChunkGraph} */ (this.chunkGraph), undefined, chunk.runtime ); const wasmModuleSrcPath = analyzable ? "wasmModuleUrl" : compilation.getPath( JSON.stringify(outputOptions.webassemblyModuleFilename), { ...fullHashPathData(compilation), module: { id: '" + wasmModuleId + "', hash: '" + wasmModuleHash + "', hashWithLength(length) { return `" + wasmModuleHash.slice(0, ${length}) + "`; } }, runtime: chunk.runtime } ); const loader = this.generateLoadBinaryCode( wasmModuleSrcPath, chunk.runtime, analyzable ); const fallback = [ `.then(${runtimeTemplate.returningFunction("x.arrayBuffer()", "x")})`, `.then(${runtimeTemplate.returningFunction( "WebAssembly.instantiate(bytes, importsObj)", "bytes" )})`, `.then(${runtimeTemplate.returningFunction( "Object.assign(exports, res.instance.exports)", "res" )})` ]; 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.instantiateStreaming === "function") {', Template.indent( this.generateBeforeInstantiateStreaming ? this.generateBeforeInstantiateStreaming() : "" ), Template.indent( streamingFallback ? [ "return WebAssembly.instantiateStreaming(res, importsObj)", Template.indent([ ".then(", Template.indent([ `${runtimeTemplate.returningFunction( "Object.assign(exports, res.instance.exports)", "res" )},`, runtimeTemplate.basicFunction("e", [ 'if(res.headers.get("Content-Type") !== "application/wasm") {', Template.indent([ '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);', "return fallback();" ]), "}", "throw e;" ]) ]), ");" ]) ] : [ `return WebAssembly.instantiateStreaming(res, importsObj).then(${runtimeTemplate.returningFunction( "Object.assign(exports, res.instance.exports)", "res" )});` ] ), "}", "return fallback();" ]), ");" ) ]; }; return `${fn} = ${runtimeTemplate.basicFunction( analyzable ? "exports, wasmModuleUrl, importsObj" : "exports, wasmModuleId, wasmModuleHash, importsObj", this.supportsStreaming ? getStreaming() : [ this.generateBeforeLoadBinaryCode ? this.generateBeforeLoadBinaryCode(wasmModuleSrcPath) : "", `return ${loader}`, `${Template.indent(fallback)};` ] )};`; } } module.exports = AsyncWasmLoadingRuntimeModule;