| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 | /*	MIT License http://www.opensource.org/licenses/mit-license.php*/"use strict";const { SyncWaterfallHook } = require("tapable");const Compilation = require("../Compilation");const RuntimeGlobals = require("../RuntimeGlobals");const RuntimeModule = require("../RuntimeModule");const Template = require("../Template");const chunkHasJs = require("../javascript/JavascriptModulesPlugin").chunkHasJs;const { getInitialChunkIds } = require("../javascript/StartupHelpers");const compileBooleanMatcher = require("../util/compileBooleanMatcher");/** @typedef {import("../Chunk")} Chunk *//** * @typedef {Object} JsonpCompilationPluginHooks * @property {SyncWaterfallHook<[string, Chunk]>} linkPreload * @property {SyncWaterfallHook<[string, Chunk]>} linkPrefetch *//** @type {WeakMap<Compilation, JsonpCompilationPluginHooks>} */const compilationHooksMap = new WeakMap();class JsonpChunkLoadingRuntimeModule extends RuntimeModule {	/**	 * @param {Compilation} compilation the compilation	 * @returns {JsonpCompilationPluginHooks} hooks	 */	static getCompilationHooks(compilation) {		if (!(compilation instanceof Compilation)) {			throw new TypeError(				"The 'compilation' argument must be an instance of Compilation"			);		}		let hooks = compilationHooksMap.get(compilation);		if (hooks === undefined) {			hooks = {				linkPreload: new SyncWaterfallHook(["source", "chunk"]),				linkPrefetch: new SyncWaterfallHook(["source", "chunk"])			};			compilationHooksMap.set(compilation, hooks);		}		return hooks;	}	constructor(runtimeRequirements) {		super("jsonp chunk loading", RuntimeModule.STAGE_ATTACH);		this._runtimeRequirements = runtimeRequirements;	}	/**	 * @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)};`;		} else {			return `${RuntimeGlobals.baseURI} = document.baseURI || self.location.href;`;		}	}	/**	 * @returns {string} runtime code	 */	generate() {		const { chunkGraph, compilation, chunk } = this;		const {			runtimeTemplate,			outputOptions: {				chunkLoadingGlobal,				hotUpdateGlobal,				crossOriginLoading,				scriptType			}		} = compilation;		const globalObject = runtimeTemplate.globalObject;		const { linkPreload, linkPrefetch } =			JsonpChunkLoadingRuntimeModule.getCompilationHooks(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 withOnChunkLoad = this._runtimeRequirements.has(			RuntimeGlobals.onChunksLoaded		);		const withHmr = this._runtimeRequirements.has(			RuntimeGlobals.hmrDownloadUpdateHandlers		);		const withHmrManifest = this._runtimeRequirements.has(			RuntimeGlobals.hmrDownloadManifest		);		const withPrefetch = this._runtimeRequirements.has(			RuntimeGlobals.prefetchChunkHandlers		);		const withPreload = this._runtimeRequirements.has(			RuntimeGlobals.preloadChunkHandlers		);		const chunkLoadingGlobalExpr = `${globalObject}[${JSON.stringify(			chunkLoadingGlobal		)}]`;		const conditionMap = chunkGraph.getChunkConditionMap(chunk, chunkHasJs);		const hasJsMatcher = compileBooleanMatcher(conditionMap);		const initialChunkIds = getInitialChunkIds(chunk, chunkGraph, chunkHasJs);		const stateExpression = withHmr			? `${RuntimeGlobals.hmrRuntimeStatePrefix}_jsonp`			: undefined;		return Template.asString([			withBaseURI ? this._generateBaseUri(chunk) : "// no baseURI",			"",			"// object to store loaded and loading chunks",			"// undefined = chunk not loaded, null = chunk preloaded/prefetched",			"// [resolve, reject, Promise] = chunk loading, 0 = chunk loaded",			`var installedChunks = ${				stateExpression ? `${stateExpression} = ${stateExpression} || ` : ""			}{`,			Template.indent(				Array.from(initialChunkIds, id => `${JSON.stringify(id)}: 0`).join(					",\n"				)			),			"};",			"",			withLoading				? Template.asString([						`${fn}.j = ${runtimeTemplate.basicFunction(							"chunkId, promises",							hasJsMatcher !== false								? Template.indent([										"// JSONP chunk loading for javascript",										`var installedChunkData = ${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId) ? installedChunks[chunkId] : undefined;`,										'if(installedChunkData !== 0) { // 0 means "already installed".',										Template.indent([											"",											'// a Promise means "currently loading".',											"if(installedChunkData) {",											Template.indent([												"promises.push(installedChunkData[2]);"											]),											"} else {",											Template.indent([												hasJsMatcher === true													? "if(true) { // all chunks have JS"													: `if(${hasJsMatcher("chunkId")}) {`,												Template.indent([													"// setup Promise in chunk cache",													`var promise = new Promise(${runtimeTemplate.expressionFunction(														`installedChunkData = installedChunks[chunkId] = [resolve, reject]`,														"resolve, reject"													)});`,													"promises.push(installedChunkData[2] = promise);",													"",													"// start chunk loading",													`var url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId);`,													"// create error before stack unwound to get useful stacktrace later",													"var error = new Error();",													`var loadingEnded = ${runtimeTemplate.basicFunction(														"event",														[															`if(${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId)) {`,															Template.indent([																"installedChunkData = installedChunks[chunkId];",																"if(installedChunkData !== 0) installedChunks[chunkId] = undefined;",																"if(installedChunkData) {",																Template.indent([																	"var errorType = event && (event.type === 'load' ? 'missing' : event.type);",																	"var realSrc = event && event.target && event.target.src;",																	"error.message = 'Loading chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';",																	"error.name = 'ChunkLoadError';",																	"error.type = errorType;",																	"error.request = realSrc;",																	"installedChunkData[1](error);"																]),																"}"															]),															"}"														]													)};`,													`${RuntimeGlobals.loadScript}(url, loadingEnded, "chunk-" + chunkId, chunkId);`												]),												"} else installedChunks[chunkId] = 0;"											]),											"}"										]),										"}"								  ])								: Template.indent(["installedChunks[chunkId] = 0;"])						)};`				  ])				: "// no chunk on demand loading",			"",			withPrefetch && hasJsMatcher !== false				? `${						RuntimeGlobals.prefetchChunkHandlers				  }.j = ${runtimeTemplate.basicFunction("chunkId", [						`if((!${							RuntimeGlobals.hasOwnProperty						}(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && ${							hasJsMatcher === true ? "true" : hasJsMatcher("chunkId")						}) {`,						Template.indent([							"installedChunks[chunkId] = null;",							linkPrefetch.call(								Template.asString([									"var link = document.createElement('link');",									crossOriginLoading										? `link.crossOrigin = ${JSON.stringify(												crossOriginLoading										  )};`										: "",									`if (${RuntimeGlobals.scriptNonce}) {`,									Template.indent(										`link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`									),									"}",									'link.rel = "prefetch";',									'link.as = "script";',									`link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId);`								]),								chunk							),							"document.head.appendChild(link);"						]),						"}"				  ])};`				: "// no prefetching",			"",			withPreload && hasJsMatcher !== false				? `${						RuntimeGlobals.preloadChunkHandlers				  }.j = ${runtimeTemplate.basicFunction("chunkId", [						`if((!${							RuntimeGlobals.hasOwnProperty						}(installedChunks, chunkId) || installedChunks[chunkId] === undefined) && ${							hasJsMatcher === true ? "true" : hasJsMatcher("chunkId")						}) {`,						Template.indent([							"installedChunks[chunkId] = null;",							linkPreload.call(								Template.asString([									"var link = document.createElement('link');",									scriptType										? `link.type = ${JSON.stringify(scriptType)};`										: "",									"link.charset = 'utf-8';",									`if (${RuntimeGlobals.scriptNonce}) {`,									Template.indent(										`link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`									),									"}",									'link.rel = "preload";',									'link.as = "script";',									`link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkScriptFilename}(chunkId);`,									crossOriginLoading										? crossOriginLoading === "use-credentials"											? 'link.crossOrigin = "use-credentials";'											: Template.asString([													"if (link.href.indexOf(window.location.origin + '/') !== 0) {",													Template.indent(														`link.crossOrigin = ${JSON.stringify(															crossOriginLoading														)};`													),													"}"											  ])										: ""								]),								chunk							),							"document.head.appendChild(link);"						]),						"}"				  ])};`				: "// no preloaded",			"",			withHmr				? Template.asString([						"var currentUpdatedModulesList;",						"var waitingUpdateResolves = {};",						"function loadUpdateChunk(chunkId, updatedModulesList) {",						Template.indent([							"currentUpdatedModulesList = updatedModulesList;",							`return new Promise(${runtimeTemplate.basicFunction(								"resolve, reject",								[									"waitingUpdateResolves[chunkId] = resolve;",									"// start update chunk loading",									`var url = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.getChunkUpdateScriptFilename}(chunkId);`,									"// create error before stack unwound to get useful stacktrace later",									"var error = new Error();",									`var loadingEnded = ${runtimeTemplate.basicFunction("event", [										"if(waitingUpdateResolves[chunkId]) {",										Template.indent([											"waitingUpdateResolves[chunkId] = undefined",											"var errorType = event && (event.type === 'load' ? 'missing' : event.type);",											"var realSrc = event && event.target && event.target.src;",											"error.message = 'Loading hot update chunk ' + chunkId + ' failed.\\n(' + errorType + ': ' + realSrc + ')';",											"error.name = 'ChunkLoadError';",											"error.type = errorType;",											"error.request = realSrc;",											"reject(error);"										]),										"}"									])};`,									`${RuntimeGlobals.loadScript}(url, loadingEnded);`								]							)});`						]),						"}",						"",						`${globalObject}[${JSON.stringify(							hotUpdateGlobal						)}] = ${runtimeTemplate.basicFunction(							"chunkId, moreModules, runtime",							[								"for(var moduleId in moreModules) {",								Template.indent([									`if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,									Template.indent([										"currentUpdate[moduleId] = moreModules[moduleId];",										"if(currentUpdatedModulesList) currentUpdatedModulesList.push(moduleId);"									]),									"}"								]),								"}",								"if(runtime) currentUpdateRuntime.push(runtime);",								"if(waitingUpdateResolves[chunkId]) {",								Template.indent([									"waitingUpdateResolves[chunkId]();",									"waitingUpdateResolves[chunkId] = undefined;"								]),								"}"							]						)};`,						"",						Template.getFunctionContent(							require("../hmr/JavascriptHotModuleReplacement.runtime.js")						)							.replace(/\$key\$/g, "jsonp")							.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",			"",			withOnChunkLoad				? `${						RuntimeGlobals.onChunksLoaded				  }.j = ${runtimeTemplate.returningFunction(						"installedChunks[chunkId] === 0",						"chunkId"				  )};`				: "// no on chunks loaded",			"",			withCallback || withLoading				? Template.asString([						"// install a JSONP callback for chunk loading",						`var webpackJsonpCallback = ${runtimeTemplate.basicFunction(							"parentChunkLoadingFunction, data",							[								runtimeTemplate.destructureArray(									["chunkIds", "moreModules", "runtime"],									"data"								),								'// add "moreModules" to the modules object,',								'// then flag all "chunkIds" as loaded and fire callback',								"var moduleId, chunkId, i = 0;",								`if(chunkIds.some(${runtimeTemplate.returningFunction(									"installedChunks[id] !== 0",									"id"								)})) {`,								Template.indent([									"for(moduleId in moreModules) {",									Template.indent([										`if(${RuntimeGlobals.hasOwnProperty}(moreModules, moduleId)) {`,										Template.indent(											`${RuntimeGlobals.moduleFactories}[moduleId] = moreModules[moduleId];`										),										"}"									]),									"}",									"if(runtime) var result = runtime(__webpack_require__);"								]),								"}",								"if(parentChunkLoadingFunction) parentChunkLoadingFunction(data);",								"for(;i < chunkIds.length; i++) {",								Template.indent([									"chunkId = chunkIds[i];",									`if(${RuntimeGlobals.hasOwnProperty}(installedChunks, chunkId) && installedChunks[chunkId]) {`,									Template.indent("installedChunks[chunkId][0]();"),									"}",									"installedChunks[chunkId] = 0;"								]),								"}",								withOnChunkLoad									? `return ${RuntimeGlobals.onChunksLoaded}(result);`									: ""							]						)}`,						"",						`var chunkLoadingGlobal = ${chunkLoadingGlobalExpr} = ${chunkLoadingGlobalExpr} || [];`,						"chunkLoadingGlobal.forEach(webpackJsonpCallback.bind(null, 0));",						"chunkLoadingGlobal.push = webpackJsonpCallback.bind(null, chunkLoadingGlobal.push.bind(chunkLoadingGlobal));"				  ])				: "// no jsonp function"		]);	}}module.exports = JsonpChunkLoadingRuntimeModule;
 |