chore: update to react router v8

This commit is contained in:
Aarnav Tale
2026-06-20 13:33:24 -04:00
parent 5d6eef5843
commit 846c030bc1
5 changed files with 368 additions and 427 deletions
+23 -17
View File
@@ -74,24 +74,30 @@ export async function dispose(): Promise<void> {
// TODO: `getLoadContext` is the right place to handle reverse proxy // TODO: `getLoadContext` is the right place to handle reverse proxy
// translation — better than doing it in the OIDC client because it // translation — better than doing it in the OIDC client because it
// applies to all requests, not just OIDC ones. // applies to all requests, not just OIDC ones.
function getLoadContext(request: Request, client: ClientAddress) {
ctx.auth.registerRequestClientAddress(request, client.address);
const routerContext = new RouterContextProvider();
routerContext.set(agentsContext, ctx.agents);
routerContext.set(appConfigContext, ctx.config);
routerContext.set(authContext, ctx.auth);
routerContext.set(dbContext, ctx.db);
routerContext.set(headscaleContext, ctx.headscale);
routerContext.set(headscaleApiKeyContext, ctx.headscaleApiKey);
routerContext.set(headscaleConfigContext, ctx.hs);
routerContext.set(headscaleLiveStoreContext, ctx.hsLive);
routerContext.set(integrationContext, ctx.integration);
routerContext.set(oidcContext, ctx.oidc);
routerContext.set(requestApiContext, ctx.apiForRequest);
return routerContext;
}
interface ClientAddress {
address?: string;
}
export default createRequestListener({ export default createRequestListener({
build, build,
mode: import.meta.env.MODE, mode: import.meta.env.MODE,
getLoadContext: (request, client) => { getLoadContext,
ctx.auth.registerRequestClientAddress(request, client.address);
const routerContext = new RouterContextProvider();
routerContext.set(agentsContext, ctx.agents);
routerContext.set(appConfigContext, ctx.config);
routerContext.set(authContext, ctx.auth);
routerContext.set(dbContext, ctx.db);
routerContext.set(headscaleContext, ctx.headscale);
routerContext.set(headscaleApiKeyContext, ctx.headscaleApiKey);
routerContext.set(headscaleConfigContext, ctx.hs);
routerContext.set(headscaleLiveStoreContext, ctx.hsLive);
routerContext.set(integrationContext, ctx.integration);
routerContext.set(oidcContext, ctx.oidc);
routerContext.set(requestApiContext, ctx.apiForRequest);
return routerContext;
},
}); });
+3 -4
View File
@@ -30,7 +30,7 @@
"@iconify/react": "^6.0.2", "@iconify/react": "^6.0.2",
"@kubernetes/client-node": "^1.4.0", "@kubernetes/client-node": "^1.4.0",
"@lezer/highlight": "^1.2.3", "@lezer/highlight": "^1.2.3",
"@react-router/node": "^7.18.0", "@react-router/node": "^8.0.1",
"@uiw/react-codemirror": "4.25.10", "@uiw/react-codemirror": "4.25.10",
"arktype": "^2.2.1", "arktype": "^2.2.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
@@ -45,8 +45,7 @@
"react-codemirror-merge": "4.25.10", "react-codemirror-merge": "4.25.10",
"react-dom": "19.2.7", "react-dom": "19.2.7",
"react-error-boundary": "^6.1.2", "react-error-boundary": "^6.1.2",
"react-router": "^7.18.0", "react-router": "^8.0.1",
"react-router-dom": "^7.18.0",
"restty": "^0.1.35", "restty": "^0.1.35",
"tailwind-merge": "3.6.0", "tailwind-merge": "3.6.0",
"ulidx": "2.4.1", "ulidx": "2.4.1",
@@ -54,7 +53,7 @@
"yaml": "2.9.0" "yaml": "2.9.0"
}, },
"devDependencies": { "devDependencies": {
"@react-router/dev": "^7.18.0", "@react-router/dev": "^8.0.1",
"@shopify/lang-jsonc": "^1.0.1", "@shopify/lang-jsonc": "^1.0.1",
"@tailwindcss/vite": "^4.3.1", "@tailwindcss/vite": "^4.3.1",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
+279 -346
View File
File diff suppressed because it is too large Load Diff
-5
View File
@@ -5,10 +5,5 @@ export default {
ssr: true, ssr: true,
future: { future: {
unstable_optimizeDeps: true, unstable_optimizeDeps: true,
v8_splitRouteModules: "enforce",
v8_middleware: true,
v8_viteEnvironmentApi: true,
v8_passThroughRequests: true,
v8_trailingSlashAwareDataRequests: true,
}, },
} satisfies Config; } satisfies Config;
+63 -55
View File
@@ -10,6 +10,7 @@ import { headplaneDevServer } from "./runtime/vite-plugin";
const PROD_ENTRY = "./app/server/main.ts"; const PROD_ENTRY = "./app/server/main.ts";
const DEV_ENTRY = "./app/server/app.ts"; const DEV_ENTRY = "./app/server/app.ts";
const REACT_ROUTER_SSR_NO_EXTERNAL = ["@react-router/node", "react-router"];
const PREFIX = process.env.__INTERNAL_PREFIX || "/admin"; const PREFIX = process.env.__INTERNAL_PREFIX || "/admin";
if (PREFIX.endsWith("/")) { if (PREFIX.endsWith("/")) {
@@ -44,62 +45,69 @@ if (!VERSION) {
const config = await readFile("config.example.yaml", "utf-8"); const config = await readFile("config.example.yaml", "utf-8");
const { server } = parse(config); const { server } = parse(config);
export default defineConfig(({ command }) => ({ export default defineConfig(({ command }) => {
base: command === "build" ? `${PREFIX}/` : undefined, const ssrNoExternal = command === "build" ? true : REACT_ROUTER_SSR_NO_EXTERNAL;
plugins: [
headplaneDevServer({ return {
entry: DEV_ENTRY, base: command === "build" ? `${PREFIX}/` : undefined,
basename: PREFIX, plugins: [
publicDir: new URL("./public", import.meta.url).pathname, headplaneDevServer({
}), entry: DEV_ENTRY,
reactRouter(), basename: PREFIX,
tailwindcss(), publicDir: new URL("./public", import.meta.url).pathname,
], }),
server: { reactRouter(),
host: server.host, tailwindcss(),
port: server.port, ],
}, server: {
resolve: { host: server.host,
tsconfigPaths: true, port: server.port,
}, },
build: { resolve: {
target: "baseline-widely-available", tsconfigPaths: true,
sourcemap: true, },
}, build: {
environments: { target: "baseline-widely-available",
client: { sourcemap: true,
build: { },
rollupOptions: environments: {
command === "build" client: {
? { build: {
// Exclude WASM from the client since it fetches from the server rollupOptions:
external: [/\.wasm(\?url)?$/], command === "build"
} ? {
: undefined, // Exclude WASM from the client since it fetches from the server
external: [/\.wasm(\?url)?$/],
}
: undefined,
},
},
ssr: {
resolve: {
noExternal: ssrNoExternal,
},
build: {
rollupOptions:
command === "build"
? {
// Override the SSR build entry so React Router emits the
// production bootstrap (`app/server/main.ts`) as
// `build/server/index.js`. It transitively imports the
// SSR entry, which pulls in the React Router server build
// via the virtual module `virtual:react-router/server-build`.
input: PROD_ENTRY,
external: [],
}
: undefined,
},
}, },
}, },
ssr: { ssr: {
build: { noExternal: ssrNoExternal,
rollupOptions:
command === "build"
? {
// Override the SSR build entry so React Router emits the
// production bootstrap (`app/server/main.ts`) as
// `build/server/index.js`. It transitively imports the
// SSR entry, which pulls in the React Router server build
// via the virtual module `virtual:react-router/server-build`.
input: PROD_ENTRY,
external: [],
}
: undefined,
},
}, },
}, define: {
ssr: { __VERSION__: JSON.stringify(isNext ? `${VERSION}-next` : VERSION),
noExternal: command === "build" ? true : undefined, __PREFIX__: JSON.stringify(PREFIX),
}, },
define: { };
__VERSION__: JSON.stringify(isNext ? `${VERSION}-next` : VERSION), });
__PREFIX__: JSON.stringify(PREFIX),
},
}));