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.

235 lines (224 loc) 8.47 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php */ "use strict"; const RuntimeGlobals = require("../RuntimeGlobals"); const RuntimeModule = require("../RuntimeModule"); const Template = require("../Template"); const { generateJavascriptHMR } = require("../hmr/JavascriptHotModuleReplacementHelper"); const { chunkHasJs } = require("../javascript/JavascriptModulesPlugin"); const { getInitialChunkIds } = require("../javascript/StartupHelpers"); const { renderBaseUri } = require("../runtime/baseUri"); const compileBooleanMatcher = require("../util/compileBooleanMatcher"); /** @import Chunk from "../Chunk" */ /** @import ChunkGraph from "../ChunkGraph" */ /** @import Compilation from "../Compilation" */ /** @import { ReadOnlyRuntimeRequirements } from "../Module" */ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { /** * @param {ReadOnlyRuntimeRequirements} runtimeRequirements runtime requirements * @param {boolean} withCreateScriptUrl with createScriptUrl support */ constructor(runtimeRequirements, withCreateScriptUrl) { super("importScripts chunk loading", RuntimeModule.STAGE_ATTACH); /** @type {ReadOnlyRuntimeRequirements} */ this.runtimeRequirements = runtimeRequirements; /** @type {boolean} */ this._withCreateScriptUrl = withCreateScriptUrl; } /** * @private * @param {Chunk} chunk chunk * @returns {string} generated code */ _generateBaseUri(chunk) { const options = chunk.getEntryOptions(); const compilation = /** @type {Compilation} */ (this.compilation); const rootOutputDir = compilation.runtimeTemplate.chunkRootOutputDir( chunk, false ); return renderBaseUri( options ? options.baseUri : undefined, `self.location + ${JSON.stringify(rootOutputDir ? `/../${rootOutputDir}` : "")}` ); } /** * Generates runtime code for this runtime module. * @returns {string | null} runtime code */ generate() { const compilation = /** @type {Compilation} */ (this.compilation); const fn = RuntimeGlobals.ensureChunkHandlers; const withBaseURI = this.runtimeRequirements.has(RuntimeGlobals.baseURI); const withLoading = this.runtimeRequirements.has( RuntimeGlobals.ensureChunkHandlers ); const withCallback = this.runtimeRequirements.has( RuntimeGlobals.chunkCallback ); const withHmr = this.runtimeRequirements.has( RuntimeGlobals.hmrDownloadUpdateHandlers ); const withHmrManifest = this.runtimeRequirements.has( RuntimeGlobals.hmrDownloadManifest ); const globalObject = compilation.runtimeTemplate.globalObject; const chunkLoadingGlobalExpr = `${globalObject}[${JSON.stringify( compilation.outputOptions.chunkLoadingGlobal )}]`; const chunkGraph = /** @type {ChunkGraph} */ (this.chunkGraph); const chunk = /** @type {Chunk} */ (this.chunk); const hasJsMatcher = compileBooleanMatcher( chunkGraph.getChunkConditionMap(chunk, chunkHasJs) ); const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs); const stateExpression = withHmr ? `${RuntimeGlobals.hmrRuntimeStatePrefix}_importScripts` : undefined; const runtimeTemplate = compilation.runtimeTemplate; const { _withCreateScriptUrl: withCreateScriptUrl } = this; const installedChunksObject = `{\n${Template.indent( Array.from(initialChunkIds, (id) => `${JSON.stringify(id)}: 1`).join( ",\n" ) )}\n}`; // Only the install/loading pair below reads the table. A chunk asking only for // `.b` gets this module for the base uri alone, and then has nothing to look up. const withInstalledChunks = withCallback || withLoading || withHmr; return Template.asString([ withBaseURI ? this._generateBaseUri(chunk) : "// no baseURI", "", withInstalledChunks ? Template.asString([ "// object to store loaded chunks", '// "1" means "already loaded"', `var installedChunks = ${ stateExpression ? runtimeTemplate.assignOr( stateExpression, installedChunksObject ) : installedChunksObject };` ]) : "// no installed chunks", "", withCallback || withLoading ? Template.asString([ "// importScripts chunk loading", `var installChunk = ${runtimeTemplate.basicFunction("data", [ runtimeTemplate.destructureArray( ["chunkIds", "moreModules", "runtime"], "data" ), "for(var moduleId in moreModules) {", Template.indent([ `if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`, Template.indent( `${RuntimeGlobals.moduleFactories}[moduleId] = moreModules[moduleId];` ), "}" ]), "}", `if(runtime) runtime(${RuntimeGlobals.require});`, "while(chunkIds.length)", Template.indent("installedChunks[chunkIds.pop()] = 1;"), "parentChunkLoadingFunction(data);" ])};` ]) : "// no chunk install function needed", withCallback || withLoading ? Template.asString([ withLoading ? `${fn}.i = ${runtimeTemplate.basicFunction( "chunkId, promises", hasJsMatcher !== false ? [ '// "1" is the signal for "already loaded"', "if(!installedChunks[chunkId]) {", Template.indent([ hasJsMatcher === true ? "if(true) { // all chunks have JS" : `if(${hasJsMatcher("chunkId")}) {`, Template.indent( `importScripts(${ withCreateScriptUrl ? `${RuntimeGlobals.createScriptUrl}(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId))` : `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId)` });` ), "}" ]), "}" ] : "installedChunks[chunkId] = 1;" )};` : "", "", `var chunkLoadingGlobal = ${runtimeTemplate.assignOr(chunkLoadingGlobalExpr, "[]")};`, "var parentChunkLoadingFunction = chunkLoadingGlobal.push.bind(chunkLoadingGlobal);", // Install chunks pushed before this runtime loaded (worklets // pre-load every chunk via `addModule` before the entry runs). "chunkLoadingGlobal.forEach(installChunk);", "chunkLoadingGlobal.push = installChunk;" ]) : "// no chunk loading", "", withHmr ? Template.asString([ "function loadUpdateChunk(chunkId, updatedModulesList) {", Template.indent([ "var success = false;", `${globalObject}[${JSON.stringify( compilation.outputOptions.hotUpdateGlobal )}] = ${runtimeTemplate.basicFunction("_, moreModules, runtime", [ "for(var moduleId in moreModules) {", Template.indent([ `if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`, Template.indent([ "currentUpdate[moduleId] = moreModules[moduleId];", `${runtimeTemplate.optionalChaining("updatedModulesList", "push(moduleId)")};` ]), "}" ]), "}", "if(runtime) currentUpdateRuntime.push(runtime);", "success = true;" ])};`, "// start update chunk loading", `importScripts(${ withCreateScriptUrl ? `${RuntimeGlobals.createScriptUrl}(${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId))` : `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId)` });`, 'if(!success) throw new Error("Loading update chunk failed for unknown reason");' ]), "}", "", generateJavascriptHMR("importScripts") ]) : "// no HMR", "", withHmrManifest ? Template.asString([ `${ RuntimeGlobals.hmrDownloadManifest } = ${runtimeTemplate.basicFunction("", [ 'if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API");', `return fetch(${RuntimeGlobals.publicPath} + ${ RuntimeGlobals.getUpdateManifestFilename }()).then(${runtimeTemplate.basicFunction("response", [ "if(response.status === 404) return; // no update available", 'if(!response.ok) throw new Error("Failed to fetch update manifest " + response.statusText);', "return response.json();" ])});` ])};` ]) : "// no HMR manifest" ]); } } module.exports = ImportScriptsChunkLoadingRuntimeModule;