Rollup tree shaking behavior with sideEffects property in nested dependencies
0 reputation · 03 Aug 2022, 01:30 UTC
0 reputation · 03 Aug 2022, 01:30 UTC
When bundling a JavaScript library using Rollup, the effectiveness of dead-code elimination depends heavily on the sideEffects property within the package.json. While Rollup performs static analysis on ES modules to identify unused exports, the presence of global mutations or imported CSS can cause the bundler to retain unnecessary code.
The uncertainty arises when a nested dependency declares "sideEffects": false, but contains modules that perform essential initialization tasks, such as registering polyfills or modifying global state. If the primary library only imports a specific function from that dependency, Rollup may omit the side-effect producing modules if they are not explicitly referenced in the dependency graph.
How does Rollup prioritize the sideEffects flag when the module graph contains dynamic imports that reference the same dependency? Can the use of /*@__PURE__*/ annotations override a global sideEffects: false declaration in a deep dependency?
33275 reputation · 03 Aug 2022, 09:31 UTC
Rollup respects the sideEffects flag on a per‑package basis. When a package declares "sideEffects": false, any import from that package is considered pure unless the import itself is used for its side effects. Dynamic imports are treated conservatively: if a module is only imported via import() and the package is marked side‑effect‑free, Rollup will keep all of that module’s exports. The /*@__PURE__*/ comment only marks an individual function call as pure; it does not override a package’s sideEffects setting or cause Rollup to retain a module that would otherwise be pruned.
sideEffects flagpackage.json that encloses the file.package.json contains "sideEffects": false, the importer assumes the file has no global side effects.import './init.js';) are kept if the package declares side‑effect‑free.Dynamic imports are considered “potentially executed at runtime.” Rollup therefore errs on the side of safety: it keeps every export of the target module unless the package’s sideEffects flag is false and the dynamic import is explicitly used for its side effects. In practice, this means that a module that registers a polyfill inside a sideEffects: false package will survive the tree‑shake only if you import it directly or if the dynamic import is used for its side effects.
The comment is a hint to the minifier that a specific function call has no observable side effects. It does not influence Rollup’s module‑level tree‑shaking decisions. A /*@__PURE__*/‑marked call will be removed if the function is unused, but the module containing that function will still be dropped if its package declares sideEffects: false and the module is never imported for its side effects.
--verbose flag to see which modules are kept. Look for entries that come from the nested dependency in question.import 'dep/init.js';) or expose a dedicated init entry point in the dependency.package.json to "sideEffects": true or remove the flag entirely.--no-treeshake to confirm that tree shaking is the cause of the missing code./*@__PURE__*/ and verifying it disappears when unused.sideEffects flag set to true for the dependency if you cannot modify its package.json.Does your code rely on a dynamic import that only triggers a side‑effect module, or do you import a specific function from that module? The answer will influence whether the module can be safely pruned or must be imported explicitly.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.