diff --git a/lib/RuntimePlugin.js b/lib/RuntimePlugin.js index 15caa5052..96cd16e06 100644 --- a/lib/RuntimePlugin.js +++ b/lib/RuntimePlugin.js @@ -8,6 +8,7 @@ const RuntimeGlobals = require("./RuntimeGlobals"); const RuntimeRequirementsDependency = require("./dependencies/RuntimeRequirementsDependency"); const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin"); +const AutoPublicPathRuntimeModule = require("./runtime/AutoPublicPathRuntimeModule"); const CompatGetDefaultExportRuntimeModule = require("./runtime/CompatGetDefaultExportRuntimeModule"); const CompatRuntimeModule = require("./runtime/CompatRuntimeModule"); const CreateFakeNamespaceObjectRuntimeModule = require("./runtime/CreateFakeNamespaceObjectRuntimeModule"); @@ -157,24 +158,25 @@ class RuntimePlugin { }); compilation.hooks.runtimeRequirementInTree .for(RuntimeGlobals.publicPath) - .tap("RuntimePlugin", (chunk, runtimeRequirements) => { + .tap("RuntimePlugin", (chunk, set) => { const { outputOptions } = compilation; - const { publicPath } = outputOptions; - const module = new PublicPathRuntimeModule(); + const { publicPath, scriptType } = outputOptions; - if ( - typeof publicPath !== "string" || - /\[(full)?hash\]/.test(publicPath) - ) { - module.fullHash = true; - } + if (publicPath === "auto") { + const module = new AutoPublicPathRuntimeModule(); + if (scriptType !== "module") set.add(RuntimeGlobals.global); + compilation.addRuntimeModule(chunk, module); + } else { + const module = new PublicPathRuntimeModule(); - compilation.addRuntimeModule(chunk, module); - const moduleRuntimeRequirements = module.getRuntimeRequirements(); + if ( + typeof publicPath !== "string" || + /\[(full)?hash\]/.test(publicPath) + ) { + module.fullHash = true; + } - if (moduleRuntimeRequirements) { - for (const req of moduleRuntimeRequirements) - runtimeRequirements.add(req); + compilation.addRuntimeModule(chunk, module); } return true; }); diff --git a/lib/runtime/AutoPublicPathRuntimeModule.js b/lib/runtime/AutoPublicPathRuntimeModule.js new file mode 100644 index 000000000..419be434d --- /dev/null +++ b/lib/runtime/AutoPublicPathRuntimeModule.js @@ -0,0 +1,67 @@ +/* + 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 JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin"); +const { getUndoPath } = require("../util/identifier"); + +class AutoPublicPathRuntimeModule extends RuntimeModule { + constructor() { + super("publicPath", 5); + } + + /** + * @returns {string} runtime code + */ + generate() { + const { compilation } = this; + const { scriptType, importFunctionName } = compilation.outputOptions; + const chunkName = compilation.getPath( + JavascriptModulesPlugin.getChunkFilenameTemplate( + this.chunk, + compilation.outputOptions + ), + { + chunk: this.chunk, + contentHashType: "javascript" + } + ); + const undoPath = getUndoPath(chunkName, false); + return Template.asString([ + "var scriptUrl;", + scriptType === "module" + ? `if (typeof ${importFunctionName}.meta.url === "string") scriptUrl = ${importFunctionName}.meta.url` + : Template.asString([ + `var document = ${RuntimeGlobals.global}.document;`, + "if (document) {", + Template.indent([ + `if (document.currentScript)`, + Template.indent(`scriptUrl = document.currentScript.src`), + "if (!scriptUrl) {", + Template.indent([ + 'var scripts = document.getElementsByTagName("script");', + "if(scripts.length) scriptUrl = scripts[scripts.length - 1].src" + ]), + "}" + ]), + "}" + ]), + "// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration", + '// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.', + 'if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");', + 'scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\\?.*$/, "").replace(/\\/[^\\/]+$/, "/");', + !undoPath + ? `${RuntimeGlobals.publicPath} = scriptUrl;` + : `${RuntimeGlobals.publicPath} = scriptUrl + ${JSON.stringify( + undoPath + )};` + ]); + } +} + +module.exports = AutoPublicPathRuntimeModule; diff --git a/lib/runtime/PublicPathRuntimeModule.js b/lib/runtime/PublicPathRuntimeModule.js index 0ee617f2e..ad4214725 100644 --- a/lib/runtime/PublicPathRuntimeModule.js +++ b/lib/runtime/PublicPathRuntimeModule.js @@ -6,27 +6,8 @@ const RuntimeGlobals = require("../RuntimeGlobals"); const RuntimeModule = require("../RuntimeModule"); -const Template = require("../Template"); -const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin"); -const { getUndoPath } = require("../util/identifier"); - -/** @typedef {import("../../declarations/WebpackOptions").Output} OutputOptions */ -/** @typedef {import("../../declarations/WebpackOptions").PublicPath} PublicPathOptions */ -/** @typedef {import("../Compilation")} Compilation */ -/** @typedef {import("../RuntimeTemplate")} RuntimeTemplate */ class PublicPathRuntimeModule extends RuntimeModule { - /** - * @returns {ReadonlyArray | null} requirements - */ - getRuntimeRequirements() { - const { compilation } = this; - const { publicPath, scriptType } = compilation.outputOptions; - if (publicPath !== "auto" || scriptType === "module") return null; - - return [RuntimeGlobals.global]; - } - constructor() { super("publicPath", 5); } @@ -36,67 +17,13 @@ class PublicPathRuntimeModule extends RuntimeModule { */ generate() { const { compilation } = this; - const { - publicPath, - scriptType, - importFunctionName - } = compilation.outputOptions; - if (publicPath === "auto") { - const chunkName = compilation.getPath( - JavascriptModulesPlugin.getChunkFilenameTemplate( - this.chunk, - compilation.outputOptions - ), - { - chunk: this.chunk, - contentHashType: "javascript" - } - ); - const undoPath = getUndoPath(chunkName, false); - return Template.asString([ - "var scriptUrl;", - scriptType === "module" - ? `if (typeof ${importFunctionName}.meta.url === "string") scriptUrl = ${importFunctionName}.meta.url` - : Template.asString([ - `var document = ${RuntimeGlobals.global}.document;`, - "if (document) {", - Template.indent([ - `if (document.currentScript)`, - Template.indent(`scriptUrl = document.currentScript.src`), - "if (!scriptUrl) {", - Template.indent([ - 'var scripts = document.getElementsByTagName("script");', - "if(scripts.length) scriptUrl = scripts[scripts.length - 1].src" - ]), - "}" - ]), - "}" - ]), - "// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration", - '// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.', - 'if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");', - 'scriptUrl = scriptUrl.replace(/#.*$/, "").replace(/\\?.*$/, "").replace(/\\/[^\\/]+$/, "/");', - !undoPath - ? `${RuntimeGlobals.publicPath} = scriptUrl;` - : `${RuntimeGlobals.publicPath} = scriptUrl + ${JSON.stringify( - undoPath - )};` - ]); - } else { - return `${RuntimeGlobals.publicPath} = ${this.definePath(publicPath)};`; - } - } + const { publicPath } = compilation.outputOptions; - /** - * @param {PublicPathOptions} publicPath public path - * @returns {string} runtime code - */ - definePath(publicPath) { - return JSON.stringify( + return `${RuntimeGlobals.publicPath} = ${JSON.stringify( this.compilation.getPath(publicPath || "", { hash: this.compilation.hash || "XXXX" }) - ); + )};`; } }