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
Crea un log de presupuesto para Claude Code antes de que el coste se vuelva borroso
Registra quién usa Claude Code, para qué trabajo y qué resultado produjo en el equipo.
Revisión de 3 minutos antes del commit: confirma qué tocó Claude Code
Cómo detectar en 3 minutos los cambios que Claude Code amplió por su cuenta antes del commit: alcance, diff, prueba y stage selectivo.
El registro de riesgos antes de llevar Claude Code a tu equipo
Cómo armar un registro de riesgos que evita accidentes de permisos, CI y publicación al llevar Claude Code a tu equipo.