Declare side-effecting modules so bundlers have better hints for what can be tree-shaken#21365
Open
NullVoxPopuli wants to merge 1 commit intonvp/tell-rollup-to-tree-shake-the-debug-packagesfrom
Open
Conversation
Contributor
📊 Size reportTarball size — dist/dev No changes
dist/prod No changes
smoke-tests/v2-app-hello-world-template/dist -35.2%↓
🤖 This report was automatically generated by wyvox/pkg-size |
2 tasks
This was referenced May 3, 2026
NullVoxPopuli
commented
May 4, 2026
Contributor
Author
There was a problem hiding this comment.
a manual lint, of sorts
fcead41 to
b5304e9
Compare
e6f0453 to
31fccff
Compare
Contributor
Author
|
The lint for this was extracted to: #21378 |
31fccff to
16e1ad8
Compare
Two purely-additive bundler hints that let consumer bundlers (vite/rolldown, webpack, esbuild) tree-shake aggressively through ember-source's internal barrel re-exports without requiring any source-file restructuring. 1. **`"sideEffects": false` on `ember-source/package.json`** - declares that no module in this package has top-level side effects that need to be preserved if the module's exports are unused. The bundler can then DCE re-exports through `index.ts` barrels that currently anchor the rest of the graph in place. This is safe in practice because rollup's chunking groups symbols with their side effects: any chunk containing the classic `Component` class also contains the `setInternalComponentManager(CURLY_COMPONENT_MANAGER, Component)` call, the `setHelperManager` registrations live in the chunk that holds `helper.ts`, etc. Importing a symbol from a chunk pulls the chunk's side effects along; apps that don't reach those symbols don't need their side effects either. 2. **`treeshake.moduleSideEffects` callback in `rollup.config.mjs`** - the package-level `sideEffects: false` declarations on `@glimmer/debug`, `@glimmer/debug-util`, and `@glimmer/local-debug-flags` get lost when rolldown emits shared chunks (debug code from these packages can leak into chunks that the renderer-only path then pulls in). The callback re-asserts module purity at the chunk level so leaked debug code drops out of the renderer-only path. Measured against `smoke-tests/v2-app-hello-world-template`: | | raw | gzip | | - | - | - | | before | 251.05 KB | 79.75 KB | | after | 172.99 KB | 55.31 KB | Classic `v2-app-template` and v1 `app-template` smoke tests still build and pass. `pnpm test:node` 20/20. `pnpm vite build --mode development` (full dev test suite app) builds clean.
16e1ad8 to
c0de606
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Important
The key thing is that the side-effects signal is only relevant if nothing is imported from a file.
Demo:
bundler hints that let consumer bundlers (vite/rolldown, webpack, esbuild) tree-shake aggressively through ember-source's internal barrel re-exports without requiring any source-file restructuring.
for the ember-source dist/tarball, the result is that chunks shake out a bit differently.
on webpack docs:
on svelte's docs
https://github.com/sveltejs/kit/pull/10691/changes#diff-4b03f147d0e7e4750663fcdb0ef3bf8ba11ad0bc47204456bf5d7aa5fd9a7e00R129
They do recommend explicitly declaring which modules have side-effects (which is something I want to do, I just want it generated)
this person says sideEffects is "standard" vitejs/vite#14321 (comment)
from rollup:
I believe this means that it happens that if something is used from a file, it can have side-effects within that file -- which is good.
Because, say you never use helpers:
we want:
to be removed, because you never used them.
let's say that file also registers helper managers.
the output removes the import entirely.
but because you don't use helpers, there is no behavior loss.
If you did
then the file is used, and any side-effects within would remain, because deeper analysis occurs.
however, if we declared
@ember/helperas having side-effects in pacakge.json, we'd likely not get the correct stripping behavior that we want.so, it is "less scary" to have all side-effects actually live in private files so that this "did you import it?" check and deeper analysis trigger isn't directly controlled by users