import { defineConfig } from 'vitest/config'; import { playwright } from '@vitest/browser-playwright'; import adapter from '@sveltejs/adapter-node'; import { sveltekit } from '@sveltejs/kit/vite'; import { realtimeDevWsPlugin } from '@webavatar/webavatar-core/vite'; import { toolRegistry } from './src/lib/server/project/tools/index.js'; import { SESSION_PERSONA } from './src/lib/project/persona.js'; import { serverOnlyGuard } from './server-only-guard.js'; export default defineConfig(({ command }) => ({ plugins: [ sveltekit({ compilerOptions: { // Force runes mode for the project, except for libraries. Can be removed in svelte 6. runes: ({ filename }) => filename.split(/[/\\]/).includes('node_modules') ? undefined : true }, // adapter-node: the realtime WS relay needs a persistent Node process // (see @webavatar/webavatar-core's server/ws-handler.ts) — no // serverless adapter can hold an upgraded WebSocket connection the // way this can. adapter: adapter() }), // Attaches the same WS upgrade handling used in production (server.js) // to Vite's dev server, so `npm run dev` supports it without a build. // Passes only raw, env-independent pieces (persona + tool registry) — // NOT a fully-resolved `defaults` object — because this plugin's config // gets evaluated under `npm run test:unit` too (see the VITEST guard in // @webavatar/webavatar-core/vite's configureServer); resolving // env-driven config here at module scope would run it there as well, // with no env vars available. realtimeDevWsPlugin({ persona: SESSION_PERSONA, toolRegistry }), serverOnlyGuard() ], // Verified against the installed Vite: for SSR, Vite externalizes a // dependency only if it resolves inside `node_modules` — a workspace // package is a symlink whose real path is `packages/core/...`, so without // this the monorepo would bundle core's server code into the SvelteKit // server build while a client project that installed the published // package gets it externalized, running a different artifact than this // template does. Deliberately not applied in `dev`: externalizing would // load the package through Node's module cache, losing hot reload of // package changes for SSR routes during development. See // docs/ARCHITECTURE.md's "Artifact parity" section. ssr: command === 'build' ? { external: ['@webavatar/webavatar-core', '@webavatar/webavatar-humanoid'] } : undefined, // three/addons/* (GLTFLoader, OrbitControls — see the eye-demo Three.js // viewer) are deep imports Vite's dependency scanner doesn't always // discover on its own; without this, dev mode can end up serving two // separate module instances of `three` (one pre-bundled, one not) and // warns "Multiple instances of Three.js being imported". Doesn't affect // the production build, which Rollup already dedupes correctly. optimizeDeps: { include: [ 'three', 'three/addons/loaders/GLTFLoader.js', 'three/addons/controls/OrbitControls.js' ] }, test: { expect: { requireAssertions: true }, projects: [ { extends: './vite.config.ts', test: { name: 'client', browser: { enabled: true, provider: playwright(), instances: [{ browser: 'chromium', headless: true }] }, include: ['src/**/*.svelte.{test,spec}.{js,ts}'], exclude: ['src/lib/server/**'] } }, { extends: './vite.config.ts', test: { name: 'server', environment: 'node', include: ['src/**/*.{test,spec}.{js,ts}'], exclude: ['src/**/*.svelte.{test,spec}.{js,ts}'] } } ] } }));