diff --git a/packages/react-devtools-core/README.md b/packages/react-devtools-core/README.md index f3487fefba75e..669ed2bf1627b 100644 --- a/packages/react-devtools-core/README.md +++ b/packages/react-devtools-core/README.md @@ -32,7 +32,7 @@ if (process.env.NODE_ENV !== 'production') { #### `Settings` | Spec | Default value | |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| -|
{
appendComponentStack: boolean,
breakOnConsoleErrors: boolean,
showInlineWarningsAndErrors: boolean,
hideConsoleLogsInStrictMode: boolean
}
|
{
appendComponentStack: true,
breakOnConsoleErrors: false,
showInlineWarningsAndErrors: true,
hideConsoleLogsInStrictMode: false
}
| +|
{
appendComponentStack: boolean,
breakOnConsoleErrors: boolean,
showInlineWarningsAndErrors: boolean,
hideConsoleLogsInStrictMode: boolean,
disableSecondConsoleLogDimmingInStrictMode: boolean
}
|
{
appendComponentStack: true,
breakOnConsoleErrors: false,
showInlineWarningsAndErrors: true,
hideConsoleLogsInStrictMode: false,
disableSecondConsoleLogDimmingInStrictMode: false
}
| ### `connectToDevTools` options | Prop | Default | Description | @@ -53,7 +53,7 @@ if (process.env.NODE_ENV !== 'production') { | `onSubscribe` | Function, which receives listener (function, with a single argument) as an argument. Called when backend subscribes to messages from the other end (frontend). | | `onUnsubscribe` | Function, which receives listener (function) as an argument. Called when backend unsubscribes to messages from the other end (frontend). | | `onMessage` | Function, which receives 2 arguments: event (string) and payload (any). Called when backend emits a message, which should be sent to the frontend. | -| `onSettingsUpdated` | A callback that will be called when the user updates the settings in the UI. You can use it for persisting user settings. | +| `onSettingsUpdated` | A callback that will be called when the user updates the settings in the UI. You can use it for persisting user settings. | Unlike `connectToDevTools`, `connectWithCustomMessagingProtocol` returns a callback, which can be used for unsubscribing the backend from the global DevTools hook. diff --git a/packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js b/packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js index e26108edac7e6..da809f65cac97 100644 --- a/packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js +++ b/packages/react-devtools-extensions/src/contentScripts/hookSettingsInjector.js @@ -24,6 +24,11 @@ async function messageListener(event: MessageEvent) { if (typeof settings.hideConsoleLogsInStrictMode !== 'boolean') { settings.hideConsoleLogsInStrictMode = false; } + if ( + typeof settings.disableSecondConsoleLogDimmingInStrictMode !== 'boolean' + ) { + settings.disableSecondConsoleLogDimmingInStrictMode = false; + } window.postMessage({ source: 'react-devtools-hook-settings-injector', diff --git a/packages/react-devtools-inline/src/backend.js b/packages/react-devtools-inline/src/backend.js index 354970446db9b..849e103f21857 100644 --- a/packages/react-devtools-inline/src/backend.js +++ b/packages/react-devtools-inline/src/backend.js @@ -27,6 +27,7 @@ function startActivation(contentWindow: any, bridge: BackendBridge) { componentFilters, showInlineWarningsAndErrors, hideConsoleLogsInStrictMode, + disableSecondConsoleLogDimmingInStrictMode, } = data; contentWindow.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = @@ -38,6 +39,8 @@ function startActivation(contentWindow: any, bridge: BackendBridge) { showInlineWarningsAndErrors; contentWindow.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = hideConsoleLogsInStrictMode; + contentWindow.__REACT_DEVTOOLS_DISABLE_SECOND_CONSOLE_LOG_DIMMING_IN_STRICT_MODE__ = + disableSecondConsoleLogDimmingInStrictMode; // TRICKY // The backend entry point may be required in the context of an iframe or the parent window. @@ -53,6 +56,8 @@ function startActivation(contentWindow: any, bridge: BackendBridge) { showInlineWarningsAndErrors; window.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = hideConsoleLogsInStrictMode; + window.__REACT_DEVTOOLS_DISABLE_SECOND_CONSOLE_LOG_DIMMING_IN_STRICT_MODE__ = + disableSecondConsoleLogDimmingInStrictMode; } finishActivation(contentWindow, bridge); diff --git a/packages/react-devtools-shared/src/__tests__/console-test.js b/packages/react-devtools-shared/src/__tests__/console-test.js index 00d6d9712679a..c37285e855a50 100644 --- a/packages/react-devtools-shared/src/__tests__/console-test.js +++ b/packages/react-devtools-shared/src/__tests__/console-test.js @@ -733,4 +733,85 @@ describe('console', () => { : 'in Child (at **)\n in Intermediate (at **)\n in Parent (at **)', ]); }); + + it('should not dim console logs if disableSecondConsoleLogDimmingInStrictMode is enabled', () => { + global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false; + global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = + false; + global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.disableSecondConsoleLogDimmingInStrictMode = + true; + + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + + function App() { + console.log('log'); + console.warn('warn'); + console.error('error'); + return
; + } + + act(() => + root.render( + + + , + ), + ); + + // Both logs should be called (double logging) + expect(global.consoleLogMock).toHaveBeenCalledTimes(2); + expect(global.consoleWarnMock).toHaveBeenCalledTimes(2); + expect(global.consoleErrorMock).toHaveBeenCalledTimes(2); + + // The second log should NOT have dimming (no ANSI codes) + expect(global.consoleLogMock.mock.calls[1]).toEqual(['log']); + expect(global.consoleWarnMock.mock.calls[1]).toEqual(['warn']); + expect(global.consoleErrorMock.mock.calls[1]).toEqual(['error']); + }); + + it('should dim console logs if disableSecondConsoleLogDimmingInStrictMode is disabled', () => { + global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.appendComponentStack = false; + global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.hideConsoleLogsInStrictMode = + false; + global.__REACT_DEVTOOLS_GLOBAL_HOOK__.settings.disableSecondConsoleLogDimmingInStrictMode = + false; + + const container = document.createElement('div'); + const root = ReactDOMClient.createRoot(container); + + function App() { + console.log('log'); + console.warn('warn'); + console.error('error'); + return
; + } + + act(() => + root.render( + + + , + ), + ); + + // Both logs should be called (double logging) + expect(global.consoleLogMock).toHaveBeenCalledTimes(2); + expect(global.consoleWarnMock).toHaveBeenCalledTimes(2); + expect(global.consoleErrorMock).toHaveBeenCalledTimes(2); + + // The second log should have dimming (ANSI codes present) + expect(global.consoleLogMock.mock.calls[1]).toEqual([ + '\x1b[2;38;2;124;124;124m%s\x1b[0m', + 'log', + ]); + expect(global.consoleWarnMock.mock.calls[1]).toEqual([ + '\x1b[2;38;2;124;124;124m%s\x1b[0m', + 'warn', + ]); + expect(global.consoleErrorMock.mock.calls[1]).toEqual([ + '\x1b[2;38;2;124;124;124m%s\x1b[0m', + 'error', + ]); + }); }); diff --git a/packages/react-devtools-shared/src/__tests__/setupTests.js b/packages/react-devtools-shared/src/__tests__/setupTests.js index aad3fbc00eaae..6efcefe2f7482 100644 --- a/packages/react-devtools-shared/src/__tests__/setupTests.js +++ b/packages/react-devtools-shared/src/__tests__/setupTests.js @@ -248,6 +248,7 @@ beforeEach(() => { breakOnConsoleErrors: false, showInlineWarningsAndErrors: true, hideConsoleLogsInStrictMode: false, + disableSecondConsoleLogDimmingInStrictMode: false, }); const bridgeListeners = []; diff --git a/packages/react-devtools-shared/src/backend/types.js b/packages/react-devtools-shared/src/backend/types.js index 67d6a5f834bc8..00c66355b2dc2 100644 --- a/packages/react-devtools-shared/src/backend/types.js +++ b/packages/react-devtools-shared/src/backend/types.js @@ -597,4 +597,5 @@ export type DevToolsHookSettings = { breakOnConsoleErrors: boolean, showInlineWarningsAndErrors: boolean, hideConsoleLogsInStrictMode: boolean, + disableSecondConsoleLogDimmingInStrictMode: boolean, }; diff --git a/packages/react-devtools-shared/src/devtools/views/Settings/DebuggingSettings.js b/packages/react-devtools-shared/src/devtools/views/Settings/DebuggingSettings.js index 5dfce3a6dc267..34b08c5bfcd55 100644 --- a/packages/react-devtools-shared/src/devtools/views/Settings/DebuggingSettings.js +++ b/packages/react-devtools-shared/src/devtools/views/Settings/DebuggingSettings.js @@ -36,6 +36,10 @@ export default function DebuggingSettings({ useState(usedHookSettings.hideConsoleLogsInStrictMode); const [showInlineWarningsAndErrors, setShowInlineWarningsAndErrors] = useState(usedHookSettings.showInlineWarningsAndErrors); + const [ + disableSecondConsoleLogDimmingInStrictMode, + setDisableSecondConsoleLogDimmingInStrictMode, + ] = useState(usedHookSettings.disableSecondConsoleLogDimmingInStrictMode); useEffect(() => { store.setShouldShowWarningsAndErrors(showInlineWarningsAndErrors); @@ -47,6 +51,7 @@ export default function DebuggingSettings({ breakOnConsoleErrors, showInlineWarningsAndErrors, hideConsoleLogsInStrictMode, + disableSecondConsoleLogDimmingInStrictMode, }); }, [ store, @@ -54,6 +59,7 @@ export default function DebuggingSettings({ breakOnConsoleErrors, showInlineWarningsAndErrors, hideConsoleLogsInStrictMode, + disableSecondConsoleLogDimmingInStrictMode, ]); return ( @@ -105,12 +111,49 @@ export default function DebuggingSettings({ { + setHideConsoleLogsInStrictMode(currentTarget.checked); + if (currentTarget.checked) { + setDisableSecondConsoleLogDimmingInStrictMode(false); + } + }} + className={styles.SettingRowCheckbox} + /> + Hide logs during additional invocations in  + + Strict Mode + + +
+ +
+