Skip to content

Commit d1a12ed

Browse files
committed
feat: implement GitHub Pages deployment workflow and enhance routing with base path support
1 parent b628202 commit d1a12ed

8 files changed

Lines changed: 360 additions & 17 deletions

File tree

.github/workflows/pages.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: GitHub Pages
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: pages
15+
cancel-in-progress: false
16+
17+
jobs:
18+
build:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v6
22+
- uses: pnpm/action-setup@v6
23+
- uses: actions/setup-node@v6
24+
with:
25+
node-version: 24.15.0
26+
cache: pnpm
27+
- name: Install dependencies
28+
run: pnpm install
29+
- name: Build
30+
run: pnpm run build
31+
env:
32+
BASE_PATH: /${{ github.event.repository.name }}/
33+
PUBLIC_MAPTILER_KEY: ${{ secrets.PUBLIC_MAPTILER_KEY }}
34+
- name: SPA fallback for GitHub Pages
35+
run: cp dist/index.html dist/404.html
36+
- name: Configure Pages
37+
uses: actions/configure-pages@v5
38+
- name: Upload artifact
39+
uses: actions/upload-pages-artifact@v3
40+
with:
41+
path: dist
42+
43+
deploy:
44+
runs-on: ubuntu-latest
45+
needs: build
46+
environment:
47+
name: github-pages
48+
url: ${{ steps.deployment.outputs.page_url }}
49+
steps:
50+
- name: Deploy to GitHub Pages
51+
id: deployment
52+
uses: actions/deploy-pages@v4

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@
2626
- **Internationalization:** Structure for multi-language support
2727
- **Production-Ready:** Optimized build process and best practices implementation
2828

29+
## GitHub Pages
30+
31+
Pushing a version tag matching `v*` runs [`.github/workflows/pages.yml`](./.github/workflows/pages.yml), which builds with `BASE_PATH=/<repository-name>/`, copies `dist/index.html` to `dist/404.html` for SPA routing, and deploys to GitHub Pages. In the repository **Settings → Pages**, set **Source** to **GitHub Actions** once.
32+
33+
```bash
34+
BASE_PATH=/react-tanstack/ pnpm run build && pnpm preview
35+
```
36+
37+
PowerShell:
38+
39+
```powershell
40+
$env:BASE_PATH="/react-tanstack/"; pnpm run build; pnpm preview
41+
```
42+
2943
## 💻 Tech Stack
3044

3145
- **Framework:** React 19.x

rsbuild.config.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,28 @@ import tailwind from "@tailwindcss/postcss";
66
import { TanStackRouterRspack } from "@tanstack/router-plugin/rspack";
77
import TurboConsole from "unplugin-turbo-console/rspack";
88

9+
function normalizeBasePath(): string {
10+
const raw = process.env.BASE_PATH?.trim();
11+
if (!raw || raw === "/") {
12+
return "/";
13+
}
14+
const prefixed = raw.startsWith("/") ? raw : `/${raw}`;
15+
return prefixed.endsWith("/") ? prefixed : `${prefixed}/`;
16+
}
17+
18+
const basePath = normalizeBasePath();
19+
const useSubpath = basePath !== "/";
20+
921
const enableRsdoctor = Boolean(process.env.RSDOCTOR);
1022
const enableTurboConsole = process.env.NODE_ENV === "development";
1123

1224
export default defineConfig({
25+
...(useSubpath
26+
? {
27+
server: { base: basePath },
28+
output: { assetPrefix: basePath },
29+
}
30+
: {}),
1331
performance: {
1432
...(enableRsdoctor ? { buildCache: false } : {}),
1533
},

src/components/root-component.tsx

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
1-
import { Outlet } from "@tanstack/react-router";
1+
import { Link, Outlet } from "@tanstack/react-router";
22
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
33
import * as React from "react";
44

55
export function RootComponent() {
66
return (
77
<React.Fragment>
8-
<TanStackRouterDevtools />
9-
<div>Hello "__root"!</div>
8+
<header className="border-b border-slate-800 bg-[#070b18]/95 px-4 py-3 text-sm text-slate-300 backdrop-blur">
9+
<nav className="mx-auto flex max-w-4xl flex-wrap items-center gap-4" aria-label="主导航">
10+
<Link
11+
to="/"
12+
className="rounded px-2 py-1 font-medium text-emerald-300 hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-400"
13+
>
14+
首页
15+
</Link>
16+
<Link
17+
to="/about"
18+
className="rounded px-2 py-1 font-medium text-emerald-300 hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-400"
19+
>
20+
关于
21+
</Link>
22+
</nav>
23+
</header>
1024
<Outlet />
25+
{import.meta.env.DEV ? <TanStackRouterDevtools /> : null}
1126
</React.Fragment>
1227
);
1328
}

src/index.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,24 @@ if (rootEl) {
2222
root.render(
2323
<React.StrictMode>
2424
<QueryClientProvider client={queryClient}>
25-
<TanStackDevtools plugins={[
26-
{
27-
name: "TanStack Query",
28-
render: <ReactQueryDevtoolsPanel />,
29-
defaultOpen: true,
30-
},
31-
{
32-
name: "TanStack Router",
33-
render: <TanStackRouterDevtoolsPanel />,
34-
defaultOpen: false,
35-
},
36-
]}
37-
/>
25+
{import.meta.env.DEV
26+
? (
27+
<TanStackDevtools
28+
plugins={[
29+
{
30+
name: "TanStack Query",
31+
render: <ReactQueryDevtoolsPanel />,
32+
defaultOpen: false,
33+
},
34+
{
35+
name: "TanStack Router",
36+
render: <TanStackRouterDevtoolsPanel />,
37+
defaultOpen: false,
38+
},
39+
]}
40+
/>
41+
)
42+
: null}
3843
<RouterProvider router={router} />
3944
</QueryClientProvider>
4045
</React.StrictMode>,

src/router.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import { createRouter } from "@tanstack/react-router";
22
import { routeTree } from "./routeTree.gen.ts";
33

4-
export const router = createRouter({ routeTree });
4+
const basepath = (import.meta.env.BASE_URL ?? "/").replace(/\/$/, "") || "/";
5+
6+
export const router = createRouter({
7+
routeTree,
8+
basepath,
9+
});

src/routes/about.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/* eslint-disable react-refresh/only-export-components -- TanStack file route */
2+
import { createFileRoute, Link } from "@tanstack/react-router";
3+
4+
export const Route = createFileRoute("/about")({
5+
component: AboutShowcase,
6+
});
7+
8+
function AboutShowcase() {
9+
return (
10+
<div className="min-h-dvh bg-[#0b1021] px-6 py-14 text-slate-100">
11+
<a
12+
href="#about-main"
13+
className="absolute left-4 top-4 z-50 -translate-y-[220%] rounded-md bg-emerald-500 px-3 py-2 text-sm font-semibold text-slate-950 transition focus:translate-y-0 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-emerald-300"
14+
>
15+
跳到主要内容
16+
</a>
17+
<main id="about-main" className="mx-auto max-w-2xl">
18+
<h1 className="text-3xl font-semibold text-white">关于本演示</h1>
19+
<p className="mt-4 leading-relaxed text-slate-400">
20+
此路由用于验证在 GitHub Pages 子路径下,TanStack Router 的 HTML5 history 与 basepath 是否一致工作(依赖部署后的
21+
{" "}
22+
<code className="rounded bg-slate-800 px-1.5 py-0.5 text-emerald-300">404.html</code>
23+
{" "}
24+
回退)。首页技术图标说明见
25+
{" "}
26+
<a
27+
href="https://icon-sets.iconify.design/logos/"
28+
target="_blank"
29+
rel="noreferrer"
30+
className="font-medium text-emerald-300 underline decoration-emerald-700 underline-offset-2 hover:text-emerald-200"
31+
>
32+
Iconify logos
33+
</a>
34+
35+
</p>
36+
<Link
37+
to="/"
38+
className="mt-8 inline-flex min-h-11 items-center rounded-lg border border-slate-600 px-5 py-2.5 text-sm font-medium text-emerald-200 transition hover:border-emerald-500/60 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-emerald-400"
39+
>
40+
返回首页
41+
</Link>
42+
</main>
43+
</div>
44+
);
45+
}

0 commit comments

Comments
 (0)