Repository: facebook/react
Pull request: [Flight] Log error if prod elements are rendered

We previously encountered these errors with MDX. The newest issue was a `lodash.cloneDeep(objWithReactElements)`.

Example:
```
Attempted to render <span key="upsell"> without development properties. This is not supported. It can happen if:
- The element is created with a production version of React but rendered in development.
- The element was cloned with a custom function instead of `React.cloneElement`.
The props of this element may help locate this element: { children: 'Free!' }
```

Changed files:
filename: packages/internal-test-utils/consoleMock.js
patch: @@ -355,7 +355,7 @@ export function createLogAssertion(
         let argIndex = 0;
         // console.* could have been called with a non-string e.g. `console.error(new Error())`
         // eslint-disable-next-line react-internal/safe-string-coercion
-        String(format).replace(/%s|%c/g, () => argIndex++);
+        String(format).replace(/%s|%c|%o/g, () => argIndex++);
         if (argIndex !== args.length) {
           if (format.includes('%c%s')) {
             // We intentionally use mismatching formatting when printing badging because we don't know

filename: packages/react-server/src/ReactFlightServer.js
patch: @@ -3354,6 +3354,27 @@ function renderModelDestructive(
           task.debugOwner = element._owner;
           task.debugStack = element._debugStack;
           task.debugTask = element._debugTask;
+          if (
+            element._owner === undefined ||
+            element._debugStack === undefined ||
+            element._debugTask === undefined
+          ) {
+            let key = '';
+            if (element.key !== null) {
+              key = ' key="' + element.key + '"';
+            }
+
+            console.error(
+              'Attempted to render <%s%s> without development properties. ' +
+                'This is not supported. It can happen if:' +
+                '\n- The element is created with a production version of React but rendered in development.' +
+                '\n- The element was cloned with a custom function instead of `React.cloneElement`.\n' +
+                'The props of this element may help locate this element: %o',
+              element.type,
+              key,
+              element.props,
+            );
+          }
           // TODO: Pop this. Since we currently don't have a point where we can pop the stack
           // this debug information will be used for errors inside sibling properties that
           // are not elements. Leading to the wrong attribution on the server. We could fix

filename: packages/react-server/src/__tests__/ReactFlightServer-test.js
patch: @@ -36,6 +36,7 @@ let ReactNoopFlightServer;
 let Scheduler;
 let advanceTimersByTime;
 let assertLog;
+let assertConsoleErrorDev;
 
 describe('ReactFlight', () => {
   beforeEach(() => {
@@ -64,6 +65,7 @@ describe('ReactFlight', () => {
     Scheduler = require('scheduler');
     const InternalTestUtils = require('internal-test-utils');
     assertLog = InternalTestUtils.assertLog;
+    assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
   });
 
   afterEach(() => {
@@ -175,4 +177,26 @@ describe('ReactFlight', () => {
       stackTwo: '\n    in OwnerStackDelayed (at **)' + '\n    in App (at **)',
     });
   });
+
+  it('logs an error when prod elements are rendered', async () => {
+    const element = ReactServer.createElement('span', {
+      key: 'one',
+      children: 'Free!',
+    });
+    ReactNoopFlightServer.render(
+      // bad clone
+      {...element},
+    );
+
+    assertConsoleErrorDev([
+      [
+        'Attempted to render <span key="one"> without development properties. This is not supported. It can happen if:' +
+          '\n- The element is created with a production version of React but rendered in development.' +
+          '\n- The element was cloned with a custom function instead of `React.cloneElement`.\n' +
+          "The props of this element may help locate this element: { children: 'Free!', [key]: [Getter] }",
+        {withoutStack: true},
+      ],
+      "TypeError: Cannot read properties of undefined (reading 'stack')",
+    ]);
+  });
 });