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.

166 lines (154 loc) 5.14 kB
/* MIT License http://www.opensource.org/licenses/mit-license.php */ "use strict"; const { SyncWaterfallHook } = require("tapable"); /** @typedef {import("../Compilation")} Compilation */ const RuntimeGlobals = require("../RuntimeGlobals"); const Template = require("../Template"); const createHooksRegistry = require("../util/createHooksRegistry"); const HelperRuntimeModule = require("./HelperRuntimeModule"); /** @typedef {import("../Chunk")} Chunk */ /** * @typedef {object} LoadScriptCompilationHooks * @property {SyncWaterfallHook<[string, Chunk]>} createScript */ class LoadScriptRuntimeModule extends HelperRuntimeModule { /** * @param {boolean=} withCreateScriptUrl use create script url for trusted types * @param {boolean=} withFetchPriority use `fetchPriority` attribute */ constructor(withCreateScriptUrl, withFetchPriority) { super("load script"); /** @type {boolean | undefined} */ this._withCreateScriptUrl = withCreateScriptUrl; /** @type {boolean | undefined} */ this._withFetchPriority = withFetchPriority; } /** * Generates runtime code for this runtime module. * @returns {string | null} runtime code */ generate() { const compilation = /** @type {Compilation} */ (this.compilation); const { runtimeTemplate, outputOptions } = compilation; const { scriptType, chunkLoadTimeout: loadTimeout, crossOriginLoading, uniqueName, charset } = outputOptions; const fn = RuntimeGlobals.loadScript; const { createScript } = LoadScriptRuntimeModule.getCompilationHooks(compilation); const code = Template.asString([ "script = document.createElement('script');", scriptType ? `script.type = ${JSON.stringify(scriptType)};` : "", charset ? "script.charset = 'utf-8';" : "", `if (${RuntimeGlobals.scriptNonce}) {`, Template.indent( `script.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});` ), "}", uniqueName ? 'script.setAttribute("data-webpack", dataWebpackPrefix + key);' : "", this._withFetchPriority ? Template.asString([ "if(fetchPriority) {", Template.indent( 'script.setAttribute("fetchpriority", fetchPriority);' ), "}" ]) : "", `script.src = ${ this._withCreateScriptUrl ? `${RuntimeGlobals.createScriptUrl}(url)` : "url" };`, crossOriginLoading ? crossOriginLoading === "use-credentials" ? 'script.crossOrigin = "use-credentials";' : Template.asString([ "if (script.src.indexOf(window.location.origin + '/') !== 0) {", Template.indent( `script.crossOrigin = ${JSON.stringify(crossOriginLoading)};` ), "}" ]) : "" ]); const cst = runtimeTemplate.renderConst(); const lt = runtimeTemplate.renderLet(); return Template.asString([ `${cst} inProgress = {};`, uniqueName ? `${cst} dataWebpackPrefix = ${JSON.stringify(`${uniqueName}:`)};` : "// data-webpack is not used as build has no uniqueName", "// loadScript function to load a script via script tag", `${fn} = ${runtimeTemplate.basicFunction( `url, done, key, chunkId${ this._withFetchPriority ? ", fetchPriority" : "" }`, [ "if(inProgress[url]) { inProgress[url].push(done); return; }", `${lt} script, needAttach;`, "if(key !== undefined) {", Template.indent([ `${cst} scripts = document.getElementsByTagName("script");`, "for(var i = 0; i < scripts.length; i++) {", Template.indent([ `${cst} s = scripts[i];`, `if(s.getAttribute("src") == url${ uniqueName ? ' || s.getAttribute("data-webpack") == dataWebpackPrefix + key' : "" }) { script = s; break; }` ]), "}" ]), "}", "if(!script) {", Template.indent([ "needAttach = true;", createScript.call(code, /** @type {Chunk} */ (this.chunk)) ]), "}", "inProgress[url] = [done];", `${cst} onScriptComplete = ${runtimeTemplate.basicFunction( "prev, event", Template.asString([ "// avoid mem leaks in IE.", "script.onerror = script.onload = null;", "clearTimeout(timeout);", `${cst} doneFns = inProgress[url];`, "delete inProgress[url];", `${runtimeTemplate.optionalChaining( "script.parentNode", "removeChild(script)" )};`, `${runtimeTemplate.optionalChaining( "doneFns", `forEach(${runtimeTemplate.returningFunction("fn(event)", "fn")})` )};`, "if(prev) return prev(event);" ]) )}`, `${cst} timeout = setTimeout(onScriptComplete.bind(null, undefined, { type: 'timeout', target: script }), ${loadTimeout});`, "script.onerror = onScriptComplete.bind(null, script.onerror);", "script.onload = onScriptComplete.bind(null, script.onload);", "needAttach && document.head.appendChild(script);" ] )};` ]); } } LoadScriptRuntimeModule.getCompilationHooks = createHooksRegistry( () => /** @type {LoadScriptCompilationHooks} */ ({ createScript: new SyncWaterfallHook(["source", "chunk"]) }) ); module.exports = LoadScriptRuntimeModule;