Skip to content

Commit 2baf2b7

Browse files
fix(site): derive base from GITHUB_REPOSITORY and fix 404 redirect target
Addresses Copilot review feedback on PR #11. vite.config.ts: Production `base` is no longer hardcoded to /coder-skill-scanner/. Resolves via: 1. VITE_BASE_PATH env (explicit override) 2. GITHUB_REPOSITORY (CI default; forks Just Work with zero config) 3. / (apex / local-build fallback) Dev keeps / so the local Vite server and report proxy work without env juggling. public/404.html + new rewrite-public-base-url Vite plugin: The GitHub Pages SPA-fallback page redirected to /?p=... (apex), which bounced users off the project page and onto the github.io apex instead of bringing them back into the SPA. Use %BASE_URL% as the redirect prefix and substitute it at build time via a tiny plugin (Vite handles the substitution for index.html but copies public/ verbatim). Verified with four build runs (GITHUB_REPOSITORY=coder/coder-skill-scanner, GITHUB_REPOSITORY=someone-else/my-scanner, VITE_BASE_PATH override, no env). In every case the bases emitted in dist/index.html and the target string in dist/404.html match.
1 parent a401700 commit 2baf2b7

2 files changed

Lines changed: 62 additions & 9 deletions

File tree

site/public/404.html

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@
55
<title>Coder Skill Scanner</title>
66
<script>
77
// GitHub Pages SPA fallback: capture the original path in a query
8-
// parameter and bounce to index.html, which restores it before React
9-
// Router boots. See site/src/main.tsx for the restore step.
10-
var target = "/?p=" + encodeURIComponent(
8+
// parameter and bounce to the SPA index, which restores it before
9+
// React Router boots. See site/src/main.tsx for the restore step.
10+
//
11+
// `%BASE_URL%` is substituted at build time by the
12+
// `rewrite-public-base-url` Vite plugin (see site/vite.config.ts),
13+
// so the bounce lands on the correct base prefix for both apex
14+
// sites (`/`) and project pages (`/<repo>/`).
15+
var target = "%BASE_URL%?p=" + encodeURIComponent(
1116
window.location.pathname + window.location.search
1217
) + window.location.hash;
1318
window.location.replace(target);

site/vite.config.ts

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { defineConfig, loadEnv } from "vite";
1+
import { defineConfig, loadEnv, type PluginOption } from "vite";
22
import react from "@vitejs/plugin-react-swc";
3+
import fs from "node:fs";
4+
import path from "node:path";
35

46
/**
57
* The React app expects to fetch `latest.json`, `schema.json`, and
@@ -14,16 +16,62 @@ import react from "@vitejs/plugin-react-swc";
1416
*/
1517
const REPORT_REGEX = /^\/(latest\.json|schema\.json|history\/.+\.json)$/;
1618

19+
/**
20+
* Resolve the production `base` path. Lookup priority:
21+
* 1. `VITE_BASE_PATH` env (explicit override; useful for local prod
22+
* builds when you want to mimic a specific Pages deployment).
23+
* 2. The repo name parsed from `GITHUB_REPOSITORY` (CI default; a fork
24+
* named `<owner>/<repo>` gets `/<repo>/` automatically with zero
25+
* config).
26+
* 3. `/` (apex / local-build fallback).
27+
*
28+
* Always returns a value with leading and trailing slashes so Vite's
29+
* own asset URL logic does not have to special-case the input.
30+
*/
31+
function resolveProductionBase(): string {
32+
const explicit = process.env.VITE_BASE_PATH?.trim();
33+
if (explicit) {
34+
const prefixed = explicit.startsWith("/") ? explicit : `/${explicit}`;
35+
return prefixed.endsWith("/") ? prefixed : `${prefixed}/`;
36+
}
37+
const repo = process.env.GITHUB_REPOSITORY?.split("/")[1]?.trim();
38+
if (repo) return `/${repo}/`;
39+
return "/";
40+
}
41+
42+
/**
43+
* Vite already substitutes `%BASE_URL%` in `index.html`, but it copies
44+
* everything under `public/` verbatim. This plugin runs after the build
45+
* finishes and substitutes the same placeholder in `dist/404.html` so
46+
* the GitHub Pages SPA-fallback redirect targets the right base prefix
47+
* regardless of which fork is publishing.
48+
*/
49+
function rewritePublicBaseUrl(base: string): PluginOption {
50+
return {
51+
name: "rewrite-public-base-url",
52+
apply: "build",
53+
writeBundle() {
54+
const targets = ["404.html"];
55+
for (const name of targets) {
56+
const file = path.resolve("dist", name);
57+
if (!fs.existsSync(file)) continue;
58+
const content = fs.readFileSync(file, "utf8");
59+
fs.writeFileSync(file, content.replace(/%BASE_URL%/g, base));
60+
}
61+
},
62+
};
63+
}
64+
1765
export default defineConfig(({ mode, command }) => {
1866
const env = loadEnv(mode, process.cwd(), "");
1967
const upstream = env.VITE_REPORT_UPSTREAM || "http://localhost:8765";
68+
const base = command === "build" ? resolveProductionBase() : "/";
2069

2170
return {
22-
// GitHub Pages serves this site at /coder-skill-scanner/. Production
23-
// builds emit asset URLs relative to that base. Dev keeps the default
24-
// `/` so the local Vite server and its report proxy keep working.
25-
base: command === "build" ? "/coder-skill-scanner/" : "/",
26-
plugins: [react()],
71+
// See `resolveProductionBase` above. Dev keeps `/` so the local Vite
72+
// server and its report proxy keep working without env juggling.
73+
base,
74+
plugins: [react(), rewritePublicBaseUrl(base)],
2775
server: {
2876
host: "0.0.0.0",
2977
port: 5173,

0 commit comments

Comments
 (0)