EvalSourceMapDevToolPlugin.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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 ModuleFilenameHelpers = require("./ModuleFilenameHelpers");
  8. const NormalModule = require("./NormalModule");
  9. const RuntimeGlobals = require("./RuntimeGlobals");
  10. const SourceMapDevToolModuleOptionsPlugin = require("./SourceMapDevToolModuleOptionsPlugin");
  11. const JavascriptModulesPlugin = require("./javascript/JavascriptModulesPlugin");
  12. const ConcatenatedModule = require("./optimize/ConcatenatedModule");
  13. const generateDebugId = require("./util/generateDebugId");
  14. const { makePathsAbsolute } = require("./util/identifier");
  15. /** @import { RawSourceMap, Source } from "webpack-sources" */
  16. /**
  17. * @import {
  18. * DevtoolNamespace,
  19. * DevtoolModuleFilenameTemplate
  20. * } from "../declarations/WebpackOptions"
  21. */
  22. /**
  23. * @import {
  24. * SourceMapDevToolPluginOptions,
  25. * Rules
  26. * } from "../declarations/plugins/SourceMapDevToolPlugin"
  27. */
  28. /** @import Compiler from "./Compiler" */
  29. /** @import { ModuleId } from "./ChunkGraph" */
  30. /** @type {WeakMap<Source, Source>} */
  31. const cache = new WeakMap();
  32. const devtoolWarning = new RawSource(`/*
  33. * ATTENTION: An "eval-source-map" devtool has been used.
  34. * This devtool is neither made for production nor for readable output files.
  35. * It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
  36. * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
  37. * or disable the default devtool with "devtool: false".
  38. * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
  39. */
  40. `);
  41. const PLUGIN_NAME = "EvalSourceMapDevToolPlugin";
  42. class EvalSourceMapDevToolPlugin {
  43. /**
  44. * Creates an instance of EvalSourceMapDevToolPlugin.
  45. * @param {SourceMapDevToolPluginOptions | string=} inputOptions Options object
  46. */
  47. constructor(inputOptions = {}) {
  48. /** @type {SourceMapDevToolPluginOptions} */
  49. let options;
  50. if (typeof inputOptions === "string") {
  51. options = {
  52. append: inputOptions
  53. };
  54. } else {
  55. options = inputOptions;
  56. }
  57. /** @type {string} */
  58. this.sourceMapComment =
  59. options.append && typeof options.append !== "function"
  60. ? options.append
  61. : "//# sourceURL=[module]\n//# sourceMappingURL=[url]";
  62. /** @type {DevtoolModuleFilenameTemplate} */
  63. this.moduleFilenameTemplate =
  64. options.moduleFilenameTemplate ||
  65. "webpack://[namespace]/[resource-path]?[hash]";
  66. /** @type {DevtoolNamespace} */
  67. this.namespace = options.namespace || "";
  68. /** @type {SourceMapDevToolPluginOptions} */
  69. this.options = options;
  70. }
  71. /**
  72. * Applies the plugin by registering its hooks on the compiler.
  73. * @param {Compiler} compiler the compiler instance
  74. * @returns {void}
  75. */
  76. apply(compiler) {
  77. const options = this.options;
  78. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  79. const hooks = JavascriptModulesPlugin.getCompilationHooks(compilation);
  80. new SourceMapDevToolModuleOptionsPlugin(options).apply(compilation);
  81. const matchModule = ModuleFilenameHelpers.matchObject.bind(
  82. ModuleFilenameHelpers,
  83. options
  84. );
  85. hooks.renderModuleContent.tap(
  86. PLUGIN_NAME,
  87. (source, m, { chunk, runtimeTemplate, chunkGraph }) => {
  88. const cachedSource = cache.get(source);
  89. if (cachedSource !== undefined) {
  90. return cachedSource;
  91. }
  92. /**
  93. * Returns result.
  94. * @param {Source} r result
  95. * @returns {Source} result
  96. */
  97. const result = (r) => {
  98. cache.set(source, r);
  99. return r;
  100. };
  101. if (m instanceof NormalModule) {
  102. if (!matchModule(m.resource)) {
  103. return result(source);
  104. }
  105. } else if (m instanceof ConcatenatedModule) {
  106. if (m.rootModule instanceof NormalModule) {
  107. if (!matchModule(m.rootModule.resource)) {
  108. return result(source);
  109. }
  110. } else {
  111. return result(source);
  112. }
  113. } else {
  114. return result(source);
  115. }
  116. const namespace = compilation.getPath(this.namespace, {
  117. chunk
  118. });
  119. /** @type {RawSourceMap} */
  120. let sourceMap;
  121. /** @type {string | Buffer} */
  122. let content;
  123. if (source.sourceAndMap) {
  124. const sourceAndMap = source.sourceAndMap(options);
  125. sourceMap = /** @type {RawSourceMap} */ (sourceAndMap.map);
  126. content = sourceAndMap.source;
  127. } else {
  128. sourceMap = /** @type {RawSourceMap} */ (source.map(options));
  129. content = source.source();
  130. }
  131. if (!sourceMap) {
  132. return result(source);
  133. }
  134. // Clone (flat) the sourcemap to ensure that the mutations below do not persist.
  135. sourceMap = { ...sourceMap };
  136. const context = compiler.context;
  137. const root = compiler.root;
  138. const cachedAbsolutify = makePathsAbsolute.bindContextCache(
  139. context,
  140. root
  141. );
  142. const modules = sourceMap.sources.map((source) => {
  143. if (!source.startsWith("webpack://")) return source;
  144. source = cachedAbsolutify(source.slice(10));
  145. const module = compilation.findModule(source);
  146. return module || source;
  147. });
  148. let moduleFilenames = modules.map((module) =>
  149. ModuleFilenameHelpers.createFilename(
  150. module,
  151. {
  152. moduleFilenameTemplate: this.moduleFilenameTemplate,
  153. namespace
  154. },
  155. {
  156. requestShortener: runtimeTemplate.requestShortener,
  157. chunkGraph,
  158. hashFunction: compilation.outputOptions.hashFunction
  159. }
  160. )
  161. );
  162. moduleFilenames = ModuleFilenameHelpers.replaceDuplicates(
  163. moduleFilenames,
  164. (filename, i, n) => {
  165. for (let j = 0; j < n; j++) filename += "*";
  166. return filename;
  167. }
  168. );
  169. sourceMap.sources = moduleFilenames;
  170. if (options.ignoreList) {
  171. const ignoreList = sourceMap.sources.reduce(
  172. /** @type {(acc: number[], sourceName: string, idx: number) => number[]} */ (
  173. (acc, sourceName, idx) => {
  174. const rule = /** @type {Rules} */ (options.ignoreList);
  175. if (ModuleFilenameHelpers.matchPart(sourceName, rule)) {
  176. acc.push(idx);
  177. }
  178. return acc;
  179. }
  180. ),
  181. []
  182. );
  183. if (ignoreList.length > 0) {
  184. sourceMap.ignoreList = ignoreList;
  185. }
  186. }
  187. if (options.noSources) {
  188. sourceMap.sourcesContent = undefined;
  189. }
  190. sourceMap.sourceRoot = options.sourceRoot || "";
  191. const moduleId =
  192. /** @type {ModuleId} */
  193. (chunkGraph.getModuleId(m));
  194. sourceMap.file =
  195. typeof moduleId === "number" ? `${moduleId}.js` : moduleId;
  196. if (options.debugIds) {
  197. sourceMap.debugId = generateDebugId(content, sourceMap.file);
  198. }
  199. const footer = `${this.sourceMapComment.replace(
  200. /\[url\]/g,
  201. `data:application/json;charset=utf-8;base64,${Buffer.from(
  202. JSON.stringify(sourceMap),
  203. "utf8"
  204. ).toString("base64")}`
  205. )}\n//# sourceURL=webpack-internal:///${moduleId}\n`; // workaround for chrome bug
  206. return result(
  207. new RawSource(
  208. `eval(${
  209. compilation.outputOptions.trustedTypes
  210. ? `${RuntimeGlobals.createScript}(${JSON.stringify(
  211. `{${content + footer}\n}`
  212. )})`
  213. : JSON.stringify(`{${content + footer}\n}`)
  214. });`
  215. )
  216. );
  217. }
  218. );
  219. hooks.inlineInRuntimeBailout.tap(
  220. PLUGIN_NAME,
  221. () => "the eval-source-map devtool is used."
  222. );
  223. hooks.render.tap(
  224. PLUGIN_NAME,
  225. (source) => new ConcatSource(devtoolWarning, source)
  226. );
  227. hooks.chunkHash.tap(PLUGIN_NAME, (chunk, hash) => {
  228. hash.update(PLUGIN_NAME);
  229. hash.update("2");
  230. });
  231. if (compilation.outputOptions.trustedTypes) {
  232. compilation.hooks.additionalModuleRuntimeRequirements.tap(
  233. PLUGIN_NAME,
  234. (module, set, context) => {
  235. set.add(RuntimeGlobals.createScript);
  236. }
  237. );
  238. }
  239. });
  240. }
  241. }
  242. module.exports = EvalSourceMapDevToolPlugin;