Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions examples/swr-devtools-demo/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,18 @@ const DevToolsArea = () => {
);
};

const fetcher = async (url) => {
const res = await fetch(url);
const json = await res.json();
if (res.ok) {
return json;
}
throw new Error(json.message);
};

function MyApp({ Component, pageProps }) {
return (
<SWRConfig
value={{
fetcher: (url) => fetch(url).then((r) => r.json()),
}}
>
<SWRConfig value={{ fetcher }}>
<SWRDevTools>
<Component {...pageProps} />
<DevToolsArea />
Expand Down
6 changes: 5 additions & 1 deletion examples/swr-devtools-demo/pages/api/hello.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@ let counter = 1;

export default (req, res) => {
++counter;
res.status(200).json({ name: `Hello World ${counter}` });
if (req.query.error) {
res.status(500).json({ message: "this is an error message" });
} else {
res.status(200).json({ name: `Hello World ${counter}` });
}
};
9 changes: 7 additions & 2 deletions examples/swr-devtools-demo/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import useSWR from "swr";
import { useEffect } from "react";

export default function Home() {
const { data, mutate } = useSWR("/api/hello");
// const { data, mutate } = useSWR("/api/hello?error=true");
const { data, mutate, error } = useSWR(
`/api/hello${typeof window !== "undefined" ? location.search : ""}`
);
const { data: data2 } = useSWR("/api/hello?foo");

useEffect(() => {
Expand Down Expand Up @@ -36,7 +39,9 @@ export default function Home() {
<section>
<p className={styles.row}>
<span className={styles.cacheKey}>/api/hello</span>
<span>{data ? data.name : "...loading"}</span>
{!data && !error && <span>...loading</span>}
{data && <span>{data.name}</span>}
{error && <span>Error: {error.message}</span>}
<span className={styles.note}>(auto increment in 5 seconds)</span>
</p>
<p className={styles.row}>
Expand Down
1 change: 0 additions & 1 deletion examples/swr-devtools-demo/tsconfig.tsbuildinfo

This file was deleted.

59 changes: 44 additions & 15 deletions packages/swr-devtools-panel/src/components/CacheData.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,42 @@
import React, { lazy, Suspense } from "react";
import { ReactJsonViewProps } from "react-json-view";
import styled from "styled-components";
import { SWRCacheData } from "swr-devtools/lib/swr-cache";
import { CacheKey } from "./CacheKey";
import { ErrorLabel } from "./ErrorLabel";

type Props = {
data: SWRCacheData;
};

export const CacheData = React.memo(({ data }: Props) => (
<>
<Wrapper>
<Title>
{data.error && <ErrorLabel />}
<CacheKey cacheKey={data.key} />
&nbsp;
<TimestampText>{data.timestampString}</TimestampText>
</Title>
<DataWrapper>
<CacheDataView data={data.data} />
{data.error && <ErrorText>{data.error}</ErrorText>}
{data.data && <CacheDataView data={data.data} />}
{data.error && <ErrorData error={data.error} />}
</DataWrapper>
</>
</Wrapper>
));
CacheData.displayName = "CacheData";

const DataWrapper = styled.div`
border-bottom: solid 1px var(--swr-devtools-border-color);
font-size: 1rem;
height: 100%;
margin: 0;
padding: 0 0.3rem;
`;
const ErrorData = ({ error }: { error: string | ReactJsonViewProps }) => (
<ErrorWrapper>
<ErrorTitle>ERROR</ErrorTitle>
{typeof error === "string" ? (
<ErrorText>{error}</ErrorText>
) : (
<CacheDataView data={error} />
)}
</ErrorWrapper>
);

const CacheDataView = ({ data }: Props) => {
const CacheDataView = ({ data }: { data: ReactJsonViewProps }) => {
if (typeof window === "undefined") return null;
return (
<Suspense fallback="loading">
Expand All @@ -39,7 +45,7 @@ const CacheDataView = ({ data }: Props) => {
);
};

const AsyncReactJson = ({ data }: Props) => {
const AsyncReactJson = ({ data }: { data: ReactJsonViewProps }) => {
const ReactJson = lazy(() => import("react-json-view"));
return (
<ReactJson
Expand All @@ -53,13 +59,36 @@ const AsyncReactJson = ({ data }: Props) => {
);
};

const Wrapper = styled.div`
padding: 0.2rem;
`;

const DataWrapper = styled.div`
font-size: 0.8rem;
height: 100%;
margin: 0;
padding: 0 0.3rem;
`;

const ErrorWrapper = styled.div`
margin-top: 1rem;
padding: 0.5rem 0;
`;

const ErrorText = styled.p`
color: var(--swr-devtools-error-text-colora);
margin: 0;
color: var(--swr-devtools-text-color);
`;

const ErrorTitle = styled.h4`
margin: 0;
padding: 0.3rem 0;
color: var(--swr-devtools-text-color);
`;

const Title = styled.h3`
margin: 0;
padding: 1rem 0.5rem;
padding: 0.5rem 0rem;
color: var(--swr-devtools-text-color);
`;

Expand Down
11 changes: 8 additions & 3 deletions packages/swr-devtools-panel/src/components/CacheKey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@ import {
export const CacheKey = ({ cacheKey }: { cacheKey: string }) => {
if (isInfiniteCache(cacheKey)) {
return (
<span>
<CacheText>
<CacheTag>Infinite</CacheTag>
{getInfiniteCacheKey(cacheKey)}
</span>
</CacheText>
);
}
return <span>{cacheKey}</span>;
return <CacheText>{cacheKey}</CacheText>;
};

const CacheText = styled.span`
display: inline-block;
padding: 0.3rem;
`;

const CacheTag = styled.b`
margin-right: 0.3rem;
padding: 0.2rem;
Expand Down
10 changes: 10 additions & 0 deletions packages/swr-devtools-panel/src/components/ErrorLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from "react";
import styled from "styled-components";

export const ErrorLabel = () => <ErrorLabelWrapper>ERROR</ErrorLabelWrapper>;

const ErrorLabelWrapper = styled.span`
padding: 0.2rem;
background-color: var(--swr-devtools-error-text-color);
color: var(--swr-devtools-text-color);
`;
10 changes: 7 additions & 3 deletions packages/swr-devtools-panel/src/components/Panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CacheData } from "./CacheData";
import { CacheKey } from "./CacheKey";
import { useDevToolsCache } from "../devtools-cache";
import { SearchInput } from "./SearchInput";
import { ErrorLabel } from "./ErrorLabel";

export const Panel = ({
cache,
Expand Down Expand Up @@ -40,7 +41,7 @@ export const Panel = ({
<CacheItems>
{cacheData
.filter(({ key }) => filterText === "" || key.includes(filterText))
.map(({ key, timestampString, timestamp }) => (
.map(({ key, error, timestampString, timestamp }) => (
<CacheItem
key={`${type}--${key}--${
type === "history" ? timestamp.getTime() : ""
Expand All @@ -54,6 +55,7 @@ export const Panel = ({
<CacheItemButton
onClick={() => onSelectItem({ key, timestamp })}
>
{error && <ErrorLabel />}
<CacheKey cacheKey={key} /> ({timestampString})
</CacheItemButton>
</CacheItem>
Expand Down Expand Up @@ -99,10 +101,12 @@ const CacheItem = styled.li<{ isSelected: boolean }>`
`;

const CacheItemButton = styled.button`
display: inline-block;
display: flex;
align-items: center;
gap: 2px;
width: 100%;
height: 100%;
padding: 0.5rem 0;
padding: 0.2rem 0;
border: none;
background: transparent;
color: var(--swr-devtools-text-color);
Expand Down
18 changes: 15 additions & 3 deletions packages/swr-devtools-panel/src/devtools-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Cache } from "swr";
import {
injectSWRCache,
isMetaCache,
isErrorCache,
getErrorCacheKey,
SWRCacheData,
} from "swr-devtools/lib/swr-cache";

Expand Down Expand Up @@ -62,15 +64,25 @@ const sortCacheDataFromLatest = (cacheData: Map<string, any>) => {
.reverse();
};

// TODO: this is a draft implementation
const toJSON = (value: any) => {
return value instanceof Error ? { message: value.message } : value;
};

const retrieveCache = (
key: string,
key_: string,
value: any
): [SWRCacheData[], SWRCacheData[]] => {
const date = new Date();

const data = toJSON(value);

const isErrorCache_ = isErrorCache(key_);
const key = isErrorCache_ ? getErrorCacheKey(key_) : key_;

const payload = isErrorCache_ ? { key, error: data } : { key, data };
currentCacheData.set(key, {
key,
data: value,
...payload,
timestamp: date,
timestampString: formatTime(date),
});
Expand Down
9 changes: 7 additions & 2 deletions packages/swr-devtools/src/SWRDevTools.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useLayoutEffect } from "react";
import { useSWRConfig, SWRConfig, Middleware, Cache } from "swr";

import { injectSWRCache, isMetaCache } from "./swr-cache";
import { injectSWRCache, isMetaCache, isErrorCache } from "./swr-cache";

const injected = new WeakSet();

Expand All @@ -17,6 +17,11 @@ export type DevToolsMessage =
type: "initialized";
};

// TOOD: we have to support more types
const convertToSerializableObject = (value: any) => {
return value instanceof Error ? { message: value.message } : value;
};

const inject = (cache: Cache) =>
injectSWRCache(cache, (key: string, value: any) => {
if (isMetaCache(key)) {
Expand All @@ -27,7 +32,7 @@ const inject = (cache: Cache) =>
type: "updated_swr_cache",
payload: {
key,
value,
value: convertToSerializableObject(value),
},
},
"*"
Expand Down
11 changes: 10 additions & 1 deletion packages/swr-devtools/src/swr-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@ export const injectSWRCache = (
};

export const isMetaCache = (key: string) => {
return /^\$(?:req|err|ctx|len)\$/.test(key);
return /^\$(?:req|ctx|len)\$/.test(key);
};

export const isErrorCache = (key: string) => {
return /^\$err\$/.test(key);
};

export const getErrorCacheKey = (key: string) => {
const match = key.match(/^\$err\$(?<cacheKey>.*)?/);
return match?.groups?.cacheKey ?? key;
};

export const isInfiniteCache = (key: string) => {
Expand Down