[Flight] Warn once if `eval` is disabled in dev environment (#35661)
Sebastian "Sebbie" Silbermann committed
Feb 2, 2026 at 12:56 UTC
ed4bd540ca67f1c4db65f009ad726a5ae1a0af01
26 files changed
+289
-11
packages/react-client/src/ReactClientDebugConfigBrowser.js
new
+36
@@ -0,0 +1,36 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+let hasConfirmedEval = false;
11
+export function checkEvalAvailabilityOnceDev(): void {
12
+ if (__DEV__) {
13
+ if (!hasConfirmedEval) {
14
+ hasConfirmedEval = true;
15
+ try {
16
+ // eslint-disable-next-line no-eval
17
+ (0, eval)('null');
18
+ } catch {
19
+ console.error(
20
+ 'eval() is not supported in this environment. ' +
21
+ 'If this page was served with a `Content-Security-Policy` header, ' +
22
+ 'make sure that `unsafe-eval` is included. ' +
23
+ 'React requires eval() in development mode for various debugging features ' +
24
+ 'like reconstructing callstacks from a different environment.\n' +
25
+ 'React will never use eval() in production mode',
26
+ );
27
+ }
28
+ }
29
+ } else {
30
+ // These errors should never make it into a build so we don't need to encode them in codes.json
31
+ // eslint-disable-next-line react-internal/prod-error-codes
32
+ throw new Error(
33
+ 'checkEvalAvailabilityOnceDev should never be called in production mode. This is a bug in React.',
34
+ );
35
+ }
36
+}
packages/react-client/src/ReactClientDebugConfigNode.js
new
+36
@@ -0,0 +1,36 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+let hasConfirmedEval = false;
11
+export function checkEvalAvailabilityOnceDev(): void {
12
+ if (__DEV__) {
13
+ if (!hasConfirmedEval) {
14
+ hasConfirmedEval = true;
15
+ try {
16
+ // eslint-disable-next-line no-eval
17
+ (0, eval)('null');
18
+ } catch {
19
+ console.error(
20
+ 'eval() is not supported in this environment. ' +
21
+ 'This can happen if you started the Node.js process with --disallow-code-generation-from-strings, ' +
22
+ 'or if `eval` was patched by other means. ' +
23
+ 'React requires eval() in development mode for various debugging features ' +
24
+ 'like reconstructing callstacks from a different environment.\n' +
25
+ 'React will never use eval() in production mode',
26
+ );
27
+ }
28
+ }
29
+ } else {
30
+ // These errors should never make it into a build so we don't need to encode them in codes.json
31
+ // eslint-disable-next-line react-internal/prod-error-codes
32
+ throw new Error(
33
+ 'checkEvalAvailabilityOnceDev should never be called in production mode. This is a bug in React.',
34
+ );
35
+ }
36
+}
packages/react-client/src/ReactClientDebugConfigPlain.js
new
+34
@@ -0,0 +1,34 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+let hasConfirmedEval = false;
11
+export function checkEvalAvailabilityOnceDev(): void {
12
+ if (__DEV__) {
13
+ if (!hasConfirmedEval) {
14
+ hasConfirmedEval = true;
15
+ try {
16
+ // eslint-disable-next-line no-eval
17
+ (0, eval)('null');
18
+ } catch {
19
+ console.error(
20
+ 'eval() is not supported in this environment. ' +
21
+ 'React requires eval() in development mode for various debugging features ' +
22
+ 'like reconstructing callstacks from a different environment.\n' +
23
+ 'React will never use eval() in production mode',
24
+ );
25
+ }
26
+ }
27
+ } else {
28
+ // These errors should never make it into a build so we don't need to encode them in codes.json
29
+ // eslint-disable-next-line react-internal/prod-error-codes
30
+ throw new Error(
31
+ 'checkEvalAvailabilityOnceDev should never be called in production mode. This is a bug in React.',
32
+ );
33
+ }
34
+}
packages/react-client/src/ReactFlightClient.js
+9
@@ -61,6 +61,7 @@ import {
61
bindToConsole,
62
rendererVersion,
63
rendererPackageName,
64
+ checkEvalAvailabilityOnceDev,
65
} from './ReactFlightClientConfig';
66
67
import {
@@ -2768,6 +2769,14 @@ export function createResponse(
2769
debugEndTime: void | number, // DEV-only
2770
debugChannel: void | DebugChannel, // DEV-only
2771
): WeakResponse {
2772
+ if (__DEV__) {
2773
+ // We use eval to create fake function stacks which includes Component stacks.
2774
+ // A warning would be noise if you used Flight without Components and don't encounter
2775
+ // errors. We're warning eagerly so that you configure your environment accordingly
2776
+ // before you encounter an error.
2777
+ checkEvalAvailabilityOnceDev();
2778
+ }
2779
+
2780
return getWeakResponse(
2781
// $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors
2782
new ResponseInstance(
packages/react-client/src/ReactFlightReplyClient.js
+12
-1
@@ -39,7 +39,10 @@ import getPrototypeOf from 'shared/getPrototypeOf';
39
40
const ObjectPrototype = Object.prototype;
41
42
-import {usedWithSSR} from './ReactFlightClientConfig';
42
+import {
43
+ usedWithSSR,
44
+ checkEvalAvailabilityOnceDev,
45
+} from './ReactFlightClientConfig';
46
47
type ReactJSONValue =
48
| string
@@ -190,6 +193,14 @@ export function processReply(
193
const writtenObjects: WeakMap<Reference, string> = new WeakMap();
194
let modelRoot: null | ReactServerValue = root;
195
196
+ if (__DEV__) {
197
+ // We use eval to create fake function stacks which includes Component stacks.
198
+ // A warning would be noise if you used Flight without Components and don't encounter
199
+ // errors. We're warning eagerly so that you configure your environment accordingly
200
+ // before you encounter an error.
201
+ checkEvalAvailabilityOnceDev();
202
+ }
203
+
204
function serializeTypedArray(
205
tag: string,
206
typedArray: $ArrayBufferView,
packages/react-client/src/__tests__/ReactFlight-test.js
+6
@@ -3715,6 +3715,12 @@ describe('ReactFlight', () => {
3715
'\n in b (at **)' +
3716
'\n in a (at **)',
3717
);
3718
+ assertConsoleErrorDev([
3719
+ 'eval() is not supported in this environment. ' +
3720
+ 'React requires eval() in development mode for various debugging features ' +
3721
+ 'like reconstructing callstacks from a different environment.\n' +
3722
+ 'React will never use eval() in production mode',
3723
+ ]);
3724
} else {
3725
expect(receivedError.message).toEqual(
3726
'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.',
packages/react-client/src/forks/ReactFlightClientConfig.custom.js
+3
@@ -53,3 +53,6 @@ export const bindToConsole = $$$config.bindToConsole;
53
54
export const rendererVersion = $$$config.rendererVersion;
55
export const rendererPackageName = $$$config.rendererPackageName;
56
+
57
+export const checkEvalAvailabilityOnceDev =
58
+ $$$config.checkEvalAvailabilityOnceDev;
packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-esm';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
15
+export * from 'react-client/src/ReactClientDebugConfigBrowser';
16
export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM';
17
export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigTargetESMBrowser';
18
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-parcel.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-parcel';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
15
+export * from 'react-client/src/ReactClientDebugConfigBrowser';
16
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel';
17
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigTargetParcelBrowser';
18
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-turbopack.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-turbopack';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
15
+export * from 'react-client/src/ReactClientDebugConfigBrowser';
16
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack';
17
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackBrowser';
18
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigTargetTurbopackBrowser';
packages/react-client/src/forks/ReactFlightClientConfig.dom-browser.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-webpack';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
15
+export * from 'react-client/src/ReactClientDebugConfigBrowser';
16
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
17
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackBrowser';
18
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackBrowser';
packages/react-client/src/forks/ReactFlightClientConfig.dom-bun.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-bun';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigPlain';
15
+export * from 'react-client/src/ReactClientDebugConfigPlain';
16
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
17
18
export opaque type ModuleLoading = mixed;
packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-parcel.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-parcel';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigServer';
15
+export * from 'react-client/src/ReactClientDebugConfigPlain';
16
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel';
17
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigTargetParcelServer';
18
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-turbopack.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-turbopack';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigServer';
15
+export * from 'react-client/src/ReactClientDebugConfigPlain';
16
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack';
17
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer';
18
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigTargetTurbopackServer';
packages/react-client/src/forks/ReactFlightClientConfig.dom-edge-webpack.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-webpack';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigServer';
15
+export * from 'react-client/src/ReactClientDebugConfigPlain';
16
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
17
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer';
18
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackServer';
packages/react-client/src/forks/ReactFlightClientConfig.dom-legacy.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'not-used';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
14
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
15
+export * from 'react-client/src/ReactClientDebugConfigBrowser';
16
17
export type Response = any;
18
export opaque type ModuleLoading = mixed;
packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-esm';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
14
export * from 'react-client/src/ReactClientConsoleConfigServer';
15
+export * from 'react-client/src/ReactClientDebugConfigNode';
16
export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM';
17
export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigTargetESMServer';
18
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
packages/react-client/src/forks/ReactFlightClientConfig.dom-node-parcel.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-parcel';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
14
export * from 'react-client/src/ReactClientConsoleConfigServer';
15
+export * from 'react-client/src/ReactClientDebugConfigNode';
16
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel';
17
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigTargetParcelServer';
18
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
packages/react-client/src/forks/ReactFlightClientConfig.dom-node-turbopack.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-turbopack';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
14
export * from 'react-client/src/ReactClientConsoleConfigServer';
15
+export * from 'react-client/src/ReactClientDebugConfigNode';
16
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack';
17
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer';
18
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigTargetTurbopackServer';
packages/react-client/src/forks/ReactFlightClientConfig.dom-node-unbundled.js
+1
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-unbundled';
12
13
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
14
export * from 'react-client/src/ReactClientConsoleConfigServer';
15
+export * from 'react-client/src/ReactClientDebugConfigNode';
16
export * from 'react-server-dom-unbundled/src/client/ReactFlightClientConfigBundlerNode';
17
export * from 'react-server-dom-unbundled/src/client/ReactFlightClientConfigTargetNodeServer';
18
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
packages/react-client/src/forks/ReactFlightClientConfig.dom-node.js
+1
@@ -11,6 +11,7 @@ export const rendererPackageName = 'react-server-dom-webpack';
11
12
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
13
export * from 'react-client/src/ReactClientConsoleConfigServer';
14
+export * from 'react-client/src/ReactClientDebugConfigNode';
15
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
16
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer';
17
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackServer';
packages/react-client/src/forks/ReactFlightClientConfig.markup.js
+1
@@ -14,6 +14,7 @@ import type {Thenable} from 'shared/ReactTypes';
14
15
export * from 'react-markup/src/ReactMarkupLegacyClientStreamConfig.js';
16
export * from 'react-client/src/ReactClientConsoleConfigPlain';
17
+export * from 'react-client/src/ReactClientDebugConfigPlain';
18
19
export type ModuleLoading = null;
20
export type ServerConsumerModuleMap = null;
packages/react-noop-renderer/src/ReactNoopFlightClient.js
+25
@@ -53,6 +53,7 @@ const {createResponse, createStreamState, processBinaryChunk, getRoot, close} =
53
[console].concat(args),
54
);
55
},
56
+ checkEvalAvailabilityOnceDev,
57
});
58
59
type ReadOptions = {|
@@ -87,4 +88,28 @@ function read<T>(source: Source, options: ReadOptions): Thenable<T> {
88
return getRoot(response);
89
}
90
91
+let hasConfirmedEval = false;
92
+function checkEvalAvailabilityOnceDev(): void {
93
+ if (__DEV__) {
94
+ if (!hasConfirmedEval) {
95
+ hasConfirmedEval = true;
96
+ try {
97
+ // eslint-disable-next-line no-eval
98
+ (0, eval)('null');
99
+ } catch {
100
+ console.error(
101
+ 'eval() is not supported in this environment. ' +
102
+ 'React requires eval() in development mode for various debugging features ' +
103
+ 'like reconstructing callstacks from a different environment.\n' +
104
+ 'React will never use eval() in production mode',
105
+ );
106
+ }
107
+ }
108
+ } else {
109
+ throw new Error(
110
+ 'checkEvalAvailabilityOnceDev should never be called in production mode. This is a bug in React.',
111
+ );
112
+ }
113
+}
114
+
115
export {read};
packages/react-server-dom-turbopack/src/__tests__/ReactFlightTurbopackDOMReply-test.js
+32
@@ -18,6 +18,7 @@ global.TextEncoder = require('util').TextEncoder;
18
global.TextDecoder = require('util').TextDecoder;
19
20
// let serverExports;
21
+let assertConsoleErrorDev;
22
let turbopackServerMap;
23
let ReactServerDOMServer;
24
let ReactServerDOMClient;
@@ -41,6 +42,9 @@ describe('ReactFlightTurbopackDOMReply', () => {
42
ReactServerDOMServer = require('react-server-dom-turbopack/server.browser');
43
jest.resetModules();
44
ReactServerDOMClient = require('react-server-dom-turbopack/client');
45
+
46
+ const InternalTestUtils = require('internal-test-utils');
47
+ assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
48
});
49
50
it('can encode a reply', async () => {
@@ -52,4 +56,32 @@ describe('ReactFlightTurbopackDOMReply', () => {
56
57
expect(decoded).toEqual({some: 'object'});
58
});
59
+
60
+ it('warns with a tailored message if eval is not available in dev', async () => {
61
+ // eslint-disable-next-line no-eval
62
+ const previousEval = globalThis.eval.bind(globalThis);
63
+ // eslint-disable-next-line no-eval
64
+ globalThis.eval = () => {
65
+ throw new Error('eval is disabled');
66
+ };
67
+
68
+ try {
69
+ const body = await ReactServerDOMClient.encodeReply({some: 'object'});
70
+ assertConsoleErrorDev([
71
+ 'eval() is not supported in this environment. ' +
72
+ 'If this page was served with a `Content-Security-Policy` header, ' +
73
+ 'make sure that `unsafe-eval` is included. ' +
74
+ 'React requires eval() in development mode for various debugging features ' +
75
+ 'like reconstructing callstacks from a different environment.\n' +
76
+ 'React will never use eval() in production mode',
77
+ ]);
78
+
79
+ await ReactServerDOMServer.decodeReply(body, turbopackServerMap);
80
+
81
+ assertConsoleErrorDev([]);
82
+ } finally {
83
+ // eslint-disable-next-line no-eval
84
+ globalThis.eval = previousEval;
85
+ }
86
+ });
87
});
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js
+50
-10
@@ -28,6 +28,7 @@ let ReactServerDOMStaticServer;
28
let ReactServerDOMClient;
29
let Stream;
30
let use;
31
+let assertConsoleErrorDev;
32
let serverAct;
33
34
// We test pass-through without encoding strings but it should work without it too.
@@ -73,6 +74,9 @@ describe('ReactFlightDOMNode', () => {
74
ReactServerDOMClient = require('react-server-dom-webpack/client');
75
Stream = require('stream');
76
use = React.use;
77
+
78
+ const InternalTestUtils = require('internal-test-utils');
79
+ assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
80
});
81
82
function filterStackFrame(filename, functionName) {
@@ -955,10 +959,10 @@ describe('ReactFlightDOMNode', () => {
959
// The concrete location may change as this test is updated.
960
// Just make sure they still point at React.use(p2)
961
(gate(flags => flags.enableAsyncDebugInfo)
958
- ? '\n at SharedComponent (./ReactFlightDOMNode-test.js:813:7)'
962
+ ? '\n at SharedComponent (./ReactFlightDOMNode-test.js:817:7)'
963
: '') +
960
- '\n at ServerComponent (file://./ReactFlightDOMNode-test.js:835:26)' +
961
- '\n at App (file://./ReactFlightDOMNode-test.js:852:25)',
964
+ '\n at ServerComponent (file://./ReactFlightDOMNode-test.js:839:26)' +
965
+ '\n at App (file://./ReactFlightDOMNode-test.js:856:25)',
966
);
967
} else {
968
expect(ownerStack).toBeNull();
@@ -1545,12 +1549,12 @@ describe('ReactFlightDOMNode', () => {
1549
'\n' +
1550
' in Dynamic' +
1551
(gate(flags => flags.enableAsyncDebugInfo)
1548
- ? ' (file://ReactFlightDOMNode-test.js:1419:27)\n'
1552
+ ? ' (file://ReactFlightDOMNode-test.js:1423:27)\n'
1553
: '\n') +
1554
' in body\n' +
1555
' in html\n' +
1552
- ' in App (file://ReactFlightDOMNode-test.js:1432:25)\n' +
1553
- ' in ClientRoot (ReactFlightDOMNode-test.js:1507:16)',
1556
+ ' in App (file://ReactFlightDOMNode-test.js:1436:25)\n' +
1557
+ ' in ClientRoot (ReactFlightDOMNode-test.js:1511:16)',
1558
);
1559
} else {
1560
expect(
@@ -1559,7 +1563,7 @@ describe('ReactFlightDOMNode', () => {
1563
'\n' +
1564
' in body\n' +
1565
' in html\n' +
1562
- ' in ClientRoot (ReactFlightDOMNode-test.js:1507:16)',
1566
+ ' in ClientRoot (ReactFlightDOMNode-test.js:1511:16)',
1567
);
1568
}
1569
@@ -1569,8 +1573,8 @@ describe('ReactFlightDOMNode', () => {
1573
normalizeCodeLocInfo(ownerStack, {preserveLocation: true}),
1574
).toBe(
1575
'\n' +
1572
- ' in Dynamic (file://ReactFlightDOMNode-test.js:1419:27)\n' +
1573
- ' in App (file://ReactFlightDOMNode-test.js:1432:25)',
1576
+ ' in Dynamic (file://ReactFlightDOMNode-test.js:1423:27)\n' +
1577
+ ' in App (file://ReactFlightDOMNode-test.js:1436:25)',
1578
);
1579
} else {
1580
expect(
@@ -1578,7 +1582,7 @@ describe('ReactFlightDOMNode', () => {
1582
).toBe(
1583
'' +
1584
'\n' +
1581
- ' in App (file://ReactFlightDOMNode-test.js:1432:25)',
1585
+ ' in App (file://ReactFlightDOMNode-test.js:1436:25)',
1586
);
1587
}
1588
} else {
@@ -1586,4 +1590,40 @@ describe('ReactFlightDOMNode', () => {
1590
}
1591
});
1592
});
1593
+
1594
+ it('warns with a tailored message if eval is not available in dev', async () => {
1595
+ // eslint-disable-next-line no-eval
1596
+ const previousEval = globalThis.eval.bind(globalThis);
1597
+ // eslint-disable-next-line no-eval
1598
+ globalThis.eval = () => {
1599
+ throw new Error('eval is disabled');
1600
+ };
1601
+
1602
+ try {
1603
+ const readable = await serverAct(() =>
1604
+ ReactServerDOMServer.renderToReadableStream({}, webpackMap),
1605
+ );
1606
+
1607
+ assertConsoleErrorDev([]);
1608
+
1609
+ await ReactServerDOMClient.createFromReadableStream(readable, {
1610
+ serverConsumerManifest: {
1611
+ moduleMap: null,
1612
+ moduleLoading: null,
1613
+ },
1614
+ });
1615
+
1616
+ assertConsoleErrorDev([
1617
+ 'eval() is not supported in this environment. ' +
1618
+ 'This can happen if you started the Node.js process with --disallow-code-generation-from-strings, ' +
1619
+ 'or if `eval` was patched by other means. ' +
1620
+ 'React requires eval() in development mode for various debugging features ' +
1621
+ 'like reconstructing callstacks from a different environment.\n' +
1622
+ 'React will never use eval() in production mode',
1623
+ ]);
1624
+ } finally {
1625
+ // eslint-disable-next-line no-eval
1626
+ globalThis.eval = previousEval;
1627
+ }
1628
+ });
1629
});
packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMReplyEdge-test.js
+31
@@ -10,6 +10,7 @@
10
11
'use strict';
12
13
+let assertConsoleErrorDev;
14
let serverExports;
15
let webpackServerMap;
16
let ReactServerDOMServer;
@@ -29,6 +30,9 @@ describe('ReactFlightDOMReplyEdge', () => {
30
ReactServerDOMServer = require('react-server-dom-webpack/server.edge');
31
jest.resetModules();
32
ReactServerDOMClient = require('react-server-dom-webpack/client.edge');
33
+
34
+ const InternalTestUtils = require('internal-test-utils');
35
+ assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
36
});
37
38
it('can encode a reply', async () => {
@@ -373,4 +377,31 @@ describe('ReactFlightDOMReplyEdge', () => {
377
expect(replyResult.method).toBe(greet);
378
expect(replyResult.boundMethod()).toBe('hi, there');
379
});
380
+
381
+ it('warns with a tailored message if eval is not available in dev', async () => {
382
+ // eslint-disable-next-line no-eval
383
+ const previousEval = globalThis.eval.bind(globalThis);
384
+ // eslint-disable-next-line no-eval
385
+ globalThis.eval = () => {
386
+ throw new Error('eval is disabled');
387
+ };
388
+
389
+ try {
390
+ const body = await ReactServerDOMClient.encodeReply({some: 'object'});
391
+
392
+ assertConsoleErrorDev([
393
+ 'eval() is not supported in this environment. ' +
394
+ 'React requires eval() in development mode for various debugging features ' +
395
+ 'like reconstructing callstacks from a different environment.\n' +
396
+ 'React will never use eval() in production mode',
397
+ ]);
398
+
399
+ await ReactServerDOMServer.decodeReply(body, webpackServerMap);
400
+
401
+ assertConsoleErrorDev([]);
402
+ } finally {
403
+ // eslint-disable-next-line no-eval
404
+ globalThis.eval = previousEval;
405
+ }
406
+ });
407
});