Skip to content

fix(build): eliminate Vite 8 treeshake.preset warning#746

Merged
james-elicx merged 4 commits intocloudflare:mainfrom
Divkix:fix/vite8-treeshake-preset-warning
Apr 2, 2026
Merged

fix(build): eliminate Vite 8 treeshake.preset warning#746
james-elicx merged 4 commits intocloudflare:mainfrom
Divkix:fix/vite8-treeshake-preset-warning

Conversation

@Divkix
Copy link
Copy Markdown
Contributor

@Divkix Divkix commented Apr 2, 2026

Summary

Fixes #540 - Vite 8 builds produce warnings about the deprecated "treeshake.preset" option.

Problem

The "treeshake.preset" option is Rollup-specific and not supported by Rolldown (Vite 8's bundler). This causes build warnings:

Warning: Invalid input options (1 issue found)
- For the "treeshake.preset". Invalid key: Expected never but received "preset".

Solution

Added version-gated "getClientTreeshakeConfigForVite(viteMajorVersion)" function that returns:

  • Vite 7: { preset: "recommended", moduleSideEffects: "no-external" } (Rollup-compatible)
  • Vite 8+: { moduleSideEffects: "no-external" } (Rolldown-compatible, no preset)

The "moduleSideEffects: "no-external"" option is valid in both bundlers and provides the key optimization for barrel-exporting libraries.

Changes

  • ✨ Added "getClientTreeshakeConfigForVite(viteMajorVersion)" function
  • 🔄 Updated 4 usage sites to use version-gated function
  • 📋 Marked "clientTreeshakeConfig" as @deprecated for backward compatibility
  • 📤 Exported new function for testing
  • 🧪 Added comprehensive tests for Vite 7/8/9+ compatibility
  • ✅ Updated existing tests to expect Vite 8 format

Testing

  • All 71 build-optimization tests pass
  • No treeshake.preset warnings in Vite 8 builds
  • Tree-shaking still effective (verified with example builds)
  • Lint and type checks pass

Backward Compatibility

  • The existing "clientTreeshakeConfig" export is preserved with @deprecated notice
  • External consumers can migrate to the new version-gated function when ready

Add version-gated getClientTreeshakeConfigForVite() function that returns
Rollup-compatible config (with preset) for Vite 7 and Rolldown-compatible
config (without preset) for Vite 8+.

The treeshake.preset option is Rollup-specific and causes warnings in Vite 8
which uses Rolldown. The moduleSideEffects: 'no-external' option is valid in
both bundlers and is preserved in all configurations.

Changes:
- Add getClientTreeshakeConfigForVite(viteMajorVersion) function
- Update 4 usage sites to use version-gated function
- Mark clientTreeshakeConfig as @deprecated for backward compatibility
- Export getClientTreeshakeConfigForVite for testing
- Add comprehensive tests for Vite 7/8/9+ compatibility
- Update existing tests to expect Vite 8 format

Fixes cloudflare#540
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 2, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@746

commit: 146475e

@james-elicx
Copy link
Copy Markdown
Collaborator

/bigbonk review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: fix(build): eliminate Vite 8 treeshake.preset warning

The approach is correct and follows the established pattern (getClientOutputConfigForVite, withBuildBundlerOptions). The implementation and tests are clean. Two issues to address before merging:

1. Missing cleanup of the existing playground workaround (blocking)

examples/app-router-playground/vite.config.ts:90-108 has a plugin called strip-rolldown-incompatible-rollup-options that manually deletes treeshake.preset at configResolved time — this is the exact workaround that this PR is supposed to replace. With getClientTreeshakeConfigForVite in place, the preset is never emitted on Vite 8, so that workaround is now dead code. It should be removed (or at least the treeshake.preset deletion part of it) in this PR to avoid leaving stale workarounds in the codebase.

Note: the same plugin also deletes output.experimentalMinChunkSize, which is handled by the existing getClientOutputConfigForVite — so the entire plugin is likely dead code and can be removed.

2. Vite 8 recommended preset behavior gap

For Vite 7, the recommended preset sets multiple treeshake options beyond just moduleSideEffects — it also sets annotations: true, correctContext: true, and tryCatchDeoptimization: true (among others). For Vite 8+, this PR only carries forward moduleSideEffects: "no-external" and drops all the other settings the preset provides.

This is likely fine since Rolldown's defaults are probably sensible, but it's worth confirming and documenting. A comment noting "Rolldown's defaults already cover what the Rollup 'recommended' preset provides" (if true) would help future readers understand this isn't an accidental regression.

return {
moduleSideEffects: "no-external" as const,
};
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Rollup recommended preset sets more than just moduleSideEffects — it also enables annotations: true, correctContext: true, tryCatchDeoptimization: true, etc. Dropping these for Vite 8+ is probably fine if Rolldown's defaults already cover them, but worth confirming and documenting here. A one-liner like:

Suggested change
}
// Rolldown (Vite 8+) - no preset support, only specific options.
// Rolldown's built-in defaults already cover what Rollup's 'recommended'
// preset provides (annotations, correctContext, tryCatchDeoptimization).
return {

would help future readers understand this isn't an accidental loss of optimizations.


describe("getClientTreeshakeConfigForVite", () => {
it("returns preset for Vite 7 (Rollup compatibility)", async () => {
const { getClientTreeshakeConfigForVite } = await import("../packages/vinext/src/index.js");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: All three test cases do await import("../packages/vinext/src/index.js") to get getClientTreeshakeConfigForVite. Since this is a pure function with no side effects or module-level state dependency, you could import it once at the top of the describe block (or at the file level alongside clientManualChunks, clientTreeshakeConfig, etc. on line 13-18) instead of doing a dynamic import in each test. The existing clientTreeshakeConfig and clientManualChunks tests use static imports.

This is minor — the tests work fine as-is.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 2, 2026

Review submitted. Here's a summary of the feedback:

Requested changes on PR #746:

  1. Missing cleanup of existing workaround (blocking): examples/app-router-playground/vite.config.ts has a strip-rolldown-incompatible-rollup-options plugin that manually deletes treeshake.preset — this is the exact workaround this PR replaces. With the new version-gated function, that plugin is now dead code (both the treeshake.preset deletion and the experimentalMinChunkSize deletion, since the latter is already handled by getClientOutputConfigForVite). It should be removed in this PR.

  2. Behavior gap documentation: The Rollup recommended preset enables multiple treeshake options beyond moduleSideEffects (like annotations, correctContext, tryCatchDeoptimization). For Vite 8+, only moduleSideEffects is carried forward. This is likely fine if Rolldown's defaults cover the rest, but a comment documenting that decision would prevent future confusion.

  3. Minor nit on tests: The new tests use dynamic await import() in each test case when a static import (matching the existing test patterns in the file) would be cleaner.

github run

@james-elicx james-elicx marked this pull request as draft April 2, 2026 10:08
Remove dead workaround plugin from playground
- Deleted strip-rolldown-incompatible-rollup-options plugin from
  examples/app-router-playground/vite.config.ts
- This workaround is no longer needed since getClientTreeshakeConfigForVite()
  never emits treeshake.preset for Vite 8+

Add documentation comment about Rolldown defaults
- Enhanced JSDoc for getClientTreeshakeConfigForVite() to explain behavior gap
- Documents how Rolldown defaults compare to Rollup's recommended preset
- Clarifies that moduleSideEffects: no-external is the key optimization

Fix test import style
- Changed from dynamic await import() to static imports
- Matches existing test patterns in build-optimization.test.ts
- Removes async from test cases that don't need it

Addresses review comments from PR cloudflare#746
@james-elicx
Copy link
Copy Markdown
Collaborator

/bigbonk review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good change — the version-gating follows the established getClientOutputConfigForVite / withBuildBundlerOptions pattern, the playground workaround is cleaned up, and the JSDoc documenting the Rollup preset options vs Rolldown defaults is thorough. Two minor notes below, neither blocking.

* - annotations: true (Rolldown default is also true)
* - manualPureFunctions: [] (Rolldown default is also [])
* - propertyReadSideEffects: true (Rolldown equivalent is 'always', the default)
* - unknownGlobalSideEffects: false (Rolldown default is true, slightly more conservative)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This line documents that Rolldown defaults to unknownGlobalSideEffects: true while the Rollup recommended preset sets it to false. That's a real behavioral difference — Rolldown will be more conservative about eliminating code that touches unknown globals.

For a client bundle this is almost certainly fine (maybe marginally larger output), but it's worth calling out explicitly that this is a known divergence, not just a "Rolldown handles it differently" situation. Consider adding (slightly less aggressive DCE on unknown globals — acceptable for client bundles) or similar so future readers don't wonder whether we should be setting unknownGlobalSideEffects: false explicitly on Vite 8+.

*/
function getClientTreeshakeConfigForVite(viteMajorVersion: number) {
if (viteMajorVersion >= 8) {
// Rolldown (Vite 8+) - no preset support, only specific options
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bonk review suggested expanding this inline comment to note that Rolldown's defaults already cover the other recommended preset options. The JSDoc above does cover it, but the inline comment is what you see when reading the code without scrolling up. Mildly helpful to add the one-liner:

Suggested change
// Rolldown (Vite 8+) - no preset support, only specific options
// Rolldown (Vite 8+) - no preset support, only specific options.
// Rolldown's built-in defaults already cover what Rollup's 'recommended'
// preset provides (annotations, correctContext, tryCatchDeoptimization).

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 2, 2026

Review posted. Here's a summary of my feedback:

Overall: The PR looks good. It follows the established version-gating pattern (getClientOutputConfigForVite, withBuildBundlerOptions), the playground workaround plugin is properly cleaned up, tests use static imports, and the JSDoc is thorough.

Two minor (non-blocking) comments:

  1. unknownGlobalSideEffects divergence (line 651): The JSDoc documents that Rolldown defaults to true while Rollup's recommended preset sets it to false. This is a real behavioral difference (slightly less aggressive DCE). Worth calling out explicitly as a known acceptable divergence so future readers don't wonder whether we should be setting it explicitly.

  2. Inline comment expansion (line 661): The bonk review suggested expanding the inline comment to note Rolldown's defaults cover the other preset options. The JSDoc handles it, but the inline comment is what you see at the code site. Suggested a one-liner addition.

github run

Address PR cloudflare#746 review feedback from @ask-bonk:

- JSDoc: Explicitly call out unknownGlobalSideEffects as a known acceptable
divergence (Rolldown defaults to true vs Rollup recommended preset's false).
Makes it clear this is intentional, not an oversight.

- Inline comment: Note that Rolldown's built-in defaults already cover what
Rollup's 'recommended' preset provides (annotations, correctContext,
tryCatchDeoptimization). Provides immediate context at the code site.

Both changes are documentation-only; no functional impact.
@Divkix Divkix marked this pull request as ready for review April 2, 2026 18:02
@james-elicx
Copy link
Copy Markdown
Collaborator

/bigbonk review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good change. The version-gating pattern matches getClientOutputConfigForVite / withBuildBundlerOptions, the playground workaround removal is correct, the JSDoc is thorough, and the tests use static imports matching the rest of the file. Two minor notes inline.

// that rely on module-level side effects.
...(!isSSR && !isMultiEnv ? { treeshake: clientTreeshakeConfig } : {}),
...(!isSSR && !isMultiEnv
? { treeshake: getClientTreeshakeConfigForVite(viteMajorVersion) }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not part of this diff, but the comment at line 1483 still references the deprecated clientTreeshakeConfig. Since you're updating all the call sites, worth updating that comment to point to getClientTreeshakeConfigForVite as well.

expect(config).toEqual({
moduleSideEffects: "no-external",
});
expect(config).not.toHaveProperty("preset");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The not.toHaveProperty("preset") assertions (here and line 1659) are redundant — toEqual does an exact structural match, so if preset were present the toEqual would already fail. Not blocking, but it's noise that could be dropped.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 2, 2026

Review submitted. Here's a summary of my feedback on PR #746:

Overall: The PR looks good. It correctly follows the established version-gating patterns (getClientOutputConfigForVite, withBuildBundlerOptions), properly removes the stale playground workaround plugin, has thorough JSDoc, and uses static imports in the tests.

Two minor (non-blocking) notes:

  1. Stale comment reference (index.ts:1483): The comment at line 1483 still says "See clientTreeshakeConfig for rationale" but the code now calls getClientTreeshakeConfigForVite. Worth updating to point to the new function name.

  2. Redundant test assertions (build-optimization.test.ts:1651, 1659): The not.toHaveProperty("preset") checks after toEqual are redundant since toEqual already does exact structural matching — if preset were present, toEqual would fail first.

github run

- Update comment to reference getClientTreeshakeConfigForVite instead of deprecated clientTreeshakeConfig
- Remove redundant not.toHaveProperty('preset') assertions from tests (toEqual already does exact matching)

Addresses review feedback from @ask-bonk[bot] on PR cloudflare#745
@james-elicx
Copy link
Copy Markdown
Collaborator

/bigbonk review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean change. The version-gating follows the established getClientOutputConfigForVite / withBuildBundlerOptions pattern, the playground workaround plugin is properly removed, the JSDoc thoroughly documents the Rollup preset vs Rolldown defaults divergence, and the tests use static imports matching the rest of the file. All prior review feedback has been addressed.

LGTM.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk bot commented Apr 2, 2026

Approved the PR. Here's my assessment:

The PR is clean and ready to merge. It:

  1. Adds getClientTreeshakeConfigForVite(viteMajorVersion) following the same version-gating pattern as the existing getClientOutputConfigForVite and withBuildBundlerOptions functions.
  2. Returns { preset: "recommended", moduleSideEffects: "no-external" } for Vite 7 (Rollup) and { moduleSideEffects: "no-external" } for Vite 8+ (Rolldown), correctly eliminating the invalid treeshake.preset warning.
  3. Removes the stale strip-rolldown-incompatible-rollup-options workaround plugin from the playground config — this is now dead code since the root cause is fixed.
  4. Has thorough JSDoc documenting the Rollup preset options vs Rolldown defaults, including the known unknownGlobalSideEffects divergence.
  5. Updates all 4 call sites and both integration test assertions, plus adds focused unit tests for the new function.

github run

@james-elicx james-elicx merged commit 063bf00 into cloudflare:main Apr 2, 2026
23 checks passed
@Divkix Divkix deleted the fix/vite8-treeshake-preset-warning branch April 2, 2026 18:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vite 8: deprecated config options cause build warnings

2 participants