1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. /**
    
  11.  * WARNING:
    
  12.  * This file contains types that are conceptually related to React internals and
    
  13.  * DevTools backends, but can be passed to frontend via the bridge.
    
  14.  * Be mindful of backwards compatibility when making changes.
    
  15.  */
    
  16. 
    
  17. import type {ReactContext, Wakeable} from 'shared/ReactTypes';
    
  18. import type {Source} from 'shared/ReactElementType';
    
  19. import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
    
  20. import type {
    
  21.   ComponentFilter,
    
  22.   ElementType,
    
  23.   Plugins,
    
  24. } from 'react-devtools-shared/src/frontend/types';
    
  25. import type {
    
  26.   ResolveNativeStyle,
    
  27.   SetupNativeStyleEditor,
    
  28. } from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
    
  29. import type {InitBackend} from 'react-devtools-shared/src/backend';
    
  30. import type {TimelineDataExport} from 'react-devtools-timeline/src/types';
    
  31. import type {BrowserTheme} from 'react-devtools-shared/src/frontend/types';
    
  32. import type {BackendBridge} from 'react-devtools-shared/src/bridge';
    
  33. import type Agent from './agent';
    
  34. 
    
  35. type BundleType =
    
  36.   | 0 // PROD
    
  37.   | 1; // DEV
    
  38. 
    
  39. export type WorkTag = number;
    
  40. export type WorkFlags = number;
    
  41. export type ExpirationTime = number;
    
  42. 
    
  43. export type WorkTagMap = {
    
  44.   CacheComponent: WorkTag,
    
  45.   ClassComponent: WorkTag,
    
  46.   ContextConsumer: WorkTag,
    
  47.   ContextProvider: WorkTag,
    
  48.   CoroutineComponent: WorkTag,
    
  49.   CoroutineHandlerPhase: WorkTag,
    
  50.   DehydratedSuspenseComponent: WorkTag,
    
  51.   ForwardRef: WorkTag,
    
  52.   Fragment: WorkTag,
    
  53.   FunctionComponent: WorkTag,
    
  54.   HostComponent: WorkTag,
    
  55.   HostPortal: WorkTag,
    
  56.   HostRoot: WorkTag,
    
  57.   HostHoistable: WorkTag,
    
  58.   HostSingleton: WorkTag,
    
  59.   HostText: WorkTag,
    
  60.   IncompleteClassComponent: WorkTag,
    
  61.   IndeterminateComponent: WorkTag,
    
  62.   LazyComponent: WorkTag,
    
  63.   LegacyHiddenComponent: WorkTag,
    
  64.   MemoComponent: WorkTag,
    
  65.   Mode: WorkTag,
    
  66.   OffscreenComponent: WorkTag,
    
  67.   Profiler: WorkTag,
    
  68.   ScopeComponent: WorkTag,
    
  69.   SimpleMemoComponent: WorkTag,
    
  70.   SuspenseComponent: WorkTag,
    
  71.   SuspenseListComponent: WorkTag,
    
  72.   TracingMarkerComponent: WorkTag,
    
  73.   YieldComponent: WorkTag,
    
  74. };
    
  75. 
    
  76. // TODO: If it's useful for the frontend to know which types of data an Element has
    
  77. // (e.g. props, state, context, hooks) then we could add a bitmask field for this
    
  78. // to keep the number of attributes small.
    
  79. export type FiberData = {
    
  80.   key: string | null,
    
  81.   displayName: string | null,
    
  82.   type: ElementType,
    
  83. };
    
  84. 
    
  85. export type NativeType = Object;
    
  86. export type RendererID = number;
    
  87. 
    
  88. type Dispatcher = any;
    
  89. export type CurrentDispatcherRef = {current: null | Dispatcher};
    
  90. 
    
  91. export type GetDisplayNameForFiberID = (
    
  92.   id: number,
    
  93.   findNearestUnfilteredAncestor?: boolean,
    
  94. ) => string | null;
    
  95. 
    
  96. export type GetFiberIDForNative = (
    
  97.   component: NativeType,
    
  98.   findNearestUnfilteredAncestor?: boolean,
    
  99. ) => number | null;
    
  100. export type FindNativeNodesForFiberID = (id: number) => ?Array<NativeType>;
    
  101. 
    
  102. export type ReactProviderType<T> = {
    
  103.   $$typeof: symbol | number,
    
  104.   _context: ReactContext<T>,
    
  105.   ...
    
  106. };
    
  107. 
    
  108. export type Lane = number;
    
  109. export type Lanes = number;
    
  110. 
    
  111. export type ReactRenderer = {
    
  112.   findFiberByHostInstance: (hostInstance: NativeType) => Fiber | null,
    
  113.   version: string,
    
  114.   rendererPackageName: string,
    
  115.   bundleType: BundleType,
    
  116.   // 16.9+
    
  117.   overrideHookState?: ?(
    
  118.     fiber: Object,
    
  119.     id: number,
    
  120.     path: Array<string | number>,
    
  121.     value: any,
    
  122.   ) => void,
    
  123.   // 17+
    
  124.   overrideHookStateDeletePath?: ?(
    
  125.     fiber: Object,
    
  126.     id: number,
    
  127.     path: Array<string | number>,
    
  128.   ) => void,
    
  129.   // 17+
    
  130.   overrideHookStateRenamePath?: ?(
    
  131.     fiber: Object,
    
  132.     id: number,
    
  133.     oldPath: Array<string | number>,
    
  134.     newPath: Array<string | number>,
    
  135.   ) => void,
    
  136.   // 16.7+
    
  137.   overrideProps?: ?(
    
  138.     fiber: Object,
    
  139.     path: Array<string | number>,
    
  140.     value: any,
    
  141.   ) => void,
    
  142.   // 17+
    
  143.   overridePropsDeletePath?: ?(
    
  144.     fiber: Object,
    
  145.     path: Array<string | number>,
    
  146.   ) => void,
    
  147.   // 17+
    
  148.   overridePropsRenamePath?: ?(
    
  149.     fiber: Object,
    
  150.     oldPath: Array<string | number>,
    
  151.     newPath: Array<string | number>,
    
  152.   ) => void,
    
  153.   // 16.9+
    
  154.   scheduleUpdate?: ?(fiber: Object) => void,
    
  155.   setSuspenseHandler?: ?(shouldSuspend: (fiber: Object) => boolean) => void,
    
  156.   // Only injected by React v16.8+ in order to support hooks inspection.
    
  157.   currentDispatcherRef?: CurrentDispatcherRef,
    
  158.   // Only injected by React v16.9+ in DEV mode.
    
  159.   // Enables DevTools to append owners-only component stack to error messages.
    
  160.   getCurrentFiber?: () => Fiber | null,
    
  161.   // 17.0.2+
    
  162.   reconcilerVersion?: string,
    
  163.   // Uniquely identifies React DOM v15.
    
  164.   ComponentTree?: any,
    
  165.   // Present for React DOM v12 (possibly earlier) through v15.
    
  166.   Mount?: any,
    
  167.   // Only injected by React v17.0.3+ in DEV mode
    
  168.   setErrorHandler?: ?(shouldError: (fiber: Object) => ?boolean) => void,
    
  169.   // Intentionally opaque type to avoid coupling DevTools to different Fast Refresh versions.
    
  170.   scheduleRefresh?: Function,
    
  171.   // 18.0+
    
  172.   injectProfilingHooks?: (profilingHooks: DevToolsProfilingHooks) => void,
    
  173.   getLaneLabelMap?: () => Map<Lane, string> | null,
    
  174.   ...
    
  175. };
    
  176. 
    
  177. export type ChangeDescription = {
    
  178.   context: Array<string> | boolean | null,
    
  179.   didHooksChange: boolean,
    
  180.   isFirstMount: boolean,
    
  181.   props: Array<string> | null,
    
  182.   state: Array<string> | null,
    
  183.   hooks?: Array<number> | null,
    
  184. };
    
  185. 
    
  186. export type CommitDataBackend = {
    
  187.   // Tuple of fiber ID and change description
    
  188.   changeDescriptions: Array<[number, ChangeDescription]> | null,
    
  189.   duration: number,
    
  190.   // Only available in certain (newer) React builds,
    
  191.   effectDuration: number | null,
    
  192.   // Tuple of fiber ID and actual duration
    
  193.   fiberActualDurations: Array<[number, number]>,
    
  194.   // Tuple of fiber ID and computed "self" duration
    
  195.   fiberSelfDurations: Array<[number, number]>,
    
  196.   // Only available in certain (newer) React builds,
    
  197.   passiveEffectDuration: number | null,
    
  198.   priorityLevel: string | null,
    
  199.   timestamp: number,
    
  200.   updaters: Array<SerializedElement> | null,
    
  201. };
    
  202. 
    
  203. export type ProfilingDataForRootBackend = {
    
  204.   commitData: Array<CommitDataBackend>,
    
  205.   displayName: string,
    
  206.   // Tuple of Fiber ID and base duration
    
  207.   initialTreeBaseDurations: Array<[number, number]>,
    
  208.   rootID: number,
    
  209. };
    
  210. 
    
  211. // Profiling data collected by the renderer interface.
    
  212. // This information will be passed to the frontend and combined with info it collects.
    
  213. export type ProfilingDataBackend = {
    
  214.   dataForRoots: Array<ProfilingDataForRootBackend>,
    
  215.   rendererID: number,
    
  216.   timelineData: TimelineDataExport | null,
    
  217. };
    
  218. 
    
  219. export type PathFrame = {
    
  220.   key: string | null,
    
  221.   index: number,
    
  222.   displayName: string | null,
    
  223. };
    
  224. 
    
  225. export type PathMatch = {
    
  226.   id: number,
    
  227.   isFullMatch: boolean,
    
  228. };
    
  229. 
    
  230. export type SerializedElement = {
    
  231.   displayName: string | null,
    
  232.   id: number,
    
  233.   key: number | string | null,
    
  234.   type: ElementType,
    
  235. };
    
  236. 
    
  237. export type OwnersList = {
    
  238.   id: number,
    
  239.   owners: Array<SerializedElement> | null,
    
  240. };
    
  241. 
    
  242. export type InspectedElement = {
    
  243.   id: number,
    
  244. 
    
  245.   displayName: string | null,
    
  246. 
    
  247.   // Does the current renderer support editable hooks and function props?
    
  248.   canEditHooks: boolean,
    
  249.   canEditFunctionProps: boolean,
    
  250. 
    
  251.   // Does the current renderer support advanced editing interface?
    
  252.   canEditHooksAndDeletePaths: boolean,
    
  253.   canEditHooksAndRenamePaths: boolean,
    
  254.   canEditFunctionPropsDeletePaths: boolean,
    
  255.   canEditFunctionPropsRenamePaths: boolean,
    
  256. 
    
  257.   // Is this Error, and can its value be overridden now?
    
  258.   canToggleError: boolean,
    
  259.   isErrored: boolean,
    
  260.   targetErrorBoundaryID: ?number,
    
  261. 
    
  262.   // Is this Suspense, and can its value be overridden now?
    
  263.   canToggleSuspense: boolean,
    
  264. 
    
  265.   // Can view component source location.
    
  266.   canViewSource: boolean,
    
  267. 
    
  268.   // Does the component have legacy context attached to it.
    
  269.   hasLegacyContext: boolean,
    
  270. 
    
  271.   // Inspectable properties.
    
  272.   context: Object | null,
    
  273.   hooks: Object | null,
    
  274.   props: Object | null,
    
  275.   state: Object | null,
    
  276.   key: number | string | null,
    
  277.   errors: Array<[string, number]>,
    
  278.   warnings: Array<[string, number]>,
    
  279. 
    
  280.   // List of owners
    
  281.   owners: Array<SerializedElement> | null,
    
  282. 
    
  283.   // Location of component in source code.
    
  284.   source: Source | null,
    
  285. 
    
  286.   type: ElementType,
    
  287. 
    
  288.   // Meta information about the root this element belongs to.
    
  289.   rootType: string | null,
    
  290. 
    
  291.   // Meta information about the renderer that created this element.
    
  292.   rendererPackageName: string | null,
    
  293.   rendererVersion: string | null,
    
  294. 
    
  295.   // UI plugins/visualizations for the inspected element.
    
  296.   plugins: Plugins,
    
  297. };
    
  298. 
    
  299. export const InspectElementErrorType = 'error';
    
  300. export const InspectElementFullDataType = 'full-data';
    
  301. export const InspectElementNoChangeType = 'no-change';
    
  302. export const InspectElementNotFoundType = 'not-found';
    
  303. 
    
  304. export type InspectElementError = {
    
  305.   id: number,
    
  306.   responseID: number,
    
  307.   type: 'error',
    
  308.   errorType: 'user' | 'unknown-hook' | 'uncaught',
    
  309.   message: string,
    
  310.   stack?: string,
    
  311. };
    
  312. 
    
  313. export type InspectElementFullData = {
    
  314.   id: number,
    
  315.   responseID: number,
    
  316.   type: 'full-data',
    
  317.   value: InspectedElement,
    
  318. };
    
  319. 
    
  320. export type InspectElementHydratedPath = {
    
  321.   id: number,
    
  322.   responseID: number,
    
  323.   type: 'hydrated-path',
    
  324.   path: Array<string | number>,
    
  325.   value: any,
    
  326. };
    
  327. 
    
  328. export type InspectElementNoChange = {
    
  329.   id: number,
    
  330.   responseID: number,
    
  331.   type: 'no-change',
    
  332. };
    
  333. 
    
  334. export type InspectElementNotFound = {
    
  335.   id: number,
    
  336.   responseID: number,
    
  337.   type: 'not-found',
    
  338. };
    
  339. 
    
  340. export type InspectedElementPayload =
    
  341.   | InspectElementError
    
  342.   | InspectElementFullData
    
  343.   | InspectElementHydratedPath
    
  344.   | InspectElementNoChange
    
  345.   | InspectElementNotFound;
    
  346. 
    
  347. export type InstanceAndStyle = {
    
  348.   instance: Object | null,
    
  349.   style: Object | null,
    
  350. };
    
  351. 
    
  352. type Type = 'props' | 'hooks' | 'state' | 'context';
    
  353. 
    
  354. export type RendererInterface = {
    
  355.   cleanup: () => void,
    
  356.   clearErrorsAndWarnings: () => void,
    
  357.   clearErrorsForFiberID: (id: number) => void,
    
  358.   clearWarningsForFiberID: (id: number) => void,
    
  359.   deletePath: (
    
  360.     type: Type,
    
  361.     id: number,
    
  362.     hookID: ?number,
    
  363.     path: Array<string | number>,
    
  364.   ) => void,
    
  365.   findNativeNodesForFiberID: FindNativeNodesForFiberID,
    
  366.   flushInitialOperations: () => void,
    
  367.   getBestMatchForTrackedPath: () => PathMatch | null,
    
  368.   getFiberForNative: (component: NativeType) => Fiber | null,
    
  369.   getFiberIDForNative: GetFiberIDForNative,
    
  370.   getDisplayNameForFiberID: GetDisplayNameForFiberID,
    
  371.   getInstanceAndStyle(id: number): InstanceAndStyle,
    
  372.   getProfilingData(): ProfilingDataBackend,
    
  373.   getOwnersList: (id: number) => Array<SerializedElement> | null,
    
  374.   getPathForElement: (id: number) => Array<PathFrame> | null,
    
  375.   getSerializedElementValueByPath: (
    
  376.     id: number,
    
  377.     path: Array<string | number>,
    
  378.   ) => ?string,
    
  379.   handleCommitFiberRoot: (fiber: Object, commitPriority?: number) => void,
    
  380.   handleCommitFiberUnmount: (fiber: Object) => void,
    
  381.   handlePostCommitFiberRoot: (fiber: Object) => void,
    
  382.   hasFiberWithId: (id: number) => boolean,
    
  383.   inspectElement: (
    
  384.     requestID: number,
    
  385.     id: number,
    
  386.     inspectedPaths: Object,
    
  387.     forceFullData: boolean,
    
  388.   ) => InspectedElementPayload,
    
  389.   logElementToConsole: (id: number) => void,
    
  390.   overrideError: (id: number, forceError: boolean) => void,
    
  391.   overrideSuspense: (id: number, forceFallback: boolean) => void,
    
  392.   overrideValueAtPath: (
    
  393.     type: Type,
    
  394.     id: number,
    
  395.     hook: ?number,
    
  396.     path: Array<string | number>,
    
  397.     value: any,
    
  398.   ) => void,
    
  399.   patchConsoleForStrictMode: () => void,
    
  400.   prepareViewAttributeSource: (
    
  401.     id: number,
    
  402.     path: Array<string | number>,
    
  403.   ) => void,
    
  404.   prepareViewElementSource: (id: number) => void,
    
  405.   renamePath: (
    
  406.     type: Type,
    
  407.     id: number,
    
  408.     hookID: ?number,
    
  409.     oldPath: Array<string | number>,
    
  410.     newPath: Array<string | number>,
    
  411.   ) => void,
    
  412.   renderer: ReactRenderer | null,
    
  413.   setTraceUpdatesEnabled: (enabled: boolean) => void,
    
  414.   setTrackedPath: (path: Array<PathFrame> | null) => void,
    
  415.   startProfiling: (recordChangeDescriptions: boolean) => void,
    
  416.   stopProfiling: () => void,
    
  417.   storeAsGlobal: (
    
  418.     id: number,
    
  419.     path: Array<string | number>,
    
  420.     count: number,
    
  421.   ) => void,
    
  422.   unpatchConsoleForStrictMode: () => void,
    
  423.   updateComponentFilters: (componentFilters: Array<ComponentFilter>) => void,
    
  424. 
    
  425.   // Timeline profiler interface
    
  426. 
    
  427.   ...
    
  428. };
    
  429. 
    
  430. export type Handler = (data: any) => void;
    
  431. 
    
  432. // Renderers use these APIs to report profiling data to DevTools at runtime.
    
  433. // They get passed from the DevTools backend to the reconciler during injection.
    
  434. export type DevToolsProfilingHooks = {
    
  435.   // Scheduling methods:
    
  436.   markRenderScheduled: (lane: Lane) => void,
    
  437.   markStateUpdateScheduled: (fiber: Fiber, lane: Lane) => void,
    
  438.   markForceUpdateScheduled: (fiber: Fiber, lane: Lane) => void,
    
  439. 
    
  440.   // Work loop level methods:
    
  441.   markRenderStarted: (lanes: Lanes) => void,
    
  442.   markRenderYielded: () => void,
    
  443.   markRenderStopped: () => void,
    
  444.   markCommitStarted: (lanes: Lanes) => void,
    
  445.   markCommitStopped: () => void,
    
  446.   markLayoutEffectsStarted: (lanes: Lanes) => void,
    
  447.   markLayoutEffectsStopped: () => void,
    
  448.   markPassiveEffectsStarted: (lanes: Lanes) => void,
    
  449.   markPassiveEffectsStopped: () => void,
    
  450. 
    
  451.   // Fiber level methods:
    
  452.   markComponentRenderStarted: (fiber: Fiber) => void,
    
  453.   markComponentRenderStopped: () => void,
    
  454.   markComponentErrored: (
    
  455.     fiber: Fiber,
    
  456.     thrownValue: mixed,
    
  457.     lanes: Lanes,
    
  458.   ) => void,
    
  459.   markComponentSuspended: (
    
  460.     fiber: Fiber,
    
  461.     wakeable: Wakeable,
    
  462.     lanes: Lanes,
    
  463.   ) => void,
    
  464.   markComponentLayoutEffectMountStarted: (fiber: Fiber) => void,
    
  465.   markComponentLayoutEffectMountStopped: () => void,
    
  466.   markComponentLayoutEffectUnmountStarted: (fiber: Fiber) => void,
    
  467.   markComponentLayoutEffectUnmountStopped: () => void,
    
  468.   markComponentPassiveEffectMountStarted: (fiber: Fiber) => void,
    
  469.   markComponentPassiveEffectMountStopped: () => void,
    
  470.   markComponentPassiveEffectUnmountStarted: (fiber: Fiber) => void,
    
  471.   markComponentPassiveEffectUnmountStopped: () => void,
    
  472. };
    
  473. 
    
  474. export type DevToolsBackend = {
    
  475.   Agent: Class<Agent>,
    
  476.   Bridge: Class<BackendBridge>,
    
  477.   initBackend: InitBackend,
    
  478.   setupNativeStyleEditor?: SetupNativeStyleEditor,
    
  479. };
    
  480. 
    
  481. export type DevToolsHook = {
    
  482.   listeners: {[key: string]: Array<Handler>, ...},
    
  483.   rendererInterfaces: Map<RendererID, RendererInterface>,
    
  484.   renderers: Map<RendererID, ReactRenderer>,
    
  485.   backends: Map<string, DevToolsBackend>,
    
  486. 
    
  487.   emit: (event: string, data: any) => void,
    
  488.   getFiberRoots: (rendererID: RendererID) => Set<Object>,
    
  489.   inject: (renderer: ReactRenderer) => number | null,
    
  490.   on: (event: string, handler: Handler) => void,
    
  491.   off: (event: string, handler: Handler) => void,
    
  492.   reactDevtoolsAgent?: ?Object,
    
  493.   sub: (event: string, handler: Handler) => () => void,
    
  494. 
    
  495.   // Used by react-native-web and Flipper/Inspector
    
  496.   resolveRNStyle?: ResolveNativeStyle,
    
  497.   nativeStyleEditorValidAttributes?: $ReadOnlyArray<string>,
    
  498. 
    
  499.   // React uses these methods.
    
  500.   checkDCE: (fn: Function) => void,
    
  501.   onCommitFiberUnmount: (rendererID: RendererID, fiber: Object) => void,
    
  502.   onCommitFiberRoot: (
    
  503.     rendererID: RendererID,
    
  504.     fiber: Object,
    
  505.     // Added in v16.9 to support Profiler priority labels
    
  506.     commitPriority?: number,
    
  507.     // Added in v16.9 to support Fast Refresh
    
  508.     didError?: boolean,
    
  509.   ) => void,
    
  510. 
    
  511.   // Timeline internal module filtering
    
  512.   getInternalModuleRanges: () => Array<[string, string]>,
    
  513.   registerInternalModuleStart: (moduleStartError: Error) => void,
    
  514.   registerInternalModuleStop: (moduleStopError: Error) => void,
    
  515. 
    
  516.   // Testing
    
  517.   dangerous_setTargetConsoleForTesting?: (fakeConsole: Object) => void,
    
  518. 
    
  519.   ...
    
  520. };
    
  521. 
    
  522. export type ConsolePatchSettings = {
    
  523.   appendComponentStack: boolean,
    
  524.   breakOnConsoleErrors: boolean,
    
  525.   showInlineWarningsAndErrors: boolean,
    
  526.   hideConsoleLogsInStrictMode: boolean,
    
  527.   browserTheme: BrowserTheme,
    
  528. };