/// import { defineConfig } from 'vitest/config'; import react from '@vitejs/plugin-react'; import tailwindcss from '@tailwindcss/vite'; import path from 'node:path'; import fs from 'node:fs'; import { fileURLToPath } from 'node:url'; import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'; import { playwright } from '@vitest/browser-playwright'; const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url)); // Read service version from package.json so DevOps panel reflects the real version. const webPackageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'package.json'), 'utf8')) as { version?: string }; const webServiceVersion = webPackageJson.version || '0.0.0'; // More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon const monacoEditorPath = path.resolve(__dirname, 'node_modules/monaco-editor'); const workspaceRoot = path.resolve(__dirname, '..', '..'); const commonPlatRoot = path.join(workspaceRoot, 'learning_ai_common_plat'); function commonPlatSourceEntry(pkg: string): string | null { const sourceEntry = path.join(commonPlatRoot, 'packages', pkg, 'src', 'index.ts'); return fs.existsSync(sourceEntry) ? sourceEntry : null; } // Resolve a @bytelyst/* package: prefer web/node_modules, fall back to vendor/ function bytelystAlias(pkg: string): string { const sourceEntry = commonPlatSourceEntry(pkg); if (sourceEntry) return sourceEntry; const nmPath = path.resolve(__dirname, 'node_modules/@bytelyst', pkg); const vendorPath = path.resolve(__dirname, '../vendor/bytelyst', pkg); if (fs.existsSync(nmPath)) return nmPath; if (fs.existsSync(vendorPath)) return vendorPath; return nmPath; // let Vite surface the missing-module error } // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()], define: { __WEB_SERVICE_VERSION__: JSON.stringify(webServiceVersion), }, // Shared files (../shared/*.ts) live outside web/ so Vite resolves their imports // from the repo root where @bytelyst/* are not installed. Redirect all @bytelyst/* // imports first to web/node_modules, then fall back to the monorepo vendor/ dir. resolve: { // Deduplicate React so the vendored react-auth dist resolves the same react instance dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'react-router-dom'], alias: [ // Vendor packages that live only in vendor/ (not in web/node_modules/) { find: '@bytelyst/api-client', replacement: bytelystAlias('api-client') }, { find: '@bytelyst/design-tokens', replacement: bytelystAlias('design-tokens') }, { find: '@bytelyst/errors', replacement: bytelystAlias('errors') }, { find: '@bytelyst/kill-switch-client', replacement: bytelystAlias('kill-switch-client') }, { find: '@bytelyst/react-auth', replacement: bytelystAlias('react-auth') }, { find: '@bytelyst/telemetry-client', replacement: bytelystAlias('telemetry-client') }, { find: '@bytelyst/ui', replacement: bytelystAlias('ui') }, // Monaco is an explicit web dependency, but this workspace often runs // against pnpm's root store without a web/node_modules symlink when the // private mobile registry is unavailable. Keep local worker imports // resolvable for Vite in that partially installed state. { find: /^monaco-editor$/, replacement: monacoEditorPath }, { find: /^monaco-editor\/(.+)/, replacement: `${monacoEditorPath}/$1` }, // General catch-all: every other @bytelyst/* → web/node_modules. // Restricted to single-segment scope names only, so subpath imports like // `@bytelyst/devops/ui` go through normal package resolution (exports map). { find: /^@bytelyst\/([^/]+)$/, replacement: path.resolve(__dirname, 'node_modules/@bytelyst/$1') }] }, build: { chunkSizeWarningLimit: 5000, rollupOptions: { output: { manualChunks(id) { if (id.includes('monaco-editor') || id.includes('@monaco-editor')) { return 'monaco-vendor'; } if (id.includes('/node_modules/')) { return 'vendor'; } return undefined; } } } }, test: { projects: [{ extends: true, test: { environment: 'jsdom', setupFiles: ['./src/test/setup.ts'], include: ['src/**/*.test.ts', 'src/**/*.test.tsx', 'src/**/*.dom.test.tsx'] } }, { extends: true, plugins: [ // The plugin will run tests for the stories defined in your Storybook config // See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest storybookTest({ configDir: path.join(dirname, '.storybook') })], test: { name: 'storybook', browser: { enabled: true, headless: true, provider: playwright({}), instances: [{ browser: 'chromium' }] } } }] } });