/** * 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 */ import * as React from 'react'; import {Fragment, useContext, useState, use} from 'react'; import {BridgeContext, StoreContext} from '../context'; import InspectedElementBadges from './InspectedElementBadges'; import InspectedElementContextTree from './InspectedElementContextTree'; import InspectedElementErrorsAndWarningsTree from './InspectedElementErrorsAndWarningsTree'; import InspectedElementHooksTree from './InspectedElementHooksTree'; import InspectedElementPropsTree from './InspectedElementPropsTree'; import InspectedElementStateTree from './InspectedElementStateTree'; import InspectedElementStyleXPlugin from './InspectedElementStyleXPlugin'; import InspectedElementSuspendedBy from './InspectedElementSuspendedBy'; import NativeStyleEditor from './NativeStyleEditor'; import FetchFileWithCachingContext from './FetchFileWithCachingContext'; import {enableStyleXFeatures} from 'react-devtools-feature-flags'; import InspectedElementSourcePanel from './InspectedElementSourcePanel'; import StackTraceView, {IgnoreListToggleButton} from './StackTraceView'; import OwnerView from './OwnerView'; import Skeleton from './Skeleton'; import { ElementTypeSuspense, ElementTypeActivity, } from 'react-devtools-shared/src/frontend/types'; import {symbolicateSourceWithCache} from 'react-devtools-shared/src/symbolicateSource'; import styles from './InspectedElementView.css'; import type { Element, InspectedElement, } from 'react-devtools-shared/src/frontend/types'; import type {HookNames} from 'react-devtools-shared/src/frontend/types'; import type {ToggleParseHookNames} from './InspectedElementContext'; import type {SourceMappedLocation} from 'react-devtools-shared/src/symbolicateSource'; type StackTraceGroupProps = { children: (showIgnoreList: boolean) => React.Node, componentStack: InspectedElement['stack'], owners: InspectedElement['owners'], }; function StackTraceGroup({ children, componentStack, owners, }: StackTraceGroupProps): React.Node { const [showIgnoreList, setShowIgnoreList] = useState(false); const fetchFileWithCaching = useContext(FetchFileWithCachingContext); const componentStackHasIgnoredFrames = componentStack !== null && componentStack.some(callSite => { const [, virtualURL, virtualLine, virtualColumn] = callSite; // symbolicated output is cached const symbolicatedCallSite: null | SourceMappedLocation = fetchFileWithCaching !== null ? use( symbolicateSourceWithCache( fetchFileWithCaching, virtualURL, virtualLine, virtualColumn, ), ) : null; return symbolicatedCallSite !== null && symbolicatedCallSite.ignored; }); const ownerStacksHaveIgnoredFrames = owners !== null && owners.some(owner => { return ( owner.stack !== null && owner.stack.some(callSite => { const [, virtualURL, virtualLine, virtualColumn] = callSite; // symbolicated output is cached const symbolicatedCallSite: null | SourceMappedLocation = fetchFileWithCaching !== null ? use( symbolicateSourceWithCache( fetchFileWithCaching, virtualURL, virtualLine, virtualColumn, ), ) : null; return symbolicatedCallSite !== null && symbolicatedCallSite.ignored; }) ); }); const hasIgnoredFrames = componentStackHasIgnoredFrames || ownerStacksHaveIgnoredFrames; return ( <> {children(showIgnoreList)} {hasIgnoredFrames && ( setShowIgnoreList(prev => !prev)} showIgnoreList={showIgnoreList} /> )} ); } type Props = { element: Element, hookNames: HookNames | null, inspectedElement: InspectedElement, parseHookNames: boolean, toggleParseHookNames: ToggleParseHookNames, symbolicatedSourcePromise: Promise, }; export default function InspectedElementView({ element, hookNames, inspectedElement, parseHookNames, toggleParseHookNames, symbolicatedSourcePromise, }: Props): React.Node { const { stack, owners, rendererPackageName, rendererVersion, rootType, source, nativeTag, type, } = inspectedElement; const bridge = useContext(BridgeContext); const store = useContext(StoreContext); const rendererLabel = rendererPackageName !== null && rendererVersion !== null ? `${rendererPackageName}@${rendererVersion}` : null; const showOwnersList = owners !== null && owners.length > 0; const showStack = stack != null && stack.length > 0; const showRenderedBy = showStack || showOwnersList || rendererLabel !== null || rootType !== null; const propsSection = (
); return (
{ // For Suspense and Activity we show the props further down. type !== ElementTypeSuspense && type !== ElementTypeActivity ? propsSection : null }
{enableStyleXFeatures && (
)}
{ // For Suspense and Activity we show the props below suspended by to give that more priority. type !== ElementTypeSuspense && type !== ElementTypeActivity ? null : propsSection } {showRenderedBy && (
rendered by
}> {(showIgnoreList: boolean) => ( <> {showStack ? ( ) : null} {showOwnersList && owners?.map(owner => ( {owner.stack != null && owner.stack.length > 0 ? ( ) : null} ))} {rootType !== null && (
{rootType}
)} {rendererLabel !== null && (
{rendererLabel}
)} )}
)} {source != null && (
)}
); }