react-router.config.ts1.6 KBView on GitHub
import { readdirSync } from 'node:fs';
import { join } from 'node:path';

import type { Config } from '@react-router/dev/config';

// Scan the blog posts directory at build time so each post is emitted as static
// HTML. Real HTML is required for AI agents (which don't execute JS) and gives
// search engines a faster, more reliable indexing path.
function getBlogPrerenderPaths(): string[] {
  const postsDir = join(process.cwd(), 'app', '(full-width)', 'blog', 'posts');
  try {
    const slugs = readdirSync(postsDir)
      .filter((f) => f.endsWith('.tsx'))
      .map((f) => f.replace(/\.tsx$/, ''));
    return ['/blog', ...slugs.map((slug) => `/blog/${slug}`)];
  } catch (err) {
    // In production, refuse to ship a /blog index with zero posts — almost
    // always a wrong-cwd build. In dev/test, fall back so tooling keeps working.
    if (process.env.NODE_ENV === 'production') {
      throw new Error(
        `[react-router.config] blog posts dir not found at ${postsDir} during production build. ` +
          `This usually means the build is running from the wrong cwd. ` +
          `Original error: ${err instanceof Error ? err.message : String(err)}`,
      );
    }
    console.warn(
      `[react-router.config] blog posts dir not found at ${postsDir} — only /blog will be prerendered.`,
      err,
    );
    return ['/blog'];
  }
}

export default {
  ssr: false,
  buildDirectory: 'build',
  appDirectory: 'app',
  routeDiscovery: {
    mode: 'initial',
  },
  future: {
    v8_viteEnvironmentApi: true,
  },
  prerender: getBlogPrerenderPaths(),
} satisfies Config;