diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 549b1ca73..d4acc1076 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -79,7 +79,9 @@ function resolveDistDir(defaultValue: string) { return toPosixPath(isAbsolute(configured) ? relative(WEB_ROOT, configured) || '.' : configured); } -const DIST_DIR = resolveDistDir(isProd ? (shouldStaticExport ? 'out' : '.next') : '.next'); +const DIST_DIR = shouldStaticExport && !process.env.OD_WEB_DIST_DIR + ? null + : resolveDistDir('.next'); function resolveDevTsconfigPath() { const configured = process.env.OD_WEB_TSCONFIG_PATH; @@ -166,9 +168,10 @@ const nextConfig: NextConfig = { root: WORKSPACE_ROOT, }, ...(DEV_TSCONFIG_PATH ? { typescript: { tsconfigPath: DEV_TSCONFIG_PATH } } : {}), - // Keep the bundle output predictable so the daemon's STATIC_DIR can point - // at it without any glob trickery. - distDir: DIST_DIR, + // Static exports keep Next.js's default `out/` output directory so static + // hosts like Vercel can publish the generated site directly. Server runtimes + // still keep a predictable traced build directory for sidecar launchers. + ...(DIST_DIR ? { distDir: DIST_DIR } : {}), ...(shouldStaticExport ? { output: 'export' as const, diff --git a/apps/web/tests/runtime/app-route-export.test.ts b/apps/web/tests/runtime/app-route-export.test.ts index 7876c51b2..af95fff7a 100644 --- a/apps/web/tests/runtime/app-route-export.test.ts +++ b/apps/web/tests/runtime/app-route-export.test.ts @@ -1,11 +1,45 @@ -import { describe, expect, it } from 'vitest'; -import nextConfig from '../../next.config'; +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import * as spaShellRoute from '../../app/[[...slug]]/page'; +const WEB_ROOT = dirname(fileURLToPath(new URL('../../..', import.meta.url))); + +async function loadNextConfig() { + vi.resetModules(); + return (await import('../../next.config')).default; +} + +afterEach(() => { + delete process.env.OD_WEB_DIST_DIR; + vi.resetModules(); +}); + describe('SPA shell export route', () => { - it('stays compatible with static export builds', () => { + it('stays compatible with static export builds', async () => { + const nextConfig = await loadNextConfig(); expect(nextConfig.output).toBe('export'); + expect(nextConfig.distDir).toBeUndefined(); expect('dynamicParams' in spaShellRoute).toBe(false); expect(spaShellRoute.generateStaticParams()).toEqual([{ slug: [] }]); }); + + it('keeps an explicit dist dir override even when static export is selected', async () => { + const configuredDistDir = resolve(WEB_ROOT, '.tmp', 'vitest-next'); + process.env.OD_WEB_DIST_DIR = configuredDistDir; + + const nextConfig = await loadNextConfig(); + + expect(nextConfig.output).toBe('export'); + expect(nextConfig.distDir).toContain('vitest-next'); + }); + + it('treats an empty dist dir override as unset for static export builds', async () => { + process.env.OD_WEB_DIST_DIR = ''; + + const nextConfig = await loadNextConfig(); + + expect(nextConfig.output).toBe('export'); + expect(nextConfig.distDir).toBeUndefined(); + }); }); diff --git a/vercel.json b/vercel.json index 6308958a7..b5780cf07 100644 --- a/vercel.json +++ b/vercel.json @@ -1,6 +1,6 @@ { "$schema": "https://openapi.vercel.sh/vercel.json", - "buildCommand": "pnpm install && pnpm --filter @open-design/web build", + "buildCommand": "OD_WEB_OUTPUT_MODE= pnpm --filter @open-design/web build", "installCommand": "pnpm install", "outputDirectory": "apps/web/out", "framework": null