From c48a106a051de9430e2b6f441afef3b610ae3ed5 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 16:41:32 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=B3=20fix:=20make=20cereale/min=20tree?= =?UTF-8?q?-shake=20too,=20and=20correct=20what=20I=20overclaimed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_01SAcqrz3FcadkYr3xG32CjK --- CHANGELOG.md | 20 +++++++++++------- FRAMEWORKS.md | 2 +- README.md | 38 ++++++++++++++++++++------------- docs/cereale.js | 4 ++-- package.json | 4 ++-- scripts/build-bundle.mjs | 45 +++++++++++++++++++++++++++++++++------- src/treeshake.test.ts | 34 ++++++++++++++++++++++++++++++ src/utils.ts | 10 ++++----- 8 files changed, 118 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d5341b..21a1848 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,16 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### `cereale/min` — one file, no bundler -The whole library flattened into a single minified ES module: **25.5 KB, 8.6 KB gzipped**, for +The whole library flattened into a single minified ES module: **33.9 KB, 9.6 KB gzipped**, for import maps, `