EvalDevToolModulePlugin.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { ConcatSource, RawSource } = require("webpack-sources");
  7. const ExternalModule = require("./ExternalModule");
  8. const ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
  9. const RuntimeGlobals = require("./RuntimeGlobals");
  10. const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
  11. /** @import { Source } from "webpack-sources" */
  12. /**
  13. * @import {
  14. * DevtoolNamespace,
  15. * DevtoolModuleFilenameTemplate
  16. * } from "../declarations/WebpackOptions"
  17. */
  18. /** @import Compiler from "./Compiler" */
  19. /** @type {WeakMap<Source, Source>} */
  20. const cache = new WeakMap();
  21. const devtoolWarning = new RawSource(`/*
  22. * ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
  23. * This devtool is neither made for production nor for readable output files.
  24. * It uses "eval()" calls to create a separate source file in the browser devtools.
  25. * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
  26. * or disable the default devtool with "devtool: false".
  27. * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
  28. */
  29. `);
  30. /**
  31. * Defines the eval dev tool module plugin options type used by this module.
  32. * @typedef {object} EvalDevToolModulePluginOptions
  33. * @property {DevtoolNamespace=} namespace namespace
  34. * @property {string=} sourceUrlComment source url comment
  35. * @property {DevtoolModuleFilenameTemplate=} moduleFilenameTemplate module filename template
  36. */
  37. const PLUGIN_NAME = "EvalDevToolModulePlugin";
  38. class EvalDevToolModulePlugin {
  39. /**
  40. * Creates an instance of EvalDevToolModulePlugin.
  41. * @param {EvalDevToolModulePluginOptions=} options options
  42. */
  43. constructor(options = {}) {
  44. /** @type {DevtoolNamespace} */
  45. this.namespace = options.namespace || "";
  46. /** @type {string} */
  47. this.sourceUrlComment = options.sourceUrlComment || "\n//# sourceURL=[url]";
  48. /** @type {DevtoolModuleFilenameTemplate} */
  49. this.moduleFilenameTemplate =
  50. options.moduleFilenameTemplate ||
  51. "webpack://[namespace]/[resourcePath]?[loaders]";
  52. }
  53. /**
  54. * Applies the plugin by registering its hooks on the compiler.
  55. * @param {Compiler} compiler the compiler instance
  56. * @returns {void}
  57. */
  58. apply(compiler) {
  59. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  60. const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
  61. hooks.renderModuleContent.tap(
  62. PLUGIN_NAME,
  63. (source, module, { chunk, runtimeTemplate, chunkGraph }) => {
  64. const cacheEntry = cache.get(source);
  65. if (cacheEntry !== undefined) return cacheEntry;
  66. if (module instanceof ExternalModule) {
  67. cache.set(source, source);
  68. return source;
  69. }
  70. const content = source.source();
  71. const namespace = compilation.getPath(this.namespace, {
  72. chunk
  73. });
  74. const str = ModuleFilenameHelpers.createFilename(
  75. module,
  76. {
  77. moduleFilenameTemplate: this.moduleFilenameTemplate,
  78. namespace
  79. },
  80. {
  81. requestShortener: runtimeTemplate.requestShortener,
  82. chunkGraph,
  83. hashFunction: compilation.outputOptions.hashFunction
  84. }
  85. );
  86. const footer = `\n${this.sourceUrlComment.replace(
  87. /\[url\]/g,
  88. encodeURI(str)
  89. .replace(/%2F/g, "/")
  90. .replace(/%20/g, "_")
  91. .replace(/%5E/g, "^")
  92. .replace(/%5C/g, "\\")
  93. .replace(/^\//, "")
  94. )}`;
  95. const result = new RawSource(
  96. `eval(${
  97. compilation.outputOptions.trustedTypes
  98. ? `${RuntimeGlobals.createScript}(${JSON.stringify(
  99. `{${content + footer}\n}`
  100. )})`
  101. : JSON.stringify(`{${content + footer}\n}`)
  102. });`
  103. );
  104. cache.set(source, result);
  105. return result;
  106. }
  107. );
  108. hooks.inlineInRuntimeBailout.tap(
  109. PLUGIN_NAME,
  110. () => "the eval devtool is used."
  111. );
  112. hooks.render.tap(
  113. PLUGIN_NAME,
  114. (source) => new ConcatSource(devtoolWarning, source)
  115. );
  116. hooks.chunkHash.tap(PLUGIN_NAME, (chunk, hash) => {
  117. hash.update(PLUGIN_NAME);
  118. hash.update("2");
  119. });
  120. if (compilation.outputOptions.trustedTypes) {
  121. compilation.hooks.additionalModuleRuntimeRequirements.tap(
  122. PLUGIN_NAME,
  123. (module, set, _context) => {
  124. set.add(RuntimeGlobals.createScript);
  125. }
  126. );
  127. }
  128. });
  129. }
  130. }
  131. module.exports = EvalDevToolModulePlugin;