Bundling Mistakes
Generated from
.agents/rules/anti-patterns.md(the same source as MCPget_anti_patterns). Each entry is a real mistake + its fix; where a detector code is listed, the linter /pyreon doctor/ MCPvalidatecatches it automatically.
A dev-only check that reads a REST parameter keeps the parameter in production.
signal()'s read closure was (...args) => { if (dev && args.length) warn(); … } — the bundler folds the warning away but not the signature, so production shipped (...e)=> and every signal READ could materialize an array it never looked at. Put the dev and prod shapes in two declarations chosen by the dev gate (const read = process.env.NODE_ENV !== 'production' ? (...args) => … : () => …). Reference: packages/core/reactivity/src/signal.ts.
A client guard that ships a name list the ELEMENT already answers.
Refusing on* handler attributes on the client used EVENT_HANDLER_ATTRS (~160 names, ~0.8 KB gz — the largest item in a minimal app bundle), yet a content attribute is only ever compiled into a handler when the element's interface defines that IDL handler, so key in el is the engine's exact answer. The client asks the element (isElementEventHandlerAttr); SSR, with no element, keeps the list. Two traps: happy-dom defines far fewer handlers than Chromium (no onload on <div>/<meta>, none on MathML), so the refusal must be proven in real Chromium, not happy-dom; and a shared guard must take the predicate as a PARAMETER (@pyreon/head's isHeadAttrSafe), because importing the list anywhere in a client module ships it. Measure these by bundling a real app with every @pyreon/* package INCLUDED — per-package budgets externalize @pyreon/core, where the list lived, and showed the win as −14 B. Reference: packages/core/core/src/url-guard.ts:isElementEventHandlerAttr, runtime-dom/src/tests/event-handler-attr.browser.test.tsx.
A barrel is a reachability edge; sideEffects, not /* @__PURE__ */, fixes it
An index.ts that export *s every layer makes one import reach every module-level call (api.endpoint(…), s.object(…), mock(routes)). Annotating each call @__PURE__ barely helps because the arguments are calls too; sideEffects is decided per module.
The declaration must be true: use the array form naming files with real module-scope effects.
Emit a
package.json(with"type": "module") beside generated output so the result does not depend on the consuming app's config.Unused data (fixture tables, route arrays) survives minification wherever reachable. Keep dev surfaces out of the production entry (
./dev, like@pyreon/server/client).Code:
packages/tools/lathe/src/emit/{entries,package-marker}.ts. Test:lathe/src/tests/entry-points.test.ts(run with and without the marker; the no-marker half catches barrel regressions).
A prop-gated optional feature cannot tree-shake
A branch on a runtime prop is reachable code. In @pyreon/charts, geometry and marks tree-shake as imports, but navigator, dataZoom and brush are props.x === true branches inside packages/fundamentals/charts/src/engine/Chart.tsx, so every chart import pays for them. An optional feature tree-shakes only when it is an import: register it at module load into a core slot (like setThemeEngine, setStyleExtraction). Detect by bundling the minimal import and grepping for a symbol only the optional half defines; a size budget only says something grew.
A bundle assertion keyed on a generated identifier is vacuous
Minification renames seedFaker, so not.toContain('seedFaker') always passes. Assert on external import specifiers (@faker-js/faker) and string data. Pair every "must be absent" suite with a control that bundles everything (export *) and requires each marker present. Reference: lathe/src/tests/entry-points.test.ts:DEV_MARKERS.
A bundle assertion keyed on a generated identifier is vacuous
Minification renames it, so expect(bundle).not.toContain('seedFaker') passes with the module bundled. Assert on what minification cannot touch: external import specifiers (@faker-js/faker) and string data (a fixture uuid). Pair every "must be absent" suite with a control that bundles the module (via export *, so every surface is reached) and requires each marker present; otherwise the suite also passes when the emitter produces nothing. Reference: lathe/src/tests/entry-points.test.ts:DEV_MARKERS.