import { fileURLToPath } from 'node:url' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { VueRouterAutoImports, getPascalCaseRouteName, } from 'unplugin-vue-router' import VueRouter from 'unplugin-vue-router/vite' import { defineConfig } from 'vite' import VueDevTools from 'vite-plugin-vue-devtools' import MetaLayouts from 'vite-plugin-vue-meta-layouts' import vuetify from 'vite-plugin-vuetify' import tailwindcss from '@tailwindcss/vite' import svgLoader from 'vite-svg-loader' import { v2RouteName } from './src/plugins/1.router/v2RouteName' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ // Docs: https://github.com/posva/unplugin-vue-router // ℹ️ This plugin should be placed before vue plugin VueRouter({ // Parallel /v2/* tree (RFC-WS-GUI-REDESIGN AD-G1). The second // routesFolder prefixes every v2 URL with /v2/; v2RouteName then // prefixes the route NAME with `v2-` so file-name twins across // pages/ and pages-v2/ cannot collide. Reverted at final cutover. routesFolder: [ { src: 'src/pages' }, { src: 'src/pages-v2', path: 'v2/' }, ], getRouteName: routeNode => { // Convert pascal case to kebab case const raw = getPascalCaseRouteName(routeNode) .replace(/([a-z\d])([A-Z])/g, '$1-$2') .toLowerCase() // Defensive path read: unplugin-vue-router 0.8.8 TreeNode exposes // `.fullPath`; fall back to `.value.path` then '' so a future // plugin bump can't silently drop the v2- prefix. Step 5 below // empirically verifies the emitted name. const nodePath = (routeNode.fullPath ?? routeNode.value?.path ?? '') as string // getPascalCaseRouteName includes the 'v2' segment from the URL // path prefix set by routesFolder (e.g. 'v2/dashboard' → 'v2-dashboard'). // Strip that prefix before v2RouteName re-adds the canonical `v2-` // so we get 'v2-dashboard' not 'v2-v2-dashboard'. const isV2 = nodePath === '/v2' || nodePath === 'v2' || nodePath.startsWith('/v2/') || nodePath.startsWith('v2/') const base = isV2 && raw.startsWith('v2-') ? raw.slice(3) : raw return v2RouteName(base, nodePath) }, }), vue(), VueDevTools(), vueJsx(), // Tailwind v4 (F3 — parallel-mode with Vuetify until F6). Placed after // vue() so it sees compiled template content; placed before vuetify() // so Vuetify's SCSS pipeline runs unimpeded. tailwindcss(), // Docs: https://github.com/vuetifyjs/vuetify-loader/tree/master/packages/vite-plugin vuetify({ styles: { // Absolute URL so resolution does not depend on process cwd (fixes common SASS 404s). configFile: fileURLToPath( new URL('./src/styles/settings.scss', import.meta.url), ), }, }), // Docs: https://github.com/dishait/vite-plugin-vue-meta-layouts?tab=readme-ov-file MetaLayouts({ target: './src/layouts', defaultLayout: 'default', }), // Docs: https://github.com/antfu/unplugin-vue-components#unplugin-vue-components Components({ dirs: ['src/@core/components', 'src/views/demos', 'src/components'], dts: true, resolvers: [ componentName => { // Auto import `VueApexCharts` if (componentName === 'VueApexCharts') { return { name: 'default', from: 'vue3-apexcharts', as: 'VueApexCharts', } } }, ], }), // Docs: https://github.com/antfu/unplugin-auto-import#unplugin-auto-import AutoImport({ imports: [ 'vue', VueRouterAutoImports, '@vueuse/core', '@vueuse/math', 'vue-i18n', 'pinia', ], dirs: [ './src/@core/utils', './src/@core/composable/', './src/composables/', './src/utils/', './src/plugins/*/composables/*', ], vueTemplate: true, // ℹ️ Disabled to avoid confusion & accidental usage ignore: ['useCookies', 'useStorage'], }), svgLoader(), ], define: { 'process.env': {} }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), '@themeConfig': fileURLToPath( new URL('./themeConfig.ts', import.meta.url), ), '@core': fileURLToPath(new URL('./src/@core', import.meta.url)), '@layouts': fileURLToPath(new URL('./src/@layouts', import.meta.url)), '@images': fileURLToPath( new URL('./src/assets/images/', import.meta.url), ), '@styles': fileURLToPath( new URL('./src/assets/styles/', import.meta.url), ), '@configured-variables': fileURLToPath( new URL( './src/assets/styles/variables/_template.scss', import.meta.url, ), ), }, }, server: { port: 5174, proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true, }, }, warmup: { clientFiles: ['./src/pages/**/*.vue', './src/components/**/*.vue'], }, }, build: { chunkSizeWarningLimit: 5000, // RFC-WS-7 §3.5 — sourcemaps generated at build, uploaded to GlitchTip // by deploy.sh, then `find dist -name '*.map' -delete` strips them // before nginx serves dist/. No public-mapped sources on production. sourcemap: true, }, optimizeDeps: { exclude: ['vuetify'], entries: ['./src/**/*.vue'], force: true, }, })