mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
Badge Environment Name on Thrown Errors from the Server (#29846)
When we replay logs we badge them with e.g. `[Server]`. That way it's easy to identify that the source of the log actually happened on the Server (RSC). However, when we threw an error we didn't have any such thing. The error was rethrown on the client and then handled just like any other client error. This transfers the `environmentName` in DEV to our restored Error "sub-class" (conceptually) along with `digest`. That way you can read `error.environmentName` to print this in your own UI. I also updated our default for `onCaughtError` (and `onError` in Fizz) to use the `printToConsole` helper that the Flight Client uses to log it with the badge format. So by default you get the same experience as console.error for caught errors: <img width="810" alt="Screenshot 2024-06-10 at 9 25 12 PM" src="https://github.com/facebook/react/assets/63648/8490fedc-09f6-4286-9332-fbe6b0faa2d3"> <img width="815" alt="Screenshot 2024-06-10 at 9 39 30 PM" src="https://github.com/facebook/react/assets/63648/bdcfc554-504a-4b1d-82bf-b717e74975ac"> Unfortunately I can't do the same thing for `onUncaughtError` nor `onRecoverableError` because they use `reportError` which doesn't have custom formatting (unless we also prevented default on window.onerror). However maybe that's ok because 1) you should always have an error boundary 2) it's not likely that an RSC error can actually recover because it's not going to be rendered again so shouldn't really happen outside some parent conditionally rendering maybe. The other problem with this approach is that the default is no longer trivial - so reimplementing the default in user space is trickier and ideally we shouldn't expose our default to be called.
This commit is contained in:
parent
7045700a6d
commit
349a99a7a3
|
|
@ -418,13 +418,18 @@ export function createLogAssertion(
|
||||||
let argIndex = 0;
|
let argIndex = 0;
|
||||||
// console.* could have been called with a non-string e.g. `console.error(new Error())`
|
// console.* could have been called with a non-string e.g. `console.error(new Error())`
|
||||||
// eslint-disable-next-line react-internal/safe-string-coercion
|
// eslint-disable-next-line react-internal/safe-string-coercion
|
||||||
String(format).replace(/%s/g, () => argIndex++);
|
String(format).replace(/%s|%c/g, () => argIndex++);
|
||||||
if (argIndex !== args.length) {
|
if (argIndex !== args.length) {
|
||||||
logsMismatchingFormat.push({
|
if (format.includes('%c%s')) {
|
||||||
format,
|
// We intentionally use mismatching formatting when printing badging because we don't know
|
||||||
args,
|
// the best default to use for different types because the default varies by platform.
|
||||||
expectedArgCount: argIndex,
|
} else {
|
||||||
});
|
logsMismatchingFormat.push({
|
||||||
|
format,
|
||||||
|
args,
|
||||||
|
expectedArgCount: argIndex,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for extra component stacks
|
// Check for extra component stacks
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@
|
||||||
module.exports = function shouldIgnoreConsoleError(format, args) {
|
module.exports = function shouldIgnoreConsoleError(format, args) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
if (typeof format === 'string') {
|
if (typeof format === 'string') {
|
||||||
|
if (format.startsWith('%c%s')) {
|
||||||
|
// Looks like a badged error message
|
||||||
|
args.splice(0, 3);
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
args[0] != null &&
|
args[0] != null &&
|
||||||
((typeof args[0] === 'object' &&
|
((typeof args[0] === 'object' &&
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {warn, error} from 'shared/consoleWithStackDev';
|
||||||
|
|
||||||
const badgeFormat = '%c%s%c ';
|
const badgeFormat = '%c%s%c ';
|
||||||
// Same badge styling as DevTools.
|
// Same badge styling as DevTools.
|
||||||
const badgeStyle =
|
const badgeStyle =
|
||||||
|
|
@ -63,7 +65,12 @@ export function printToConsole(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-internal/no-production-logging
|
if (methodName === 'error') {
|
||||||
console[methodName].apply(console, newArgs);
|
error.apply(console, newArgs);
|
||||||
return;
|
} else if (methodName === 'warn') {
|
||||||
|
warn.apply(console, newArgs);
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line react-internal/no-production-logging
|
||||||
|
console[methodName].apply(console, newArgs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {warn, error} from 'shared/consoleWithStackDev';
|
||||||
|
|
||||||
const badgeFormat = '[%s] ';
|
const badgeFormat = '[%s] ';
|
||||||
const pad = ' ';
|
const pad = ' ';
|
||||||
|
|
||||||
|
|
@ -44,7 +46,12 @@ export function printToConsole(
|
||||||
newArgs.splice(offset, 0, badgeFormat, pad + badgeName + pad);
|
newArgs.splice(offset, 0, badgeFormat, pad + badgeName + pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-internal/no-production-logging
|
if (methodName === 'error') {
|
||||||
console[methodName].apply(console, newArgs);
|
error.apply(console, newArgs);
|
||||||
return;
|
} else if (methodName === 'warn') {
|
||||||
|
warn.apply(console, newArgs);
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line react-internal/no-production-logging
|
||||||
|
console[methodName].apply(console, newArgs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,8 @@
|
||||||
* @flow
|
* @flow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {warn, error} from 'shared/consoleWithStackDev';
|
||||||
|
|
||||||
// This flips color using ANSI, then sets a color styling, then resets.
|
// This flips color using ANSI, then sets a color styling, then resets.
|
||||||
const badgeFormat = '\x1b[0m\x1b[7m%c%s\x1b[0m%c ';
|
const badgeFormat = '\x1b[0m\x1b[7m%c%s\x1b[0m%c ';
|
||||||
// Same badge styling as DevTools.
|
// Same badge styling as DevTools.
|
||||||
|
|
@ -64,7 +66,12 @@ export function printToConsole(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-internal/no-production-logging
|
if (methodName === 'error') {
|
||||||
console[methodName].apply(console, newArgs);
|
error.apply(console, newArgs);
|
||||||
return;
|
} else if (methodName === 'warn') {
|
||||||
|
warn.apply(console, newArgs);
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line react-internal/no-production-logging
|
||||||
|
console[methodName].apply(console, newArgs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1730,6 +1730,7 @@ function resolveErrorDev(
|
||||||
digest: string,
|
digest: string,
|
||||||
message: string,
|
message: string,
|
||||||
stack: string,
|
stack: string,
|
||||||
|
env: string,
|
||||||
): void {
|
): void {
|
||||||
if (!__DEV__) {
|
if (!__DEV__) {
|
||||||
// These errors should never make it into a build so we don't need to encode them in codes.json
|
// These errors should never make it into a build so we don't need to encode them in codes.json
|
||||||
|
|
@ -1769,6 +1770,7 @@ function resolveErrorDev(
|
||||||
}
|
}
|
||||||
|
|
||||||
(error: any).digest = digest;
|
(error: any).digest = digest;
|
||||||
|
(error: any).environmentName = env;
|
||||||
const errorWithDigest: ErrorWithDigest = (error: any);
|
const errorWithDigest: ErrorWithDigest = (error: any);
|
||||||
const chunks = response._chunks;
|
const chunks = response._chunks;
|
||||||
const chunk = chunks.get(id);
|
const chunk = chunks.get(id);
|
||||||
|
|
@ -2056,6 +2058,8 @@ function resolveConsoleEntry(
|
||||||
task.run(callStack);
|
task.run(callStack);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// TODO: Set the current owner so that consoleWithStackDev adds the component
|
||||||
|
// stack during the replay - if needed.
|
||||||
}
|
}
|
||||||
const rootTask = response._debugRootTask;
|
const rootTask = response._debugRootTask;
|
||||||
if (rootTask != null) {
|
if (rootTask != null) {
|
||||||
|
|
@ -2198,6 +2202,7 @@ function processFullRow(
|
||||||
errorInfo.digest,
|
errorInfo.digest,
|
||||||
errorInfo.message,
|
errorInfo.message,
|
||||||
errorInfo.stack,
|
errorInfo.stack,
|
||||||
|
errorInfo.env,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
resolveErrorProd(response, id, errorInfo.digest);
|
resolveErrorProd(response, id, errorInfo.digest);
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ describe('ReactFlight', () => {
|
||||||
this.props.expectedMessage,
|
this.props.expectedMessage,
|
||||||
);
|
);
|
||||||
expect(this.state.error.digest).toBe('a dev digest');
|
expect(this.state.error.digest).toBe('a dev digest');
|
||||||
|
expect(this.state.error.environmentName).toBe('Server');
|
||||||
} else {
|
} else {
|
||||||
expect(this.state.error.message).toBe(
|
expect(this.state.error.message).toBe(
|
||||||
'An error occurred in the Server Components render. The specific message is omitted in production' +
|
'An error occurred in the Server Components render. The specific message is omitted in production' +
|
||||||
|
|
@ -143,6 +144,7 @@ describe('ReactFlight', () => {
|
||||||
expectedDigest = '[]';
|
expectedDigest = '[]';
|
||||||
}
|
}
|
||||||
expect(this.state.error.digest).toContain(expectedDigest);
|
expect(this.state.error.digest).toContain(expectedDigest);
|
||||||
|
expect(this.state.error.environmentName).toBe(undefined);
|
||||||
expect(this.state.error.stack).toBe(
|
expect(this.state.error.stack).toBe(
|
||||||
'Error: ' + this.state.error.message,
|
'Error: ' + this.state.error.message,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigBrowser';
|
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||||
export * from 'react-server-dom-esm/src/ReactFlightClientConfigBundlerESM';
|
export * from 'react-server-dom-esm/src/ReactFlightClientConfigBundlerESM';
|
||||||
export * from 'react-server-dom-esm/src/ReactFlightClientConfigTargetESMBrowser';
|
export * from 'react-server-dom-esm/src/ReactFlightClientConfigTargetESMBrowser';
|
||||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigBrowser';
|
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopack';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopack';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopackBrowser';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopackBrowser';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigTargetTurbopackBrowser';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigTargetTurbopackBrowser';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigBrowser';
|
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpack';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpack';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpackBrowser';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpackBrowser';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigTargetWebpackBrowser';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigTargetWebpackBrowser';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigPlain';
|
export * from 'react-client/src/ReactClientConsoleConfigPlain';
|
||||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||||
|
|
||||||
export type Response = any;
|
export type Response = any;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigServer';
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopack';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopack';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopackServer';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopackServer';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigTargetTurbopackServer';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigTargetTurbopackServer';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigServer';
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpack';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpack';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpackServer';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpackServer';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigTargetWebpackServer';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigTargetWebpackServer';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigBrowser';
|
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||||
|
|
||||||
export type Response = any;
|
export type Response = any;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigServer';
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
export * from 'react-server-dom-esm/src/ReactFlightClientConfigBundlerESM';
|
export * from 'react-server-dom-esm/src/ReactFlightClientConfigBundlerESM';
|
||||||
export * from 'react-server-dom-esm/src/ReactFlightClientConfigTargetESMServer';
|
export * from 'react-server-dom-esm/src/ReactFlightClientConfigTargetESMServer';
|
||||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigServer';
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopack';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopack';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopackServer';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerTurbopackServer';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigTargetTurbopackServer';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigTargetTurbopackServer';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigServer';
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerNode';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigBundlerNode';
|
||||||
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigTargetTurbopackServer';
|
export * from 'react-server-dom-turbopack/src/ReactFlightClientConfigTargetTurbopackServer';
|
||||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigServer';
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpack';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpack';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpackServer';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerWebpackServer';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigTargetWebpackServer';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigTargetWebpackServer';
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||||
export * from 'react-client/src/ReactFlightClientConsoleConfigServer';
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerNode';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigBundlerNode';
|
||||||
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigTargetWebpackServer';
|
export * from 'react-server-dom-webpack/src/ReactFlightClientConfigTargetWebpackServer';
|
||||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,11 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
|
||||||
NotPendingTransition: (null: TransitionStatus),
|
NotPendingTransition: (null: TransitionStatus),
|
||||||
|
|
||||||
resetFormInstance(form: Instance) {},
|
resetFormInstance(form: Instance) {},
|
||||||
|
|
||||||
|
printToConsole(methodName, args, badgeName) {
|
||||||
|
// eslint-disable-next-line react-internal/no-production-logging
|
||||||
|
console[methodName].apply(console, args);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const hostConfig = useMutation
|
const hostConfig = useMutation
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||||
|
|
||||||
import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
|
import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
|
||||||
|
|
||||||
|
import {printToConsole} from './ReactFiberConfig';
|
||||||
|
|
||||||
// Side-channel since I'm not sure we want to make this part of the public API
|
// Side-channel since I'm not sure we want to make this part of the public API
|
||||||
let componentName: null | string = null;
|
let componentName: null | string = null;
|
||||||
let errorBoundaryName: null | string = null;
|
let errorBoundaryName: null | string = null;
|
||||||
|
|
@ -94,13 +96,33 @@ export function defaultOnCaughtError(
|
||||||
}.`;
|
}.`;
|
||||||
|
|
||||||
if (enableOwnerStacks) {
|
if (enableOwnerStacks) {
|
||||||
console.error(
|
if (
|
||||||
'%o\n\n%s\n\n%s\n',
|
typeof error === 'object' &&
|
||||||
error,
|
error !== null &&
|
||||||
componentNameMessage,
|
typeof error.environmentName === 'string'
|
||||||
recreateMessage,
|
) {
|
||||||
// We let our consoleWithStackDev wrapper add the component stack to the end.
|
// This was a Server error. We print the environment name in a badge just like we do with
|
||||||
);
|
// replays of console logs to indicate that the source of this throw as actually the Server.
|
||||||
|
printToConsole(
|
||||||
|
'error',
|
||||||
|
[
|
||||||
|
'%o\n\n%s\n\n%s\n',
|
||||||
|
error,
|
||||||
|
componentNameMessage,
|
||||||
|
recreateMessage,
|
||||||
|
// We let our consoleWithStackDev wrapper add the component stack to the end.
|
||||||
|
],
|
||||||
|
error.environmentName,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
'%o\n\n%s\n\n%s\n',
|
||||||
|
error,
|
||||||
|
componentNameMessage,
|
||||||
|
recreateMessage,
|
||||||
|
// We let our consoleWithStackDev wrapper add the component stack to the end.
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// The current Fiber is disconnected at this point which means that console printing
|
// The current Fiber is disconnected at this point which means that console printing
|
||||||
// cannot add a component stack since it terminates at the deletion node. This is not
|
// cannot add a component stack since it terminates at the deletion node. This is not
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-art/src/ReactFiberConfigART';
|
export * from 'react-art/src/ReactFiberConfigART';
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ export const suspendInstance = $$$config.suspendInstance;
|
||||||
export const waitForCommitToBeReady = $$$config.waitForCommitToBeReady;
|
export const waitForCommitToBeReady = $$$config.waitForCommitToBeReady;
|
||||||
export const NotPendingTransition = $$$config.NotPendingTransition;
|
export const NotPendingTransition = $$$config.NotPendingTransition;
|
||||||
export const resetFormInstance = $$$config.resetFormInstance;
|
export const resetFormInstance = $$$config.resetFormInstance;
|
||||||
|
export const printToConsole = $$$config.printToConsole;
|
||||||
|
|
||||||
// -------------------
|
// -------------------
|
||||||
// Microtasks
|
// Microtasks
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM';
|
export * from 'react-dom-bindings/src/client/ReactFiberConfigDOM';
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-native-renderer/src/ReactFiberConfigFabric';
|
export * from 'react-native-renderer/src/ReactFiberConfigFabric';
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigPlain';
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-native-renderer/src/ReactFiberConfigNative';
|
export * from 'react-native-renderer/src/ReactFiberConfigNative';
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigPlain';
|
||||||
|
|
|
||||||
|
|
@ -8,3 +8,4 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from 'react-test-renderer/src/ReactFiberConfigTestHost';
|
export * from 'react-test-renderer/src/ReactFiberConfigTestHost';
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigPlain';
|
||||||
|
|
|
||||||
13
packages/react-server/src/ReactFizzServer.js
vendored
13
packages/react-server/src/ReactFizzServer.js
vendored
|
|
@ -78,6 +78,7 @@ import {
|
||||||
resetResumableState,
|
resetResumableState,
|
||||||
completeResumableState,
|
completeResumableState,
|
||||||
emitEarlyPreloads,
|
emitEarlyPreloads,
|
||||||
|
printToConsole,
|
||||||
} from './ReactFizzConfig';
|
} from './ReactFizzConfig';
|
||||||
import {
|
import {
|
||||||
constructClassInstance,
|
constructClassInstance,
|
||||||
|
|
@ -363,7 +364,17 @@ export opaque type Request = {
|
||||||
const DEFAULT_PROGRESSIVE_CHUNK_SIZE = 12800;
|
const DEFAULT_PROGRESSIVE_CHUNK_SIZE = 12800;
|
||||||
|
|
||||||
function defaultErrorHandler(error: mixed) {
|
function defaultErrorHandler(error: mixed) {
|
||||||
console['error'](error); // Don't transform to our wrapper
|
if (
|
||||||
|
typeof error === 'object' &&
|
||||||
|
error !== null &&
|
||||||
|
typeof error.environmentName === 'string'
|
||||||
|
) {
|
||||||
|
// This was a Server error. We print the environment name in a badge just like we do with
|
||||||
|
// replays of console logs to indicate that the source of this throw as actually the Server.
|
||||||
|
printToConsole('error', [error], error.environmentName);
|
||||||
|
} else {
|
||||||
|
console['error'](error); // Don't transform to our wrapper
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2774,11 +2774,18 @@ function emitErrorChunk(
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
let message;
|
let message;
|
||||||
let stack = '';
|
let stack = '';
|
||||||
|
let env = request.environmentName();
|
||||||
try {
|
try {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
// eslint-disable-next-line react-internal/safe-string-coercion
|
// eslint-disable-next-line react-internal/safe-string-coercion
|
||||||
message = String(error.message);
|
message = String(error.message);
|
||||||
stack = getStack(error);
|
stack = getStack(error);
|
||||||
|
const errorEnv = (error: any).environmentName;
|
||||||
|
if (typeof errorEnv === 'string') {
|
||||||
|
// This probably came from another FlightClient as a pass through.
|
||||||
|
// Keep the environment name.
|
||||||
|
env = errorEnv;
|
||||||
|
}
|
||||||
} else if (typeof error === 'object' && error !== null) {
|
} else if (typeof error === 'object' && error !== null) {
|
||||||
message = describeObjectForErrorMessage(error);
|
message = describeObjectForErrorMessage(error);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2788,7 +2795,7 @@ function emitErrorChunk(
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
message = 'An error occurred but serializing the error message failed.';
|
message = 'An error occurred but serializing the error message failed.';
|
||||||
}
|
}
|
||||||
errorInfo = {digest, message, stack};
|
errorInfo = {digest, message, stack, env};
|
||||||
} else {
|
} else {
|
||||||
errorInfo = {digest};
|
errorInfo = {digest};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ export const isPrimaryRenderer = false;
|
||||||
export const supportsRequestStorage = false;
|
export const supportsRequestStorage = false;
|
||||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||||
|
|
||||||
|
export const printToConsole = $$$config.printToConsole;
|
||||||
|
|
||||||
export const resetResumableState = $$$config.resetResumableState;
|
export const resetResumableState = $$$config.resetResumableState;
|
||||||
export const completeResumableState = $$$config.completeResumableState;
|
export const completeResumableState = $$$config.completeResumableState;
|
||||||
export const getChildFormatContext = $$$config.getChildFormatContext;
|
export const getChildFormatContext = $$$config.getChildFormatContext;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ import type {Request} from 'react-server/src/ReactFizzServer';
|
||||||
|
|
||||||
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
|
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
|
||||||
|
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
|
|
||||||
// For now, we get this from the global scope, but this will likely move to a module.
|
// For now, we get this from the global scope, but this will likely move to a module.
|
||||||
export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
|
export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
|
||||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,7 @@ import type {Request} from 'react-server/src/ReactFizzServer';
|
||||||
|
|
||||||
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOMLegacy';
|
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOMLegacy';
|
||||||
|
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigPlain';
|
||||||
|
|
||||||
export const supportsRequestStorage = false;
|
export const supportsRequestStorage = false;
|
||||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import type {Request} from 'react-server/src/ReactFizzServer';
|
||||||
|
|
||||||
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
|
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
|
||||||
|
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||||
|
|
||||||
export const supportsRequestStorage = true;
|
export const supportsRequestStorage = true;
|
||||||
export const requestStorage: AsyncLocalStorage<Request | void> =
|
export const requestStorage: AsyncLocalStorage<Request | void> =
|
||||||
new AsyncLocalStorage();
|
new AsyncLocalStorage();
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,7 @@ import type {Request} from 'react-server/src/ReactFizzServer';
|
||||||
|
|
||||||
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
|
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
|
||||||
|
|
||||||
|
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||||
|
|
||||||
export const supportsRequestStorage = false;
|
export const supportsRequestStorage = false;
|
||||||
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user