Analisis de bundles con Claude Code para Vite, Astro y Next.js
Analiza bundles JS, dependencias duplicadas, dynamic import y presupuestos CI con Claude Code.
Un bundle de JavaScript no se vuelve pesado de golpe. Normalmente crece con cambios razonables: una grafica para el panel, un editor enriquecido, una libreria de fechas, un mapa, un reproductor de video, un SDK de autenticacion o codigo para A/B testing. El problema no es que esas funciones sobren, sino que muchas veces se entregan en la primera carga a usuarios que todavia no las necesitan.
El analisis de bundle consiste en abrir el resultado de produccion y preguntar: “que estamos enviando al navegador?”. Para alguien que empieza, es como pesar una maleta y luego revisar que objetos la hicieron pesada. Claude Code puede ayudar, pero necesita una tarea con medicion, diagnostico, cambio pequeno, verificacion y presupuesto en CI. Pedir solo “optimiza performance” suele producir cambios grandes y dificiles de revisar.
Esta guia cubre proyectos tipo Vite, Astro y Next.js. Veras rollup-plugin-visualizer, source-map-explorer, deteccion de paquetes duplicados, dynamic import, bundle budget en CI y un flujo de revision con Claude Code. Para el contexto completo, enlaza esta lectura con code splitting, tree shaking y optimizacion de performance.
Flujo de trabajo
Los numeros habituales son raw, gzip y brotli. Raw sirve para leer diagramas; gzip y brotli se parecen mas a lo que viaja por la red. Aun asi, una dependencia que comprime bien puede seguir costando parseo y ejecucion, asi que no conviene mirar un unico numero.
flowchart LR
A["production build"] --> B["visual report"]
B --> C["duplicate packages"]
C --> D["replace or dedupe"]
B --> E["route-level split"]
D --> F["bundle budget in CI"]
E --> F
F --> G["Claude Code review"]
Usa documentacion oficial como base: Building for Production de Vite, la receta de Astro para analyze bundle size, Package Bundling de Next.js, Performance budgets 101 de web.dev y la guia oficial de Claude Code.
Visualizar Vite y Astro
En Vite, el primer paso practico es rollup-plugin-visualizer. Genera un HTML con treemap para ver que librerias ocupan espacio.
npm install -D rollup-plugin-visualizer
// vite.config.ts
import { defineConfig } from "vite";
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig({
plugins: [
visualizer({
filename: "dist/bundle-stats.html",
template: "treemap",
gzipSize: true,
brotliSize: true,
open: false
})
],
build: {
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
react: ["react", "react-dom"],
charts: ["recharts"],
editor: ["@tiptap/react", "@tiptap/starter-kit"]
}
}
}
}
});
En Astro se coloca dentro de la configuracion de Vite:
// astro.config.mjs
import { defineConfig } from "astro/config";
import { visualizer } from "rollup-plugin-visualizer";
export default defineConfig({
vite: {
plugins: [
visualizer({
filename: "dist/bundle-stats.html",
template: "treemap",
gzipSize: true,
brotliSize: true
})
],
build: { sourcemap: true }
}
});
npm run build
open dist/bundle-stats.html
En Windows PowerShell:
start dist/bundle-stats.html
Busca elementos que no pertenecen a la primera vista: graficas de administracion, editores, mapas, video, procesadores Markdown, librerias de fechas y UI interna.
Source maps y Next.js
source-map-explorer usa los archivos JS y sus source maps para mostrar de donde viene cada parte del bundle.
npm install -D source-map-explorer
npm run build
npx source-map-explorer "dist/assets/*.js" --html dist/source-map-report.html
En Next.js, confirma primero la version y el builder. Segun el caso, usa el analizador oficial o @next/bundle-analyzer.
// next.config.mjs
import bundleAnalyzer from "@next/bundle-analyzer";
const withBundleAnalyzer = bundleAnalyzer({
enabled: process.env.ANALYZE === "true"
});
export default withBundleAnalyzer({
reactStrictMode: true
});
npm install -D @next/bundle-analyzer
ANALYZE=true npm run build
En PowerShell:
$env:ANALYZE="true"; npm run build
Dependencias duplicadas
Una libreria grande se ve rapido, pero los duplicados se esconden. Dos versiones de date-fns, mezcla de lodash y lodash-es, o dependencias peer mal alineadas pueden duplicar trabajo.
npm ls date-fns lodash lodash-es
npm dedupe
Con pnpm:
pnpm why date-fns
pnpm dedupe
Prompt recomendado:
Analiza el bundle de produccion de este repositorio.
1. Lista dependencias pesadas usando dist/bundle-stats.html o source-map-explorer
2. Usa npm ls o pnpm why para detectar paquetes duplicados
3. Separa candidatos en replace, dedupe, dynamic import y remove
4. Conserva UI existente, texto SEO, CTAs y eventos analytics
5. Haz el cambio minimo seguro y ejecuta npm run build mas el bundle budget check
| Causa | Solucion | Verificacion |
|---|---|---|
moment en todo el sitio | Intl.DateTimeFormat o helper pequeno | zona horaria e idioma |
import completo de lodash | import por funcion o API nativa | mezcla ESM/CommonJS |
| editor solo admin | cargar tras clic o ruta admin | loading y error |
| graficas en home | separar reportes | responsive |
| versiones duplicadas | dedupe o alinear versiones | peer dependency |
dynamic import
dynamic import no borra codigo; lo mueve al momento en que el usuario lo necesita. Es util para reportes, editores, mapas y modales poco frecuentes.
// src/features/reports/ReportsButton.tsx
import { useState } from "react";
export function ReportsButton() {
const [html, setHtml] = useState<string>("");
const [loading, setLoading] = useState(false);
async function handleClick() {
setLoading(true);
const { renderRevenueReport } = await import("./renderRevenueReport");
setHtml(renderRevenueReport([12000, 18400, 9300]));
setLoading(false);
}
return (
<section>
<button type="button" onClick={handleClick} disabled={loading}>
{loading ? "Generando reporte" : "Ver reporte de ingresos"}
</button>
<output aria-live="polite">{html}</output>
</section>
);
}
// src/features/reports/renderRevenueReport.ts
export function renderRevenueReport(values: number[]): string {
const total = values.reduce((sum, value) => sum + value, 0);
return `Total del mes: ${new Intl.NumberFormat("es-ES").format(total)} EUR`;
}
En Next.js, deja el texto SEO, precios, enlaces de compra y CTA de consulta fuera del chunk tardio. Retrasa solo la UI pesada.
// app/admin/EditorSlot.tsx
"use client";
import dynamic from "next/dynamic";
const RichEditor = dynamic(() => import("./RichEditor"), {
ssr: false,
loading: () => <p aria-live="polite">Cargando editor...</p>
});
export function EditorSlot() {
return <RichEditor initialMarkdown="# Draft" />;
}
Presupuesto en CI
Sin CI, el bundle vuelve a crecer. Este script falla si el total gzip o un archivo individual supera el presupuesto.
// scripts/check-bundle-budget.mjs
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
import path from "node:path";
import { brotliCompressSync, gzipSync } from "node:zlib";
const targetDir = "dist/assets";
const maxTotalGzip = 220 * 1024;
const maxSingleGzip = 140 * 1024;
function walk(dir) {
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) return walk(fullPath);
return /\.(js|css)$/.test(entry.name) ? [fullPath] : [];
});
}
if (!existsSync(targetDir)) {
console.error(`Missing ${targetDir}. Run npm run build first.`);
process.exit(1);
}
const rows = walk(targetDir).map((file) => {
const content = readFileSync(file);
return {
file,
raw: statSync(file).size,
gzip: gzipSync(content).byteLength,
brotli: brotliCompressSync(content).byteLength
};
});
const totalGzip = rows.reduce((sum, row) => sum + row.gzip, 0);
const tooLarge = rows.filter((row) => row.gzip > maxSingleGzip);
if (totalGzip > maxTotalGzip || tooLarge.length > 0) {
console.error(`Bundle budget failed. total gzip=${totalGzip} bytes`);
process.exit(1);
}
console.log(`Bundle budget passed. total gzip=${totalGzip} bytes`);
# .github/workflows/bundle-budget.yml
name: Bundle Budget
on: [pull_request]
jobs:
bundle-budget:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm run build
- run: node scripts/check-bundle-budget.mjs
El primer presupuesto debe ser realista: toma el valor actual, suma alrededor de 10% y pide explicacion cuando un PR lo supere.
Casos reales
Primero, un dashboard SaaS. La lista, busqueda, navegacion y CTA principal deben cargar pronto; graficas, auditoria, CSV y reportes de facturacion pueden ir en otro chunk.
Segundo, un sitio de contenidos o cursos. El texto, los enlaces de compra y la CTA de consulta deben renderizar temprano. La vista previa Markdown, recorte de imagenes y panel de campanas pueden esperar. Esto afecta anuncios, afiliados y leads; mide el resultado con analytics implementation.
Tercero, una landing con mapa, video, calculadora o formulario. La primera vista muestra oferta, precio y accion; los widgets pesados cargan al hacer scroll o clic. Revisa tambien video player y accesibilidad.
Cuarto, una libreria UI interna. Un import de @company/ui puede arrastrar DatePicker, Modal, Chart e iconos aunque solo uses Button. Divide exports y declara side effects de CSS y tema.
Errores frecuentes
No midas con dev build. No dividas cada componente en chunks pequenos. No publiques source maps sin una politica de seguridad. No congeles manualChunks para siempre. Y no dejes que Claude Code solo modifique codigo: pidelo tambien como reviewer.
Revisa este PR desde el punto de vista de bundle analysis.
- Son necesarias las dependencias del primer load?
- Hay versiones duplicadas?
- Los dynamic imports tienen loading y error state?
- Texto SEO, precios, CTA y analytics quedaron fuera de chunks tardios?
- El log del budget ayuda a encontrar la causa?
- Resume npm run build y las rutas de reportes generados.
Monetizacion
Reducir bundle no es solo limpieza tecnica. Si el primer render mejora, texto, productos, recursos gratuitos y CTA de consultoria aparecen antes. En ClaudeCodeLab eso impacta anuncios, venta de plantillas y solicitudes de formacion.
Para practicar, empieza por la hoja gratuita. Para plantillas reutilizables, revisa productos. Si tu equipo necesita ordenar Vite, Astro, Next.js, CLAUDE.md, CI y reglas de review, usa formacion y consultoria Claude Code.
Verificacion practica
Los ejemplos se comprobaron contra una estructura tipo Vite/React: el visualizer escribe HTML, source-map-explorer requiere source maps, y el script de presupuesto usa fs, path y zlib. La conclusion practica fue separar contenido critico de contenido diferible. Editores y graficas suelen ser buenos candidatos; texto, precios, enlaces de compra y CTA de consulta normalmente no.
Resumen
Un flujo serio con Claude Code empieza con evidencia: build de produccion, visualizacion, duplicados, dynamic import, presupuesto CI y review. Bundle analysis, tree shaking y code splitting forman una misma disciplina para mantener rapida una aplicacion que ya recibe trafico y genera ingresos.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Permission receipt para Claude Code: alcance, prueba y rollback
Patrón de permission receipt para Claude Code: acciones permitidas, aprobación, pruebas, rollback y CTA de ingresos.
Agent Harness seguro para Claude Code y Codex: permisos, verificacion y rollback
Diseña un Agent Harness seguro para Claude Code y Codex con permisos, plan, verificaciones y rollback.
Subagentes de Claude Code: guía práctica para delegar trabajo de forma segura
Guía práctica de subagentes en Claude Code para dividir artículos y código: reglas, prompts, riesgos y checklist.