1# Bundle Size Development Workflow
2
3This package contains the internal bundle-size tooling used to measure Effect
4entrypoints with Rollup, minification, and gzip.
5
6## AI Agent Instructions
7
8Use this workflow when a user wants to quickly understand how a local source
9change affects bundle size for one or more explicitly selected fixture files.
10
11Do not scan `scratchpad/` or `packages/tools/bundle/fixtures/` for this workflow.
12Only measure files that the user explicitly names, or files you create
13specifically for the current investigation.
14
15Use the default cleanup mode. Pass `--keep-base` only when repeated measurements
16are explicitly requested or clearly necessary, then clean up after the final
17run.
18
19If the user asks what a generated bundle is made of, use the bundle composition
20workflow instead of the size comparison workflow.
21
22### Analyze Bundle Composition
23
24Use this workflow when the user gives an explicit fixture and wants to
25understand which modules make up the produced bundle.
26
27Agent procedure:
28
291. Confirm the exact fixture path or paths to analyze. Do not scan directories.
302. Run `pnpm bundle-analyze` with only those explicit paths.
313. Read the printed Markdown table and identify the generated `*.raw-data.json`
32 path for each fixture.
334. Analyze `*.raw-data.json` first. Use `*.treemap.html` only when the user wants
34 a visual artifact to open.
355. Report the largest modules, notable dependency groups, and any surprising
36 inclusions. Mention the generated artifact paths in the answer.
37
38Prefer the repository-level wrapper:
39
40```sh
41pnpm bundle-analyze scratchpad/my-fixture.ts
42```
43
44You can pass multiple explicit files:
45
46```sh
47pnpm bundle-analyze \
48 scratchpad/schema-codec.ts \
49 scratchpad/schema-arbitrary.ts
50```
51
52The wrapper builds the current checkout with `pnpm build` before generating
53analysis artifacts, so the bundle is not produced from stale `dist` files.
54
55By default, artifacts are written to `tmp/bundle-analysis`. Use `--output-dir`
56to choose another directory:
57
58```sh
59pnpm bundle-analyze --output-dir tmp/schema-analysis scratchpad/my-fixture.ts
60```
61
62For each selected fixture, the tool writes:
63
64- `<name>.min.js`: the generated Rollup output for inspection;
65- `<name>.treemap.html`: an interactive treemap for human inspection;
66- `<name>.raw-data.json`: raw visualizer data suitable for AI analysis.
67
68The analysis build disables identifier mangling so module and export names stay
69readable in the visualizer output. Use `bundle-compare-selected` or `report`
70when exact size measurement is the goal.
71
72After running the command, inspect the Markdown table it prints to find the exact
73paths. For an AI analysis, read the `*.raw-data.json` file first and summarize
74the largest modules and dependency groups. Use the `*.treemap.html` file when
75the user wants to inspect the bundle visually.
76
77The raw data contains a tree plus module metadata. For a quick size-oriented
78summary, rank module parts by `renderedLength` or `gzipLength`, then map each
79part through `metaUid` to `nodeMetas[metaUid].id`.
80
81Example inspection command:
82
83```sh
84node -e 'const data = JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8")); console.log(Object.entries(data.nodeParts).map(([uid, part]) => ({ uid, id: data.nodeMetas[part.metaUid]?.id, rendered: part.renderedLength, gzip: part.gzipLength })).sort((a, b) => b.rendered - a.rendered).slice(0, 20))' tmp/bundle-analysis/my-fixture.raw-data.json
85```
86
87Do not treat the generated `.min.js` size from this workflow as the exact bundle
88size comparison number. The analysis build keeps names readable for the
89visualizer. Use `bundle-compare-selected` or `report` for exact size reporting.
90
91Do not use this workflow to compare against a base ref. Use
92`bundle-compare-selected` for size impact and `bundle-analyze` for current bundle
93composition.
94
95### Compare Explicit Scratchpad Fixtures
96
97Prefer the repository-level wrapper:
98
99```sh
100pnpm bundle-compare-selected --base main scratchpad/my-fixture.ts
101```
102
103You can pass multiple explicit files:
104
105```sh
106pnpm bundle-compare-selected --base main \
107 scratchpad/schema-codec.ts \
108 scratchpad/schema-arbitrary.ts
109```
110
111If `--base` is omitted, the script compares against `main`.
112
113By default, the wrapper removes `tmp/bundle-base` before exiting, including on
114failure. This avoids leaving extra git worktree state after a one-off
115measurement.
116
117For repeated measurements where the user explicitly wants a faster feedback loop,
118pass `--keep-base`:
119
120```sh
121pnpm bundle-compare-selected --base main --keep-base scratchpad/my-fixture.ts
122```
123
124When using `--keep-base`, clean up `tmp/bundle-base` after the last measurement.
125The explicit final cleanup command is:
126
127```sh
128git worktree remove --force tmp/bundle-base
129```
130
131The wrapper:
132
133- builds the current checkout with `pnpm build`;
134- creates or reuses `tmp/bundle-base` at the requested base ref;
135- builds the base checkout when needed and writes
136 `tmp/bundle-base/.bundle-build-stamp` only after a successful build;
137- reuses a kept base checkout only when its HEAD and `.bundle-build-stamp`
138 both match the requested base ref;
139- copies only the selected files into the base checkout at the same relative
140 paths;
141- invokes the internal bundle CLI to compare current vs base sizes.
142
143The selected files are copied into the base checkout so the report isolates
144source changes instead of changes to the fixture text.
145
146Expected output is a Markdown table:
147
148```md
149| File Name | Current Size | Previous Size | Difference |
150| :------------------------- | :----------: | :-----------: | :---------------: |
151| `scratchpad/my-fixture.ts` | 42.10 KB | 40.80 KB | +1.30 KB (+3.19%) |
152```
153
154### Internal CLI
155
156Use the internal CLI directly only when the base checkout is already prepared
157and built:
158
159```sh
160node packages/tools/bundle/src/bin.ts compare-selected \
161 --base-dir tmp/bundle-base \
162 scratchpad/my-fixture.ts
163```
164
165The `--base-dir` value must be the root of the base checkout, not the base
166fixture directory.
167
168### Current Size Only
169
170If the user only asks for the current bundled size, use:
171
172```sh
173pnpm build
174pnpm --dir packages/tools/bundle report ../../../scratchpad/my-fixture.ts
175```
176
177Run the build immediately before the report because the direct CLI resolves
178Effect packages from `dist`. This does not compare against a base ref.
179
180### Fixture Guidance
181
182Keep temporary fixtures in `scratchpad/`.
183
184Good temporary fixtures are small, focused entrypoints that import public
185package APIs:
186
187```ts
188import * as Effect from "effect/Effect"
189import * as Schema from "effect/Schema"
190
191const schema = Schema.Struct({
192 name: Schema.String
193})
194
195Schema.decodeUnknownEffect(schema)({ name: "effect" }).pipe(Effect.runFork)
196```
197
198Prefer self-contained fixtures. The compare-selected workflow copies only the
199explicit entry files into the base checkout. If a fixture imports local relative
200helpers, either avoid that shape or make the entry fixture self-contained before
201measuring.
202
203Do not add temporary fixtures to `packages/tools/bundle/fixtures/`. That
204directory is for stable comparison fixtures used by the regular bundle-size
205workflow.
206
207### Cleanup
208
209The wrapper cleans up `tmp/bundle-base` by default. If `--keep-base` was used for
210a multi-run investigation, remove the worktree when the user is done.
211
212To remove it:
213
214```sh
215git worktree remove --force tmp/bundle-base
216```
217
218To verify that cleanup succeeded:
219
220```sh
221git worktree list
222```
223
224Only the main repository worktree should remain.