/// import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' import { TanStackRouterVite } from '@tanstack/router-vite-plugin' import path from 'path' import { execSync } from 'node:child_process' import { readFileSync } from 'node:fs' // Build identity injected at compile time. Used by /update banner — client // сравнивает свой commit с server's /api/v1/version и показывает "Доступна // новая версия" если различаются. // // Fallbacks для CI / sandboxed builds где git unavailable: env var override, // затем "unknown". const APP_VERSION: string = JSON.parse( readFileSync(path.resolve(__dirname, 'package.json'), 'utf-8'), ).version const APP_COMMIT: string = process.env.CI_COMMIT_SHORT_SHA ?? (() => { try { return execSync('git rev-parse --short HEAD', { stdio: ['ignore', 'pipe', 'ignore'] }) .toString() .trim() } catch { return 'unknown' } })() const APP_BUILT_AT: string = new Date().toISOString() export default defineConfig({ define: { __APP_VERSION__: JSON.stringify(APP_VERSION), __APP_COMMIT__: JSON.stringify(APP_COMMIT), __APP_BUILT_AT__: JSON.stringify(APP_BUILT_AT), }, test: { environment: 'jsdom', setupFiles: ['./src/test/setup.ts'], globals: true, css: false, }, plugins: [ TanStackRouterVite({ routesDirectory: 'src/routes', generatedRouteTree: 'src/routeTree.gen.ts' }), react(), tailwindcss(), ], resolve: { alias: { '@': path.resolve(__dirname, 'src'), }, }, server: { host: '0.0.0.0', port: 5173, proxy: { '/api': { target: process.env.VITE_API_PROXY ?? 'http://localhost:8080', changeOrigin: true, // secure: false нужен когда target — HTTPS endpoint с self-signed // сертификатом (port-forward с локальным CA или внутренний cluster // service). Для prod/staging Let's Encrypt cert validation не нужно // отключать но игнор не вредит для dev mode. secure: false, // ws: true — на будущее когда Server-Sent Events / WebSocket каналы // появятся (real-time notifications, audit stream). ws: true, }, }, }, build: { chunkSizeWarningLimit: 600, rollupOptions: { output: { // Группировка по принципу: что меняется редко → отдельный chunk → // браузер кэширует надолго. Что меняется часто (наш код) — в main. manualChunks: (id) => { if (!id.includes('node_modules')) return undefined // React core — стабильный, основа всего if (id.match(/node_modules\/(react|react-dom|scheduler)\//)) return 'vendor-react' // TanStack — Router + Query, крупные но стабильные if (id.includes('node_modules/@tanstack/')) return 'vendor-tanstack' // @nstart/ui — самый тяжёлый: brand stickers/animations + tons of UI // (проверяй динамически: lottie, echarts если есть, motion) if (id.includes('node_modules/@nstart/ui')) return 'vendor-nstart-ui' // Иконки — отдельно, ленивая загрузка эффективна if (id.includes('node_modules/@phosphor-icons/')) return 'vendor-icons' // Формы + JSON Schema валидация if (id.match(/node_modules\/(react-hook-form|@hookform|ajv|ajv-formats)\//)) { return 'vendor-forms' } // OIDC / auth if (id.match(/node_modules\/(oidc-client-ts|react-oidc-context)\//)) { return 'vendor-auth' } // i18next — отдельно if (id.match(/node_modules\/(i18next|react-i18next|i18next-browser-languagedetector)\//)) { return 'vendor-i18n' } // Axios + парсеры if (id.match(/node_modules\/(axios|jose|crypto-js)\//)) return 'vendor-net' // Остальное — в общий vendor (не должно быть много) return 'vendor-misc' }, }, }, }, })