import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import { visualizer } from "rollup-plugin-visualizer"; import { ViteImageOptimizer } from "vite-plugin-image-optimizer"; import { VitePWA } from "vite-plugin-pwa"; import path from "path"; import fs from "fs-extra"; // Serve as atividades de letramento direto de src em dev e copia para dist no build. // Isso evita manter cópias versionadas em public. function copyLetramentoAtividades() { let resolvedOutDir = null; return { name: 'copy-letramento-atividades', configResolved(config) { resolvedOutDir = path.resolve(config.root, config.build.outDir); }, async writeBundle() { const srcDir = path.resolve(__dirname, 'src/atividades/letramento/'); const distDir = path.resolve(resolvedOutDir ?? path.resolve(__dirname, 'dist'), 'atividades/letramento/'); await fs.remove(distDir); await fs.ensureDir(distDir); await fs.copy(srcDir, distDir, { filter: (src) => !src.includes('node_modules') && !src.endsWith('.md'), }); }, configureServer(server) { // Durante o desenvolvimento, servir os arquivos diretamente de src server.middlewares.use((req, res, next) => { const requestUrl = req.url ?? ''; const pathname = requestUrl.split('?')[0]; if (pathname.startsWith('/atividades/letramento/')) { const relativePath = pathname.replace('/atividades/letramento/', ''); const srcPath = path.resolve(__dirname, 'src/atividades/letramento/', relativePath); if (fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) { const content = fs.readFileSync(srcPath); const ext = path.extname(srcPath); const contentType = ext === '.html' ? 'text/html' : ext === '.js' ? 'application/javascript' : ext === '.css' ? 'text/css' : ext === '.json' ? 'application/json' : ext === '.svg' ? 'image/svg+xml' : 'application/octet-stream'; res.setHeader('Content-Type', contentType); res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate'); res.setHeader('Pragma', 'no-cache'); res.setHeader('Expires', '0'); res.end(content); return; } } next(); }); } }; } export default defineConfig({ resolve: { alias: { "@": path.resolve(__dirname, "./src"), }, }, server: { allowedHosts: ["localhost", "dev.local", "decoda.mtst.tec.br"], }, plugins: [ react(), copyLetramentoAtividades(), VitePWA({ registerType: "autoUpdate", injectRegister: "auto", includeAssets: [ "puzzle.svg", "img/logo_192x192.png", "img/logo_512x512.png", "offline.html", ], manifest: { name: "Decoda", short_name: "Decoda", description: "Aprenda programação brincando com blocos!", start_url: ".", display: "standalone", background_color: "#ffffff", theme_color: "#FE0002", icons: [ { src: "img/logo_192x192.png", sizes: "192x192", type: "image/png", }, { src: "img/logo_512x512.png", sizes: "512x512", type: "image/png", }, ], }, workbox: { globPatterns: ["**/*.{js,css,html,svg,png,json,ico,webp}"], navigateFallback: "offline.html", navigateFallbackDenylist: [ /^\/atividades\/letramento\//, /^\/docs\//, /^\/jupyter\//, ], }, }), visualizer({ open: false, gzipSize: true, brotliSize: true, }), ViteImageOptimizer({ png: { quality: 80, }, jpg: { quality: 80, }, }), ], base: "./", test: { globals: true, environment: "jsdom", setupFiles: [], }, preview: { port: 3000, strictPort: true, }, build: { rollupOptions: { output: { manualChunks(id) { if (id.includes("node_modules")) { if (id.includes("react-router")) { return "vendor-react"; } if (id.includes("react") || id.includes("react-dom")) { return "vendor-react"; } if (id.includes("blockly")) { return "vendor-blockly"; } if (id.includes("phaser")) { return "vendor-phaser"; } if (id.includes("shepherd") || id.includes("react-shepherd")) { return "vendor-shepherd"; } if ( id.includes("codemirror") || id.includes("@uiw/react-codemirror") ) { return "vendor-codemirror"; } if (id.includes("lucide-react")) { return "vendor-icons"; } return "vendor"; } }, }, }, chunkSizeWarningLimit: 600, }, });