Repository: facebook/react
Pull request: DevTools: Inline references to fiber flags

We shouldn't be referencing internal fields like fiber's `flag` directly of DevTools. It's an implementation detail. However, over the years a few of these have snuck in. Because of how DevTools is currently shipped, where it's expected to be backwards compatible with older versions of React, this prevents us from refactoring those fields inside the reconciler.

The plan we have to address this is to fix how DevTools is shipped: DevTools will be released in lockstep with each version of React.

Until then, though, I need a temporary solution because it's blocking a feature I'm working on. So in meantime, I'm going to have to fork the DevTool's code based on the React version, like we already do with the fiber TypeOfWork enum.

As a first step, I've inlined all the references to fiber flags into the specific call sites where they are used. Eventually we'll import these functions from the reconciler so they stay in sync, rather than maintaining duplicate copies of the logic.

Changed files:
filename: packages/react-devtools-shared/src/backend/ReactFiberFlags.js
patch: @@ -1,17 +0,0 @@
-/**
- * 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
- */
-
-// This list of flags must be synced with the following file:
-// https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberFlags.js
-
-export const NoFlags = /*                      */ 0b000000000000000000000000000;
-export const PerformedWork = /*                */ 0b000000000000000000000000001;
-export const Placement = /*                    */ 0b000000000000000000000000010;
-export const DidCapture = /*                   */ 0b000000000000000000010000000;
-export const Hydrating = /*                    */ 0b000000000000001000000000000;

filename: packages/react-devtools-shared/src/backend/renderer.js
patch: @@ -88,13 +88,6 @@ import {
   MEMO_SYMBOL_STRING,
   SERVER_CONTEXT_SYMBOL_STRING,
 } from './ReactSymbols';
-import {
-  DidCapture,
-  NoFlags,
-  PerformedWork,
-  Placement,
-  Hydrating,
-} from './ReactFiberFlags';
 import {format} from './utils';
 import {
   enableProfilerChangedHookIndices,
@@ -1555,7 +1548,10 @@ export function attach(
       case ForwardRef:
         // For types that execute user code, we check PerformedWork effect.
         // We don't reflect bailouts (either referential or sCU) in DevTools.
-        // eslint-disable-next-line no-bitwise
+        // TODO: This flag is a leaked implementation detail. Once we start
+        // releasing DevTools in lockstep with React, we should import a
+        // function from the reconciler instead.
+        const PerformedWork = 0b000000000000000000000000001;
         return (getFiberFlags(nextFiber) & PerformedWork) === PerformedWork;
       // Note: ContextConsumer only gets PerformedWork effect in 16.3.3+
       // so it won't get highlighted with React 16.3.0 to 16.3.2.
@@ -2843,7 +2839,12 @@ export function attach(
       let nextNode: Fiber = node;
       do {
         node = nextNode;
-        if ((node.flags & (Placement | Hydrating)) !== NoFlags) {
+        // TODO: This function, and these flags, are a leaked implementation
+        // detail. Once we start releasing DevTools in lockstep with React, we
+        // should import a function from the reconciler instead.
+        const Placement = 0b000000000000000000000000010;
+        const Hydrating = 0b000000000000001000000000000;
+        if ((node.flags & (Placement | Hydrating)) !== 0) {
           // This is an insertion or in-progress hydration. The nearest possible
           // mounted fiber is the parent but we need to continue to figure out
           // if that one is still mounted.
@@ -3300,16 +3301,21 @@ export function attach(
     const errors = fiberIDToErrorsMap.get(id) || new Map();
     const warnings = fiberIDToWarningsMap.get(id) || new Map();
 
-    const isErrored =
-      (fiber.flags & DidCapture) !== NoFlags ||
-      forceErrorForFiberIDs.get(id) === true;
-
+    let isErrored = false;
     let targetErrorBoundaryID;
     if (isErrorBoundary(fiber)) {
       // if the current inspected element is an error boundary,
       // either that we want to use it to toggle off error state
       // or that we allow to force error state on it if it's within another
       // error boundary
+      //
+      // TODO: This flag is a leaked implementation detail. Once we start
+      // releasing DevTools in lockstep with React, we should import a function
+      // from the reconciler instead.
+      const DidCapture = 0b000000000000000000010000000;
+      isErrored =
+        (fiber.flags & DidCapture) !== 0 ||
+        forceErrorForFiberIDs.get(id) === true;
       targetErrorBoundaryID = isErrored ? id : getNearestErrorBoundaryID(fiber);
     } else {
       targetErrorBoundaryID = getNearestErrorBoundaryID(fiber);