- Añadir react-resizable-panels, input-otp, cmdk, vaul a chunks específicos - Protección adicional en vendor-other para excluir dependencias de React - Asegurar que todas las dependencias de React se cargan después de vendor-react
209 lines
8 KiB
TypeScript
209 lines
8 KiB
TypeScript
import { defineConfig } from "vite";
|
|
import react from "@vitejs/plugin-react-swc";
|
|
import path from "path";
|
|
import { manifestPlugin } from "./vite-plugin-manifest";
|
|
|
|
// Detectar si estamos en GitHub Pages
|
|
// GitHub Pages usa el formato: https://username.github.io/repository-name/
|
|
const isGitHubPages = process.env.GITHUB_PAGES === 'true';
|
|
const repositoryName = process.env.GITHUB_REPOSITORY_NAME || 'guia-tes-digital';
|
|
const base = isGitHubPages ? `/${repositoryName}/` : '/';
|
|
|
|
// https://vitejs.dev/config/
|
|
export default defineConfig({
|
|
// Base path para GitHub Pages (necesario para rutas SPA)
|
|
base: base,
|
|
server: {
|
|
host: "::",
|
|
port: 8096,
|
|
fs: {
|
|
// Permitir acceso a archivos fuera del proyecto si es necesario
|
|
strict: true,
|
|
},
|
|
// SPA fallback: todas las rutas no encontradas redirigen a index.html
|
|
// Esto permite que React Router maneje el enrutamiento del lado del cliente
|
|
middlewareMode: false,
|
|
},
|
|
preview: {
|
|
port: 4173,
|
|
// Configurar preview para SPA routing
|
|
// Esto asegura que el servidor de preview también maneje rutas correctamente
|
|
},
|
|
plugins: [
|
|
react(),
|
|
manifestPlugin(),
|
|
],
|
|
resolve: {
|
|
alias: {
|
|
"@": path.resolve(__dirname, "./src"),
|
|
},
|
|
},
|
|
// Configuración para procesar archivos .md e imágenes
|
|
assetsInclude: ["**/*.md", "**/*.png", "**/*.jpg", "**/*.jpeg", "**/*.svg", "**/*.gif"],
|
|
|
|
// Configuración de build para incluir archivos .md e imágenes
|
|
build: {
|
|
rollupOptions: {
|
|
// Code splitting: dividir el bundle en chunks más pequeños
|
|
output: {
|
|
manualChunks: (id) => {
|
|
// Separar node_modules en chunks por librería
|
|
if (id.includes('node_modules')) {
|
|
// React y React DOM juntos (crítico, cargar primero)
|
|
// IMPORTANTE: Debe ser el primer chunk para evitar errores de useLayoutEffect
|
|
if (id.includes('react') || id.includes('react-dom') || id.includes('scheduler')) {
|
|
return 'vendor-react';
|
|
}
|
|
// React Router (crítico para navegación, depende de React)
|
|
if (id.includes('react-router')) {
|
|
return 'vendor-router';
|
|
}
|
|
// Markdown y procesamiento de texto (grande, separar)
|
|
if (id.includes('react-markdown') || id.includes('remark') || id.includes('rehype') || id.includes('unified') || id.includes('micromark') || id.includes('mdast')) {
|
|
return 'vendor-markdown';
|
|
}
|
|
// Radix UI (componentes UI, agrupar, depende de React)
|
|
if (id.includes('@radix-ui')) {
|
|
return 'vendor-ui';
|
|
}
|
|
// TanStack Query (depende de React)
|
|
if (id.includes('@tanstack')) {
|
|
return 'vendor-query';
|
|
}
|
|
// Icons (lucide-react, depende de React)
|
|
if (id.includes('lucide-react')) {
|
|
return 'vendor-icons';
|
|
}
|
|
// Charts (recharts, si se usa, depende de React)
|
|
if (id.includes('recharts')) {
|
|
return 'vendor-charts';
|
|
}
|
|
// Formularios (react-hook-form, zod, depende de React)
|
|
if (id.includes('react-hook-form') || id.includes('zod') || id.includes('@hookform')) {
|
|
return 'vendor-forms';
|
|
}
|
|
// Date/time (date-fns, react-day-picker, depende de React)
|
|
if (id.includes('date-fns') || id.includes('react-day-picker')) {
|
|
return 'vendor-dates';
|
|
}
|
|
// Carousel (embla, puede depender de React)
|
|
if (id.includes('embla')) {
|
|
return 'vendor-carousel';
|
|
}
|
|
// Themes (next-themes, depende de React)
|
|
if (id.includes('next-themes')) {
|
|
return 'vendor-themes';
|
|
}
|
|
// Sonner (toasts, depende de React)
|
|
if (id.includes('sonner')) {
|
|
return 'vendor-toasts';
|
|
}
|
|
// React Resizable Panels (depende de React)
|
|
if (id.includes('react-resizable-panels')) {
|
|
return 'vendor-panels';
|
|
}
|
|
// Input OTP (puede usar React)
|
|
if (id.includes('input-otp')) {
|
|
return 'vendor-forms'; // Agrupar con otros formularios
|
|
}
|
|
// CMDK (Command Menu, puede usar React)
|
|
if (id.includes('cmdk')) {
|
|
return 'vendor-ui'; // Agrupar con UI
|
|
}
|
|
// Vaul (Drawer, usa React)
|
|
if (id.includes('vaul')) {
|
|
return 'vendor-ui'; // Agrupar con UI
|
|
}
|
|
// Resto de node_modules pequeños (SOLO cosas que NO usan React)
|
|
// Excluir explícitamente cualquier cosa que pueda usar React
|
|
if (
|
|
id.includes('react') ||
|
|
id.includes('@radix') ||
|
|
id.includes('@tanstack') ||
|
|
id.includes('lucide') ||
|
|
id.includes('recharts') ||
|
|
id.includes('embla') ||
|
|
id.includes('next-themes') ||
|
|
id.includes('sonner') ||
|
|
id.includes('react-hook-form') ||
|
|
id.includes('react-day-picker') ||
|
|
id.includes('react-markdown') ||
|
|
id.includes('react-router') ||
|
|
id.includes('react-resizable') ||
|
|
id.includes('input-otp') ||
|
|
id.includes('cmdk') ||
|
|
id.includes('vaul')
|
|
) {
|
|
// Si contiene algo relacionado con React, no debe estar aquí
|
|
// Forzar a un chunk específico (vendor-ui como fallback)
|
|
return 'vendor-ui';
|
|
}
|
|
return 'vendor-other';
|
|
}
|
|
|
|
// Separar páginas en chunks individuales
|
|
if (id.includes('/src/pages/')) {
|
|
const pageName = id.split('/src/pages/')[1]?.split('.')[0];
|
|
if (pageName) {
|
|
// ManualViewer es muy grande, mantenerlo separado
|
|
if (pageName === 'ManualViewer') {
|
|
return 'page-manual-viewer';
|
|
}
|
|
return `page-${pageName.toLowerCase()}`;
|
|
}
|
|
}
|
|
|
|
// Separar componentes grandes
|
|
if (id.includes('/src/components/')) {
|
|
// MarkdownViewer es grande (usa react-markdown)
|
|
if (id.includes('MarkdownViewer')) {
|
|
return 'component-markdown';
|
|
}
|
|
}
|
|
},
|
|
assetFileNames: (assetInfo) => {
|
|
const name = assetInfo.name || '';
|
|
|
|
// Mantener estructura de carpetas para archivos .md en public/
|
|
if (name.endsWith('.md')) {
|
|
if (name.includes('manual')) {
|
|
return 'manual/[name][extname]';
|
|
}
|
|
return 'assets/[name]-[hash][extname]';
|
|
}
|
|
|
|
// Mantener estructura de carpetas para imágenes en public/assets/
|
|
if (name.match(/\.(png|jpg|jpeg|svg|gif)$/i)) {
|
|
// Si está en public/assets/infografias/, mantener estructura
|
|
if (assetInfo.source && typeof assetInfo.source === 'string') {
|
|
// Mantener estructura de carpetas para assets
|
|
if (assetInfo.source.includes('assets/infografias')) {
|
|
// Extraer ruta relativa desde public/
|
|
const relativePath = assetInfo.source.split('public/')[1] || name;
|
|
return relativePath;
|
|
}
|
|
}
|
|
return 'assets/[name]-[hash][extname]';
|
|
}
|
|
|
|
return 'assets/[name]-[hash][extname]';
|
|
},
|
|
},
|
|
},
|
|
// Incluir archivos .md e imágenes en el build
|
|
assetsInclude: ['**/*.md', '**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.svg', '**/*.gif'],
|
|
// Copiar todo el directorio public/ a dist/ (incluye imágenes)
|
|
copyPublicDir: true,
|
|
},
|
|
// Configuración para PWA
|
|
// El service worker se copia automáticamente desde public/ con copyPublicDir
|
|
|
|
// Configuración para importar archivos .md como texto usando ?raw
|
|
// Ejemplo de uso:
|
|
// import content from './file.md?raw'
|
|
// Esto importará el contenido del archivo como string
|
|
optimizeDeps: {
|
|
exclude: [],
|
|
},
|
|
});
|