diff --git a/packages/react-dev-utils/evalSourceMapMiddleware.js b/packages/react-dev-utils/evalSourceMapMiddleware.js new file mode 100644 index 0000000000000000000000000000000000000000..dee6370f8ee702c25d2fba0ff22984ced5ae890b --- /dev/null +++ b/packages/react-dev-utils/evalSourceMapMiddleware.js @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +function base64SourceMap(source) { + const base64 = Buffer.from(JSON.stringify(source.map()), 'utf8').toString( + 'base64' + ); + return `data:application/json;charset=utf-8;base64,${base64}`; +} + +function getSourceById(server, id) { + const module = server._stats.compilation.modules.find(m => m.id == id); + return module.originalSource(); +} + +/* + * Middleware responsible for retrieving a generated source + * Receives a webpack internal url: "webpack-internal:///<module-id>" + * Returns a generated source: "<source-text><sourceMappingURL><sourceURL>" + * + * Based on EvalSourceMapDevToolModuleTemplatePlugin.js + */ +module.exports = function createEvalSourceMapMiddleware(server) { + return function handleWebpackInternalMiddleware(req, res, next) { + if (req.url.startsWith('/__get-internal-source')) { + const fileName = req.query.fileName; + const id = fileName.match(/webpack-internal:\/\/\/(.+)/)[1]; + if (!id || !server._stats) { + next(); + } + + const source = getSourceById(server, id); + const sourceMapURL = `//# sourceMappingURL=${base64SourceMap(source)}`; + const sourceURL = `//# sourceURL=webpack-internal:///${module.id}`; + res.end(`${source.source()}\n${sourceMapURL}\n${sourceURL}`); + } else { + next(); + } + }; +}; diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 23ff82d5c753f88a78065702ec15145888bbf1a3..74093b4a60ae49c27ea52766750dac89e4591214 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -17,6 +17,7 @@ "crossSpawn.js", "errorOverlayMiddleware.js", "eslintFormatter.js", + "evalSourceMapMiddleware.js", "FileSizeReporter.js", "formatWebpackMessages.js", "getCSSModuleLocalIdent.js", diff --git a/packages/react-error-overlay/src/utils/mapper.js b/packages/react-error-overlay/src/utils/mapper.js index 92407be537dc8bc2c1f5b1b64dbd450a9ed00490..95aa006a2c01b5727a06a30dba95c95668f7a951 100644 --- a/packages/react-error-overlay/src/utils/mapper.js +++ b/packages/react-error-overlay/src/utils/mapper.js @@ -34,7 +34,11 @@ async function map( }); await settle( files.map(async fileName => { - const fileSource = await fetch(fileName).then(r => r.text()); + const fetchUrl = fileName.startsWith('webpack-internal:') + ? `/__get-internal-source?fileName=${encodeURIComponent(fileName)}` + : fileName; + + const fileSource = await fetch(fetchUrl).then(r => r.text()); const map = await getSourceMap(fileName, fileSource); cache[fileName] = { fileSource, map }; }) diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index e967bb94818fb71dd703565976d2069598d6cc55..12b8f8cf2dd8fc579e354db59c7e58e9941c0ba0 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -76,7 +76,7 @@ module.exports = { mode: 'development', // You may want 'eval' instead if you prefer to see the compiled output in DevTools. // See the discussion in https://github.com/facebook/create-react-app/issues/343 - devtool: 'cheap-module-source-map', + devtool: 'eval-source-map', // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. // The first two entry points enable "hot" CSS and auto-refreshes for JS. diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js index 17dac9dc0fcc5655ea391c8ea443c53b5fc03690..b5eb9349aa68a23edb4ee21f4b9f72f9de85368a 100644 --- a/packages/react-scripts/config/webpackDevServer.config.js +++ b/packages/react-scripts/config/webpackDevServer.config.js @@ -9,6 +9,7 @@ 'use strict'; const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware'); +const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware'); const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware'); const ignoredFiles = require('react-dev-utils/ignoredFiles'); const config = require('./webpack.config.dev'); @@ -89,7 +90,10 @@ module.exports = function(proxy, allowedHost) { }, public: allowedHost, proxy, - before(app) { + before(app, server) { + // This lets us fetch source contents from webpack for the error overlay + app.use(evalSourceMapMiddleware(server)); + // This lets us open files from the runtime error overlay. app.use(errorOverlayMiddleware()); // This service worker file is effectively a 'no-op' that will reset any