Guía de configuración y optimización aplicada al panel de campañas de marketing IA
// vite.config.ts — CULTIVA Dashboard import { defineConfig, loadEnv } from 'vite' import react from '@vitejs/plugin-react-swc' import checker from 'vite-plugin-checker' import tsconfigPaths from 'vite-tsconfig-paths' export default defineConfig(({ command, mode }) => { // Solo vars VITE_ — sin secretos expuestos const env = loadEnv(mode, process.cwd(), ['VITE_']) return { plugins: [ react(), // SWC: HMR más rápido que Babel tsconfigPaths(), // alias de tsconfig.json → sin duplicar checker({ typescript: true }), // type-check en build ], server: { host: true, // 0.0.0.0 — accesible en Docker port: 3000, hmr: { clientPort: 3000 }, warmup: { clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'], }, proxy: { '/api': { target: env.VITE_API_URL || 'http://localhost:8080', changeOrigin: true, rewrite: (p) => p.replace(/^\/api/, ''), }, }, fs: { allow: ['..', '../../packages'] }, // monorepo }, optimizeDeps: { include: ['lodash-es', 'date-fns'], // CJS → ESM }, build: { sourcemap: false, // no leak de fuente en producción rolldownOptions: { output: { manualChunks: { 'react-vendor': ['react', 'react-dom'], 'ui-vendor': ['@radix-ui/react-dialog'], }, }, }, }, } })
localhost, imposible llegar desde el contenedor o red local.server.host: true + hmr.clientPort: 3000loadEnv(mode, root, '').loadEnv(mode, cwd(), ['VITE_'])VITE_ son públicas. Tokens de API y DB permanecen server-side.src/components/index.ts re-exportaba 80+ componentes — Vite cargaba todos en cada cambio.warmup.clientFiles| Variable | Lado | Visibilidad | Estado |
|---|---|---|---|
VITE_API_URL |
Cliente | Bundle público | OK |
VITE_POSTHOG_KEY |
Cliente | Bundle público | OK |
VITE_APP_VERSION |
Cliente | Bundle público | OK |
DATABASE_URL |
Servidor | Solo server-side | Server |
OPENAI_API_KEY |
Servidor | Solo server-side | Server |
STRIPE_SECRET_KEY |
Servidor | Solo server-side | NUNCA VITE_ |
# .env.local (gitignoreado) VITE_API_URL=http://localhost:8080 VITE_POSTHOG_KEY=phc_cultiva_dev_xxx # .env.production VITE_API_URL=https://api.cultivaia.com VITE_APP_VERSION=2.4.1 # Acceso en componentes const url = import.meta.env.VITE_API_URL const isDev = import.meta.env.DEV
build: { rolldownOptions: { output: { manualChunks: { // vendores React aislados 'react-vendor': [ 'react', 'react-dom', 'react/jsx-runtime', ], // UI separado de lógica 'ui-vendor': [ '@radix-ui/react-dialog', '@radix-ui/react-popover', 'lucide-react', ], // utils pesados 'utils-vendor': [ 'lodash-es', 'date-fns', 'zod', ], }, }, }, }
// Acceso monorepo a packages/ server: { fs: { allow: [ '.', // project root '..', // workspace root '../../packages', ], }, }, // SSR: arreglar paquetes ESM-only ssr: { noExternal: [ '@cultivaia/ui-kit', // ESM-only 'some-esm-dep', ], external: [ 'node-native-pkg', // keep as require() ], target: 'node', }, // Pre-bundle deps CJS problemáticos optimizeDeps: { include: [ 'lodash-es', 'date-fns', 'cjs-package', ], }
// src/stores/campaigns.ts // Store de campañas con HMR preservado let state = { campaigns: [] as Campaign[], activeFilter: 'all', } if (import.meta.hot) { // Recuperar estado previo al hot-reload const saved = import.meta.hot.data.state if (saved) state = saved // Guardar estado ANTES de reemplazar módulo import.meta.hot.dispose((data) => { data.state = state // mutar, no reasignar }) import.meta.hot.accept() } // Optimización: evitar barrel // BAD: import { useCampaigns } from '@/stores' // GOOD: import { useCampaigns } from '@/stores/campaigns'
envPrefix: '' — expone TODAS las variables de entorno (incluyendo secretos) al bundle del cliente.loadEnv(mode, root, '') — igual de peligroso. Usar siempre el tercer argumento con prefijos explícitos.require('lib') en código fuente — Vite es ESM-first. Usar import. require() solo funciona en Node server-side.manualChunks — crea cientos de ficheros pequeños, el browser corre el límite de conexiones HTTP.vite preview como servidor de producción — es un smoke test local. Desplegar dist/ en NGINX, Cloudflare Pages o Vercel.vite build type-chequea — solo transpila. Errores de tipos llegan silenciosamente a producción sin vite-plugin-checker.import.meta.hot.data = {...} — hay que mutar propiedades. La reasignación rompe la persistencia de estado entre hot-reloads.| Opción config | Default | Descripción |
|---|---|---|
root |
'.' |
Directorio raíz del proyecto |
base |
'/' |
Ruta pública para assets desplegados |
build.minify |
'oxc' |
Minificador: oxc (def), terser, false |
build.sourcemap |
false |
true | 'inline' | 'hidden' | false |
envPrefix |
'VITE_' |
Prefijo para vars públicas al cliente |