--- name: reduce-bundle-size description: Systematically reduce the shipped bundle size of a JS/TS library without sacrificing code readability or breaking consumer APIs. Use this skill whenever the user mentions bundle size, tree-shaking, code duplication to cut, "reduce size of", "make lighter", "shrink output", looks for wins in `dist/`, or asks to audit a library's heaviest files. Also trigger when the user pastes a `bun run build` / `npm run build` / `bunup` / `tsup` / `rollup` output showing `dist/index.js` size and asks for reductions. Prefer this over vague "refactor to be smaller" answers — it enforces a measure-first loop, concrete opportunity categories, and readability guardrails. --- # Reduce Bundle Size A disciplined workflow for shrinking a JavaScript/TypeScript library bundle. The core move is **measure every change, revert anything that regresses, and stay within readability guardrails** — not "shorten the code." ## The measure-first loop Do this for every change, no exceptions: 1. **Baseline.** Run the production build and record `dist/index.js` size (raw + gzip). For bunup/tsup, the output includes both. Write it down. 2. **Make one focused change.** A single dedup, a single extraction, a single dep removal. 3. **Run CI.** `bun run ci` (or equivalent `npm test && npm run build`). Tests must stay green. If any test fails, fix the test (if the behavior truly changed) or fix the refactor (if the test caught a regression) — never weaken the assertion. 4. **Compare.** New size vs baseline. Both raw and gzip. Gzip is the truth consumers experience; raw matters for budget claims. 5. **Keep or revert.** If the delta is meaningful (≥0.5 KB raw or clear structural win), keep it. If it's a wash AND hurts readability, revert. Anti-habit to avoid: batching 10 changes then running CI once. You lose signal on which change caused the regression, and you can't cherry-pick. ## Check the bundle's starting assumptions first Before hunting for wins, verify the mental model: - **Are deps inlined or externalized?** `head -c 500 dist/index.js`. If you see `import { X } from "some-dep"` at the top, deps are externalized and the bundle is mostly your own source code. Removing a dep won't shrink the bundle much — but it saves install footprint. If deps are inlined, dep removal is a bigger lever. - **Does the bundler report unused deps?** Most modern bundlers do (bunup's `unused()` plugin, etc.). A reported unused dep may be a false positive (type-only import that still has runtime co-entry) — grep the source for actual runtime usage before removing. - **Does `sideEffects: false` apply?** If the whole build is one minified file, consumer tree-shaking can't re-split it. Multi-entry bundling (when you're OK with the API impact) enables downstream tree-shaking. ## Opportunities to look for (ranked by typical payoff) Scan in roughly this order. Each subsection lists the shape of the opportunity and what the cleanup looks like. ### 1. Unused deps and dead files (biggest easy wins) - Bundler's unused-dep report → grep for each in `src/`. If truly unused, `bun remove `. - Components imported only by demo/example code that isn't part of the public entry → those UI components can be deleted along with the demo if the demo is also non-shipping. Confirm by following the import graph from `src/index.ts`. - Dead `export` statements (re-exports nobody consumes) → remove from the top-level entry. - An entire heavyweight dep whose only user is one internal component → consider replacing with a lightweight internal implementation. `react-day-picker` (30-50 KB) replaced with ~100 lines of dayjs-based month grid is a typical win. ### 2. Duplicated identical JSX blocks (high dedup value) When three near-identical `