Skip to content

Commit c0c3706

Browse files
authored
[Flight] Restore original function name in dev, server callstacks served with unsafe-eval (facebook#35650)
1 parent 87ae75b commit c0c3706

File tree

2 files changed

+58
-0
lines changed

2 files changed

+58
-0
lines changed

packages/react-client/src/ReactFlightClient.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3730,6 +3730,14 @@ function createFakeFunction<T>(
37303730
fn = function (_) {
37313731
return _();
37323732
};
3733+
// Using the usual {[name]: _() => _()}.bind() trick to avoid minifiers
3734+
// doesn't work here since this will produce `Object.*` names.
3735+
Object.defineProperty(
3736+
fn,
3737+
// $FlowFixMe[cannot-write] -- `name` is configurable though.
3738+
'name',
3739+
{value: name},
3740+
);
37333741
}
37343742
return fn;
37353743
}

packages/react-client/src/__tests__/ReactFlight-test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3677,6 +3677,56 @@ describe('ReactFlight', () => {
36773677
expect(caughtError.digest).toBe('digest("my-error")');
36783678
});
36793679

3680+
it('can transport function names in stackframes in dev even without eval', async () => {
3681+
function a() {
3682+
return b();
3683+
}
3684+
function b() {
3685+
return c();
3686+
}
3687+
function c() {
3688+
return new Error('boom');
3689+
}
3690+
3691+
// eslint-disable-next-line no-eval
3692+
const previousEval = globalThis.eval.bind(globalThis);
3693+
// eslint-disable-next-line no-eval
3694+
globalThis.eval = () => {
3695+
throw new Error('eval is disabled');
3696+
};
3697+
3698+
try {
3699+
const transport = ReactNoopFlightServer.render(
3700+
{model: a()},
3701+
{onError: () => 'digest'},
3702+
);
3703+
3704+
const root = await ReactNoopFlightClient.read(transport);
3705+
const receivedError = await root.model;
3706+
3707+
if (__DEV__) {
3708+
const normalizedErrorStack = normalizeCodeLocInfo(
3709+
receivedError.stack.split('\n').slice(0, 4).join('\n'),
3710+
);
3711+
3712+
expect(normalizedErrorStack).toEqual(
3713+
'Error: boom' +
3714+
'\n in c (at **)' +
3715+
'\n in b (at **)' +
3716+
'\n in a (at **)',
3717+
);
3718+
} else {
3719+
expect(receivedError.message).toEqual(
3720+
'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.',
3721+
);
3722+
expect(receivedError).not.toHaveProperty('digest');
3723+
}
3724+
} finally {
3725+
// eslint-disable-next-line no-eval
3726+
globalThis.eval = previousEval;
3727+
}
3728+
});
3729+
36803730
// @gate __DEV__ && enableComponentPerformanceTrack
36813731
it('can render deep but cut off JSX in debug info', async () => {
36823732
function createDeepJSX(n) {

0 commit comments

Comments
 (0)