Skip to content

Commit

Permalink
Update (base update)
Browse files Browse the repository at this point in the history
[ghstack-poisoned]
  • Loading branch information
mofeiZ committed Oct 1, 2024
2 parents 250a41d + d8c90fa commit a9b7a83
Show file tree
Hide file tree
Showing 17 changed files with 378 additions and 841 deletions.
95 changes: 52 additions & 43 deletions packages/react-client/src/ReactFlightClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,21 @@ function parseModelString(
createFormData,
);
}
case 'Z': {
// Error
if (__DEV__) {
const ref = value.slice(2);
return getOutlinedModel(
response,
ref,
parentObject,
key,
resolveErrorDev,
);
} else {
return resolveErrorProd(response);
}
}
case 'i': {
// Iterator
const ref = value.slice(2);
Expand Down Expand Up @@ -1881,11 +1896,7 @@ function formatV8Stack(
}

type ErrorWithDigest = Error & {digest?: string};
function resolveErrorProd(
response: Response,
id: number,
digest: string,
): void {
function resolveErrorProd(response: Response): Error {
if (__DEV__) {
// These errors should never make it into a build so we don't need to encode them in codes.json
// eslint-disable-next-line react-internal/prod-error-codes
Expand All @@ -1899,25 +1910,17 @@ function resolveErrorProd(
' may provide additional details about the nature of the error.',
);
error.stack = 'Error: ' + error.message;
(error: any).digest = digest;
const errorWithDigest: ErrorWithDigest = (error: any);
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createErrorChunk(response, errorWithDigest));
} else {
triggerErrorOnChunk(chunk, errorWithDigest);
}
return error;
}

function resolveErrorDev(
response: Response,
id: number,
digest: string,
message: string,
stack: ReactStackTrace,
env: string,
): void {
errorInfo: {message: string, stack: ReactStackTrace, env: string, ...},
): Error {
const message: string = errorInfo.message;
const stack: ReactStackTrace = errorInfo.stack;
const env: string = errorInfo.env;

if (!__DEV__) {
// These errors should never make it into a build so we don't need to encode them in codes.json
// eslint-disable-next-line react-internal/prod-error-codes
Expand Down Expand Up @@ -1957,16 +1960,8 @@ function resolveErrorDev(
}
}

(error: any).digest = digest;
(error: any).environmentName = env;
const errorWithDigest: ErrorWithDigest = (error: any);
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createErrorChunk(response, errorWithDigest));
} else {
triggerErrorOnChunk(chunk, errorWithDigest);
}
return error;
}

function resolvePostponeProd(response: Response, id: number): void {
Expand Down Expand Up @@ -2622,17 +2617,20 @@ function processFullStringRow(
}
case 69 /* "E" */: {
const errorInfo = JSON.parse(row);
let error;
if (__DEV__) {
resolveErrorDev(
response,
id,
errorInfo.digest,
errorInfo.message,
errorInfo.stack,
errorInfo.env,
);
error = resolveErrorDev(response, errorInfo);
} else {
error = resolveErrorProd(response);
}
(error: any).digest = errorInfo.digest;
const errorWithDigest: ErrorWithDigest = (error: any);
const chunks = response._chunks;
const chunk = chunks.get(id);
if (!chunk) {
chunks.set(id, createErrorChunk(response, errorWithDigest));
} else {
resolveErrorProd(response, id, errorInfo.digest);
triggerErrorOnChunk(chunk, errorWithDigest);
}
return;
}
Expand All @@ -2642,11 +2640,22 @@ function processFullStringRow(
}
case 68 /* "D" */: {
if (__DEV__) {
const debugInfo: ReactComponentInfo | ReactAsyncInfo = parseModel(
response,
row,
);
resolveDebugInfo(response, id, debugInfo);
const chunk: ResolvedModelChunk<ReactComponentInfo | ReactAsyncInfo> =
createResolvedModelChunk(response, row);
initializeModelChunk(chunk);
const initializedChunk: SomeChunk<ReactComponentInfo | ReactAsyncInfo> =
chunk;
if (initializedChunk.status === INITIALIZED) {
resolveDebugInfo(response, id, initializedChunk.value);
} else {
// TODO: This is not going to resolve in the right order if there's more than one.
chunk.then(
v => resolveDebugInfo(response, id, v),
e => {
// Ignore debug info errors for now. Unnecessary noise.
},
);
}
return;
}
// Fallthrough to share the error with Console entries.
Expand Down
73 changes: 73 additions & 0 deletions packages/react-client/src/__tests__/ReactFlight-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,10 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {
firstName: 'Seb',
lastName: 'Smith',
},
},
]
: undefined,
Expand Down Expand Up @@ -347,6 +351,10 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {
firstName: 'Seb',
lastName: 'Smith',
},
},
]
: undefined,
Expand Down Expand Up @@ -653,6 +661,46 @@ describe('ReactFlight', () => {
`);
});

it('can transport Error objects as values', async () => {
function ComponentClient({prop}) {
return `
is error: ${prop instanceof Error}
message: ${prop.message}
stack: ${normalizeCodeLocInfo(prop.stack).split('\n').slice(0, 2).join('\n')}
environmentName: ${prop.environmentName}
`;
}
const Component = clientReference(ComponentClient);

function ServerComponent() {
const error = new Error('hello');
return <Component prop={error} />;
}

const transport = ReactNoopFlightServer.render(<ServerComponent />);

await act(async () => {
ReactNoop.render(await ReactNoopFlightClient.read(transport));
});

if (__DEV__) {
expect(ReactNoop).toMatchRenderedOutput(`
is error: true
message: hello
stack: Error: hello
in ServerComponent (at **)
environmentName: Server
`);
} else {
expect(ReactNoop).toMatchRenderedOutput(`
is error: true
message: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.
stack: Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.
environmentName: undefined
`);
}
});

it('can transport cyclic objects', async () => {
function ComponentClient({prop}) {
expect(prop.obj.obj.obj).toBe(prop.obj.obj);
Expand Down Expand Up @@ -2625,6 +2673,9 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {
transport: expect.arrayContaining([]),
},
},
]
: undefined,
Expand All @@ -2643,6 +2694,7 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {},
},
]
: undefined,
Expand All @@ -2658,6 +2710,7 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in myLazy (at **)\n in lazyInitializer (at **)'
: undefined,
props: {},
},
]
: undefined,
Expand All @@ -2673,6 +2726,7 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {},
},
]
: undefined,
Expand Down Expand Up @@ -2747,6 +2801,9 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {
transport: expect.arrayContaining([]),
},
},
]
: undefined,
Expand All @@ -2764,6 +2821,9 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in ServerComponent (at **)'
: undefined,
props: {
children: {},
},
},
]
: undefined,
Expand All @@ -2780,6 +2840,7 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {},
},
]
: undefined,
Expand Down Expand Up @@ -2938,6 +2999,7 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {},
},
{
env: 'B',
Expand Down Expand Up @@ -3068,6 +3130,9 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Object.<anonymous> (at **)'
: undefined,
props: {
firstName: 'Seb',
},
};
expect(getDebugInfo(greeting)).toEqual([
greetInfo,
Expand All @@ -3079,6 +3144,14 @@ describe('ReactFlight', () => {
stack: gate(flag => flag.enableOwnerStacks)
? ' in Greeting (at **)'
: undefined,
props: {
children: expect.objectContaining({
type: 'span',
props: {
children: ['Hello, ', 'Seb'],
},
}),
},
},
]);
// The owner that created the span was the outer server component.
Expand Down
8 changes: 4 additions & 4 deletions packages/react-devtools-core/src/standalone.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {registerDevToolsEventLogger} from 'react-devtools-shared/src/registerDev
import {Server} from 'ws';
import {join} from 'path';
import {readFileSync} from 'fs';
import {installHook} from 'react-devtools-shared/src/hook';
import DevTools from 'react-devtools-shared/src/devtools/views/DevTools';
import {doesFilePathExist, launchEditor} from './editor';
import {
Expand All @@ -29,8 +28,6 @@ import {localStorageSetItem} from 'react-devtools-shared/src/storage';
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type {Source} from 'react-devtools-shared/src/shared/types';

installHook(window);

export type StatusTypes = 'server-connected' | 'devtools-connected' | 'error';
export type StatusListener = (message: string, status: StatusTypes) => void;
export type OnDisconnectedCallback = () => void;
Expand Down Expand Up @@ -371,9 +368,12 @@ function startServer(
'\n;' +
backendFile.toString() +
'\n;' +
'ReactDevToolsBackend.initialize();' +
'\n' +
`ReactDevToolsBackend.connectToDevTools({port: ${port}, host: '${host}', useHttps: ${
useHttps ? 'true' : 'false'
}});`,
}});
`,
);
});

Expand Down
Loading

0 comments on commit a9b7a83

Please sign in to comment.