🧹 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
This commit is contained in:
+16
-23
@@ -31,15 +31,9 @@ await build({
|
||||
bundle: true,
|
||||
format: 'esm',
|
||||
target: 'esnext',
|
||||
// NOT `minify: true`. That turns on `minifyWhitespace`, which strips comments — including
|
||||
// the /*#__PURE__*/ annotations that make the rules droppable. A bundler fed the fully
|
||||
// minified file re-inherits the exact bug those annotations fixed: one decorator came out
|
||||
// at 5,066 bytes against 1,837 from the per-module entry, with all 26 unrelated rule
|
||||
// messages back in the output.
|
||||
//
|
||||
// Syntax and identifier minification keep them. The cost is 34.6 KB raw against 26.0 KB,
|
||||
// but 9.8 KB gzipped against 8.7 KB — about a kilobyte over the wire, which is a fair price
|
||||
// for a file that behaves correctly however someone ends up using it.
|
||||
// NOT `minify: true`: `minifyWhitespace` strips comments, /*#__PURE__*/ included, and a
|
||||
// bundler fed the result keeps every unused rule — 5,066 bytes for one decorator against
|
||||
// 1,837. Asserted below. Costs about a kilobyte gzipped.
|
||||
minifySyntax: true,
|
||||
minifyIdentifiers: true,
|
||||
sourcemap: true,
|
||||
@@ -52,22 +46,21 @@ await build({
|
||||
outfile,
|
||||
});
|
||||
|
||||
// The banner names a version, so a stale bundle would misreport itself rather than merely be
|
||||
// out of date. Cheap to assert, and the build is the only place that can.
|
||||
const emitted = await readFile(outfile, 'utf8');
|
||||
if (!emitted.includes(`cereale ${pkg.version}`)) {
|
||||
throw new Error('the bundle banner does not carry the current version');
|
||||
}
|
||||
for (const name of ['toInstanceSync', 'IsString']) {
|
||||
if (!emitted.includes(name)) throw new Error(`${name} is missing from the flat bundle`);
|
||||
}
|
||||
|
||||
// The whole reason this file is not fully minified. Asserting it here means a future change to
|
||||
// the minify options fails the build rather than silently tripling what a bundler keeps.
|
||||
const annotations = (emitted.match(/__PURE__/g) ?? []).length;
|
||||
const expected = (await readFile(path.join(dist, 'esm/decorators.js'), 'utf8').then(
|
||||
(s) => (s.match(/__PURE__/g) ?? []).length
|
||||
));
|
||||
//
|
||||
// The floor on `expected` is not decoration: comparing the two counts alone passes vacuously if
|
||||
// tsc ever stops emitting the annotations, since 0 >= 0. It has to be wrong in both directions.
|
||||
const count = (s) => (s.match(/__PURE__/g) ?? []).length;
|
||||
const emitted = await readFile(outfile, 'utf8');
|
||||
const annotations = count(emitted);
|
||||
const expected = count(await readFile(path.join(dist, 'esm/decorators.js'), 'utf8'));
|
||||
if (expected < 30) {
|
||||
throw new Error(
|
||||
`dist/esm/decorators.js carries only ${expected} /*#__PURE__*/ annotations; src/decorators.ts ` +
|
||||
'writes 30. The compiler is dropping them, so every consumer keeps all 68 rules.'
|
||||
);
|
||||
}
|
||||
if (annotations < expected) {
|
||||
throw new Error(
|
||||
`the flat bundle kept ${annotations} /*#__PURE__*/ annotations but dist/esm/decorators.js has ` +
|
||||
|
||||
Reference in New Issue
Block a user