♻️ refactor: serve Vite SPA static assets under /_spa (#13409)

Made-with: Cursor
This commit is contained in:
Innei 2026-03-30 21:54:20 +08:00 committed by GitHub
parent cded932f1a
commit f6314cc673
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 10 additions and 9 deletions

1
.gitignore vendored
View file

@ -52,6 +52,7 @@ bun.lockb
# Build outputs
dist/
public/_spa/
public/spa/
es/
lib/

View file

@ -111,7 +111,7 @@ COPY --from=base /distroless/ /
COPY --from=builder /app/.next/standalone /app/
COPY --from=builder /app/.next/static /app/.next/static
# Copy SPA assets (Vite build output)
COPY --from=builder /app/public/spa /app/public/spa
COPY --from=builder /app/public/_spa /app/public/_spa
# Copy database migrations
COPY --from=builder /app/packages/database/migrations /app/migrations
COPY --from=builder /app/scripts/migrateServerDB/docker.cjs /app/docker.cjs

View file

@ -11,7 +11,7 @@ const vercelConfig = {
'node_modules/.pnpm/@napi-rs+canvas-*-musl*',
'node_modules/.pnpm/@img+sharp-libvips-*musl*',
// Exclude SPA/desktop/mobile build artifacts from serverless functions
'public/spa/**',
'public/_spa/**',
'dist/desktop/**',
'dist/mobile/**',
'apps/desktop/**',

View file

@ -43,7 +43,7 @@
"build:spa": "cross-env NODE_OPTIONS=--max-old-space-size=7168 pnpm run build:spa:raw",
"build:spa:copy": "tsx scripts/copySpaBuild.mts && tsx scripts/generateSpaTemplates.mts",
"build:spa:mobile": "cross-env NODE_OPTIONS=--max-old-space-size=8192 MOBILE=true vite build",
"build:spa:raw": "rm -rf public/spa && vite build",
"build:spa:raw": "rm -rf public/_spa && vite build",
"build:vercel": "cross-env-shell NODE_OPTIONS=--max-old-space-size=6144 \"bun run build:raw && bun run db:migrate\"",
"build-migrate-db": "bun run db:migrate",
"build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts",

View file

@ -2,7 +2,7 @@ import { cpSync, existsSync, mkdirSync } from 'node:fs';
import path from 'node:path';
const root = path.resolve(import.meta.dirname, '..');
const spaDir = path.resolve(root, 'public/spa');
const spaDir = path.resolve(root, 'public/_spa');
const distDirs = ['desktop', 'mobile'] as const;
const copyDirs = ['assets', 'i18n', 'vendor'] as const;
@ -16,6 +16,6 @@ for (const distDir of distDirs) {
if (!existsSync(sourceDir)) continue;
cpSync(sourceDir, targetDir, { recursive: true });
console.log(`Copied dist/${distDir}/${dir} -> public/spa/${dir}`);
console.log(`Copied dist/${distDir}/${dir} -> public/_spa/${dir}`);
}
}

View file

@ -36,7 +36,7 @@ export function defineConfig(config: CustomNextConfig) {
...(buildWithDocker
? [
// Exclude SPA/desktop/mobile build artifacts from serverless functions
'public/spa/**',
'public/_spa/**',
'dist/desktop/**',
'dist/mobile/**',

View file

@ -1,6 +1,6 @@
'use client';
// Use Vite's ?url import to get the correct hashed asset path (e.g. /spa/assets/pdf.worker-xxx.mjs)
// Use Vite's ?url import to get the correct hashed asset path (e.g. /_spa/assets/pdf.worker-xxx.mjs)
// This overrides react-pdf's auto-detected bare filename which breaks under SPA routing.
import pdfjsWorkerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?url';
import { type ComponentProps } from 'react';

View file

@ -2,7 +2,7 @@
"buildCommand": "bun run build:vercel",
"headers": [
{
"source": "/spa/assets/(.*)",
"source": "/_spa/(.*)",
"headers": [
{
"key": "Cache-Control",

View file

@ -22,7 +22,7 @@ const isDev = process.env.NODE_ENV !== 'production';
const platform = isMobile ? 'mobile' : 'web';
export default defineConfig({
base: isDev ? '/' : process.env.VITE_CDN_BASE || '/spa/',
base: isDev ? '/' : process.env.VITE_CDN_BASE || '/_spa/',
build: {
outDir: isMobile ? 'dist/mobile' : 'dist/desktop',
rollupOptions: {