Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
pivanov committed Jan 17, 2025
1 parent 090db9b commit 81b1176
Show file tree
Hide file tree
Showing 10 changed files with 762 additions and 175 deletions.
1 change: 0 additions & 1 deletion packages/scan/src/new-outlines/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,6 @@ export const initReactScanInstrumentation = () => {
if (!isOverlayPaused) {
outlineFiber(fiber);
}

if (ReactScanInternals.options.value.log) {
// this can be expensive given enough re-renders
log(renders);
Expand Down
192 changes: 117 additions & 75 deletions packages/scan/src/web/components/inspector/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { signal } from '@preact/signals';
import { type Fiber, getFiberId } from 'bippy';
import type { Fiber } from 'bippy';
import { Component } from 'preact';
import { useEffect, useRef } from 'preact/hooks';
import { Store } from '~core/index';
Expand All @@ -9,18 +8,20 @@ import { constant } from '~web/utils/preact/constant';
import { Icon } from '../icon';
import { flashManager } from './flash-overlay';
import {
type InspectorData,
collectInspectorData,
getStateNames,
resetStateTracking,
} from './overlay/utils';
import { PropertySection } from './propeties';
import {
TIMELINE_MAX_UPDATES,
type TimelineUpdate,
inspectorState,
timelineState,
} from './states';
import { getCompositeFiberFromElement } from './utils';
import { WhatChanged } from './what-changed';

interface InspectorState extends InspectorData {
fiber: Fiber | null;
}

export const globalInspectorState = {
lastRendered: new Map<string, unknown>(),
expandedPaths: new Set<string>(),
Expand All @@ -38,23 +39,20 @@ export const globalInspectorState = {
},
};

export const inspectorState = signal<InspectorState>({
fiber: null,
fiberProps: { current: [], changes: new Set() },
fiberState: { current: [], changes: new Set() },
fiberContext: { current: [], changes: new Set() },
});

// todo: add reset button and error message
class InspectorErrorBoundary extends Component {
state: { error: Error | null; hasError: boolean } = { hasError: false, error: null };
state: { error: Error | null; hasError: boolean } = {
hasError: false,
error: null,
};

static getDerivedStateFromError(e: Error) {
return { hasError: true, error: e };
}

handleReset = () => {
this.setState({ hasError: false, error: null });
globalInspectorState.cleanup();
};

render() {
Expand Down Expand Up @@ -85,42 +83,52 @@ class InspectorErrorBoundary extends Component {

export const Inspector = constant(() => {
const refLastInspectedFiber = useRef<Fiber | null>(null);

const refPendingUpdates = useRef<Set<Fiber>>(new Set<Fiber>());
const isSettingsOpen = signalIsSettingsOpen.value;

useEffect(() => {
let isProcessing = false;
const pendingUpdates = new Set<Fiber>();

const processNextUpdate = () => {
if (pendingUpdates.size === 0) {
isProcessing = false;
return;
}
const processUpdate = () => {
if (refPendingUpdates.current.size === 0) return;

const nextFiber = Array.from(pendingUpdates)[0];
pendingUpdates.delete(nextFiber);
const fiber = Array.from(refPendingUpdates.current)[0];
refPendingUpdates.current.clear();

try {
refLastInspectedFiber.current = nextFiber;
const collectedData = collectInspectorData(nextFiber);
const timeline = timelineState.value;
if (timeline.isReplaying) {
const update = timeline.updates[timeline.currentIndex];
refLastInspectedFiber.current = update.fiber;

inspectorState.value = {
fiber: nextFiber,
...collectedData,
fiber: update.fiber,
fiberProps: update.props,
fiberState: update.state,
fiberContext: update.context,
};
} finally {
if (pendingUpdates.size > 0) {
queueMicrotask(processNextUpdate);
} else {
isProcessing = false;
}
timelineState.value.isReplaying = false;
return;
}

refLastInspectedFiber.current = fiber;
inspectorState.value = {
fiber,
...collectInspectorData(fiber),
};

if (refPendingUpdates.current.size > 0) {
queueMicrotask(processUpdate);
}
};

const scheduleUpdate = (fiber: Fiber) => {
refPendingUpdates.current.add(fiber);
queueMicrotask(processUpdate);
};

const unSubState = Store.inspectState.subscribe((state) => {
if (state.kind !== 'focused' || !state.focusedDomElement) {
pendingUpdates.clear();
refPendingUpdates.current.clear();
refLastInspectedFiber.current = null;
globalInspectorState.cleanup();
return;
}

Expand All @@ -133,35 +141,19 @@ export const Inspector = constant(() => {
);
if (!parentCompositeFiber) return;

pendingUpdates.clear();
globalInspectorState.cleanup();
refLastInspectedFiber.current = parentCompositeFiber;

const { fiberProps, fiberState, fiberContext } =
collectInspectorData(parentCompositeFiber);

inspectorState.value = {
fiber: parentCompositeFiber,
fiberProps: {
...fiberProps,
changes: new Set(),
},
fiberState: {
...fiberState,
changes: new Set(),
},
fiberContext: {
...fiberContext,
changes: new Set(),
},
};
if (refLastInspectedFiber.current?.type !== parentCompositeFiber.type) {
refPendingUpdates.current.clear();
globalInspectorState.cleanup();
scheduleUpdate(parentCompositeFiber);
}
});

const unSubReport = Store.lastReportTime.subscribe(() => {

const unSubLastReportTime = Store.lastReportTime.subscribe(() => {
const inspectState = Store.inspectState.value;
if (inspectState.kind !== 'focused') {
pendingUpdates.clear();
if (inspectState.kind !== 'focused' || !inspectState.focusedDomElement) {
refPendingUpdates.current.clear();
refLastInspectedFiber.current = null;
globalInspectorState.cleanup();
return;
}

Expand All @@ -175,26 +167,76 @@ export const Inspector = constant(() => {
return;
}

if (parentCompositeFiber.type === refLastInspectedFiber.current?.type) {
pendingUpdates.add(parentCompositeFiber);
scheduleUpdate(parentCompositeFiber);

requestAnimationFrame(() => {
if (!element.isConnected) {
refPendingUpdates.current.clear();
refLastInspectedFiber.current = null;
globalInspectorState.cleanup();
Store.inspectState.value = {
kind: 'inspecting',
hoveredDomElement: null,
};
}
});
});

const unSubInspectorState = inspectorState.subscribe((state) => {
if (!state.fiber || !refLastInspectedFiber.current) return;
if (state.fiber.type !== refLastInspectedFiber.current.type) return;
if (timelineState.value.isReplaying) return;

const update: TimelineUpdate = {
fiber: { ...state.fiber },
timestamp: Date.now(),
props: state.fiberProps,
state: state.fiberState,
context: state.fiberContext,
stateNames: getStateNames(state.fiber),
};

if (!isProcessing) {
isProcessing = true;
queueMicrotask(processNextUpdate);
const { updates, currentIndex, totalUpdates } = timelineState.value;
let newUpdates: TimelineUpdate[];

if (updates.length >= TIMELINE_MAX_UPDATES) {
if (currentIndex < updates.length - 1) {
newUpdates = [...updates.slice(0, currentIndex + 1), update].slice(
-TIMELINE_MAX_UPDATES,
);
} else {
newUpdates = [...updates.slice(1), update];
}
} else {
if (currentIndex < updates.length - 1) {
newUpdates = [...updates.slice(0, currentIndex + 1), update];
} else {
newUpdates = [...updates, update];
}
}

const newIndex = newUpdates.length - 1;
const newTotal =
currentIndex < updates.length - 1
? totalUpdates - (updates.length - currentIndex - 1) + 1
: totalUpdates + 1;

timelineState.value = {
...timelineState.value,
updates: newUpdates,
currentIndex: newIndex,
totalUpdates: newTotal,
};
});

return () => {
unSubState();
unSubReport();
pendingUpdates.clear();
unSubLastReportTime();
unSubInspectorState();
refPendingUpdates.current.clear();
globalInspectorState.cleanup();
resetStateTracking();
};
}, []);
const fiber = inspectorState.value.fiber;
const fiberID = fiber ? getFiberId(fiber) : null;

return (
<InspectorErrorBoundary>
Expand All @@ -212,7 +254,7 @@ export const Inspector = constant(() => {
},
)}
>
<WhatChanged key={fiberID} />
<WhatChanged />
<PropertySection title="Props" section="props" />
<PropertySection title="State" section="state" />
<PropertySection title="Context" section="context" />
Expand Down
18 changes: 7 additions & 11 deletions packages/scan/src/web/components/inspector/overlay/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ import {
ClassComponentTag,
type ContextDependency,
type Fiber,
// type Fiber,
ForwardRefTag,
FunctionComponentTag,
MemoComponentTag,
type MemoizedState,
// type MemoizedState,
SimpleMemoComponentTag,
} from 'bippy';
import { isEqual } from '~core/utils';
Expand Down Expand Up @@ -223,13 +221,6 @@ export const resetStateTracking = () => {
contextChangeCounts.clear();
};

export const getStateChangeCount = (name: string): number =>
stateChangeCounts.get(name) ?? 0;
export const getPropsChangeCount = (name: string): number =>
propsChangeCounts.get(name) ?? 0;
export const getContextChangeCount = (name: string): number =>
contextChangeCounts.get(name) ?? 0;

export const getStateNames = (fiber: Fiber): Array<string> => {
const componentSource = fiber.type?.toString?.() || '';
return componentSource
Expand Down Expand Up @@ -281,6 +272,7 @@ interface ExtendedMemoizedState extends MemoizedState {

export const getStateFromFiber = (fiber: Fiber) => {
if (!fiber) return {};

// only funtional components have memo tags,
if (
fiber.tag === FunctionComponentTag ||
Expand Down Expand Up @@ -343,9 +335,10 @@ const getPropsOrder = (fiber: Fiber): Array<string> => {
.filter(Boolean);
};

interface SectionData {
export interface SectionData {
current: Array<{ name: string; value: unknown }>;
changes: Set<string>;
changesCounts?: Map<string, number>;
}

export interface InspectorData {
Expand All @@ -371,7 +364,6 @@ export const collectInspectorData = (recvFiber: Fiber): InspectorData => {

if (fiber.memoizedProps) {
const baseProps = fiber.memoizedProps;

const orderedProps = getPropsOrder(fiber);
const remainingProps = new Set(Object.keys(baseProps));

Expand Down Expand Up @@ -446,6 +438,7 @@ export const collectInspectorData = (recvFiber: Fiber): InspectorData => {
}),
),
changes: new Set<string>(),
changesCounts: new Map(),
};

const contexts = getAllFiberContexts(fiber);
Expand All @@ -456,6 +449,7 @@ export const collectInspectorData = (recvFiber: Fiber): InspectorData => {
value: ctx.value,
})),
changes: collectedChanges,
changesCounts: new Map(),
};

return {
Expand All @@ -465,6 +459,7 @@ export const collectInspectorData = (recvFiber: Fiber): InspectorData => {
name,
value,
})),
changesCounts: propsChangeCounts,
},
fiberState,
// todo: show the whole chain to reliably know context values
Expand All @@ -479,6 +474,7 @@ interface ContextInfo {
displayName: string;
contextType: unknown;
}

export const getAllFiberContexts = (
fiber: Fiber,
): Map<unknown, ContextInfo> => {
Expand Down
3 changes: 2 additions & 1 deletion packages/scan/src/web/components/inspector/propeties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import { isEqual } from '~core/utils';
import { CopyToClipboard } from '~web/components/copy-to-clipboard';
import { Icon } from '~web/components/icon';
import { cn } from '~web/utils/helpers';
import { globalInspectorState, inspectorState } from '.';
import { globalInspectorState } from '.';
import { flashManager } from './flash-overlay';
import {
getCurrentFiberState,
isPromise,
} from './overlay/utils';
import { inspectorState } from './states';
import {
detectValueType,
formatForClipboard,
Expand Down
Loading

0 comments on commit 81b1176

Please sign in to comment.