Skip to content

Commit

Permalink
refactor: move entry id from context to url
Browse files Browse the repository at this point in the history
  • Loading branch information
byCedric committed Mar 31, 2024
1 parent f951a99 commit 37b303b
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 144 deletions.
76 changes: 5 additions & 71 deletions webui/src/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,75 +1,9 @@
import { useQuery } from '@tanstack/react-query';
import { Redirect } from 'expo-router';

import type { EntryGraphData } from '~/app/api/stats/[entry]/modules/index+api';
import { Page, PageHeader, PageTitle } from '~/components/Page';
import { StatsModuleFilter } from '~/components/forms/StatsModuleFilter';
import { TreemapGraph } from '~/components/graphs/TreemapGraph';
import {
type ModuleFilters,
useModuleFilterContext,
filtersToUrlParams,
} from '~/providers/modules';
import { useStatsEntryContext } from '~/providers/stats';
import { Tag } from '~/ui/Tag';
import { fetchApi } from '~/utils/api';
import { formatFileSize } from '~/utils/formatString';
import { useStatsEntry } from '~/providers/stats';

export default function GraphScreen() {
const { entryId } = useStatsEntryContext();
const { filters } = useModuleFilterContext();
export default function HomeScreen() {
const { entry } = useStatsEntry();

const graph = useBundleGraphData(entryId, filters);

return (
<Page variant="viewport">
<div className="flex flex-1 flex-col">
<PageHeader>
<PageTitle>
<h1 className="text-lg font-bold mr-4">Bundle</h1>
{!!graph.data && <BundleSummary data={graph.data} />}
</PageTitle>
<StatsModuleFilter />
</PageHeader>
<TreemapGraph key={`bundle-graph-${entryId}`} modules={graph.data?.data?.modules ?? []} />
</div>
</Page>
);
}

function BundleSummary({ data }: { data: EntryGraphData }) {
return (
<div className="font-sm text-secondary inline-block">
<Tag variant={data.metadata.platform} />
<span className="text-tertiary mx-2 select-none">-</span>
<span>{data.metadata.modulesCount} modules</span>
<span className="text-tertiary mx-2 select-none">-</span>
<span>{formatFileSize(data.metadata.size)}</span>
{data.metadata.modulesCount !== data.data.modulesCount && (
<div className="text-tertiary italic inline">
<span className="mx-2 select-none"></span>
<span className="mr-2 select-none italic ">visible:</span>
<span>{data.data.modulesCount} modules</span>
<span className="mx-2 select-none">-</span>
<span>{formatFileSize(data.data.size)}</span>
</div>
)}
</div>
);
}

/** Load the bundle graph data from API, with default or custom filters */
function useBundleGraphData(entryId: string, filters?: ModuleFilters) {
return useQuery<EntryGraphData>({
queryKey: [`bundle-graph`, entryId, filters],
queryFn: ({ queryKey }) => {
const [_key, entry, filters] = queryKey as [string, string, ModuleFilters | undefined];
const url = filters
? `/api/stats/${entry}/modules?${filtersToUrlParams(filters)}`
: `/api/stats/${entry}/modules`;

return fetchApi(url)
.then((res) => (res.ok ? res : Promise.reject(res)))
.then((res) => res.json());
},
});
return <Redirect href={{ pathname: '/stats/[entry]/', params: { entry: entry.id } }} />;
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { useQuery } from '@tanstack/react-query';
import { useLocalSearchParams } from 'expo-router';

import { type FolderGraphData } from '../api/stats/[entry]/folders/index+api';

import { type FolderGraphData } from '~/app/api/stats/[entry]/folders/index+api';
import { Page, PageHeader, PageTitle } from '~/components/Page';
import { TreemapGraph } from '~/components/graphs/TreemapGraph';
import { useStatsEntryContext } from '~/providers/stats';
import { useStatsEntry } from '~/providers/stats';
import { Skeleton } from '~/ui/Skeleton';
import { Tag } from '~/ui/Tag';
import { fetchApi } from '~/utils/api';
import { formatFileSize } from '~/utils/formatString';
import { relativeEntryPath } from '~/utils/stats';
import { type PartialStatsEntry } from '~core/data/types';

export default function FolderPage() {
const { entryId, entry, entryFilePath } = useStatsEntryContext();
const { entry } = useStatsEntry();
const { path: absolutePath } = useLocalSearchParams<{ path: string }>();
const folder = useFolderData(entryId, absolutePath!);
const folder = useFolderData(entry.id, absolutePath!);

if (folder.isLoading) {
return <FolderPageSkeleton />;
Expand All @@ -39,15 +39,15 @@ export default function FolderPage() {
className="text-slate-50 font-bold text-lg mr-4"
title={folder.data.metadata.folderPath}
>
{entryFilePath(folder.data.metadata.folderPath)}/
{relativeEntryPath(entry, folder.data.metadata.folderPath)}/
</h1>
<FolderSummary platform={entry?.platform} folder={folder.data.metadata} />
</PageTitle>
</PageHeader>

<TreemapGraph
key={`folder-graph-${entryId}`}
name={`.../${entryFilePath(folder.data.metadata.folderName)}`}
key={`folder-graph-${entry.id}`}
name={`.../${relativeEntryPath(entry, folder.data.metadata.folderName)}`}
modules={folder.data?.data?.modules ?? []}
/>
</div>
Expand Down
75 changes: 75 additions & 0 deletions webui/src/app/stats/[entry]/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useQuery } from '@tanstack/react-query';

import type { EntryGraphData } from '~/app/api/stats/[entry]/modules/index+api';
import { Page, PageHeader, PageTitle } from '~/components/Page';
import { StatsModuleFilter } from '~/components/forms/StatsModuleFilter';
import { TreemapGraph } from '~/components/graphs/TreemapGraph';
import {
type ModuleFilters,
useModuleFilterContext,
filtersToUrlParams,
} from '~/providers/modules';
import { useStatsEntry } from '~/providers/stats';
import { Tag } from '~/ui/Tag';
import { fetchApi } from '~/utils/api';
import { formatFileSize } from '~/utils/formatString';

export default function StatsScreen() {
const { entry } = useStatsEntry();
const { filters } = useModuleFilterContext();

const graph = useBundleGraphData(entry.id, filters);

return (
<Page variant="viewport">
<div className="flex flex-1 flex-col">
<PageHeader>
<PageTitle>
<h1 className="text-lg font-bold mr-4">Bundle</h1>
{!!graph.data && <BundleSummary data={graph.data} />}
</PageTitle>
<StatsModuleFilter />
</PageHeader>
<TreemapGraph key={`bundle-graph-${entry.id}`} modules={graph.data?.data?.modules ?? []} />
</div>
</Page>
);
}

function BundleSummary({ data }: { data: EntryGraphData }) {
return (
<div className="font-sm text-secondary inline-block">
<Tag variant={data.metadata.platform} />
<span className="text-tertiary mx-2 select-none">-</span>
<span>{data.metadata.modulesCount} modules</span>
<span className="text-tertiary mx-2 select-none">-</span>
<span>{formatFileSize(data.metadata.size)}</span>
{data.metadata.modulesCount !== data.data.modulesCount && (
<div className="text-tertiary italic inline">
<span className="mx-2 select-none"></span>
<span className="mr-2 select-none italic ">visible:</span>
<span>{data.data.modulesCount} modules</span>
<span className="mx-2 select-none">-</span>
<span>{formatFileSize(data.data.size)}</span>
</div>
)}
</div>
);
}

/** Load the bundle graph data from API, with default or custom filters */
function useBundleGraphData(entryId: string, filters?: ModuleFilters) {
return useQuery<EntryGraphData>({
queryKey: [`bundle-graph`, entryId, filters],
queryFn: ({ queryKey }) => {
const [_key, entry, filters] = queryKey as [string, string, ModuleFilters | undefined];
const url = filters
? `/api/stats/${entry}/modules?${filtersToUrlParams(filters)}`
: `/api/stats/${entry}/modules`;

return fetchApi(url)
.then((res) => (res.ok ? res : Promise.reject(res)))
.then((res) => res.json());
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import { useQuery } from '@tanstack/react-query';
import { Link, useLocalSearchParams } from 'expo-router';

import { Page, PageHeader, PageTitle } from '~/components/Page';
import { useStatsEntryContext } from '~/providers/stats';
import { useStatsEntry } from '~/providers/stats';
import { CodeBlock, CodeBlockSectionWithPrettier, guessLanguageFromPath } from '~/ui/CodeBlock';
import { Skeleton } from '~/ui/Skeleton';
import { Tag } from '~/ui/Tag';
import { fetchApi } from '~/utils/api';
import { formatFileSize } from '~/utils/formatString';
import { relativeEntryPath } from '~/utils/stats';
import { type PartialStatsEntry, type StatsModule } from '~core/data/types';

export default function ModulePage() {
const { entryId, entry, entryFilePath } = useStatsEntryContext();
const { entry } = useStatsEntry();
const { path: absolutePath } = useLocalSearchParams<{ path: string }>();
const module = useModuleData(entryId, absolutePath!);
const module = useModuleData(entry.id, absolutePath!);

const outputCode = module.data?.output?.map((output) => output.data.code).join('\n');

Expand All @@ -35,7 +36,7 @@ export default function ModulePage() {
<PageHeader>
<PageTitle>
<h1 className="text-slate-50 font-bold text-lg mr-4" title={module.data.path}>
{entryFilePath(module.data.path)}
{relativeEntryPath(entry, module.data.path)}
</h1>
<ModuleSummary platform={entry?.platform} module={module.data} />
</PageTitle>
Expand All @@ -50,9 +51,12 @@ export default function ModulePage() {
<li key={path} className="ml-4">
<Link
className="text-link hover:underline"
href={{ pathname: '/modules/[path]', params: { path } }}
href={{
pathname: '/stats/[entry]/modules/[path]',
params: { entry: entry.id, path },
}}
>
{entryFilePath(path)}
{relativeEntryPath(entry, path)}
</Link>
</li>
))}
Expand Down
27 changes: 12 additions & 15 deletions webui/src/components/forms/StatsEntrySelect.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import * as Select from '@radix-ui/react-select';
import cn from 'classnames';
import { useRouter } from 'expo-router';
// @ts-expect-error
import ChevronDownIcon from 'lucide-react/dist/esm/icons/chevron-down';
// @ts-expect-error
import ChevronUpIcon from 'lucide-react/dist/esm/icons/chevron-up';

import { useStatsEntryContext } from '~/providers/stats';
import { useStatsEntry } from '~/providers/stats';
import { Button } from '~/ui/Button';
import { Tag } from '~/ui/Tag';
import { relativeEntryPath } from '~/utils/stats';

export function StatsEntrySelect() {
const { entryId, setEntryId, entry, entries } = useStatsEntryContext();

function onEntryChange(value: string) {
setEntryId(value);
}
const router = useRouter();
const { entry, entries } = useStatsEntry();

return (
<Select.Root value={String(entryId)} onValueChange={onEntryChange}>
<Select.Root value={entry.id} onValueChange={(entry) => router.setParams({ entry })}>
<Select.Trigger asChild>
<Button variant="quaternary" size="sm">
{!!entry && <Tag variant={entry?.platform} size="xs" className="mr-2" />}
<Tag variant={entry.platform} size="xs" className="mr-2" />
<Select.Value placeholder="Select bundle to inspect" />
<Select.Icon className="text-icon-default">
<ChevronDownIcon size={16} className="m-1 mr-0 align-middle" />
Expand All @@ -41,14 +40,12 @@ export function StatsEntrySelect() {
<ChevronUpIcon />
</Select.ScrollUpButton>
<Select.Viewport className="SelectViewport">
{entries?.data?.map((entry) => (
<div key={entry.id}>
<Select.Item value={String(entry.id)} asChild>
{entries.map((item) => (
<div key={item.id}>
<Select.Item value={item.id} asChild>
<Button variant="quaternary" size="sm" className="w-full">
<Tag variant={entry.platform} className="mr-2" />
<Select.ItemText>
{entry.entryPoint.replace(entry.projectRoot + '/', '')}
</Select.ItemText>
<Tag variant={item.platform} className="mr-2" />
<Select.ItemText>{relativeEntryPath(entry, item.entryPoint)}</Select.ItemText>
</Button>
</Select.Item>
</div>
Expand Down
28 changes: 19 additions & 9 deletions webui/src/components/graphs/TreemapGraph.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import * as echarts from 'echarts';
import { useRouter } from 'expo-router';
import { useMemo } from 'react';
import { useCallback, useMemo } from 'react';

import { Graph } from './Graph';

import type { ModuleMetadata } from '~/app/api/stats/[entry]/modules/index+api';
import { useStatsEntry } from '~/providers/stats';
import { formatFileSize } from '~/utils/formatString';

type TreemapGraphProps = {
Expand All @@ -18,16 +19,23 @@ const ICON_STRINGS = {
pkg: `<svg fill="white" xmlns="http://www.w3.org/2000/svg" height="16" viewBox="0 -960 960 960" width="16"><path d="M440-183v-274L200-596v274l240 139Zm80 0 240-139v-274L520-457v274Zm-40-343 237-137-237-137-237 137 237 137ZM160-252q-19-11-29.5-29T120-321v-318q0-22 10.5-40t29.5-29l280-161q19-11 40-11t40 11l280 161q19 11 29.5 29t10.5 40v318q0 22-10.5 40T800-252L520-91q-19 11-40 11t-40-11L160-252Zm320-228Z"/></svg>`,
};

export function TreemapGraph(props: TreemapGraphProps) {
function useInspectCallback() {
const router = useRouter();
const { entry } = useStatsEntry();

return useCallback(
(type: 'folder' | 'module', path: string) => {
router.push({
pathname:
type === 'module' ? '/stats/[entry]/modules/[path]' : '/stats/[entry]/folders/[path]',
params: { entry: entry.id, path },
});
},
[entry.id]
);
}

function onInspectPath(type: 'folder' | 'module', absolutePath: string) {
router.push({
pathname: type === 'module' ? '/modules/[path]' : '/folders/[path]',
params: { path: absolutePath },
});
}

export function TreemapGraph(props: TreemapGraphProps) {
const { data, maxDepth, maxNodeModules } = useMemo(
() => createModuleTree(props.modules.filter((module) => module.path.startsWith('/'))),
[props.modules]
Expand Down Expand Up @@ -55,6 +63,8 @@ export function TreemapGraph(props: TreemapGraphProps) {
...getLabelObj({ multiLevel: false }),
};

const onInspectPath = useInspectCallback();

return (
<Graph
theme="dark"
Expand Down
Loading

0 comments on commit 37b303b

Please sign in to comment.