skills/bundle-size-optimization/SKILL.md
Optimize measured client bundles, not source text. The source of truth is @benchmarks/bundle-size:build, benchmarks/bundle-size/results/current.json, and emitted JS in benchmarks/bundle-size/dist/.
| Need | Command |
|---|---|
| Full benchmark | CI=1 NX_DAEMON=false pnpm nx run @benchmarks/bundle-size:build --outputStyle=stream --skipRemoteCache --skipNxCache >/tmp/bundle-size-build.log 2>&1 && pnpm benchmark:bundle-size:query |
| One scenario | CI=1 NX_DAEMON=false pnpm nx run @benchmarks/bundle-size:build --outputStyle=stream --skipRemoteCache --skipNxCache -- --scenario react-router.minimal >/tmp/bundle-size-build.log 2>&1 && pnpm benchmark:bundle-size:query --id react-router.minimal |
| Read result | pnpm benchmark:bundle-size:query --id react-router.minimal |
| Compare results | pnpm benchmark:bundle-size:diff --baseline /tmp/base-current.json --id react-router.minimal |
| History deltas | git fetch --quiet origin gh-pages && pnpm benchmark:bundle-size:history --id react-router.minimal --top-deltas 20 |
| Source attribution | CI=1 NX_DAEMON=false pnpm nx run @benchmarks/bundle-size:build --outputStyle=stream --skipRemoteCache --skipNxCache -- --scenario react-router.minimal --analysis >/tmp/bundle-size-build.log 2>&1 && pnpm benchmark:bundle-size:analyze --id react-router.minimal |
| Symbol refs | pnpm ts:symbol-references -- --project packages/router-core/tsconfig.json --file packages/router-core/src/utils.ts --symbol last |
query, diff, or analyze output. If the build fails, search/read the log for the error instead of printing the full log.gzipBytes first; also inspect initialGzipBytes, rawBytes, brotliBytes, jsFiles, and per-file files.scenarioDir/outDir, not metric ids: react-router.minimal maps to dist/react-router-minimal/./var/folders/6f/2t42ntqs4yv4h6qwzbh5pmcm0000gn/T/opencode and diff the two current.json files.pnpm ts:symbol-references -- --project <package>/tsconfig.json --file <decl-file> --symbol <name>. If the helper is used elsewhere, inlining one use is usually not worth it for bundle size unless measurement proves otherwise. If no references remain, delete the helper and verify with the script.e2e/.*.bench.ts) comparing candidate implementations across realistic and edge-case inputs, like packages/router-core/tests/closing-tag-detection.bench.ts; verify implementations produce identical results before bench() cases.react-router.minimal for router-core and react-router changes by default; use solid-router.minimal for solid-router changes, vue-router.minimal for vue-router changes, react-start.minimal or react-start.rsbuild.minimal for React Start changes, and solid-start.minimal for Solid Start changes.solid-router.full, iterate on solid-router.full instead of solid-router.minimal.--scenario and compare all scenarios. Look for outliers/anomalies even when the targeted scenario improved.-t <pattern>; all-in-one suites can perturb tiny operations.hz, mean, p99/p999, rme, and samples together. Do not trust one noisy hz value.rme or large p999 outliers as directional only; rerun narrower cases before deciding.Before calling an optimization final, prove which exact production hunks should remain:
git diff --check.For hot files, split the code into phases and optimize each phase by work removed, not characters removed:
After each candidate, run focused perf benchmarks before bundle measurement. Reject wins that hide runtime regressions or make invariants hard to audit.
When done optimizing:
RESULT-optimization-{topic}.md.Useful patterns: remove prod-only strings, remove unused exports, flatten wrappers, inline one-use helpers, avoid duplicate literals, improve treeshaking boundaries, simplify branches after preserving behavior.
Rolldown removes code only when unused and side-effect-free. Property reads may trigger getters; storage/global access can observe or throw.
| Annotation | Valid | Unsafe |
|---|---|---|
/* @__PURE__ */ call() | immediately before a call/new expression whose unused result can be dropped | declarations, property reads, setup, storage, DOM/history/listener code |
/* @__NO_SIDE_EFFECTS__ */ function f() | every call of the function is side-effect-free | functions touching globals, storage, DOM, history, subscriptions, warnings, caches |
sideEffects/module flags | module has no import-time effects when unused | CSS, polyfills, storage hydration, DOM/history setup |
test:build as a size proxy.gzipBytes.dist/<metric-id> instead of dist/<scenarioDir>.