tree-shaking-dead-code-review
Verifies that a bundler's tree-shaking actually eliminated dead code by inspecting output bytes and sideEffects/module-format configuration, rather than trusting a clean build as proof of elimination.
Install
npx skills add https://github.com/VincentChuWaiChow/vanguard-frontier-agentic/tree/master/skills/frontend/tree-shaking-dead-code-review
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install vincentchuwaichow-vanguard-frontier-agentic@llmmart
git clone https://github.com/VincentChuWaiChow/vanguard-frontier-agentic.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole vincentchuwaichow/vanguard-frontier-agentic collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Tree-Shaking & Dead-Code Review
Purpose
"The build succeeded" and "the code was tree-shaken" are unrelated claims. A production build with zero errors can still ship a fully dead, unreferenced module byte-for-byte, because tree-shaking is not a guaranteed pass — it is a conditional static-analysis optimization that silently no-ops the moment it hits a CJS require, a missing or wrong sideEffects field, a barrel-file re-export, or a development-mode build. This skill treats "eliminated" as a claim that must be proven from bundle-analyzer output bytes and module lists, before and after, in a production build — never inferred from build success, and never inferred from a sideEffects: false change applied without reading the target module for real side effects first, since that specific mistake causes silent runtime breakage (dropped polyfills, dropped CSS injection, dropped security-relevant initialization), not just a size regression.
When to use
Use this skill when the user asks to:
- verify why a supposedly-unused import is still present in bundle-analyzer output,
- evaluate whether a new dependency is tree-shakeable before it is added,
- configure or review a
sideEffectsfield in package.json for an app or a library the user is authoring, - explain why a
sideEffects: falsechange either had no effect or broke something at runtime.
When NOT to use
- General bundle-size triage with no specific dead-code suspicion yet — use
bundle-budget-code-splitting-reviewfirst to establish the numeric budget and rank contributors; hand off here once a specific module is suspected of being fully unused rather than merely heavy. - Deciding route- or component-level code-splitting boundaries — that is a splitting-boundary decision, not an elimination-verification decision;
bundle-budget-code-splitting-reviewowns it. - Diagnosing which Core Web Vitals sub-phase is regressing with no analyzer report yet — hand off to
core-web-vitals-triagefirst, return here once dead code is the named suspect.
Context7 Documentation Protocol
Tree-shaking configuration surface differs by bundler and has shifted across majors (Rolldown replacing Rollup inside Vite 7+, webpack's sideEffects/usedExports interaction, Rollup's treeshake preset system). Do not prescribe a fix from memorized training data.
- Call
ToolSearchwith query"context7"(or"select:mcp__Context7__resolve-library-id,mcp__Context7__query-docs") to load the Context7 tools if not already loaded this session. - Call
mcp__Context7__resolve-library-idfor the bundler actually installed in the project (webpack, Rollup, or Vite/Rolldown) before prescribing anysideEffectsortreeshakeconfiguration — do not assume the bundler from the framework name alone. - Call
mcp__Context7__query-docsfor the specific mechanism in question — e.g. "sideEffects array vs boolean", "treeshake.moduleSideEffects options", "production mode requirement for usedExports" — before ruling on it. Do this per review; do not reuse a prior session's memory of bundler internals. - Context7-grounded facts to confirm, not assume:
- webpack: tree-shaking requires
mode: 'production'(oroptimization.usedExportsexplicitly enabled) to take visible effect;sideEffects: falsein package.json additionally requiresoptimization.providedExports(on by default in production mode) to let webpack drop unused-export modules; a module with real side effects (e.g. CSS imports) must be listed explicitly, e.g."sideEffects": ["**/*.css"], or that side effect is silently dropped. - Rollup:
treeshakeacceptsfalse, a preset ('smallest' | 'safest' | 'recommended'), or a fine-grained object —moduleSideEffects(boolean,'no-external', a string array, or a predicate function) andpropertyReadSideEffectsare the two options most often responsible for either under- or over-aggressive elimination. - Vite 7+ ships Rolldown as its production bundler;
build.rollupOptionsis now an alias forbuild.rolldownOptionsand is deprecated in favor of it — verify which option surface the installed Vite major actually documents before prescribing config keys.
- webpack: tree-shaking requires
- If Context7 is unavailable or returns no relevant match for the installed bundler, fall back to
official_docsand mark the claimdocumentation-based (Context7 unavailable)rather than presenting it as freshly verified. - Never invent a bundler config key, CLI flag, or
package.jsonfield that no queried source confirms.
Lean operating rules
- Require confirmation the build ran in production mode before evaluating any tree-shaking claim. A development-mode build routinely skips or partially applies elimination by design; "it's still there in dev" proves nothing.
- Determine the suspect dependency's actual module format from its package.json (
exports/modulewith animportcondition vs. amain/require-only CJS entry) — do not infer format from the package's popularity or age. CJS defeats static tree-shaking analysis regardless of bundler, and is the most common silent cause of "unused code that won't go away." - Check
sideEffectsin both the app's own package.json and the suspect dependency's package.json. A missing field orsideEffects: truetells the bundler to assume every import has a side effect and keep it — this is often correct behavior being mistaken for a bug. - Before proposing or accepting
sideEffects: falseon any module, read that module for top-level side-effecting code — global polyfills, CSS-in-JS injection, prototype patching, analytics auto-init, CSP nonce injection, sanitizer initialization. A falsesideEffects: falseclaim is a correctness bug that ships silently; it will not show up as a build error. - Rule out a barrel-file re-export pattern (
import * as utils from './utils', or a packageindex.jsthat re-exports everything) in the app's own code before blaming the dependency — this is a common tree-shaking blocker that has nothing to do with the dependency's configuration. - Never accept "no build errors" or "the build completed" as proof of elimination. Require a bundle-analyzer or output-file diff showing the specific module or byte range is actually absent, taken from a production build, before and after the change.
- After any
sideEffects: falsechange, require a runtime smoke test of the affected surface, not just a rebuild — dropped initialization code fails at runtime, not at build time. - Confirm the installed bundler and its major version via Context7 before prescribing exact config syntax — see Context7 Documentation Protocol. Do not assume a memorized API is still current.
References
Load these only when needed:
- Module format and sideEffects field — use when determining whether a dependency is ESM or CJS, and when reading or writing the
sideEffectsfield in package.json (webpack's flag semantics and the production-mode requirement). - Rollup and Vite treeshake options — use when the project is Rollup- or Vite/Rolldown-based and the review needs
treeshake.moduleSideEffects,propertyReadSideEffects, or preset-level configuration. - ESM/CJS interop and verification — use when the suspect module's format is ambiguous, when CJS interop is suspected as the blocker, and for the before/after diff and runtime-smoke-test verification workflow that closes out every review.
Response minimum
Return, at minimum:
- the module format (ESM/CJS) and
sideEffectsfield state for both the app and the suspect dependency, cited from the actual package.json content read, - confirmation the evidence came from a production-mode build, not development mode,
- the exact package.json or bundler-config change proposed, with syntax version-confirmed via Context7 against the installed bundler major,
- the before/after bundle-analyzer byte and module-count diff proving elimination, not just build success,
- a correctness caveat and runtime-smoke-test requirement whenever
sideEffects: falseis newly applied.
Files (vanguard-frontier-agentic)
-
references
-
esm-cjs-interop-and-verification.md 6 KB
# ESM/CJS Interop and the Verification Workflow Use this reference when the suspect module's format is ambiguous, when CJS interop is suspected as the actual blocker, and for the before/after diff and runtime-smoke-test steps that close out every review in this skill. ## What people get wrong The naive story is: > I imported it with `import`, so it's an ES module now. Wrong. Writing `import x from 'pkg'` in your own source does not change what `pkg` itself is. Per Node.js's package.json documentation, a package's actual module system is determined by its own `"type"` field, its `"exports"` map conditions, and file extensions (`.mjs`/`.cjs` override `"type"`) — not by the syntax the *importer* happens to use. Bundlers perform CJS/ESM interop specifically so that `import`-syntax consumers can still consume CJS packages, and that interop is exactly what defeats static tree-shaking: the bundler cannot statically prove which of a CJS module's dynamically-assigned `module.exports` properties are unused, so it must keep the whole thing. ## Diagnosing interop as the cause Before concluding a bundler configuration is wrong, rule out CJS interop as the actual and unfixable-by-config cause: 1. Check the dependency's `package.json` `"exports"` map for an `"import"` condition pointing at a real ESM build. If the only entry is `"require"` or a bare `"main"` pointing at a `.js` file with no `"type": "module"`, the package is CJS-only at that entry point — no bundler `sideEffects` or `treeshake` setting will make it statically analyzable. 2. Check whether the package ships *both* CJS and ESM builds but exposes only a barrel-style default export from the ESM entry (e.g. `lodash-es`'s top-level `index.js` re-exporting every function) versus a per-function subpath (`lodash-es/debounce`). Importing the barrel entry can still pull in far more than needed even when the package is technically ESM, because the bundler must still resolve and analyze the whole re-export graph; importing the specific subpath sidesteps that entirely and is usually the more reliable fix regardless of `sideEffects` configuration. 3. Check the app's *own* code for a barrel-file pattern before blaming the dependency at all: `import * as utils from './utils'` where `./utils/index.js` re-exports every internal module, or a component library's internal `index.ts` that re-exports every component. This is one of the most common tree-shaking blockers and has nothing to do with any external dependency's configuration — the fix is importing the specific submodule directly, or restructuring the barrel to preserve per-export side-effect metadata the bundler can act on. ## Example: the "why is lodash still in my bundle" case A report of "lodash is in the bundle even though we only use `lodash-es`'s `debounce`" is almost always one of: - The app actually imports from the CJS `lodash` package somewhere (a transitive dependency, or a stray import), not `lodash-es` — confirm via the analyzer's module-resolution path, not the package name alone. - The app imports the `lodash-es` barrel default (`import _ from 'lodash-es'` or `import { debounce } from 'lodash-es'`) rather than the subpath (`import debounce from 'lodash-es/debounce'`). Named imports from a barrel *can* still tree-shake correctly under a fully ESM, side-effect-clean barrel, but many barrels re-export in ways that retain more than expected — subpath import is the more reliable fix and should be verified by diff, not assumed to work either way. Recommend the narrowest import path the package actually exposes, then confirm via analyzer diff that only the intended submodule remains — do not close the finding on the recommendation alone. ## The verification workflow Every finding in this skill closes only with evidence, not a config change alone: 1. **Confirm production mode.** Re-run (or ask for) the project's production build command. A development-mode analyzer run is not admissible evidence either for "it's broken" or "it's fixed." 2. **Capture the before state.** Record the suspect module's presence and byte size (parsed and gzipped/brotli, matching how the project already measures) from the analyzer output. 3. **Apply the narrowest fix.** Prefer, in order: correcting an app-side barrel import → correcting a `sideEffects` field with side-effecting files explicitly listed → switching to a bundler-level `moduleSideEffects`/`treeshake` override only when the first two do not apply → replacing a CJS-only dependency with an ESM-native alternative or its narrowest subpath. 4. **Capture the after state.** Re-run the identical production build and analyzer command. Diff module list and byte counts against the before state. 5. **Runtime smoke test, mandatory whenever `sideEffects: false` changed.** Load the affected page or component in the built (not dev-server) output and confirm styling, polyfilled behavior, and any global registration the module was responsible for still function. A passing build after `sideEffects: false` proves nothing about runtime correctness — that field's entire failure mode is silent at build time. ## Hard stops - Do not apply `sideEffects: false` without having read the target module for top-level side effects. - Do not claim tree-shaking success, or failure, from a development-mode build. - Do not treat "no build errors" as proof of elimination in either direction. - Do not close a finding without a before/after analyzer diff attached. ## Adversarial checklist Before closing any review under this skill, confirm: - Was the diff taken from a production build, not development mode? - Is the dependency's actual module format verified from its own `package.json`, not assumed from its name or popularity? - If `sideEffects: false` was applied or proposed, was the target module's source actually read for side effects first? - Is a barrel-file import pattern in the app's *own* code ruled out as the real blocker, separately from the dependency's configuration? - Was a runtime smoke test performed after any `sideEffects: false` change, not just a rebuild? If any answer is no, the review is not done. -
module-format-and-sideeffects.md 5.2 KB
# Module Format and the sideEffects Field Use this reference when determining whether a dependency is structurally tree-shakeable, and when reading or writing the `sideEffects` field for webpack-family bundlers. ## What people get wrong The naive story is: > The bundler is smart enough to figure out what's unused. If it doesn't, the bundler is broken. Wrong. Tree-shaking is a conditional optimization built on two independent prerequisites, and if either is missing the bundler is not broken — it is behaving exactly as documented: 1. The module must be statically analyzable ES modules (`import`/`export`), not `require`/`module.exports`. 2. The bundler must be told, explicitly or by default assumption, which modules have no side effects, so it is safe to drop unreferenced exports. Neither prerequisite is automatic. Both must be verified, not assumed. ## Module format: the first gate Static analysis of `import`/`export` bindings is what lets a bundler know, at build time, exactly which exports are used and which are not. `require()` calls are dynamic and CommonJS's `module.exports` object can be mutated at runtime in ways a static analyzer cannot fully prove safe — so a CJS module is generally not tree-shakeable, regardless of which bundler processes it. Check module format from the dependency's own `package.json`, not from assumption: - An `"exports"` field with an `"import"` condition, or a top-level `"module"` field pointing at an ESM build, signals the package ships (or can ship) ES modules. - A `"main"` field with no ESM entry point, or a package that only exposes `require()`-compatible output, is CJS-only. Static tree-shaking of that package's internals is not achievable through configuration; the fix is choosing a narrower import path the package exposes, or an ESM-native alternative. Do not assume format from the package's popularity, age, or name. Long-lived, widely used packages are exactly the ones most likely to still ship a CJS-only default entry for backward compatibility while also shipping an ESM build the app is failing to resolve. ## The sideEffects field: the second gate Per webpack's own guidance, tree-shaking of unused *exports* within an otherwise-imported ESM module is a separate mechanism (`usedExports`) from tree-shaking of unused *whole modules* (`sideEffects`). The `sideEffects` field governs the latter: it tells webpack whether a module can be dropped entirely if none of its exports are referenced. - `"sideEffects": false` at the package level tells the bundler: assume nothing in this package has an import-time side effect; safe to drop any file whose exports are unused. - Omitting the field, or `"sideEffects": true`, tells the bundler to assume every file might have a side effect and to keep every imported file regardless of whether its exports are used. - A module that genuinely does have import-time side effects — CSS imports, global polyfills, prototype patching, analytics auto-registration — must be listed explicitly if the rest of the package is marked side-effect-free, e.g.: ```json { "name": "awesome-ui", "sideEffects": ["**/*.css"] } ``` Marking the whole package `sideEffects: false` while a CSS import exists uninstrumented inside it will silently drop the CSS from the production bundle. This does not produce a build error. It produces broken styling in production that only shows up after deploy. ## The production-mode requirement Per webpack's documented guidance, tree-shaking's user-visible effect (actual removal of dead code from output) requires `mode: 'production'`, or `optimization.usedExports` explicitly enabled in a non-production config. In development mode, webpack intentionally may keep `usedExports` analysis observable in the module graph without physically removing the code from the bundle, specifically so developers can inspect what *would* be removed. A development-mode bundle showing a module present is not evidence tree-shaking is broken — it may be evidence tree-shaking was never active for that build. `"sideEffects": false` additionally depends on `optimization.providedExports` to identify which exports are actually used before webpack can drop unused ones; this is enabled by default under `mode: 'production'`, but if a project runs a custom production-like config outside the `mode` shorthand, confirm the option is not disabled. ## What to check, in order 1. Confirm the analyzer output being reviewed came from a production-mode build. If not, stop and get one — nothing below is meaningful otherwise. 2. Read the suspect dependency's `package.json` `exports`/`module`/`main` fields to determine module format. 3. Read `sideEffects` on both the app's own `package.json` and the dependency's `package.json`. 4. If proposing `sideEffects: false` (or adding a package to an existing `sideEffects` array as an exception), read the module's source for top-level side-effecting statements before proposing it — do not propose it from the field being merely absent. ## Verification target A `package.json` diff and a bundle-analyzer module-list diff, both taken from a production build, one before and one after the change. "The build still succeeds" is not verification of either format or side-effect claims. -
rollup-vite-treeshake-options.md 5.4 KB
# Rollup and Vite Tree-Shaking Options Use this reference when the project bundles with Rollup directly, or with Vite (which bundles production builds through Rollup on Vite 5/6, and through Rolldown — a Rust reimplementation with a Rollup-compatible option surface — on Vite 7+). Confirm the installed major via Context7 before applying any snippet below; the option surface has moved. ## What people get wrong The naive story is: > Rollup's ESM-first design means everything just gets tree-shaken automatically; there's nothing to configure. Incomplete. Rollup's `treeshake` option is not a single on/off switch — it accepts `false` (fully disabled), a named preset, or a fine-grained options object, and the two settings most responsible for either under-elimination or unsafe over-elimination are `moduleSideEffects` and `propertyReadSideEffects`, both of which default to conservative (safe but less aggressive) behavior unless explicitly overridden. ## The treeshake option surface ```javascript export default { input: 'src/index.js', output: { format: 'es', dir: 'dist' }, treeshake: { moduleSideEffects: false, // assume modules have no side effects propertyReadSideEffects: false, // property reads are side-effect-free tryCatchDeoptimization: false, // don't pessimistically include try-catch bodies unknownGlobalSideEffects: false, // accessing unknown globals is safe preset: 'smallest', // or 'safest' / 'recommended' }, }; ``` `treeshake` also accepts the boolean `true` (default behavior) or one of the preset strings directly (`'smallest' | 'safest' | 'recommended'`) without a full options object. ### moduleSideEffects `moduleSideEffects` controls whether an entire imported module is retained even when none of its exports are used, mirroring in spirit what webpack's `sideEffects` package.json field does, but configured at the bundler level rather than (or in addition to) the package level. It accepts: - `boolean` — blanket true/false for all modules. - `'no-external'` — treat only external (`node_modules`) modules as side-effect-free; local project code keeps its analyzed side effects. - a string array — explicit module ID patterns to treat as side-effect-free. - a predicate function `(id, external) => boolean` for per-module logic. Setting this to `false` globally without auditing what actually lives in `node_modules` is the Rollup-side equivalent of the webpack `sideEffects: false` correctness trap: any dependency that performs import-time registration (polyfills, global CSS, `window` patches) will be silently dropped. ### propertyReadSideEffects This governs whether Rollup assumes a property read (including getter invocation) can safely be removed if its result is unused: ```javascript // Removed if treeshake.propertyReadSideEffects === false const foo = { get bar() { console.log('effect'); return 'bar'; }, }; const result = foo.bar; const illegalAccess = foo.quux.tooDeep; // would also throw at runtime if kept ``` Setting this `false` can eliminate code whose only purpose was a getter side effect (logging, lazy initialization, a Proxy trap). This is a narrower and less commonly needed lever than `moduleSideEffects` — do not reach for it as a first attempt to eliminate a stubborn module; confirm the actual blocker is a property read, not module-level retention, before touching this option. ### External module elimination example ```javascript // input import { unused } from 'external-a'; import 'external-b'; console.log(42); ``` With `treeshake.moduleSideEffects === true` (default-safe), both imports are retained even though `unused` is never referenced, because `moduleSideEffects: true` means Rollup assumes importing `external-a` and `external-b` might do something even without using their exports. With `moduleSideEffects: false`, both are dropped — which is correct only if neither package performs import-time work. ## Vite / Rolldown specifics Vite's production build has historically gone through Rollup, exposed via `build.rollupOptions`. Starting with Vite 7.3.1, the production bundler is Rolldown, and `build.rollupOptions` is now documented as a deprecated alias for `build.rolldownOptions` — if both are set, `rollupOptions` is ignored and Vite emits a warning. Verify the installed Vite major via Context7 before choosing which option key to write into a config; a `rollupOptions.output.manualChunks` object-form snippet, or a Rollup-specific `treeshake` shape, is not guaranteed to carry over unchanged into `rolldownOptions`. `import.meta.env.DEV` and `import.meta.hot` guards are statically replaceable constants in Vite's build pipeline — code inside an `if (import.meta.env.DEV)` block is tree-shaken out of production builds by design. If a user reports dev-only code appearing in a production bundle, check whether the guard condition is actually one of these statically-analyzable forms rather than a runtime environment-variable read that the bundler cannot constant-fold. ## Verification target A Rollup/Rolldown build with `--sourcemap` or the project's configured `rollup-plugin-visualizer`/equivalent output, diffed before and after any `treeshake` option change, on a production-mode build. Confirm via Context7 that the exact option key exists on the installed bundler version before writing it into config — `rollupOptions` vs `rolldownOptions` is the most likely place this silently drifts.
-
-
metadata.json 1.7 KB
{ "id": "tree-shaking-dead-code-review", "name": "Tree-Shaking & Dead-Code Review", "type": "skill", "provider": "frontend", "harnesses": [ "codex", "claude-code", "cursor", "gemini", "kiro", "other" ], "summary": "Verifies that a bundler's tree-shaking actually eliminated dead code by inspecting output bytes, module format, and sideEffects/treeshake configuration against a production-mode before/after diff, rather than trusting a clean build as proof of elimination, loaded progressively.", "source_type": "original", "official_docs": [ "https://webpack.js.org/guides/tree-shaking/", "https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking", "https://rollupjs.org/configuration-options/#treeshake", "https://nodejs.org/api/packages.html#packagejson-and-file-extensions", "https://web.dev/articles/reduce-javascript-payloads-with-tree-shaking" ], "security_notes": "Do not recommend blanket sideEffects: false on a package the reviewer has not verified is actually side-effect-free (e.g., polyfills, CSS imports, analytics auto-init) -- this silently drops required initialization code, which is a correctness bug, not just a size issue, and can remove security-relevant setup such as CSP nonce injection or sanitizer initialization. Require a runtime smoke test, not just a rebuild, after any sideEffects: false change. Do not accept or echo any credential-shaped string found in a pasted build config, package.json, or analyzer report as if it were safe to keep in the transcript.", "last_verified": "2026-07-02", "path": "skills/frontend/tree-shaking-dead-code-review", "author": "github: VincentChuWaiChow", "version": "0.1.0" } -
SKILL.md 8.4 KB
--- name: tree-shaking-dead-code-review description: Verifies that a bundler's tree-shaking actually eliminated dead code by inspecting output bytes and sideEffects/module-format configuration, rather than trusting a clean build as proof of elimination. allowed-tools: Read Grep Glob Bash(npm run build:*) metadata: author: "github: VincentChuWaiChow" version: "0.1.0" updated: "2026-07-02" category: operational --- # Tree-Shaking & Dead-Code Review ## Purpose "The build succeeded" and "the code was tree-shaken" are unrelated claims. A production build with zero errors can still ship a fully dead, unreferenced module byte-for-byte, because tree-shaking is not a guaranteed pass — it is a conditional static-analysis optimization that silently no-ops the moment it hits a CJS `require`, a missing or wrong `sideEffects` field, a barrel-file re-export, or a development-mode build. This skill treats "eliminated" as a claim that must be proven from bundle-analyzer output bytes and module lists, before and after, in a production build — never inferred from build success, and never inferred from a `sideEffects: false` change applied without reading the target module for real side effects first, since that specific mistake causes silent runtime breakage (dropped polyfills, dropped CSS injection, dropped security-relevant initialization), not just a size regression. ## When to use Use this skill when the user asks to: - verify why a supposedly-unused import is still present in bundle-analyzer output, - evaluate whether a new dependency is tree-shakeable before it is added, - configure or review a `sideEffects` field in package.json for an app or a library the user is authoring, - explain why a `sideEffects: false` change either had no effect or broke something at runtime. ## When NOT to use - General bundle-size triage with no specific dead-code suspicion yet — use `bundle-budget-code-splitting-review` first to establish the numeric budget and rank contributors; hand off here once a specific module is suspected of being fully unused rather than merely heavy. - Deciding route- or component-level code-splitting boundaries — that is a splitting-boundary decision, not an elimination-verification decision; `bundle-budget-code-splitting-review` owns it. - Diagnosing which Core Web Vitals sub-phase is regressing with no analyzer report yet — hand off to `core-web-vitals-triage` first, return here once dead code is the named suspect. ## Context7 Documentation Protocol Tree-shaking configuration surface differs by bundler and has shifted across majors (Rolldown replacing Rollup inside Vite 7+, webpack's `sideEffects`/`usedExports` interaction, Rollup's `treeshake` preset system). Do not prescribe a fix from memorized training data. 1. Call `ToolSearch` with query `"context7"` (or `"select:mcp__Context7__resolve-library-id,mcp__Context7__query-docs"`) to load the Context7 tools if not already loaded this session. 2. Call `mcp__Context7__resolve-library-id` for the bundler actually installed in the project (webpack, Rollup, or Vite/Rolldown) before prescribing any `sideEffects` or `treeshake` configuration — do not assume the bundler from the framework name alone. 3. Call `mcp__Context7__query-docs` for the specific mechanism in question — e.g. "sideEffects array vs boolean", "treeshake.moduleSideEffects options", "production mode requirement for usedExports" — before ruling on it. Do this per review; do not reuse a prior session's memory of bundler internals. 4. Context7-grounded facts to confirm, not assume: - webpack: tree-shaking requires `mode: 'production'` (or `optimization.usedExports` explicitly enabled) to take visible effect; `sideEffects: false` in package.json additionally requires `optimization.providedExports` (on by default in production mode) to let webpack drop unused-export modules; a module with real side effects (e.g. CSS imports) must be listed explicitly, e.g. `"sideEffects": ["**/*.css"]`, or that side effect is silently dropped. - Rollup: `treeshake` accepts `false`, a preset (`'smallest' | 'safest' | 'recommended'`), or a fine-grained object — `moduleSideEffects` (boolean, `'no-external'`, a string array, or a predicate function) and `propertyReadSideEffects` are the two options most often responsible for either under- or over-aggressive elimination. - Vite 7+ ships Rolldown as its production bundler; `build.rollupOptions` is now an alias for `build.rolldownOptions` and is deprecated in favor of it — verify which option surface the installed Vite major actually documents before prescribing config keys. 5. If Context7 is unavailable or returns no relevant match for the installed bundler, fall back to `official_docs` and mark the claim `documentation-based (Context7 unavailable)` rather than presenting it as freshly verified. 6. Never invent a bundler config key, CLI flag, or `package.json` field that no queried source confirms. ## Lean operating rules - Require confirmation the build ran in production mode before evaluating any tree-shaking claim. A development-mode build routinely skips or partially applies elimination by design; "it's still there in dev" proves nothing. - Determine the suspect dependency's actual module format from its package.json (`exports`/`module` with an `import` condition vs. a `main`/`require`-only CJS entry) — do not infer format from the package's popularity or age. CJS defeats static tree-shaking analysis regardless of bundler, and is the most common silent cause of "unused code that won't go away." - Check `sideEffects` in both the app's own package.json and the suspect dependency's package.json. A missing field or `sideEffects: true` tells the bundler to assume every import has a side effect and keep it — this is often correct behavior being mistaken for a bug. - Before proposing or accepting `sideEffects: false` on any module, read that module for top-level side-effecting code — global polyfills, CSS-in-JS injection, prototype patching, analytics auto-init, CSP nonce injection, sanitizer initialization. A false `sideEffects: false` claim is a correctness bug that ships silently; it will not show up as a build error. - Rule out a barrel-file re-export pattern (`import * as utils from './utils'`, or a package `index.js` that re-exports everything) in the app's own code before blaming the dependency — this is a common tree-shaking blocker that has nothing to do with the dependency's configuration. - Never accept "no build errors" or "the build completed" as proof of elimination. Require a bundle-analyzer or output-file diff showing the specific module or byte range is actually absent, taken from a production build, before and after the change. - After any `sideEffects: false` change, require a runtime smoke test of the affected surface, not just a rebuild — dropped initialization code fails at runtime, not at build time. - Confirm the installed bundler and its major version via Context7 before prescribing exact config syntax — see Context7 Documentation Protocol. Do not assume a memorized API is still current. ## References Load these only when needed: - [Module format and sideEffects field](references/module-format-and-sideeffects.md) — use when determining whether a dependency is ESM or CJS, and when reading or writing the `sideEffects` field in package.json (webpack's flag semantics and the production-mode requirement). - [Rollup and Vite treeshake options](references/rollup-vite-treeshake-options.md) — use when the project is Rollup- or Vite/Rolldown-based and the review needs `treeshake.moduleSideEffects`, `propertyReadSideEffects`, or preset-level configuration. - [ESM/CJS interop and verification](references/esm-cjs-interop-and-verification.md) — use when the suspect module's format is ambiguous, when CJS interop is suspected as the blocker, and for the before/after diff and runtime-smoke-test verification workflow that closes out every review. ## Response minimum Return, at minimum: - the module format (ESM/CJS) and `sideEffects` field state for both the app and the suspect dependency, cited from the actual package.json content read, - confirmation the evidence came from a production-mode build, not development mode, - the exact package.json or bundler-config change proposed, with syntax version-confirmed via Context7 against the installed bundler major, - the before/after bundle-analyzer byte and module-count diff proving elimination, not just build success, - a correctness caveat and runtime-smoke-test requirement whenever `sideEffects: false` is newly applied.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.