123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- /*
- 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 {
- getChunkFilenameTemplate,
- chunkHasJs
- } = require("../javascript/JavascriptModulesPlugin");
- const { getInitialChunkIds } = require("../javascript/StartupHelpers");
- const compileBooleanMatcher = require("../util/compileBooleanMatcher");
- const { getUndoPath } = require("../util/identifier");
- /** @typedef {import("../Chunk")} Chunk */
- /** @typedef {import("../ChunkGraph")} ChunkGraph */
- /** @typedef {import("../Compilation")} Compilation */
- /** @typedef {import("../Module").ReadOnlyRuntimeRequirements} ReadOnlyRuntimeRequirements */
- 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);
- this.runtimeRequirements = runtimeRequirements;
- this._withCreateScriptUrl = withCreateScriptUrl;
- }
- /**
- * @private
- * @param {Chunk} chunk chunk
- * @returns {string} generated code
- */
- _generateBaseUri(chunk) {
- const options = chunk.getEntryOptions();
- if (options && options.baseUri) {
- return `${RuntimeGlobals.baseURI} = ${JSON.stringify(options.baseUri)};`;
- }
- const compilation = /** @type {Compilation} */ (this.compilation);
- const outputName = compilation.getPath(
- getChunkFilenameTemplate(chunk, compilation.outputOptions),
- {
- chunk,
- contentHashType: "javascript"
- }
- );
- const rootOutputDir = getUndoPath(
- outputName,
- /** @type {string} */ (compilation.outputOptions.path),
- false
- );
- return `${RuntimeGlobals.baseURI} = self.location + ${JSON.stringify(
- rootOutputDir ? "/../" + rootOutputDir : ""
- )};`;
- }
- /**
- * @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 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;
- return Template.asString([
- withBaseURI ? this._generateBaseUri(chunk) : "// no baseURI",
- "",
- "// object to store loaded chunks",
- '// "1" means "already loaded"',
- `var installedChunks = ${
- stateExpression ? `${stateExpression} = ${stateExpression} || ` : ""
- }{`,
- Template.indent(
- Array.from(initialChunkIds, id => `${JSON.stringify(id)}: 1`).join(
- ",\n"
- )
- ),
- "};",
- "",
- 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",
- withLoading
- ? Template.asString([
- `${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 = ${chunkLoadingGlobalExpr} = ${chunkLoadingGlobalExpr} || [];`,
- "var parentChunkLoadingFunction = chunkLoadingGlobal.push.bind(chunkLoadingGlobal);",
- "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];",
- "if(updatedModulesList) 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");'
- ]),
- "}",
- "",
- Template.getFunctionContent(
- require("../hmr/JavascriptHotModuleReplacement.runtime.js")
- )
- .replace(/\$key\$/g, "importScripts")
- .replace(/\$installedChunks\$/g, "installedChunks")
- .replace(/\$loadUpdateChunk\$/g, "loadUpdateChunk")
- .replace(/\$moduleCache\$/g, RuntimeGlobals.moduleCache)
- .replace(/\$moduleFactories\$/g, RuntimeGlobals.moduleFactories)
- .replace(
- /\$ensureChunkHandlers\$/g,
- RuntimeGlobals.ensureChunkHandlers
- )
- .replace(/\$hasOwnProperty\$/g, RuntimeGlobals.hasOwnProperty)
- .replace(/\$hmrModuleData\$/g, RuntimeGlobals.hmrModuleData)
- .replace(
- /\$hmrDownloadUpdateHandlers\$/g,
- RuntimeGlobals.hmrDownloadUpdateHandlers
- )
- .replace(
- /\$hmrInvalidateModuleHandlers\$/g,
- RuntimeGlobals.hmrInvalidateModuleHandlers
- )
- ])
- : "// 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;
|