Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/experimental' into supplemental-…
Browse files Browse the repository at this point in the history
…recordings
  • Loading branch information
bhackett1024 committed Sep 13, 2024
2 parents e79961c + 9acf667 commit c69ef05
Show file tree
Hide file tree
Showing 22 changed files with 820 additions and 146 deletions.
9 changes: 6 additions & 3 deletions packages/accordion/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, {
FC,
MouseEventHandler,
ReactElement,
ReactNode,
useEffect,
useReducer,
useRef,
Expand Down Expand Up @@ -75,9 +76,9 @@ export function AccordionPane({
dispatch = () => ({}),
expanded,
header,
headerNode,
height = 0,
index,
initialHeight,
isBeingResized = false,
isResizable = false,
onResizeStart = () => ({}),
Expand All @@ -90,9 +91,9 @@ export function AccordionPane({
dispatch?: Dispatch<AccordionAction>;
expanded?: boolean;
header: string;
headerNode?: ReactNode;
height?: SectionHeight;
index?: number;
initialHeight?: number;
isBeingResized?: boolean;
isResizable?: boolean;
onResizeStart?: (e: React.MouseEvent) => void;
Expand Down Expand Up @@ -128,7 +129,9 @@ export function AccordionPane({
>
<div className="flex select-none items-center space-x-2">
<div className={classNames("img arrow", { expanded: _expanded })} />
<span className="overflow-hidden overflow-ellipsis whitespace-pre">{header}</span>
<span className="overflow-hidden overflow-ellipsis whitespace-pre">
{headerNode ?? header}
</span>
</div>
{button ? button : null}
</div>
Expand Down
5 changes: 1 addition & 4 deletions packages/replay-next/components/console/StackRenderer.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { CallStack, Frame, FrameId } from "@replayio/protocol";
import React, { Suspense, memo, useContext } from "react";
import { Suspense, memo } from "react";

import Loader from "replay-next/components/Loader";
import { ReplayClientContext } from "shared/client/ReplayClientContext";

import Source from "./Source";
import styles from "./StackRenderer.module.css";
Expand All @@ -26,8 +25,6 @@ function StackRenderer({ frames, stack }: { frames: Frame[]; stack: CallStack })
}

function StackFrameRenderer({ frameId, frames }: { frameId: FrameId; frames: Frame[] }) {
const client = useContext(ReplayClientContext);

const frame = frames.find(frame => frame.frameId === frameId);
if (frame == null) {
return null;
Expand Down
19 changes: 18 additions & 1 deletion packages/shared/client/ReplayClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ import throttle from "lodash/throttle";
import uniqueId from "lodash/uniqueId";

// eslint-disable-next-line no-restricted-imports
import { addEventListener, client, initSocket, removeEventListener } from "protocol/socket";
import { addEventListener, removeEventListener } from "protocol/socket";
// eslint-disable-next-line no-restricted-imports
import { client, initSocket, sendMessage } from "protocol/socket";
import { assert, compareExecutionPoints, defer, waitForTime, transformSupplementalId, breakdownSupplementalId } from "protocol/utils";
import { initProtocolMessagesStore } from "replay-next/components/protocol/ProtocolMessagesStore";
import { insert } from "replay-next/src/utils/array";
Expand All @@ -87,6 +89,8 @@ import { isPointInRegion, isRangeInRegions } from "shared/utils/time";

import {
AnnotationListener,
DependencyChainStep,
DependencyGraphMode,
ReplayClientEvents,
ReplayClientInterface,
SourceLocationRange,
Expand Down Expand Up @@ -1310,6 +1314,19 @@ export class ReplayClient implements ReplayClientInterface {
return this.focusWindow;
}

async getDependencies(
point: ExecutionPoint,
mode?: DependencyGraphMode
): Promise<DependencyChainStep[]> {
const sessionId = await this.waitForSession();
const result = await sendMessage(
"Session.experimentalCommand",
{ name: "analyzeDependencies", params: { point, mode } },
sessionId
);
return result.rval.dependencies;
}

_dispatchEvent(type: ReplayClientEvents, ...args: any[]): void {
const handlers = this._eventHandlers.get(type);
if (handlers) {
Expand Down
140 changes: 140 additions & 0 deletions packages/shared/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,142 @@ export interface SupplementalSession extends SupplementalRecording {
sessionId: string;
}

// Copied from `src/analysis/dependencies/dependencyDescription.ts` in the backend

export interface URLLocation {
line: number;
column: number;
url: string;
}

export type DependencyChainStepInfo =
| {
// The document has started to load.
code: "DocumentBeginLoad";
url: string;
}
| {
// A script in a document began execution after all other required
// resources were received.
code: "DocumentExecuteBlockedScript";
url: string;
}
| {
// A script in a document began execution after being downloaded.
code: "DocumentExecuteScript";
url: string;
}
| {
// A script in a document has been scheduled for async compilation.
code: "DocumentAsyncCompileScript";
url: string;
}
| {
// A network request referenced by a document's contents was initiated.
code: "DocumentInitiateNetworkRequest";
url: string;
}
| {
// A script triggered a network request.
code: "ScriptInitiateNetworkRequest";
url: string;
}
| {
// Some data has been received over the network.
code: "NetworkReceiveData";
numBytes: number;
}
| {
// A network resource finished being received.
code: "NetworkReceiveResource";
}
| {
// Event handlers for user input were called.
code: "DispatchInputEventHandler";
type: string;
}
| {
// A script created a new websocket.
code: "ScriptCreateWebSocket";
url: string;
}
| {
// A websocket connected and open handlers were called.
code: "WebSocketConnected";
}
| {
// A script sent a message over a websocket.
code: "ScriptSendWebSocketMessage";
}
| {
// A websocket message determined to be a response to an earlier message
// was received and message handlers were called.
code: "WebSocketMessageReceived";
}
| {
// A promise settled and its then/catch hooks were called.
code: "PromiseSettled";
}
| {
// React hydration has started.
code: "ReactHydrateRoot";
}
| {
// React has rendered a component.
code: "ReactRender";
calleeLocation?: URLLocation;
}
| {
// React was able to resume rendering after a suspense promise resolved.
code: "ReactResumeSuspendedRender";
}
| {
// An application render function returned an existing element object for
// converting into a component.
code: "ReactReturnElement";
}
| {
// An application render function created an element object for converting
// into a component.
code: "ReactCreateElement";
}
| {
// An application render function called setState().
code: "ReactCallSetState";
}
| {
// A React external store triggered a rerender.
code: "ReactExternalStoreRerender";
}
| {
// An application render function called useEffect().
code: "ReactCallUseEffect";
}
| {
// An effect hook is called for the first time after the original useEffect().
code: "ReactEffectFirstCall";
calleeLocation?: URLLocation;
}
| {
code: "UnknownNode";
node: unknown;
}
| {
code: "UnknownEdge";
edge: unknown;
};

export type DependencyChainStep = DependencyChainStepInfo & {
time?: number;
point?: ExecutionPoint;
};

export enum DependencyGraphMode {
// Renders of a fiber depend on the last time the parent of that fiber was
// rendered, instead of whatever triggered the fiber's render.
ReactParentRenders = "ReactParentRenders",
}

export interface ReplayClientInterface {
get loadedRegions(): LoadedRegions | null;
addEventListener(type: ReplayClientEvents, handler: Function): void;
Expand Down Expand Up @@ -314,5 +450,9 @@ export interface ReplayClientInterface {
}) => void,
onSourceContentsChunk: ({ chunk, sourceId }: { chunk: string; sourceId: SourceId }) => void
): Promise<void>;
getDependencies(
point: ExecutionPoint,
mode?: DependencyGraphMode
): Promise<DependencyChainStep[]>;
waitForSession(): Promise<string>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export default function PrimaryPanes() {
className={classnames("sources-pane", enableLargeText ? "text-base" : "text-xs")}
expanded={!sourcesCollapsed}
onToggle={() => setSourcesCollapsed(!sourcesCollapsed)}
initialHeight={400}
button={<QuickOpenButton />}
>
<SourcesTree />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.Panel {
display: flex;
flex-direction: column;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { ExecutionPoint, Location } from "@replayio/protocol";
import { Suspense, useContext } from "react";

import { PanelLoader } from "replay-next/components/PanelLoader";
import { sourcesByIdCache, sourcesByUrlCache } from "replay-next/src/suspense/SourcesCache";
import { getSourceToDisplayForUrl } from "replay-next/src/utils/sources";
import { suspendInParallel } from "replay-next/src/utils/suspense";
import { ReplayClientContext } from "shared/client/ReplayClientContext";
import { DependencyGraphMode } from "shared/client/types";
import { depGraphCache } from "ui/suspense/depGraphCache";

import { Item } from "./Item";
import styles from "./DependencyGraph.module.css";

type Props = {
mode?: DependencyGraphMode;
point: ExecutionPoint | undefined;
};

export function DependencyGraph(props: Props) {
return (
<Suspense fallback={<PanelLoader />}>
<DependencyGraphSuspends {...props} />
</Suspense>
);
}

function DependencyGraphSuspends({ mode, point }: Props) {
const replayClient = useContext(ReplayClientContext);

const [depGraphValue, sourcesById, sourcesByUrl] = suspendInParallel(
() => depGraphCache.read(replayClient, point ?? null, mode ?? null),
() => sourcesByIdCache.read(replayClient),
() => sourcesByUrlCache.read(replayClient)
);

const valueDescending = depGraphValue?.slice().reverse();

return (
<div className={styles.Panel}>
{valueDescending?.map((entry, index) => {
let location: Location | null = null;
if ("calleeLocation" in entry && entry.calleeLocation != null) {
const source = getSourceToDisplayForUrl(
sourcesById,
sourcesByUrl,
entry.calleeLocation.url
);
if (source) {
location = {
...entry.calleeLocation,
sourceId: source.sourceId,
};
}
}

return (
<Item
key={index}
location={location}
name={entry.code}
timeStampedPoint={
entry.point != null && entry.time != null
? {
point: entry.point,
time: entry.time,
}
: null
}
/>
);
})}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.Item {
padding: 0.25rem 0.5rem;
display: flex;
flex-direction: row;
gap: 0.25rem;
white-space: nowrap;
}
.Item:hover {
background-color: var(--theme-selection-background-hover);
cursor: pointer;
}
.Item[data-disabled] {
color: var(--color-dim);
}
.Item[data-disabled]:hover {
background-color: transparent;
cursor: default;
}
.Item[data-selected],
.Item[data-selected]:hover {
background-color: var(--theme-selection-background);
color: white;
}

.NameColumn {
overflow: hidden;
text-overflow: ellipsis;
}

.TimestampColumn {
flex: 1;
font-size: var(--font-size-small);
overflow: hidden;
text-overflow: ellipsis;
}

.LocationColumn {
color: var(--color-dim);
flex-grow: 0;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.Item[data-selected] .LocationColumn {
color: white;
}
Loading

0 comments on commit c69ef05

Please sign in to comment.