e4b8c0f6aacb71fc3c0659a6bb2133518d6e65fc
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5714fab42e |
🧹 refactor: cut the duplicated checks and prose from the tree-shaking work
Acting on ponytail-review. The findings were about verification written twice
and comments restating the CHANGELOG, not about the fixes themselves.
- dist/cjs/package.json had two writers: the build script echoed it, then
build-bundle.mjs rewrote it three lines later with the sideEffects entry.
One writer now, the one that knows what belongs in it.
- Dropped the banner-version assert. Same process, same `pkg.version` going in
and coming out — the "stale bundle" it claimed to catch cannot happen.
- Dropped the `includes('toInstanceSync')` text check. ci.yml imports the flat
bundle and typeof-checks the export, which is the same claim actually tested.
- Dropped the treeshake case asserting annotations survive minification; the
build already asserts it. Its one non-duplicated assertion was the floor on
the expected count, and that gap was real: the build compared flat >= esm, so
if tsc ever stopped emitting annotations both sides would read 0 and the
assert would pass on nothing. Folded in as an explicit `expected < 30` check,
verified by stripping the annotations and watching the build fail.
- Removed the `CEREALE` placeholder from treeshake.test.ts. A template language
for one variable, with two no-op `.replace('CEREALE', 'unused')` calls left
behind by it. Interpolated directly.
- Removed `.replace(/\.js$/, '.js')`, which was the identity function.
- Trimmed the comment above the `Symbol.metadata` install from 16 lines to 7,
and the one above `minifySyntax` from 9 to 3, keeping the parts that are not
written down anywhere else.
Also synced the CHANGELOG's byte table to the README's. The two had already
diverged in the webpack column — which is the duplication the review warned
about, showing up before anyone edited either on purpose.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK
|
||
|
|
c48a106a05 |
🌳 fix: make cereale/min tree-shake too, and correct what I overclaimed
An audit of the last commit found two real problems and several claims of mine that went further than the evidence. **cereale/min was not tree-shakable.** scripts/build-bundle.mjs used esbuild's `minify: true`, whose minifyWhitespace pass strips comments — /*#__PURE__*/ annotations included. The published entry point therefore reproduced exactly the bug the previous commit fixed: one decorator came out at 5,066 bytes with all 26 unrelated rule messages, against 1,837 from the per-module entry. Every source-level check stayed green, because they all bundled src/ and the annotations are stripped on the way into dist/. It is now minified for syntax and identifiers but not whitespace: 33.9 KB raw and 9.6 KB gzipped against 26.0/8.7, so about a kilobyte over the wire for a file that behaves correctly however it is used. One decorator via cereale/min is now 1,996 bytes. The build asserts the annotation count survives, and treeshake.test.ts now bundles the published artifact as well as the source — the gap that let this through. **sideEffects was partly inert.** `./dist/cjs/metadata.js` could never match: the build writes dist/cjs/package.json, which becomes the nearest descriptor for everything beneath it, so bundlers read sideEffects from there. That file now carries its own declaration. `./src/metadata.ts` was missing while src/ is published, which declared the Symbol.metadata install droppable in the source tree. Five module-scope caches in utils.ts are annotated for the same reason as the rules. I checked the audit's third blocker — that the Symbol.metadata install is dropped by bundlers — and it is not. It survives every case where it is load-bearing (a decorator import, toPlainSync, modelOf, and a decorated model bundled with an app). It is dropped only when importing nothing but flattenErrors, which needs no metadata, so that is correct. Corrections to my own wording: - "all three bundlers land within 20 bytes" held only for the one-decorator row; larger imports differ by up to a few hundred bytes - "measured through three bundlers, and pinned by a test" read as though the test covered all three; it covers esbuild, on source and on dist - "every rule is a top-level call" — 30 of the 68 are - the docs page "loads nothing from the network" — it fetches its own vendored compiler, same-origin, on first Run. It has no third-party dependencies, which is the claim I should have made - cereale/min's size, everywhere it appears Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK |
||
|
|
b4f0657d09 |
📦 feat: add cereale/min, and a verified framework guide
**cereale/min** — the library flattened into one minified ES module, 25.5 KB / 8.6 KB gzipped, for import maps, <script type="module">, Deno and Workers. Built from dist/esm/index.js, so the decorator lowering and the ES2025 target are tsc's; esbuild only flattens and minifies. It is an addition rather than a replacement, and the measurement is the reason. Bundled through esbuild the flat and per-module builds land within 2 bytes of each other; through rollup + terser the flat one is 165 bytes smaller; unused decorators tree-shake out of both. With the size argument a wash, per-module stays the default import for the one thing it does better — readable stack traces without source maps. (My first pass at that measurement reported "shaken" for every symbol because both rollup builds had failed and grep was reading missing files as absence. The check now asserts the bundle is non-empty and that a *used* symbol is present, so it can tell a real result from a broken harness.) **FRAMEWORKS.md** — a recipe per framework, each one run before it was written, with the versions and date verified against. Angular works, which was not obvious: the CLI scaffolds experimentalDecorators: true, but ngtsc erases @Component and @Injectable into static properties rather than leaning on TypeScript's decorator emit. Flip the flag and both systems coexist. Verified with ngc on Angular 21.2 with strictTemplates — templates still type-check and a wrong cereale rule is still TS1240 inside the Angular build. Next.js cannot work inline, structurally: it derives both the SWC parser's decorator support and the transform mode from the one flag, so on gives legacy emit and off makes @ a syntax error. NestJS cannot either — its DI needs design:type from emitDecoratorMetadata. Both have the same answer: keep the cereale classes in a package compiled by tsc and import the built output. Verified inside a program with BOTH legacy flags on, alongside @Injectable() — mapping and validation work, and the compile-time guarantee still holds where the rules are written. Also verified: Bun 1.3 needs no configuration, and a real Vite 8 build with the plugin works where the same build without it silently leaves decorator syntax in the bundle. Version 0.4.0: cereale/min is a new public entry point, and cutting a minor keeps the existing v0.3.0 tag meaningful instead of force-moving it onto a commit it was never cut from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK |