AutoPublicPathRuntimeModule.js 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. */
  4. "use strict";
  5. const RuntimeGlobals = require("../RuntimeGlobals");
  6. const RuntimeModule = require("../RuntimeModule");
  7. const Template = require("../Template");
  8. const JavascriptModulesPlugin = require("../javascript/JavascriptModulesPlugin");
  9. const { getUndoPath } = require("../util/identifier");
  10. /** @typedef {import("../Chunk")} Chunk */
  11. /** @typedef {import("../Compilation")} Compilation */
  12. class AutoPublicPathRuntimeModule extends RuntimeModule {
  13. constructor() {
  14. super("publicPath", RuntimeModule.STAGE_BASIC);
  15. }
  16. /**
  17. * Generates runtime code for this runtime module.
  18. * @returns {string | null} runtime code
  19. */
  20. generate() {
  21. const compilation = /** @type {Compilation} */ (this.compilation);
  22. const { scriptType, importMetaName, path, environment } =
  23. compilation.outputOptions;
  24. const chunkName = compilation.getPath(
  25. JavascriptModulesPlugin.getChunkFilenameTemplate(
  26. /** @type {Chunk} */
  27. (this.chunk),
  28. compilation.outputOptions
  29. ),
  30. {
  31. chunk: this.chunk,
  32. contentHashType: "javascript"
  33. }
  34. );
  35. const undoPath = getUndoPath(
  36. chunkName,
  37. /** @type {string} */ (path),
  38. false
  39. );
  40. const global = environment.globalThis
  41. ? "globalThis"
  42. : RuntimeGlobals.global;
  43. return Template.asString([
  44. "var scriptUrl;",
  45. scriptType === "module"
  46. ? `if (typeof ${importMetaName}.url === "string") scriptUrl = ${importMetaName}.url`
  47. : Template.asString([
  48. `if (${global}.importScripts) scriptUrl = ${global}.location + "";`,
  49. `var document = ${global}.document;`,
  50. "if (!scriptUrl && document) {",
  51. Template.indent([
  52. // Technically we could use `document.currentScript instanceof window.HTMLScriptElement`,
  53. // but an attacker could try to inject `<script>HTMLScriptElement = HTMLImageElement</script>`
  54. // and use `<img name="currentScript" src="https://attacker.controlled.server/"></img>`
  55. "if (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT')",
  56. Template.indent("scriptUrl = document.currentScript.src;"),
  57. "if (!scriptUrl) {",
  58. Template.indent([
  59. 'var scripts = document.getElementsByTagName("script");',
  60. "if(scripts.length) {",
  61. Template.indent([
  62. "var i = scripts.length - 1;",
  63. "while (i > -1 && (!scriptUrl || !/^http(s?):/.test(scriptUrl))) scriptUrl = scripts[i--].src;"
  64. ]),
  65. "}"
  66. ]),
  67. "}"
  68. ]),
  69. "}"
  70. ]),
  71. "// When supporting browsers where an automatic publicPath is not supported you must specify an output.publicPath manually via configuration",
  72. '// or pass an empty string ("") and set the __webpack_public_path__ variable from your code to use your own logic.',
  73. 'if (!scriptUrl) throw new Error("Automatic publicPath is not supported in this browser");',
  74. 'scriptUrl = scriptUrl.replace(/^blob:/, "").replace(/#.*$/, "").replace(/\\?.*$/, "").replace(/\\/[^\\/]+$/, "/");',
  75. !undoPath
  76. ? `${RuntimeGlobals.publicPath} = scriptUrl;`
  77. : `${RuntimeGlobals.publicPath} = scriptUrl + ${JSON.stringify(
  78. undoPath
  79. )};`
  80. ]);
  81. }
  82. }
  83. module.exports = AutoPublicPathRuntimeModule;