phy-bundle-size-audit
JavaScript bundle size auditor and budget enforcer. Parses webpack stats JSON, Vite bundle report, Rollup output, or Next.js build output to identify largest chunks, flag chunks that exceed configurable size budgets, detect duplicate dependencies across chunks, find treeshaking failures (packages that should be side-effect-free but aren't), and generate CI fail-gate commands. Outputs a GitHub PR annotation-ready summary with per-chunk size, gzip estimate, and actionable optimization suggestions. Zero external API — pure local file analysis. Triggers on "bundle too large", "webpack stats", "chunk size", "bundle budget", "treeshaking", "bundle analyzer", "/bundle-size-audit".
What this skill does
# Bundle Size Auditor
You added one import. Your Lighthouse score dropped 12 points. Your LCP went from 1.8s to 3.1s.
This skill parses your bundler's output — webpack stats JSON, Vite build report, or Next.js `.next/` directory — identifies the largest chunks, finds which packages are bloating them, detects treeshaking failures, and gives you the exact code changes to fix it.
**Supports webpack, Vite, Rollup, Next.js. Zero external API.**
---
## Trigger Phrases
- "bundle too large", "my bundle is huge", "reduce bundle size"
- "webpack stats", "analyze webpack output", "chunk size"
- "bundle budget", "size limit", "bundle regression"
- "treeshaking not working", "dead code in bundle"
- "which package is largest", "find large dependencies"
- "Next.js bundle", "Vite build report"
- "/bundle-size-audit"
---
## How to Provide Input
```bash
# Option 1: Webpack stats JSON (most detailed)
webpack --profile --json > stats.json
/bundle-size-audit stats.json
# Option 2: Vite build (auto-detected)
vite build
/bundle-size-audit dist/
# Option 3: Next.js build (auto-detected)
next build
/bundle-size-audit .next/
# Option 4: Rollup stats
rollup -c --bundleConfigAsCjs
/bundle-size-audit rollup-stats.json
# Option 5: Set size budgets
/bundle-size-audit --max-chunk 250kb --max-initial 500kb
# Option 6: Focus on a specific chunk or package
/bundle-size-audit --chunk main --package lodash
# Option 7: Generate CI fail-gate only
/bundle-size-audit --ci-config
```
---
## Step 1: Detect Build Artifacts
```bash
python3 -c "
import os, json
from pathlib import Path
# Priority order for detection
checks = [
('webpack stats', ['stats.json', 'webpack-stats.json', 'build/stats.json']),
('Next.js', ['.next/build-manifest.json', '.next/server/pages-manifest.json']),
('Vite', ['dist/', 'dist/assets/']),
('Rollup', ['dist/', 'build/']),
('Create React App', ['build/static/js/', 'build/asset-manifest.json']),
]
found = []
for name, paths in checks:
for p in paths:
if os.path.exists(p):
found.append((name, p))
break
if found:
for name, path in found:
size = 0
if os.path.isfile(path):
size = os.path.getsize(path)
print(f'Detected {name}: {path} ({size:,} bytes)')
else:
# Directory — count JS files
js_files = list(Path(path).rglob('*.js'))
total = sum(f.stat().st_size for f in js_files)
print(f'Detected {name}: {path}/ ({len(js_files)} JS files, {total/1024:.0f} KB total)')
else:
print('No build artifacts found.')
print('Run your build first:')
print(' webpack --profile --json > stats.json')
print(' vite build')
print(' next build')
"
```
---
## Step 2: Parse Bundle Data
### For Webpack Stats JSON
```python
import json
from pathlib import Path
def parse_webpack_stats(stats_path):
"""Parse webpack stats.json into chunk/asset summary."""
with open(stats_path) as f:
stats = json.load(f)
assets = []
for asset in stats.get('assets', []):
name = asset['name']
size = asset['size']
# Only JS and CSS assets
if not any(name.endswith(ext) for ext in ['.js', '.css', '.mjs']):
continue
assets.append({
'name': name,
'size': size,
'size_kb': round(size / 1024, 1),
'gzip_estimate_kb': round(size / 1024 * 0.3, 1), # ~30% compression typical
'chunks': asset.get('chunkNames', []),
'initial': asset.get('isOverSizeLimit', False),
})
# Sort by size desc
assets.sort(key=lambda x: x['size'], reverse=True)
# Module analysis — find largest modules
modules = []
for module in stats.get('modules', []):
if module.get('size', 0) > 10 * 1024: # >10KB only
modules.append({
'name': module.get('name', 'unknown'),
'size': module.get('size', 0),
'size_kb': round(module.get('size', 0) / 1024, 1),
'reasons': [r.get('moduleName', '') for r in module.get('reasons', [])[:3]],
})
modules.sort(key=lambda x: x['size'], reverse=True)
return assets, modules[:50] # top 50 modules
def parse_webpack_chunks(stats_path):
"""Identify chunks and their composition."""
with open(stats_path) as f:
stats = json.load(f)
chunks = []
for chunk in stats.get('chunks', []):
chunks.append({
'id': chunk.get('id'),
'names': chunk.get('names', []),
'size': chunk.get('size', 0),
'size_kb': round(chunk.get('size', 0) / 1024, 1),
'initial': chunk.get('initial', False),
'entry': chunk.get('entry', False),
'module_count': len(chunk.get('modules', [])),
'files': chunk.get('files', []),
})
chunks.sort(key=lambda x: x['size'], reverse=True)
return chunks
```
### For Vite / CRA (dist/ directory)
```python
import os
from pathlib import Path
def parse_dist_directory(dist_path):
"""Parse build output directory for Vite/CRA/Rollup."""
js_dir = Path(dist_path)
# Find all JS files
js_files = []
for fpath in js_dir.rglob('*.js'):
if 'node_modules' in str(fpath):
continue
size = fpath.stat().st_size
js_files.append({
'name': str(fpath.relative_to(dist_path)),
'path': str(fpath),
'size': size,
'size_kb': round(size / 1024, 1),
'gzip_estimate_kb': round(size / 1024 * 0.3, 1),
'is_chunk': 'chunk' in fpath.name.lower() or fpath.parent.name == 'assets',
'is_vendor': 'vendor' in fpath.name.lower(),
})
js_files.sort(key=lambda x: x['size'], reverse=True)
# CSS files
css_files = []
for fpath in js_dir.rglob('*.css'):
size = fpath.stat().st_size
css_files.append({
'name': str(fpath.relative_to(dist_path)),
'size_kb': round(size / 1024, 1),
})
return js_files, css_files
def estimate_gzip(size_bytes):
"""Estimate gzip size. JS typically compresses 65-75%."""
return round(size_bytes * 0.30 / 1024, 1)
```
### For Next.js (.next/ directory)
```bash
# Next.js provides build-manifest.json and .next/analyze/ if ANALYZE=true
python3 -c "
import json, os
from pathlib import Path
next_dir = Path('.next')
if not next_dir.exists():
print('No .next/ directory found. Run: next build')
exit(1)
# Read build-manifest
manifest_path = next_dir / 'build-manifest.json'
if manifest_path.exists():
manifest = json.loads(manifest_path.read_text())
# Collect all JS files referenced
all_files = set()
for page, files in manifest.get('pages', {}).items():
for f in files:
all_files.add(f)
print(f'Pages: {len(manifest.get(\"pages\", {}))}')
print(f'Unique JS chunks: {len(all_files)}')
# Read static/chunks/
chunks_dir = next_dir / 'static' / 'chunks'
if chunks_dir.exists():
chunks = []
for fpath in chunks_dir.rglob('*.js'):
size = fpath.stat().st_size
chunks.append((fpath.name, size))
chunks.sort(key=lambda x: x[1], reverse=True)
total = sum(s for _, s in chunks)
print(f'\\nTotal chunks: {len(chunks)} ({total/1024:.0f} KB)')
print('\\nTop 10 chunks:')
for name, size in chunks[:10]:
print(f' {name}: {size/1024:.1f} KB (~{size*0.3/1024:.1f} KB gzip)')
"
```
---
## Step 3: Detect Duplicate Dependencies
```python
import re
from pathlib import Path
def find_duplicate_modules(stats_path):
"""Find the same package duplicated across chunks (common with npm hoisting issues)."""
with open(stats_path) as f:
stats = json.load(f)
# Track package versions seen in different chunks
package_chunks = {}
for module in stats.get('modules', []):
name = module.get('name', '')
# Extract package name from ./node_modRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.