Skip to content

Commit b7c1bd4

Browse files
motiz88facebook-github-bot
authored andcommitted
Remove console logs from Metro when native debugger console is available
Summary: Changelog: [Internal] Remove console logs from Metro when native Fusebox debugger console is available React Native currently sends all `console.log` messages to Metro, which prints them to the terminal. This feature has been in place since 2019 (D15559151) but is pretty limited when compared to what's available in modern browsers. Most of the limitations can't really be fixed within the constraints of Metro's relatively simple terminal infrastructure. With the new React Native debugger (codenamed "Fusebox") we aim to fundamentally elevate the debugging experience by shipping a well-tested version of Chrome DevTools with React Native. Chrome DevTools has a rich, interactive console, as well as a host of other debugging features we want developers to notice and use. To that end, we plan to **strongly nudge users towards the Fusebox console from day 1**. Specifically, if we detect that Fusebox is available and is using an engine which implements CDP `console` support ( = Hermes only for now), we'll no longer send logs to Metro, and will instead display an explanatory message directing users to Fusebox. Reviewed By: huntie Differential Revision: D54829811 fbshipit-source-id: 2b1cdb666094f901ff4e7f42b123271be4ce7d10
1 parent b79e488 commit b7c1bd4

File tree

11 files changed

+66
-2
lines changed

11 files changed

+66
-2
lines changed

packages/react-native/Libraries/Core/setUpDeveloperTools.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ if (__DEV__) {
4242
if (!Platform.isTesting) {
4343
const HMRClient = require('../Utilities/HMRClient');
4444

45-
if (console._isPolyfilled) {
45+
if (global.__FUSEBOX_HAS_FULL_CONSOLE_SUPPORT__) {
46+
HMRClient.unstable_notifyFuseboxConsoleEnabled();
47+
} else if (console._isPolyfilled) {
4648
// We assume full control over the console and send JavaScript logs to Metro.
4749
[
4850
'trace',

packages/react-native/Libraries/Utilities/HMRClient.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ let hmrUnavailableReason: string | null = null;
2626
let currentCompileErrorMessage: string | null = null;
2727
let didConnect: boolean = false;
2828
let pendingLogs: Array<[LogLevel, $ReadOnlyArray<mixed>]> = [];
29+
let pendingFuseboxConsoleNotification = false;
2930

3031
type LogLevel =
3132
| 'trace'
@@ -51,6 +52,7 @@ export type HMRClientNativeInterface = {|
5152
isEnabled: boolean,
5253
scheme?: string,
5354
): void,
55+
unstable_notifyFuseboxConsoleEnabled(): void,
5456
|};
5557

5658
/**
@@ -140,6 +142,29 @@ const HMRClient: HMRClientNativeInterface = {
140142
}
141143
},
142144

145+
unstable_notifyFuseboxConsoleEnabled() {
146+
if (!hmrClient) {
147+
pendingFuseboxConsoleNotification = true;
148+
return;
149+
}
150+
hmrClient.send(
151+
JSON.stringify({
152+
type: 'log',
153+
level: 'info',
154+
data: [
155+
'\n' +
156+
'\x1b[7m' +
157+
' \x1b[1mJavaScript logs have moved!\x1b[22m They will now appear in the debugger console. ' +
158+
'Tip: Type \x1b[1mj\x1b[22m in the terminal to open the debugger (requires Google Chrome ' +
159+
'or Microsoft Edge).' +
160+
'\x1b[27m' +
161+
'\n',
162+
],
163+
}),
164+
);
165+
pendingFuseboxConsoleNotification = false;
166+
},
167+
143168
// Called once by the bridge on startup, even if Fast Refresh is off.
144169
// It creates the HMR client but doesn't actually set up the socket yet.
145170
setup(
@@ -316,6 +341,9 @@ function flushEarlyLogs(client: MetroHMRClient) {
316341
pendingLogs.forEach(([level, data]) => {
317342
HMRClient.log(level, data);
318343
});
344+
if (pendingFuseboxConsoleNotification) {
345+
HMRClient.unstable_notifyFuseboxConsoleEnabled();
346+
}
319347
} finally {
320348
pendingLogs.length = 0;
321349
}

packages/react-native/Libraries/Utilities/HMRClientProdShim.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const HMRClientProdShim: HMRClientNativeInterface = {
2525
disable() {},
2626
registerBundle() {},
2727
log() {},
28+
unstable_notifyFuseboxConsoleEnabled() {},
2829
};
2930

3031
module.exports = HMRClientProdShim;

packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8417,6 +8417,7 @@ export type HMRClientNativeInterface = {|
84178417
isEnabled: boolean,
84188418
scheme?: string
84198419
): void,
8420+
unstable_notifyFuseboxConsoleEnabled(): void,
84208421
|};
84218422
declare const HMRClient: HMRClientNativeInterface;
84228423
declare module.exports: HMRClient;

packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeTargetDelegate.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ class HermesRuntimeTargetDelegate::Impl final : public RuntimeTargetDelegate {
122122
HermesConsoleMessage{message.timestamp, type, std::move(message.args)});
123123
}
124124

125+
bool supportsConsole() const override {
126+
return true;
127+
}
128+
125129
private:
126130
HermesRuntimeTargetDelegate& delegate_;
127131
std::shared_ptr<HermesRuntime> runtime_;
@@ -173,6 +177,10 @@ void HermesRuntimeTargetDelegate::addConsoleMessage(
173177
impl_->addConsoleMessage(runtime, std::move(message));
174178
}
175179

180+
bool HermesRuntimeTargetDelegate::supportsConsole() const {
181+
return impl_->supportsConsole();
182+
}
183+
176184
#ifdef HERMES_ENABLE_DEBUGGER
177185
CDPDebugAPI& HermesRuntimeTargetDelegate::getCDPDebugAPI() {
178186
return impl_->getCDPDebugAPI();

packages/react-native/ReactCommon/hermes/inspector-modern/chrome/HermesRuntimeTargetDelegate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class HermesRuntimeTargetDelegate : public RuntimeTargetDelegate {
4848
void addConsoleMessage(jsi::Runtime& runtime, ConsoleMessage message)
4949
override;
5050

51+
bool supportsConsole() const override;
52+
5153
private:
5254
// We use the private implementation idiom to ensure this class has the same
5355
// layout regardless of whether HERMES_ENABLE_DEBUGGER is defined. The net

packages/react-native/ReactCommon/jsinspector-modern/FallbackRuntimeTargetDelegate.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@ void FallbackRuntimeTargetDelegate::addConsoleMessage(
3232
// TODO: Best-effort printing (without RemoteObjects)
3333
}
3434

35+
bool FallbackRuntimeTargetDelegate::supportsConsole() const {
36+
return false;
37+
}
38+
3539
} // namespace facebook::react::jsinspector_modern

packages/react-native/ReactCommon/jsinspector-modern/FallbackRuntimeTargetDelegate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class FallbackRuntimeTargetDelegate : public RuntimeTargetDelegate {
3434
void addConsoleMessage(jsi::Runtime& runtime, ConsoleMessage message)
3535
override;
3636

37+
bool supportsConsole() const override;
38+
3739
private:
3840
std::string engineDescription_;
3941
};

packages/react-native/ReactCommon/jsinspector-modern/RuntimeTarget.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ class RuntimeTargetDelegate {
6767
virtual void addConsoleMessage(
6868
jsi::Runtime& runtime,
6969
ConsoleMessage message) = 0;
70+
71+
/**
72+
* \returns true if the runtime supports reporting console API calls over CDP.
73+
* \c addConsoleMessage MAY be called even if this method returns false.
74+
*/
75+
virtual bool supportsConsole() const = 0;
7076
};
7177

7278
/**

packages/react-native/ReactCommon/jsinspector-modern/RuntimeTargetConsole.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,10 @@ double getTimestampMs() {
102102
} // namespace
103103

104104
void RuntimeTarget::installConsoleHandler() {
105+
auto delegateSupportsConsole = delegate_.supportsConsole();
105106
jsExecutor_([selfWeak = weak_from_this(),
106-
selfExecutor = executorFromThis()](jsi::Runtime& runtime) {
107+
selfExecutor = executorFromThis(),
108+
delegateSupportsConsole](jsi::Runtime& runtime) {
107109
jsi::Value consolePrototype = jsi::Value::null();
108110
auto originalConsoleVal = runtime.global().getProperty(runtime, "console");
109111
std::shared_ptr<jsi::Object> originalConsole;
@@ -441,6 +443,13 @@ void RuntimeTarget::installConsoleHandler() {
441443
}
442444

443445
runtime.global().setProperty(runtime, "console", console);
446+
if (delegateSupportsConsole) {
447+
// NOTE: If the delegate doesn't report console support, we'll still
448+
// install the console handler for consistency of the runtime environment,
449+
// but not claim that it has full console support.
450+
runtime.global().setProperty(
451+
runtime, "__FUSEBOX_HAS_FULL_CONSOLE_SUPPORT__", true);
452+
}
444453
});
445454
}
446455

packages/react-native/ReactCommon/jsinspector-modern/tests/InspectorMocks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ class MockRuntimeTargetDelegate : public RuntimeTargetDelegate {
141141
addConsoleMessage,
142142
(jsi::Runtime & runtime, ConsoleMessage message),
143143
(override));
144+
MOCK_METHOD(bool, supportsConsole, (), (override, const));
144145
};
145146

146147
class MockRuntimeAgentDelegate : public RuntimeAgentDelegate {

0 commit comments

Comments
 (0)