From c21ce4a39667c4094208bc35dc86fc9f49fceec6 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin Date: Thu, 19 Sep 2024 15:44:34 +0100 Subject: [PATCH] feat: display message if user ended up opening hook script (#31000) In https://github.com/facebook/react/pull/30596 we've moved console patching to the global hook. Generally speaking, the patching happens even before React is loaded on the page. If browser DevTools were opened after when `console.error` or `console.warn` were called, the source script will be `hook.js`, because of the patching. ![devtools-opened-after-the-message](https://github.com/user-attachments/assets/3d3dbc16-96b8-4234-b061-57b21b60cf2e) This is because ignore listing is not applied retroactively by Chrome DevTools. If you had it open before console calls, Hook script would be correctly filtered out from the stack: ![devtools-opened-before-the-message](https://github.com/user-attachments/assets/3e99cb22-97b0-4b49-9a76-f7bc948e6452) I had hopes that the fix for https://issues.chromium.org/issues/345248263 will also apply ignore listing retroactively, but looks like we need to open a separate feature request for the Chrome DevTools team. With these changes, if user attempts to open `hook.js` script, they are going to see this message: ![Screenshot 2024-09-19 at 11 30 59](https://github.com/user-attachments/assets/5850b74c-329f-4fbe-a3dd-33f9ac717ee9) --- .../webpack.config.js | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/react-devtools-extensions/webpack.config.js b/packages/react-devtools-extensions/webpack.config.js index e6d40a1f20ad2..51b8f4e2105e3 100644 --- a/packages/react-devtools-extensions/webpack.config.js +++ b/packages/react-devtools-extensions/webpack.config.js @@ -154,6 +154,62 @@ module.exports = { ); }, }), + { + apply(compiler) { + if (__DEV__) { + return; + } + + const {RawSource} = compiler.webpack.sources; + compiler.hooks.compilation.tap( + 'CustomContentForHookScriptPlugin', + compilation => { + compilation.hooks.processAssets.tap( + { + name: 'CustomContentForHookScriptPlugin', + stage: Webpack.Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING, + additionalAssets: true, + }, + assets => { + // eslint-disable-next-line no-for-of-loops/no-for-of-loops + for (const [name, asset] of Object.entries(assets)) { + if (name !== 'installHook.js.map') { + continue; + } + + const mapContent = asset.source().toString(); + if (!mapContent) { + continue; + } + + const map = JSON.parse(mapContent); + map.sourcesContent = map.sources.map(sourceName => { + if (!sourceName.endsWith('/hook.js')) { + return null; + } + + return ( + '/*\n' + + ' * This script is from React DevTools.\n' + + " * You're likely here because you thought it sent an error or warning to the console.\n" + + ' * React DevTools patches the console to support features like appending component stacks, \n' + + ' * so this file appears as a source. However, the console call actually came from another script.\n' + + " * To remove this script from stack traces, open your browser's DevTools (to enable source mapping) before these console calls happen.\n" + + ' */' + ); + }); + + compilation.updateAsset( + name, + new RawSource(JSON.stringify(map)), + ); + } + }, + ); + }, + ); + }, + }, ], module: { defaultRules: [