151 lines
6.6 KiB
TypeScript
151 lines
6.6 KiB
TypeScript
/// <reference types="vitest" />
|
||
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 VersionBadge / UpdateBanner.
|
||
//
|
||
// Priority chain (most authoritative first):
|
||
// 1. CI_COMMIT_TAG → set ONLY on tag pipelines (e.g. "v1.3.1"). Это source
|
||
// of truth для release builds. Strip leading 'v' → "1.3.1".
|
||
// 2. package.json.version → fallback floor для local dev и branch builds.
|
||
// НЕ обновляется вручную — release flow добавит автоматический bump
|
||
// позже (release script tag + npm version в одном commit'е).
|
||
//
|
||
// __APP_TAG__ — пустая строка если не tag build. VersionBadge решает что
|
||
// показать (приоритет: serverTag → clientTag → v${clientVersion}).
|
||
//
|
||
// __APP_COMMIT__ — 8-char short SHA. CI передаёт через CI_COMMIT_SHORT_SHA
|
||
// (GitLab default 8 chars), local через `git rev-parse --short=8`.
|
||
const APP_VERSION: string = JSON.parse(
|
||
readFileSync(path.resolve(__dirname, 'package.json'), 'utf-8'),
|
||
).version
|
||
// 'none' — Dockerfile default ARG value (см. .gitlab-ci.yml passes
|
||
// --build-arg GIT_TAG="${CI_COMMIT_TAG:-none}"). Filter → пустая строка.
|
||
const RAW_TAG = process.env.CI_COMMIT_TAG ?? ''
|
||
const APP_TAG: string = RAW_TAG === 'none' ? '' : RAW_TAG.replace(/^v/, '')
|
||
// Distinguish "CI branch build" (badge → 'dev') vs "local pnpm dev" (→ 'local').
|
||
// Canonical CI signal — наличие CI_COMMIT_SHORT_SHA (GitLab CI always sets).
|
||
// process.env.CI тоже check'аем но он НЕ пробрасывается через Docker ARG'и
|
||
// (см. ordinis-admin-ui/Dockerfile), поэтому SHORT_SHA — primary detect.
|
||
const APP_CI: boolean =
|
||
Boolean(process.env.CI_COMMIT_SHORT_SHA) || process.env.CI === 'true'
|
||
const APP_COMMIT: string =
|
||
process.env.CI_COMMIT_SHORT_SHA ??
|
||
(() => {
|
||
try {
|
||
return execSync('git rev-parse --short=8 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_TAG__: JSON.stringify(APP_TAG),
|
||
__APP_CI__: JSON.stringify(APP_CI),
|
||
__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'
|
||
|
||
// HOTFIX: graph3d isolated chunk был причиной runtime "Super
|
||
// expression must either be null or a function" (cyclic chunk
|
||
// dependency vendor-misc ↔ vendor-graph3d ломает порядок
|
||
// initialization three.js class inheritance). Откатываем
|
||
// isolation — three.js + d3-shared идут в общий vendor-misc.
|
||
//
|
||
// Trade-off: vendor-misc вырастет до ~700kb gz (с three.js),
|
||
// default page load грузит three.js eager. Lazy advantage 3D
|
||
// mode потерян. Это acceptable hotfix чтобы вернуть сайт; clean
|
||
// chunk split с separate worker bundle / preload strategy —
|
||
// separate follow-up. См. также: React.lazy на DictionaryGraph3D
|
||
// остаётся валидным (component split), просто его vendor deps
|
||
// теперь в общем chunk.
|
||
return 'vendor-misc'
|
||
},
|
||
},
|
||
},
|
||
},
|
||
})
|