import {log, logColors} from '@augment-vir/common'; import {alwaysReloadPlugin} from '@virmator/frontend/src/always-reload.vite.js'; import {existsSync} from 'node:fs'; import {basename, dirname, join, relative} from 'node:path'; import {type UserConfig, type UserConfigExport} from 'vite'; export const basePlugins = [ alwaysReloadPlugin(), ] as const; export function findGitRepoRoot(dir: string): string { if (existsSync(join(dir, '.git'))) { return dir; } const nextDir = dirname(dir); if (nextDir === dir) { // failed to find an ancestor dir with a .git folder return ''; } return findGitRepoRoot(nextDir); } export type BaseConfigOptions = { /** If your prod deploy is going to be GitHub pages, set this to true. */ forGitHubPages: boolean; /** * Path to the package directory that owns this vite config. Should most likely be left empty or * set to resolve(__dirname, '..'). */ packageDirPath?: string; /** Disables printing of server directory paths. */ disableStartupLogs?: boolean; }; export type ConfigPaths = { srcDir: string; staticDir: string; outDir: string; cwd: string; }; export type OverrideCallback = ( baseConfig: Readonly, basePaths: Readonly, ) => UserConfig | Promise; const externalDeps: string[] = [ /** These are specified, but not actually used in a browser, in `@augment-vir/common` exports. */ 'node:util', 'node:path', /** Imported in object-shape-tester but only in backend code. */ 'node:fs/promises', /** This is specified, but not actually used in a browser, in `@augment-vir/test` exports. */ '@playwright/test', /** For extra measure, also block playwright. */ 'playwright', ]; export function createBaseConfig({forGitHubPages, packageDirPath}: BaseConfigOptions): { baseConfig: UserConfig; basePaths: ConfigPaths; } { const cwd = packageDirPath || process.cwd(); const basePath: string = forGitHubPages && process.env.CI ? `/${basename(findGitRepoRoot(cwd))}` : '/'; const srcDir = join(cwd, 'src'); const staticDir = join(cwd, 'www-static'); const outDir = join(cwd, 'dist'); const target = 'es2024'; // This should be kept in sync with `@virmator/compile/configs/tsconfig.base.json`. return { baseConfig: { server: { host: true, watch: { ignored: [ '**/node_modules/**', '**/.git/**', '**/.history/**', ], }, }, clearScreen: false, base: basePath, publicDir: staticDir, root: srcDir, plugins: [ ...basePlugins, ], build: { outDir, emptyOutDir: true, target, rollupOptions: { external: externalDeps, }, }, optimizeDeps: { rolldownOptions: {}, exclude: externalDeps, force: true, }, }, basePaths: { cwd, outDir, srcDir, staticDir, }, }; } export async function combineConfigs( baseConfigOptions: BaseConfigOptions, overrideCallback: OverrideCallback | undefined, ): Promise { const {baseConfig, basePaths} = createBaseConfig(baseConfigOptions); const fullConfig = overrideCallback ? await overrideCallback(baseConfig, basePaths) : baseConfig; if (!baseConfigOptions.disableStartupLogs) { log.faint( `public dir: ${logColors.info}${relative(process.cwd(), fullConfig.publicDir || '')}${ logColors.reset }`, ); log.faint( `root dir: ${logColors.info}${relative(process.cwd(), fullConfig.root || '')}${ logColors.reset }`, ); log.faint( `out dir: ${logColors.info}${relative( process.cwd(), fullConfig.build?.outDir || '', )}${logColors.reset}`, ); log.faint(`base path: ${logColors.info}${fullConfig.base}${logColors.reset}`); } return fullConfig; } export function defineConfig( baseConfigOptions: BaseConfigOptions, overrideCallback: OverrideCallback | undefined, ): UserConfigExport { return combineConfigs(baseConfigOptions, overrideCallback); }