diff --git a/packages/react-devtools-core/src/standalone.js b/packages/react-devtools-core/src/standalone.js index 7ab88b952a111..5a6d8320e29e4 100644 --- a/packages/react-devtools-core/src/standalone.js +++ b/packages/react-devtools-core/src/standalone.js @@ -17,8 +17,9 @@ import { import Bridge from 'react-devtools-shared/src/bridge'; import Store from 'react-devtools-shared/src/devtools/store'; import { - getSavedComponentFilters, getAppendComponentStack, + getBreakOnConsoleErrors, + getSavedComponentFilters, } from 'react-devtools-shared/src/utils'; import {Server} from 'ws'; import {join} from 'path'; @@ -282,11 +283,14 @@ function startServer(port?: number = 8097) { // Because of this it relies on the extension to pass filters, so include them wth the response here. // This will ensure that saved filters are shared across different web pages. const savedPreferencesString = ` - window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = ${JSON.stringify( - getSavedComponentFilters(), - )}; window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = ${JSON.stringify( getAppendComponentStack(), + )}; + window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ = ${JSON.stringify( + getBreakOnConsoleErrors(), + )}; + window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = ${JSON.stringify( + getSavedComponentFilters(), )};`; response.end( diff --git a/packages/react-devtools-extensions/src/main.js b/packages/react-devtools-extensions/src/main.js index 570d0158f3f74..a405255ff59e8 100644 --- a/packages/react-devtools-extensions/src/main.js +++ b/packages/react-devtools-extensions/src/main.js @@ -7,8 +7,9 @@ import Store from 'react-devtools-shared/src/devtools/store'; import {getBrowserName, getBrowserTheme} from './utils'; import {LOCAL_STORAGE_TRACE_UPDATES_ENABLED_KEY} from 'react-devtools-shared/src/constants'; import { - getSavedComponentFilters, getAppendComponentStack, + getBreakOnConsoleErrors, + getSavedComponentFilters, } from 'react-devtools-shared/src/utils'; import { localStorageGetItem, @@ -28,17 +29,18 @@ let panelCreated = false; // because they are stored in localStorage within the context of the extension. // Instead it relies on the extension to pass filters through. function syncSavedPreferences() { - const componentFilters = getSavedComponentFilters(); - chrome.devtools.inspectedWindow.eval( - `window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = ${JSON.stringify( - componentFilters, - )};`, - ); - const appendComponentStack = getAppendComponentStack(); + const breakOnConsoleErrors = getBreakOnConsoleErrors(); + const componentFilters = getSavedComponentFilters(); chrome.devtools.inspectedWindow.eval( `window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = ${JSON.stringify( appendComponentStack, + )}; + window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ = ${JSON.stringify( + breakOnConsoleErrors, + )}; + window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = ${JSON.stringify( + componentFilters, )};`, ); } diff --git a/packages/react-devtools-extensions/webpack.backend.js b/packages/react-devtools-extensions/webpack.backend.js index c6cf7142dd85c..0148c2b217f82 100644 --- a/packages/react-devtools-extensions/webpack.backend.js +++ b/packages/react-devtools-extensions/webpack.backend.js @@ -2,6 +2,7 @@ const {resolve} = require('path'); const {DefinePlugin} = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); const {GITHUB_URL, getVersionString} = require('./utils'); const NODE_ENV = process.env.NODE_ENV; @@ -39,6 +40,16 @@ module.exports = { scheduler: resolve(builtModulesDir, 'scheduler'), }, }, + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: {drop_debugger: false}, + output: {comments: true}, + }, + }), + ], + }, plugins: [ new DefinePlugin({ __DEV__: true, diff --git a/packages/react-devtools-inline/src/backend.js b/packages/react-devtools-inline/src/backend.js index a6e901029a182..fb46276924652 100644 --- a/packages/react-devtools-inline/src/backend.js +++ b/packages/react-devtools-inline/src/backend.js @@ -20,9 +20,14 @@ function startActivation(contentWindow: window) { // so it's safe to cleanup after we've received it. contentWindow.removeEventListener('message', onMessage); - const {appendComponentStack, componentFilters} = data; + const { + appendComponentStack, + breakOnConsoleErrors, + componentFilters, + } = data; contentWindow.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = appendComponentStack; + contentWindow.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ = breakOnConsoleErrors; contentWindow.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters; // TRICKY @@ -33,6 +38,7 @@ function startActivation(contentWindow: window) { // but it doesn't really hurt anything to store them there too. if (contentWindow !== window) { window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = appendComponentStack; + window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ = breakOnConsoleErrors; window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters; } diff --git a/packages/react-devtools-inline/src/frontend.js b/packages/react-devtools-inline/src/frontend.js index 0172c8bbf8cfd..9a5c145471139 100644 --- a/packages/react-devtools-inline/src/frontend.js +++ b/packages/react-devtools-inline/src/frontend.js @@ -6,8 +6,9 @@ import Bridge from 'react-devtools-shared/src/bridge'; import Store from 'react-devtools-shared/src/devtools/store'; import DevTools from 'react-devtools-shared/src/devtools/views/DevTools'; import { - getSavedComponentFilters, getAppendComponentStack, + getBreakOnConsoleErrors, + getSavedComponentFilters, } from 'react-devtools-shared/src/utils'; import { MESSAGE_TYPE_GET_SAVED_PREFERENCES, @@ -38,6 +39,7 @@ export function initialize( { type: MESSAGE_TYPE_SAVED_PREFERENCES, appendComponentStack: getAppendComponentStack(), + breakOnConsoleErrors: getBreakOnConsoleErrors(), componentFilters: getSavedComponentFilters(), }, '*', diff --git a/packages/react-devtools-inline/webpack.config.js b/packages/react-devtools-inline/webpack.config.js index 3f0677f80b793..84b6ed0ac19d4 100644 --- a/packages/react-devtools-inline/webpack.config.js +++ b/packages/react-devtools-inline/webpack.config.js @@ -1,5 +1,6 @@ const {resolve} = require('path'); const {DefinePlugin} = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); const { GITHUB_URL, getVersionString, @@ -36,6 +37,16 @@ module.exports = { 'react-is': 'react-is', scheduler: 'scheduler', }, + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: {drop_debugger: false}, + output: {comments: true}, + }, + }), + ], + }, plugins: [ new DefinePlugin({ __DEV__, diff --git a/packages/react-devtools-shared/src/__tests__/console-test.js b/packages/react-devtools-shared/src/__tests__/console-test.js index 710499ce396a1..f5e2ff65eaea8 100644 --- a/packages/react-devtools-shared/src/__tests__/console-test.js +++ b/packages/react-devtools-shared/src/__tests__/console-test.js @@ -44,7 +44,10 @@ describe('console', () => { // Note the Console module only patches once, // so it's important to patch the test console before injection. - patchConsole(); + patchConsole({ + appendComponentStack: true, + breakOnWarn: false, + }); const inject = global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject; global.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject = internals => { @@ -79,7 +82,10 @@ describe('console', () => { it('should only patch the console once', () => { const {error, warn} = fakeConsole; - patchConsole(); + patchConsole({ + appendComponentStack: true, + breakOnWarn: false, + }); expect(fakeConsole.error).toBe(error); expect(fakeConsole.warn).toBe(warn); @@ -330,7 +336,10 @@ describe('console', () => { expect(mockError.mock.calls[0]).toHaveLength(1); expect(mockError.mock.calls[0][0]).toBe('error'); - patchConsole(); + patchConsole({ + appendComponentStack: true, + breakOnWarn: false, + }); act(() => ReactDOM.render(, document.createElement('div'))); expect(mockWarn).toHaveBeenCalledTimes(2); diff --git a/packages/react-devtools-shared/src/backend/agent.js b/packages/react-devtools-shared/src/backend/agent.js index 030fe0b69d4f7..14592952f434f 100644 --- a/packages/react-devtools-shared/src/backend/agent.js +++ b/packages/react-devtools-shared/src/backend/agent.js @@ -161,8 +161,8 @@ export default class Agent extends EventEmitter<{| ); bridge.addListener('shutdown', this.shutdown); bridge.addListener( - 'updateAppendComponentStack', - this.updateAppendComponentStack, + 'updateConsolePatchSettings', + this.updateConsolePatchSettings, ); bridge.addListener('updateComponentFilters', this.updateComponentFilters); bridge.addListener('viewAttributeSource', this.viewAttributeSource); @@ -443,13 +443,19 @@ export default class Agent extends EventEmitter<{| } }; - updateAppendComponentStack = (appendComponentStack: boolean) => { + updateConsolePatchSettings = ({ + appendComponentStack, + breakOnConsoleErrors, + }: {| + appendComponentStack: boolean, + breakOnConsoleErrors: boolean, + |}) => { // If the frontend preference has change, // or in the case of React Native- if the backend is just finding out the preference- // then install or uninstall the console overrides. // It's safe to call these methods multiple times, so we don't need to worry about that. - if (appendComponentStack) { - patchConsole(); + if (appendComponentStack || breakOnConsoleErrors) { + patchConsole({appendComponentStack, breakOnConsoleErrors}); } else { unpatchConsole(); } diff --git a/packages/react-devtools-shared/src/backend/console.js b/packages/react-devtools-shared/src/backend/console.js index 80a69701158ca..00358b057d4ca 100644 --- a/packages/react-devtools-shared/src/backend/console.js +++ b/packages/react-devtools-shared/src/backend/console.js @@ -80,9 +80,25 @@ export function registerRenderer(renderer: ReactRenderer): void { } } +const consoleSettingsRef = { + appendComponentStack: false, + breakOnConsoleErrors: false, +}; + // Patches whitelisted console methods to append component stack for the current fiber. // Call unpatch() to remove the injected behavior. -export function patch(): void { +export function patch({ + appendComponentStack, + breakOnConsoleErrors, +}: { + appendComponentStack: boolean, + breakOnConsoleErrors: boolean, +}): void { + // Settings may change after we've patched the console. + // Using a shared ref allows the patch function to read the latest values. + consoleSettingsRef.appendComponentStack = appendComponentStack; + consoleSettingsRef.breakOnConsoleErrors = breakOnConsoleErrors; + if (unpatchFn !== null) { // Don't patch twice. return; @@ -105,40 +121,56 @@ export function patch(): void { targetConsole[method]); const overrideMethod = (...args) => { - try { - // If we are ever called with a string that already has a component stack, e.g. a React error/warning, - // don't append a second stack. - const lastArg = args.length > 0 ? args[args.length - 1] : null; - const alreadyHasComponentStack = - lastArg !== null && - (PREFIX_REGEX.test(lastArg) || - ROW_COLUMN_NUMBER_REGEX.test(lastArg)); - - if (!alreadyHasComponentStack) { - // If there's a component stack for at least one of the injected renderers, append it. - // We don't handle the edge case of stacks for more than one (e.g. interleaved renderers?) - // eslint-disable-next-line no-for-of-loops/no-for-of-loops - for (const { - currentDispatcherRef, - getCurrentFiber, - workTagMap, - } of injectedRenderers.values()) { - const current: ?Fiber = getCurrentFiber(); - if (current != null) { - const componentStack = getStackByFiberInDevAndProd( - workTagMap, - current, - currentDispatcherRef, - ); - if (componentStack !== '') { - args.push(componentStack); + const latestAppendComponentStack = + consoleSettingsRef.appendComponentStack; + const latestBreakOnConsoleErrors = + consoleSettingsRef.breakOnConsoleErrors; + + if (latestAppendComponentStack) { + try { + // If we are ever called with a string that already has a component stack, e.g. a React error/warning, + // don't append a second stack. + const lastArg = args.length > 0 ? args[args.length - 1] : null; + const alreadyHasComponentStack = + lastArg !== null && + (PREFIX_REGEX.test(lastArg) || + ROW_COLUMN_NUMBER_REGEX.test(lastArg)); + + if (!alreadyHasComponentStack) { + // If there's a component stack for at least one of the injected renderers, append it. + // We don't handle the edge case of stacks for more than one (e.g. interleaved renderers?) + // eslint-disable-next-line no-for-of-loops/no-for-of-loops + for (const { + currentDispatcherRef, + getCurrentFiber, + workTagMap, + } of injectedRenderers.values()) { + const current: ?Fiber = getCurrentFiber(); + if (current != null) { + const componentStack = getStackByFiberInDevAndProd( + workTagMap, + current, + currentDispatcherRef, + ); + if (componentStack !== '') { + args.push(componentStack); + } + break; } - break; } } + } catch (error) { + // Don't let a DevTools or React internal error interfere with logging. } - } catch (error) { - // Don't let a DevTools or React internal error interfere with logging. + } + + if (latestBreakOnConsoleErrors) { + // --- Welcome to debugging with React DevTools --- + // This debugger statement means that you've enabled the "break on warnings" feature. + // Use the browser's Call Stack panel to step out of this override function- + // to where the original warning or error was logged. + // eslint-disable-next-line no-debugger + debugger; } originalMethod(...args); diff --git a/packages/react-devtools-shared/src/backend/renderer.js b/packages/react-devtools-shared/src/backend/renderer.js index d39ec771ca60c..e08ee43c3a754 100644 --- a/packages/react-devtools-shared/src/backend/renderer.js +++ b/packages/react-devtools-shared/src/backend/renderer.js @@ -430,11 +430,18 @@ export function attach( if (process.env.NODE_ENV !== 'test') { registerRendererWithConsole(renderer); - // The renderer interface can't read this preference directly, + // The renderer interface can't read these preferences directly, // because it is stored in localStorage within the context of the extension. // It relies on the extension to pass the preference through via the global. - if (window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ !== false) { - patchConsole(); + const appendComponentStack = + window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ !== false; + const breakOnConsoleErrors = + window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ === true; + if (appendComponentStack || breakOnConsoleErrors) { + patchConsole({ + appendComponentStack, + breakOnConsoleErrors, + }); } } diff --git a/packages/react-devtools-shared/src/bridge.js b/packages/react-devtools-shared/src/bridge.js index c3827f00e2d47..f30d3034db94c 100644 --- a/packages/react-devtools-shared/src/bridge.js +++ b/packages/react-devtools-shared/src/bridge.js @@ -85,6 +85,11 @@ type NativeStyleEditor_SetValueParams = {| value: string, |}; +type UpdateConsolePatchSettingsParams = {| + appendComponentStack: boolean, + breakOnConsoleErrors: boolean, +|}; + type BackendEvents = {| extensionBackendInitialized: [], inspectedElement: [InspectedElementPayload], @@ -133,8 +138,8 @@ type FrontendEvents = {| stopInspectingNative: [boolean], stopProfiling: [], storeAsGlobal: [StoreAsGlobalParams], - updateAppendComponentStack: [boolean], updateComponentFilters: [Array], + updateConsolePatchSettings: [UpdateConsolePatchSettingsParams], viewAttributeSource: [ViewAttributeSourceParams], viewElementSource: [ElementAndRendererID], diff --git a/packages/react-devtools-shared/src/constants.js b/packages/react-devtools-shared/src/constants.js index e6e2a8ad35382..c5223f7cb43d9 100644 --- a/packages/react-devtools-shared/src/constants.js +++ b/packages/react-devtools-shared/src/constants.js @@ -27,6 +27,9 @@ export const SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY = export const SESSION_STORAGE_RELOAD_AND_PROFILE_KEY = 'React::DevTools::reloadAndProfile'; +export const LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS = + 'React::DevTools::breakOnConsoleErrors'; + export const LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY = 'React::DevTools::appendComponentStack'; diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/DebuggingSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/DebuggingSettings.js new file mode 100644 index 0000000000000..bc26ead4fc150 --- /dev/null +++ b/packages/react-devtools-shared/src/devtools/views/Settings/DebuggingSettings.js @@ -0,0 +1,53 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import * as React from 'react'; +import {useContext} from 'react'; +import {SettingsContext} from './SettingsContext'; + +import styles from './SettingsShared.css'; + +export default function DebuggingSettings(_: {||}) { + const { + appendComponentStack, + breakOnConsoleErrors, + setAppendComponentStack, + setBreakOnConsoleErrors, + } = useContext(SettingsContext); + + return ( +
+
+ +
+ +
+ +
+
+ ); +} diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js index 6595c04d71d96..5c5b8a0079643 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/GeneralSettings.js @@ -17,9 +17,7 @@ import styles from './SettingsShared.css'; export default function GeneralSettings(_: {||}) { const { - appendComponentStack, displayDensity, - setAppendComponentStack, setDisplayDensity, setTheme, setTraceUpdatesEnabled, @@ -71,19 +69,6 @@ export default function GeneralSettings(_: {||}) { )} -
- -
-
void, + breakOnConsoleErrors: boolean, + setBreakOnConsoleErrors: (value: boolean) => void, + theme: Theme, setTheme(value: Theme): void, @@ -79,6 +83,13 @@ function SettingsContextController({ appendComponentStack, setAppendComponentStack, ] = useLocalStorage(LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY, true); + const [ + breakOnConsoleErrors, + setBreakOnConsoleErrors, + ] = useLocalStorage( + LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS, + false, + ); const [ traceUpdatesEnabled, setTraceUpdatesEnabled, @@ -133,8 +144,11 @@ function SettingsContextController({ }, [browserTheme, theme, documentElements]); useEffect(() => { - bridge.send('updateAppendComponentStack', appendComponentStack); - }, [bridge, appendComponentStack]); + bridge.send('updateConsolePatchSettings', { + appendComponentStack, + breakOnConsoleErrors, + }); + }, [bridge, appendComponentStack, breakOnConsoleErrors]); useEffect(() => { bridge.send('setTraceUpdatesEnabled', traceUpdatesEnabled); @@ -143,12 +157,14 @@ function SettingsContextController({ const value = useMemo( () => ({ appendComponentStack, + breakOnConsoleErrors, displayDensity, lineHeight: displayDensity === 'compact' ? COMPACT_LINE_HEIGHT : COMFORTABLE_LINE_HEIGHT, setAppendComponentStack, + setBreakOnConsoleErrors, setDisplayDensity, setTheme, setTraceUpdatesEnabled, @@ -157,8 +173,10 @@ function SettingsContextController({ }), [ appendComponentStack, + breakOnConsoleErrors, displayDensity, setAppendComponentStack, + setBreakOnConsoleErrors, setDisplayDensity, setTheme, setTraceUpdatesEnabled, diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsModal.js b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsModal.js index adad478c6842b..29d138f1c95fb 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/SettingsModal.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/SettingsModal.js @@ -20,6 +20,7 @@ import { useSubscription, } from '../hooks'; import ComponentsSettings from './ComponentsSettings'; +import DebuggingSettings from './DebuggingSettings'; import GeneralSettings from './GeneralSettings'; import ProfilerSettings from './ProfilerSettings'; @@ -78,15 +79,18 @@ function SettingsModalImpl(_: {||}) { let view = null; switch (selectedTabID) { + case 'components': + view = ; + break; + case 'debugging': + view = ; + break; case 'general': view = ; break; case 'profiler': view = ; break; - case 'components': - view = ; - break; default: break; } @@ -119,6 +123,11 @@ const tabs = [ icon: 'settings', label: 'General', }, + { + id: 'debugging', + icon: 'bug', + label: 'Debugging', + }, { id: 'components', icon: 'components', diff --git a/packages/react-devtools-shared/src/hook.js b/packages/react-devtools-shared/src/hook.js index f63346bdbe409..4b018e61421b1 100644 --- a/packages/react-devtools-shared/src/hook.js +++ b/packages/react-devtools-shared/src/hook.js @@ -174,6 +174,11 @@ export function installHook(target: any): DevToolsHook | null { // Don't patch in test environments because we don't want to interfere with Jest's own console overrides. if (process.env.NODE_ENV !== 'test') { try { + const appendComponentStack = + window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ !== false; + const breakOnConsoleErrors = + window.__REACT_DEVTOOLS_BREAK_ON_CONSOLE_ERRORS__ === true; + // The installHook() function is injected by being stringified in the browser, // so imports outside of this function do not get included. // @@ -181,9 +186,12 @@ export function installHook(target: any): DevToolsHook | null { // but Webpack wraps imports with an object (e.g. _backend_console__WEBPACK_IMPORTED_MODULE_0__) // and the object itself will be undefined as well for the reasons mentioned above, // so we use try/catch instead. - if (window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ !== false) { + if (appendComponentStack || breakOnConsoleErrors) { registerRendererWithConsole(renderer); - patchConsole(); + patchConsole({ + appendComponentStack, + breakOnConsoleErrors, + }); } } catch (error) {} } diff --git a/packages/react-devtools-shared/src/utils.js b/packages/react-devtools-shared/src/utils.js index ece95ff0cec7b..2bbf7d8b2454d 100644 --- a/packages/react-devtools-shared/src/utils.js +++ b/packages/react-devtools-shared/src/utils.js @@ -31,6 +31,7 @@ import { import {ElementTypeRoot} from 'react-devtools-shared/src/types'; import { LOCAL_STORAGE_FILTER_PREFERENCES_KEY, + LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS, LOCAL_STORAGE_SHOULD_PATCH_CONSOLE_KEY, } from './constants'; import {ComponentFilterElementType, ElementTypeHostComponent} from './types'; @@ -248,6 +249,25 @@ export function setAppendComponentStack(value: boolean): void { ); } +export function getBreakOnConsoleErrors(): boolean { + try { + const raw = localStorageGetItem( + LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS, + ); + if (raw != null) { + return JSON.parse(raw); + } + } catch (error) {} + return true; +} + +export function setBreakOnConsoleErrors(value: boolean): void { + localStorageSetItem( + LOCAL_STORAGE_SHOULD_BREAK_ON_CONSOLE_ERRORS, + JSON.stringify(value), + ); +} + export function separateDisplayNameAndHOCs( displayName: string | null, type: ElementType, diff --git a/packages/react-devtools-shell/webpack.config.js b/packages/react-devtools-shell/webpack.config.js index 095c12cbd4709..c1687f54d47e7 100644 --- a/packages/react-devtools-shell/webpack.config.js +++ b/packages/react-devtools-shell/webpack.config.js @@ -1,5 +1,6 @@ const {resolve} = require('path'); const {DefinePlugin} = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); const { GITHUB_URL, getVersionString, @@ -39,6 +40,16 @@ const config = { scheduler: resolve(builtModulesDir, 'scheduler'), }, }, + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: {drop_debugger: false}, + output: {comments: true}, + }, + }), + ], + }, plugins: [ new DefinePlugin({ __DEV__,