Repository: facebook/react
Pull request: [Flight] Enable sync stack traces for errors and console replay

This was gated behind `enableOwnerStacks` since they share some code paths but it's really part of `enableServerComponentLogs`.

This just includes the server-side regular stack on Error/replayed logs but doesn't use console.createTask and doesn't include owner stacks.

Changed files:
filename: packages/react-client/src/ReactFlightClient.js
patch: @@ -46,6 +46,7 @@ import {
   enableRefAsProp,
   enableFlightReadableStream,
   enableOwnerStacks,
+  enableServerComponentLogs,
 } from 'shared/ReactFeatureFlags';
 
 import {
@@ -1928,7 +1929,7 @@ function resolveErrorDev(
   }
 
   let error;
-  if (!enableOwnerStacks) {
+  if (!enableOwnerStacks && !enableServerComponentLogs) {
     // Executing Error within a native stack isn't really limited to owner stacks
     // but we gate it behind the same flag for now while iterating.
     // eslint-disable-next-line react-internal/prod-error-codes
@@ -2463,9 +2464,7 @@ function resolveConsoleEntry(
   const env = payload[3];
   const args = payload.slice(4);
 
-  if (!enableOwnerStacks) {
-    // Printing with stack isn't really limited to owner stacks but
-    // we gate it behind the same flag for now while iterating.
+  if (!enableOwnerStacks && !enableServerComponentLogs) {
     bindToConsole(methodName, args, env)();
     return;
   }

filename: packages/react-client/src/__tests__/ReactFlight-test.js
patch: @@ -1346,7 +1346,10 @@ describe('ReactFlight', () => {
         errors: [
           {
             message: 'This is an error',
-            stack: gate(flags => flags.enableOwnerStacks)
+            stack: gate(
+              flags =>
+                flags.enableOwnerStacks || flags.enableServerComponentLogs,
+            )
               ? expect.stringContaining(
                   'Error: This is an error\n' +
                     '    at eval (eval at testFunction (eval at createFakeFunction (**), <anonymous>:1:35)\n' +
@@ -1378,7 +1381,17 @@ describe('ReactFlight', () => {
               ['file:///testing.js', 'Server'],
               [__filename, 'Server'],
             ]
-          : [],
+          : gate(flags => flags.enableServerComponentLogs)
+            ? [
+                // TODO: What should we request here? The outer (<anonymous>) or the inner (inspected-page.html)?
+                ['inspected-page.html:29:11), <anonymous>', 'Server'],
+                [
+                  'file://~/(some)(really)(exotic-directory)/ReactFlight-test.js',
+                  'Server',
+                ],
+                ['file:///testing.js', 'Server'],
+              ]
+            : [],
       });
     } else {
       expect(errors.map(getErrorForJestMatcher)).toEqual([
@@ -2940,7 +2953,11 @@ describe('ReactFlight', () => {
       .join('\n')
       .replaceAll(
         ' (/',
-        gate(flags => flags.enableOwnerStacks) ? ' (file:///' : ' (/',
+        gate(
+          flags => flags.enableOwnerStacks || flags.enableServerComponentLogs,
+        )
+          ? ' (file:///'
+          : ' (/',
       ); // The eval will end up normalizing these
 
     let sawReactPrefix = false;
@@ -2974,6 +2991,12 @@ describe('ReactFlight', () => {
         'third-party',
         'third-party',
       ]);
+    } else if (__DEV__ && gate(flags => flags.enableServerComponentLogs)) {
+      expect(environments.slice(0, 3)).toEqual([
+        'third-party',
+        'third-party',
+        'third-party',
+      ]);
     } else {
       expect(environments).toEqual([]);
     }