Skip to content

Commit

Permalink
fix/router: handle trailing slash (#637)
Browse files Browse the repository at this point in the history
and history api fallback, especially for SSG.
  • Loading branch information
dai-shi authored Mar 31, 2024
1 parent 2ff401b commit 3b87654
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 123 deletions.
28 changes: 0 additions & 28 deletions examples/07_router/src/components/ErrorBoundary.tsx

This file was deleted.

6 changes: 1 addition & 5 deletions examples/07_router/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ import { StrictMode } from 'react';
import { createRoot, hydrateRoot } from 'react-dom/client';
import { Router } from 'waku/router/client';

import { ErrorBoundary } from './components/ErrorBoundary';

const rootElement = (
<StrictMode>
<ErrorBoundary fallback={(error) => <h1>{String(error)}</h1>}>
<Router />
</ErrorBoundary>
<Router />
</StrictMode>
);

Expand Down
28 changes: 0 additions & 28 deletions examples/10_fs-router/src/components/ErrorBoundary.tsx

This file was deleted.

6 changes: 1 addition & 5 deletions examples/10_fs-router/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ import { StrictMode } from 'react';
import { createRoot, hydrateRoot } from 'react-dom/client';
import { Router } from 'waku/router/client';

import { ErrorBoundary } from './components/ErrorBoundary';

const rootElement = (
<StrictMode>
<ErrorBoundary fallback={(error) => <h1>{String(error)}</h1>}>
<Router />
</ErrorBoundary>
<Router />
</StrictMode>
);

Expand Down
28 changes: 0 additions & 28 deletions examples/14_react-tweet/src/components/error-boundary.tsx

This file was deleted.

6 changes: 1 addition & 5 deletions examples/14_react-tweet/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,9 @@ import { StrictMode } from 'react';
import { createRoot, hydrateRoot } from 'react-dom/client';
import { Router } from 'waku/router/client';

import { ErrorBoundary } from './components/error-boundary';

const rootElement = (
<StrictMode>
<ErrorBoundary fallback={(error) => <h1>{String(error)}</h1>}>
<Router />
</ErrorBoundary>
<Router />
</StrictMode>
);

Expand Down
12 changes: 11 additions & 1 deletion packages/waku/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,17 @@ export const Slot = ({
if (!elementsPromise) {
throw new Error('Missing Root component');
}
const elements = use(elementsPromise);
let elements: Awaited<Elements>;
try {
elements = use(elementsPromise);
} catch (e) {
if (e instanceof Error) {
// HACK we assume any error as Not Found,
// probably caused by history api fallback
(e as any).statusCode = 404;
}
throw e;
}
if (!(id in elements)) {
if (fallback) {
return fallback;
Expand Down
20 changes: 1 addition & 19 deletions packages/waku/src/lib/plugins/vite-plugin-rsc-managed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,9 @@ import { Component, StrictMode } from 'react';
import { createRoot, hydrateRoot } from 'react-dom/client';
import { Router } from 'waku/router/client';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {};
}
static getDerivedStateFromError(error) {
return { error };
}
render() {
if ('error' in this.state) {
return this.props.fallback(this.state.error);
}
return this.props.children;
}
}
const rootElement = (
<StrictMode>
<ErrorBoundary fallback={(error) => <h1>{String(error)}</h1>}>
<Router />
</ErrorBoundary>
<Router />
</StrictMode>
);
Expand Down
49 changes: 45 additions & 4 deletions packages/waku/src/router/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import {
Component,
createContext,
createElement,
useCallback,
Expand Down Expand Up @@ -36,6 +37,15 @@ declare global {
}
}

const normalizeRoutePath = (path: string) => {
for (const suffix of ['/', '/index.html']) {
if (path.endsWith(suffix)) {
return path.slice(0, -suffix.length) || '/';
}
}
return path;
};

const parseRoute = (url: URL): RouteProps => {
if ((globalThis as any).__WAKU_ROUTER_404__) {
return { path: '/404', searchParams: new URLSearchParams() };
Expand All @@ -44,7 +54,7 @@ const parseRoute = (url: URL): RouteProps => {
if (searchParams.has(PARAM_KEY_SKIP)) {
console.warn(`The search param "${PARAM_KEY_SKIP}" is reserved`);
}
return { path: pathname, searchParams };
return { path: normalizeRoutePath(pathname), searchParams };
};

type ChangeRoute = (
Expand Down Expand Up @@ -425,9 +435,13 @@ export function Router({ routerData = DEFAULT_ROUTER_DATA }) {
.catch(() => {});
};
return createElement(
Root as FunctionComponent<Omit<ComponentProps<typeof Root>, 'children'>>,
{ initialInput, initialSearchParamsString, unstable_onFetchData },
createElement(InnerRouter, { routerData }),
ErrorBoundary,
null,
createElement(
Root as FunctionComponent<Omit<ComponentProps<typeof Root>, 'children'>>,
{ initialInput, initialSearchParamsString, unstable_onFetchData },
createElement(InnerRouter, { routerData }),
),
);
}

Expand Down Expand Up @@ -462,3 +476,30 @@ export function ServerRouter({
),
);
}

class ErrorBoundary extends Component<
{ children: ReactNode },
{ error?: unknown }
> {
constructor(props: { children: ReactNode }) {
super(props);
this.state = {};
}

static getDerivedStateFromError(error: unknown) {
return { error };
}

render() {
if ('error' in this.state) {
if (
this.state.error instanceof Error &&
(this.state.error as any).statusCode === 404
) {
return createElement('h1', null, 'Not Found');
}
return createElement('h1', null, String(this.state.error));
}
return this.props.children;
}
}

0 comments on commit 3b87654

Please sign in to comment.