[Flight Server] Run Server Components in console.createTask when available (#30140)
Same as #30142 but for Flight Server. This is rarely used but it does allow seeing component stacks when inspecting the Node.js server running Flight using `--inspect` and the Chrome DevTools. <img width="595" alt="Screenshot 2024-06-29 at 1 08 47 PM" src="https://github.com/facebook/react/assets/63648/7f643e1e-a251-4e4d-b015-22a22a47031d">
Sebastian Markbåge committed
Jul 1, 2024 at 09:26 UTC
d40ea875a42d5726e8a7e5a6f47db2dbe314957d
2 files changed
+40
-4
fixtures/flight/package.json
+1
-1
@@ -71,7 +71,7 @@
71
"prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/",
72
"dev": "concurrently \"npm run dev:region\" \"npm run dev:global\"",
73
"dev:global": "NODE_ENV=development BUILD_PATH=dist node --experimental-loader ./loader/global.js server/global",
74
- "dev:region": "NODE_ENV=development BUILD_PATH=dist nodemon --watch src --watch dist -- --enable-source-maps --experimental-loader ./loader/region.js --conditions=react-server server/region",
74
+ "dev:region": "NODE_ENV=development BUILD_PATH=dist nodemon --watch src --watch dist -- --enable-source-maps --experimental-loader ./loader/region.js --conditions=react-server --inspect server/region",
75
"start": "node scripts/build.js && concurrently \"npm run start:region\" \"npm run start:global\"",
76
"start:global": "NODE_ENV=production node --experimental-loader ./loader/global.js server/global",
77
"start:region": "NODE_ENV=production node --experimental-loader ./loader/region.js --conditions=react-server server/region",
packages/react-server/src/ReactFlightServer.js
+39
-3
@@ -168,7 +168,7 @@ function getStack(error: Error): string {
168
169
function initCallComponentFrame(): string {
170
// Extract the stack frame of the callComponentInDEV function.
171
- const error = callComponentInDEV(Error, 'react-stack-top-frame', {});
171
+ const error = callComponentInDEV(Error, 'react-stack-top-frame', {}, null);
172
const stack = getStack(error);
173
const startIdx = stack.startsWith('Error: react-stack-top-frame\n') ? 29 : 0;
174
const endIdx = stack.indexOf('\n', startIdx);
@@ -991,6 +991,7 @@ function callComponentInDEV<Props, R>(
991
Component: (p: Props, arg: void) => R,
992
props: Props,
993
componentDebugInfo: ReactComponentInfo,
994
+ debugTask: null | ConsoleTask,
995
): R {
996
// The secondArg is always undefined in Server Components since refs error early.
997
const secondArg = undefined;
@@ -998,6 +999,18 @@ function callComponentInDEV<Props, R>(
999
try {
1000
if (supportsComponentStorage) {
1001
// Run the component in an Async Context that tracks the current owner.
1002
+ if (enableOwnerStacks && debugTask) {
1003
+ return debugTask.run(
1004
+ // $FlowFixMe[method-unbinding]
1005
+ componentStorage.run.bind(
1006
+ componentStorage,
1007
+ componentDebugInfo,
1008
+ Component,
1009
+ props,
1010
+ secondArg,
1011
+ ),
1012
+ );
1013
+ }
1014
return componentStorage.run(
1015
componentDebugInfo,
1016
Component,
@@ -1005,6 +1018,9 @@ function callComponentInDEV<Props, R>(
1018
secondArg,
1019
);
1020
} else {
1021
+ if (enableOwnerStacks && debugTask) {
1022
+ return debugTask.run(Component.bind(null, props, secondArg));
1023
+ }
1024
return Component(props, secondArg);
1025
}
1026
} finally {
@@ -1028,6 +1044,7 @@ function renderFunctionComponent<Props>(
1044
props: Props,
1045
owner: null | ReactComponentInfo, // DEV-only
1046
stack: null | string, // DEV-only
1047
+ debugTask: null | ConsoleTask, // DEV-only
1048
validated: number, // DEV-only
1049
): ReactJSONValue {
1050
// Reset the task's thenable state before continuing, so that if a later
@@ -1075,11 +1092,22 @@ function renderFunctionComponent<Props>(
1092
task.environmentName = componentEnv;
1093
1094
if (enableOwnerStacks) {
1078
- warnForMissingKey(request, key, validated, componentDebugInfo);
1095
+ warnForMissingKey(
1096
+ request,
1097
+ key,
1098
+ validated,
1099
+ componentDebugInfo,
1100
+ debugTask,
1101
+ );
1102
}
1103
}
1104
prepareToUseHooksForComponent(prevThenableState, componentDebugInfo);
1082
- result = callComponentInDEV(Component, props, componentDebugInfo);
1105
+ result = callComponentInDEV(
1106
+ Component,
1107
+ props,
1108
+ componentDebugInfo,
1109
+ debugTask,
1110
+ );
1111
} else {
1112
prepareToUseHooksForComponent(prevThenableState, null);
1113
// The secondArg is always undefined in Server Components since refs error early.
@@ -1235,6 +1263,7 @@ function warnForMissingKey(
1263
key: null | string,
1264
validated: number,
1265
componentDebugInfo: ReactComponentInfo,
1266
+ debugTask: null | ConsoleTask,
1267
): void {
1268
if (__DEV__) {
1269
if (validated !== 2) {
@@ -1267,6 +1296,7 @@ function warnForMissingKey(
1296
},
1297
null,
1298
componentDebugInfo,
1299
+ debugTask,
1300
);
1301
}
1302
}
@@ -1482,6 +1512,7 @@ function renderElement(
1512
props: any,
1513
owner: null | ReactComponentInfo, // DEV only
1514
stack: null | string, // DEV only
1515
+ debugTask: null | ConsoleTask, // DEV only
1516
validated: number, // DEV only
1517
): ReactJSONValue {
1518
if (ref !== null && ref !== undefined) {
@@ -1514,6 +1545,7 @@ function renderElement(
1545
props,
1546
owner,
1547
stack,
1548
+ debugTask,
1549
validated,
1550
);
1551
} else if (type === REACT_FRAGMENT_TYPE && key === null) {
@@ -1562,6 +1594,7 @@ function renderElement(
1594
props,
1595
owner,
1596
stack,
1597
+ debugTask,
1598
validated,
1599
);
1600
}
@@ -1574,6 +1607,7 @@ function renderElement(
1607
props,
1608
owner,
1609
stack,
1610
+ debugTask,
1611
validated,
1612
);
1613
}
@@ -1587,6 +1621,7 @@ function renderElement(
1621
props,
1622
owner,
1623
stack,
1624
+ debugTask,
1625
validated,
1626
);
1627
}
@@ -2190,6 +2225,7 @@ function renderModelDestructive(
2225
? element._debugStack
2226
: filterDebugStack(element._debugStack)
2227
: null,
2228
+ __DEV__ && enableOwnerStacks ? element._debugTask : null,
2229
__DEV__ && enableOwnerStacks ? element._store.validated : 0,
2230
);
2231
if (