mirror of
https://github.com/facebook/react.git
synced 2026-02-22 03:42:05 +00:00
[Flight] Warn once if eval is disabled in dev environment (#35661)
This commit is contained in:
committed by
GitHub
parent
64b4605cb8
commit
ed4bd540ca
36
packages/react-client/src/ReactClientDebugConfigBrowser.js
vendored
Normal file
36
packages/react-client/src/ReactClientDebugConfigBrowser.js
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
let hasConfirmedEval = false;
|
||||
export function checkEvalAvailabilityOnceDev(): void {
|
||||
if (__DEV__) {
|
||||
if (!hasConfirmedEval) {
|
||||
hasConfirmedEval = true;
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)('null');
|
||||
} catch {
|
||||
console.error(
|
||||
'eval() is not supported in this environment. ' +
|
||||
'If this page was served with a `Content-Security-Policy` header, ' +
|
||||
'make sure that `unsafe-eval` is included. ' +
|
||||
'React requires eval() in development mode for various debugging features ' +
|
||||
'like reconstructing callstacks from a different environment.\n' +
|
||||
'React will never use eval() in production mode',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
throw new Error(
|
||||
'checkEvalAvailabilityOnceDev should never be called in production mode. This is a bug in React.',
|
||||
);
|
||||
}
|
||||
}
|
||||
36
packages/react-client/src/ReactClientDebugConfigNode.js
vendored
Normal file
36
packages/react-client/src/ReactClientDebugConfigNode.js
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
let hasConfirmedEval = false;
|
||||
export function checkEvalAvailabilityOnceDev(): void {
|
||||
if (__DEV__) {
|
||||
if (!hasConfirmedEval) {
|
||||
hasConfirmedEval = true;
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)('null');
|
||||
} catch {
|
||||
console.error(
|
||||
'eval() is not supported in this environment. ' +
|
||||
'This can happen if you started the Node.js process with --disallow-code-generation-from-strings, ' +
|
||||
'or if `eval` was patched by other means. ' +
|
||||
'React requires eval() in development mode for various debugging features ' +
|
||||
'like reconstructing callstacks from a different environment.\n' +
|
||||
'React will never use eval() in production mode',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
throw new Error(
|
||||
'checkEvalAvailabilityOnceDev should never be called in production mode. This is a bug in React.',
|
||||
);
|
||||
}
|
||||
}
|
||||
34
packages/react-client/src/ReactClientDebugConfigPlain.js
vendored
Normal file
34
packages/react-client/src/ReactClientDebugConfigPlain.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
let hasConfirmedEval = false;
|
||||
export function checkEvalAvailabilityOnceDev(): void {
|
||||
if (__DEV__) {
|
||||
if (!hasConfirmedEval) {
|
||||
hasConfirmedEval = true;
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)('null');
|
||||
} catch {
|
||||
console.error(
|
||||
'eval() is not supported in this environment. ' +
|
||||
'React requires eval() in development mode for various debugging features ' +
|
||||
'like reconstructing callstacks from a different environment.\n' +
|
||||
'React will never use eval() in production mode',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 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
|
||||
throw new Error(
|
||||
'checkEvalAvailabilityOnceDev should never be called in production mode. This is a bug in React.',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ import {
|
||||
bindToConsole,
|
||||
rendererVersion,
|
||||
rendererPackageName,
|
||||
checkEvalAvailabilityOnceDev,
|
||||
} from './ReactFlightClientConfig';
|
||||
|
||||
import {
|
||||
@@ -2768,6 +2769,14 @@ export function createResponse(
|
||||
debugEndTime: void | number, // DEV-only
|
||||
debugChannel: void | DebugChannel, // DEV-only
|
||||
): WeakResponse {
|
||||
if (__DEV__) {
|
||||
// We use eval to create fake function stacks which includes Component stacks.
|
||||
// A warning would be noise if you used Flight without Components and don't encounter
|
||||
// errors. We're warning eagerly so that you configure your environment accordingly
|
||||
// before you encounter an error.
|
||||
checkEvalAvailabilityOnceDev();
|
||||
}
|
||||
|
||||
return getWeakResponse(
|
||||
// $FlowFixMe[invalid-constructor]: the shapes are exact here but Flow doesn't like constructors
|
||||
new ResponseInstance(
|
||||
|
||||
@@ -39,7 +39,10 @@ import getPrototypeOf from 'shared/getPrototypeOf';
|
||||
|
||||
const ObjectPrototype = Object.prototype;
|
||||
|
||||
import {usedWithSSR} from './ReactFlightClientConfig';
|
||||
import {
|
||||
usedWithSSR,
|
||||
checkEvalAvailabilityOnceDev,
|
||||
} from './ReactFlightClientConfig';
|
||||
|
||||
type ReactJSONValue =
|
||||
| string
|
||||
@@ -190,6 +193,14 @@ export function processReply(
|
||||
const writtenObjects: WeakMap<Reference, string> = new WeakMap();
|
||||
let modelRoot: null | ReactServerValue = root;
|
||||
|
||||
if (__DEV__) {
|
||||
// We use eval to create fake function stacks which includes Component stacks.
|
||||
// A warning would be noise if you used Flight without Components and don't encounter
|
||||
// errors. We're warning eagerly so that you configure your environment accordingly
|
||||
// before you encounter an error.
|
||||
checkEvalAvailabilityOnceDev();
|
||||
}
|
||||
|
||||
function serializeTypedArray(
|
||||
tag: string,
|
||||
typedArray: $ArrayBufferView,
|
||||
|
||||
@@ -3715,6 +3715,12 @@ describe('ReactFlight', () => {
|
||||
'\n in b (at **)' +
|
||||
'\n in a (at **)',
|
||||
);
|
||||
assertConsoleErrorDev([
|
||||
'eval() is not supported in this environment. ' +
|
||||
'React requires eval() in development mode for various debugging features ' +
|
||||
'like reconstructing callstacks from a different environment.\n' +
|
||||
'React will never use eval() in production mode',
|
||||
]);
|
||||
} else {
|
||||
expect(receivedError.message).toEqual(
|
||||
'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.',
|
||||
|
||||
@@ -53,3 +53,6 @@ export const bindToConsole = $$$config.bindToConsole;
|
||||
|
||||
export const rendererVersion = $$$config.rendererVersion;
|
||||
export const rendererPackageName = $$$config.rendererPackageName;
|
||||
|
||||
export const checkEvalAvailabilityOnceDev =
|
||||
$$$config.checkEvalAvailabilityOnceDev;
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-esm';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||
export * from 'react-client/src/ReactClientDebugConfigBrowser';
|
||||
export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM';
|
||||
export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigTargetESMBrowser';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-parcel';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||
export * from 'react-client/src/ReactClientDebugConfigBrowser';
|
||||
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel';
|
||||
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigTargetParcelBrowser';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-turbopack';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||
export * from 'react-client/src/ReactClientDebugConfigBrowser';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackBrowser';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigTargetTurbopackBrowser';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-webpack';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||
export * from 'react-client/src/ReactClientDebugConfigBrowser';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackBrowser';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackBrowser';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-bun';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigPlain';
|
||||
export * from 'react-client/src/ReactClientDebugConfigPlain';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
|
||||
export opaque type ModuleLoading = mixed;
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-parcel';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-client/src/ReactClientDebugConfigPlain';
|
||||
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel';
|
||||
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigTargetParcelServer';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-turbopack';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-client/src/ReactClientDebugConfigPlain';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigTargetTurbopackServer';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-webpack';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-client/src/ReactClientDebugConfigPlain';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackServer';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'not-used';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigBrowser';
|
||||
export * from 'react-client/src/ReactClientDebugConfigBrowser';
|
||||
|
||||
export type Response = any;
|
||||
export opaque type ModuleLoading = mixed;
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-esm';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-client/src/ReactClientDebugConfigNode';
|
||||
export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigBundlerESM';
|
||||
export * from 'react-server-dom-esm/src/client/ReactFlightClientConfigTargetESMServer';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-parcel';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-client/src/ReactClientDebugConfigNode';
|
||||
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigBundlerParcel';
|
||||
export * from 'react-server-dom-parcel/src/client/ReactFlightClientConfigTargetParcelServer';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-turbopack';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-client/src/ReactClientDebugConfigNode';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer';
|
||||
export * from 'react-server-dom-turbopack/src/client/ReactFlightClientConfigTargetTurbopackServer';
|
||||
|
||||
@@ -12,6 +12,7 @@ export const rendererPackageName = 'react-server-dom-unbundled';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-client/src/ReactClientDebugConfigNode';
|
||||
export * from 'react-server-dom-unbundled/src/client/ReactFlightClientConfigBundlerNode';
|
||||
export * from 'react-server-dom-unbundled/src/client/ReactFlightClientConfigTargetNodeServer';
|
||||
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
|
||||
|
||||
@@ -11,6 +11,7 @@ export const rendererPackageName = 'react-server-dom-webpack';
|
||||
|
||||
export * from 'react-client/src/ReactFlightClientStreamConfigNode';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigServer';
|
||||
export * from 'react-client/src/ReactClientDebugConfigNode';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer';
|
||||
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackServer';
|
||||
|
||||
@@ -14,6 +14,7 @@ import type {Thenable} from 'shared/ReactTypes';
|
||||
|
||||
export * from 'react-markup/src/ReactMarkupLegacyClientStreamConfig.js';
|
||||
export * from 'react-client/src/ReactClientConsoleConfigPlain';
|
||||
export * from 'react-client/src/ReactClientDebugConfigPlain';
|
||||
|
||||
export type ModuleLoading = null;
|
||||
export type ServerConsumerModuleMap = null;
|
||||
|
||||
@@ -53,6 +53,7 @@ const {createResponse, createStreamState, processBinaryChunk, getRoot, close} =
|
||||
[console].concat(args),
|
||||
);
|
||||
},
|
||||
checkEvalAvailabilityOnceDev,
|
||||
});
|
||||
|
||||
type ReadOptions = {|
|
||||
@@ -87,4 +88,28 @@ function read<T>(source: Source, options: ReadOptions): Thenable<T> {
|
||||
return getRoot(response);
|
||||
}
|
||||
|
||||
let hasConfirmedEval = false;
|
||||
function checkEvalAvailabilityOnceDev(): void {
|
||||
if (__DEV__) {
|
||||
if (!hasConfirmedEval) {
|
||||
hasConfirmedEval = true;
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
(0, eval)('null');
|
||||
} catch {
|
||||
console.error(
|
||||
'eval() is not supported in this environment. ' +
|
||||
'React requires eval() in development mode for various debugging features ' +
|
||||
'like reconstructing callstacks from a different environment.\n' +
|
||||
'React will never use eval() in production mode',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
'checkEvalAvailabilityOnceDev should never be called in production mode. This is a bug in React.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export {read};
|
||||
|
||||
@@ -18,6 +18,7 @@ global.TextEncoder = require('util').TextEncoder;
|
||||
global.TextDecoder = require('util').TextDecoder;
|
||||
|
||||
// let serverExports;
|
||||
let assertConsoleErrorDev;
|
||||
let turbopackServerMap;
|
||||
let ReactServerDOMServer;
|
||||
let ReactServerDOMClient;
|
||||
@@ -41,6 +42,9 @@ describe('ReactFlightTurbopackDOMReply', () => {
|
||||
ReactServerDOMServer = require('react-server-dom-turbopack/server.browser');
|
||||
jest.resetModules();
|
||||
ReactServerDOMClient = require('react-server-dom-turbopack/client');
|
||||
|
||||
const InternalTestUtils = require('internal-test-utils');
|
||||
assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
|
||||
});
|
||||
|
||||
it('can encode a reply', async () => {
|
||||
@@ -52,4 +56,32 @@ describe('ReactFlightTurbopackDOMReply', () => {
|
||||
|
||||
expect(decoded).toEqual({some: 'object'});
|
||||
});
|
||||
|
||||
it('warns with a tailored message if eval is not available in dev', async () => {
|
||||
// eslint-disable-next-line no-eval
|
||||
const previousEval = globalThis.eval.bind(globalThis);
|
||||
// eslint-disable-next-line no-eval
|
||||
globalThis.eval = () => {
|
||||
throw new Error('eval is disabled');
|
||||
};
|
||||
|
||||
try {
|
||||
const body = await ReactServerDOMClient.encodeReply({some: 'object'});
|
||||
assertConsoleErrorDev([
|
||||
'eval() is not supported in this environment. ' +
|
||||
'If this page was served with a `Content-Security-Policy` header, ' +
|
||||
'make sure that `unsafe-eval` is included. ' +
|
||||
'React requires eval() in development mode for various debugging features ' +
|
||||
'like reconstructing callstacks from a different environment.\n' +
|
||||
'React will never use eval() in production mode',
|
||||
]);
|
||||
|
||||
await ReactServerDOMServer.decodeReply(body, turbopackServerMap);
|
||||
|
||||
assertConsoleErrorDev([]);
|
||||
} finally {
|
||||
// eslint-disable-next-line no-eval
|
||||
globalThis.eval = previousEval;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@ let ReactServerDOMStaticServer;
|
||||
let ReactServerDOMClient;
|
||||
let Stream;
|
||||
let use;
|
||||
let assertConsoleErrorDev;
|
||||
let serverAct;
|
||||
|
||||
// We test pass-through without encoding strings but it should work without it too.
|
||||
@@ -73,6 +74,9 @@ describe('ReactFlightDOMNode', () => {
|
||||
ReactServerDOMClient = require('react-server-dom-webpack/client');
|
||||
Stream = require('stream');
|
||||
use = React.use;
|
||||
|
||||
const InternalTestUtils = require('internal-test-utils');
|
||||
assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
|
||||
});
|
||||
|
||||
function filterStackFrame(filename, functionName) {
|
||||
@@ -955,10 +959,10 @@ describe('ReactFlightDOMNode', () => {
|
||||
// The concrete location may change as this test is updated.
|
||||
// Just make sure they still point at React.use(p2)
|
||||
(gate(flags => flags.enableAsyncDebugInfo)
|
||||
? '\n at SharedComponent (./ReactFlightDOMNode-test.js:813:7)'
|
||||
? '\n at SharedComponent (./ReactFlightDOMNode-test.js:817:7)'
|
||||
: '') +
|
||||
'\n at ServerComponent (file://./ReactFlightDOMNode-test.js:835:26)' +
|
||||
'\n at App (file://./ReactFlightDOMNode-test.js:852:25)',
|
||||
'\n at ServerComponent (file://./ReactFlightDOMNode-test.js:839:26)' +
|
||||
'\n at App (file://./ReactFlightDOMNode-test.js:856:25)',
|
||||
);
|
||||
} else {
|
||||
expect(ownerStack).toBeNull();
|
||||
@@ -1545,12 +1549,12 @@ describe('ReactFlightDOMNode', () => {
|
||||
'\n' +
|
||||
' in Dynamic' +
|
||||
(gate(flags => flags.enableAsyncDebugInfo)
|
||||
? ' (file://ReactFlightDOMNode-test.js:1419:27)\n'
|
||||
? ' (file://ReactFlightDOMNode-test.js:1423:27)\n'
|
||||
: '\n') +
|
||||
' in body\n' +
|
||||
' in html\n' +
|
||||
' in App (file://ReactFlightDOMNode-test.js:1432:25)\n' +
|
||||
' in ClientRoot (ReactFlightDOMNode-test.js:1507:16)',
|
||||
' in App (file://ReactFlightDOMNode-test.js:1436:25)\n' +
|
||||
' in ClientRoot (ReactFlightDOMNode-test.js:1511:16)',
|
||||
);
|
||||
} else {
|
||||
expect(
|
||||
@@ -1559,7 +1563,7 @@ describe('ReactFlightDOMNode', () => {
|
||||
'\n' +
|
||||
' in body\n' +
|
||||
' in html\n' +
|
||||
' in ClientRoot (ReactFlightDOMNode-test.js:1507:16)',
|
||||
' in ClientRoot (ReactFlightDOMNode-test.js:1511:16)',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1569,8 +1573,8 @@ describe('ReactFlightDOMNode', () => {
|
||||
normalizeCodeLocInfo(ownerStack, {preserveLocation: true}),
|
||||
).toBe(
|
||||
'\n' +
|
||||
' in Dynamic (file://ReactFlightDOMNode-test.js:1419:27)\n' +
|
||||
' in App (file://ReactFlightDOMNode-test.js:1432:25)',
|
||||
' in Dynamic (file://ReactFlightDOMNode-test.js:1423:27)\n' +
|
||||
' in App (file://ReactFlightDOMNode-test.js:1436:25)',
|
||||
);
|
||||
} else {
|
||||
expect(
|
||||
@@ -1578,7 +1582,7 @@ describe('ReactFlightDOMNode', () => {
|
||||
).toBe(
|
||||
'' +
|
||||
'\n' +
|
||||
' in App (file://ReactFlightDOMNode-test.js:1432:25)',
|
||||
' in App (file://ReactFlightDOMNode-test.js:1436:25)',
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -1586,4 +1590,40 @@ describe('ReactFlightDOMNode', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('warns with a tailored message if eval is not available in dev', async () => {
|
||||
// eslint-disable-next-line no-eval
|
||||
const previousEval = globalThis.eval.bind(globalThis);
|
||||
// eslint-disable-next-line no-eval
|
||||
globalThis.eval = () => {
|
||||
throw new Error('eval is disabled');
|
||||
};
|
||||
|
||||
try {
|
||||
const readable = await serverAct(() =>
|
||||
ReactServerDOMServer.renderToReadableStream({}, webpackMap),
|
||||
);
|
||||
|
||||
assertConsoleErrorDev([]);
|
||||
|
||||
await ReactServerDOMClient.createFromReadableStream(readable, {
|
||||
serverConsumerManifest: {
|
||||
moduleMap: null,
|
||||
moduleLoading: null,
|
||||
},
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'eval() is not supported in this environment. ' +
|
||||
'This can happen if you started the Node.js process with --disallow-code-generation-from-strings, ' +
|
||||
'or if `eval` was patched by other means. ' +
|
||||
'React requires eval() in development mode for various debugging features ' +
|
||||
'like reconstructing callstacks from a different environment.\n' +
|
||||
'React will never use eval() in production mode',
|
||||
]);
|
||||
} finally {
|
||||
// eslint-disable-next-line no-eval
|
||||
globalThis.eval = previousEval;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
let assertConsoleErrorDev;
|
||||
let serverExports;
|
||||
let webpackServerMap;
|
||||
let ReactServerDOMServer;
|
||||
@@ -29,6 +30,9 @@ describe('ReactFlightDOMReplyEdge', () => {
|
||||
ReactServerDOMServer = require('react-server-dom-webpack/server.edge');
|
||||
jest.resetModules();
|
||||
ReactServerDOMClient = require('react-server-dom-webpack/client.edge');
|
||||
|
||||
const InternalTestUtils = require('internal-test-utils');
|
||||
assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
|
||||
});
|
||||
|
||||
it('can encode a reply', async () => {
|
||||
@@ -373,4 +377,31 @@ describe('ReactFlightDOMReplyEdge', () => {
|
||||
expect(replyResult.method).toBe(greet);
|
||||
expect(replyResult.boundMethod()).toBe('hi, there');
|
||||
});
|
||||
|
||||
it('warns with a tailored message if eval is not available in dev', async () => {
|
||||
// eslint-disable-next-line no-eval
|
||||
const previousEval = globalThis.eval.bind(globalThis);
|
||||
// eslint-disable-next-line no-eval
|
||||
globalThis.eval = () => {
|
||||
throw new Error('eval is disabled');
|
||||
};
|
||||
|
||||
try {
|
||||
const body = await ReactServerDOMClient.encodeReply({some: 'object'});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'eval() is not supported in this environment. ' +
|
||||
'React requires eval() in development mode for various debugging features ' +
|
||||
'like reconstructing callstacks from a different environment.\n' +
|
||||
'React will never use eval() in production mode',
|
||||
]);
|
||||
|
||||
await ReactServerDOMServer.decodeReply(body, webpackServerMap);
|
||||
|
||||
assertConsoleErrorDev([]);
|
||||
} finally {
|
||||
// eslint-disable-next-line no-eval
|
||||
globalThis.eval = previousEval;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user