Skip to content

Fix #__PURE__ annotations being ignored by Rolldown/Vite 8 (#568)#569

Merged
nev21 merged 5 commits into
nevware21:mainfrom
hrithik-infinite:fix/issue-568-pure-annotations
May 25, 2026
Merged

Fix #__PURE__ annotations being ignored by Rolldown/Vite 8 (#568)#569
nev21 merged 5 commits into
nevware21:mainfrom
hrithik-infinite:fix/issue-568-pure-annotations

Conversation

@hrithik-infinite
Copy link
Copy Markdown
Contributor

@hrithik-infinite hrithik-infinite commented May 22, 2026

Summary

Fixes #568 by making sure #__PURE__ annotations survive the build in a form that Rolldown/Vite 8 accepts, so tree-shaking works and [INVALID_ANNOTATION] warnings go away.

Root cause

Some emitted chunks place #__PURE__ annotations inside parentheses in a way that current Rolldown does not recognize reliably. A few boolean helper call sites also carried redundant inner pure annotations that caused warnings in the !!(...) form.

Fix

  • Keep the source-level pure-call patterns in place where they are useful for older toolchains.
  • Add a Rollup renderChunk normalization step that rewrites spaced/invalid pure-annotation forms into canonical /*#__PURE__*/ placement in the final output.
  • Remove the redundant inner #__PURE__ annotations from the hasDocument / hasWindow / hasNavigator / hasHistory / isNode / isWebWorker boolean wrappers, since those functions are already marked #__NO_SIDE_EFFECTS__.
  • Include a small lint cleanup so the branch builds cleanly.

Verification

  • Reproduced the original Vite 8 / Rolldown warning scenario and confirmed the invalid annotation warnings are gone.
  • Verified the emitted bundles still contain valid #__PURE__ annotations where expected.
  • Ran the test suite and build checks successfully.

Remove the redundant surrounding parens from `(/*#__PURE__*/foo())`
patterns. TypeScript inserts a space between the opening `(` and the
leading comment when emitting, producing `( /*#__PURE__*/foo())` in the
published bundles. Rolldown (used by Vite 8) cannot associate the
annotation with the call when the comment is positioned between the
open paren and the expression, so the annotation is silently ignored
and the call is no longer tree-shaken.

Removing the wrapping parens lets TypeScript emit
`/*#__PURE__*/ foo()`, which Rolldown interprets correctly.

Patterns where the parens are required for a top-level `as` cast are
left untouched, because TypeScript already strips both the parens and
the cast from the emit, producing a clean annotation.

For the `!!(/*#__PURE__*/foo())` callers in `hasDocument`/`hasWindow`/
`hasNavigator`/`hasHistory`, removing the parens places the comment
adjacent to `!!`, which TypeScript then drops. The inner annotations
were redundant — the parent functions are already marked
`/*#__NO_SIDE_EFFECTS__*/`, so callers can drop the calls when the
result is unused.

Verified the published bundles now contain zero
`( /*#__PURE__*/` occurrences (was 159 in v0.14.0); 153
`#__PURE__` annotations are still emitted in valid positions.
All node tests pass (1361 es5 + 1371 esnext).
@hrithik-infinite hrithik-infinite requested review from a team as code owners May 22, 2026 06:34
@hrithik-infinite
Copy link
Copy Markdown
Contributor Author

@nev21 could you please take a look when you get a chance? This fixes the Rolldown/Vite 8 warnings reported in #568. Happy to adjust the approach if you'd prefer a different pattern. Thanks!

@hrithik-infinite
Copy link
Copy Markdown
Contributor Author

Heads up — I'm also seeing 13 @typescript-eslint/no-unused-vars warnings in the current build output. They're pre-existing (not introduced by this PR), but happy to clean them up if you'd like.

Let me know which you'd prefer:

  • Fold the lint fixes into this PR (keeps everything in one place)
  • Leave them out and open a separate PR (keeps this change focused on the #__PURE__ fix)

Either is fine with me.

The original PR removed the wrapping parens around all /*#__PURE__*/
annotations to eliminate Rolldown [INVALID_ANNOTATION] warnings. That
worked for ~150 sites but rollup-plugin-cleanup silently dropped the
comment in 8 `return !!fn()` sites where the comment was no longer
adjacent to a parenthesized expression.

On inspection those 8 annotations were already ineffective:

- 6 sit inside functions marked with the outer /*#__NO_SIDE_EFFECTS__*/
  pragma (hasDocument, hasWindow, hasNavigator, hasHistory, hasSymbol,
  hasIdleCallback). The outer annotation already tells the bundler the
  whole function call is side-effect-free, making the inner PURE
  redundant.
- 2 sit inside lambda bodies whose outer _getGlobalInstFn(...) call
  already carries its own PURE (isNode, isWebWorker). The inner PURE
  only fires when the const is invoked, and the result is consumed by
  !!safe().v, so PURE has no tree-shaking effect there.

Removing them at source keeps the bundle output Rolldown-clean
(0 [INVALID_ANNOTATION] warnings) without reintroducing parens that
rollup-plugin-cleanup would space-pad back into the warning shape.
Minified bundle sizes change by +3 bytes; tree-shaking is unchanged.
@hrithik-infinite
Copy link
Copy Markdown
Contributor Author

hrithik-infinite commented May 23, 2026

@nev21 — full empirical verification (bundle sizes, __PURE__ counts, Rolldown warning counts, and explanation of the small follow-up commit 88427b6) is in the issue thread: #568 (comment)

tl;dr: tree-shaking is intact (minified bundles ±3 bytes), 0 [INVALID_ANNOTATION] patterns remaining, npm run rebuild passes end-to-end on this branch.

…tion

Adopts the maintainer's preferred approach: keep the wrapping parens on
`(<comment>fn())` sites (and the _pureRef/_pureAssign helpers) so tree-shaking
still works on older Rollup/Webpack/Terser toolchains that only honor the
annotation when it hugs the call, and instead fix the Rolldown/Vite 8
[INVALID_ANNOTATION] warnings by normalizing the emitted spacing at the build
layer rather than stripping parens from source.

Source: reverts the paren-stripping (8a830ba); parens are restored everywhere.
The only remaining source change vs the release is the 8 redundant inner
annotations dropped in 88427b6 (the parent fns are already NO_SIDE_EFFECTS, so
the inner PURE never affected DCE) - those stay removed.

Build: adds a `fixPureAnnotations` renderChunk plugin to lib/rollup.config.js,
wired into all three bundle factories and run last (after uglify on minified
targets). It rewrites the spaced annotation forms emitted by TypeScript - an
open paren followed by whitespace then the comment, plus the inner-space and
at-sign variants - back to the canonical flush-against-paren form every bundler
accepts. Uses magic-string so sourcemaps stay valid (added to devDependencies;
already present transitively via @rollup/plugin-commonjs).

Verified: rebuilt non-minified dist/ and bundle/ contain 0 spaced occurrences
(was 152 pre-bundle), 167 canonical paren-wrapped, 178 total annotations;
minified bundles strip annotations after DCE as before; config loads with no
annotation warnings. 1361 es5 + 1371 esnext node tests pass; all size budgets
pass.
…used vars

Removes six genuinely unreferenced imports (getLength in drop_while/rotate/
take_while, UNDEFINED in fill, normalizeJsName in unique, isNullOrUndefined in
copy) and adds varsIgnorePattern "^_" to no-unused-vars, mirroring the existing
argsIgnorePattern. This clears all 13 warnings without deleting the seven
compile-time type-assertion aliases in types.test.ts - those `type _x =
AssertTrue<IsEqual<...>>` declarations are the test (a wrong type fails to
compile) and are unreferenced by design.

Business Impact: Restores a clean, zero-warning lint baseline so real issues
stand out in review, while preserving the type-inference test coverage for the
public utility types (ValueOf, DeepPartial, DeepReadonly, etc.).
@hrithik-infinite
Copy link
Copy Markdown
Contributor Author

hrithik-infinite commented May 24, 2026

Pushed the reworked approach here, following your guidance on #568 — kept the parens + _pureRef/_pureAssign helpers and added a build-layer normalization step instead of stripping parens from source.

What changed

  • Reverted the source paren-stripping (8a830ba) — parens restored everywhere.
  • Removed the 8 redundant inner return !!( /*#__PURE__*/getXXX()) annotations (88427b6), since the parent fns are already __NO_SIDE_EFFECTS__.
  • Added a fixPureAnnotations renderChunk plugin (magic-string-based, valid sourcemaps) that normalizes ( /*#__PURE__*/(/*#__PURE__*/ across all bundle targets.
  • Separate chore(lint) commit clears pre-existing unused-vars warnings.

I then reproduced #568 exactly (Vite 8.0.14 / Rolldown 1.0.2, published 0.14.0, import { isNode }) and traced precisely what triggers the warnings — and it changes the attribution:

  • The 2 [INVALID_ANNOTATION] warnings (lines 920/923) come only from the inner return !!( /*#__PURE__*/safe(...)) form. The 160 outer ( /*#__PURE__*/fn()) sites do not warn on current Rolldown.
  • De-spacing the inner form is not enough!!(/*#__PURE__*/safe(...)) still warns; the annotation can't associate inside !!(...) regardless of spacing. (So the ( /*#__PURE__*/!!getXXX()) suggestion wouldn't have helped either — removal is the reliable fix.)
  • Net: removing those inner annotations is what clears the warnings; the spacing-normalization plugin doesn't affect them on current Rolldown (it's canonical-output hygiene + helps older toolchains).

Verification: published 0.14.0 → 2 warnings; this branch → 0 warnings; 1361 es5 + 1371 esnext tests pass; all size budgets pass; config loads with no annotation warnings.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 24, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.42%. Comparing base (5b5854b) to head (a875b74).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #569   +/-   ##
=======================================
  Coverage   99.42%   99.42%           
=======================================
  Files         158      158           
  Lines        4830     4830           
  Branches     1042     1039    -3     
=======================================
  Hits         4802     4802           
  Misses         28       28           
Files with missing lines Coverage Δ
lib/src/array/drop_while.ts 100.00% <ø> (ø)
lib/src/array/fill.ts 100.00% <100.00%> (ø)
lib/src/array/rotate.ts 100.00% <ø> (ø)
lib/src/array/take_while.ts 100.00% <ø> (ø)
lib/src/array/unique.ts 100.00% <ø> (ø)
lib/src/helpers/environment.ts 98.78% <100.00%> (ø)
lib/src/object/copy.ts 100.00% <100.00%> (ø)
lib/src/symbol/symbol.ts 100.00% <100.00%> (ø)
lib/src/timer/idle.ts 92.75% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses Rolldown/Vite 8 tree-shaking warnings and missed DCE by ensuring #__PURE__ annotations land in positions Rolldown accepts (notably normalizing parenthesized pure-call spacing), and includes a few related cleanups.

Changes:

  • Add a Rollup post-processing plugin (fixPureAnnotations) to canonicalize parenthesized #__PURE__ annotation placement while preserving sourcemaps.
  • Remove redundant parenthesized pure-annotated boolean coercions in a few environment/timer helpers.
  • Clean up unused imports and tweak ESLint unused-var rules to allow _-prefixed intentionally-unused locals/types.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
package.json Adds magic-string to support code rewriting while preserving sourcemaps.
lib/rollup.config.js Introduces and wires a Rollup plugin to normalize #__PURE__ annotation placement.
lib/src/helpers/environment.ts Removes redundant (/*#__PURE__*/...) wrappers in !!... sites to avoid invalid annotation locations.
lib/src/timer/idle.ts Removes redundant parenthesized pure annotation usage in hasIdleCallback().
lib/src/symbol/symbol.ts Removes redundant parenthesized pure annotation usage in hasSymbol().
lib/src/object/copy.ts Removes an unused import from helpers.
lib/src/array/unique.ts Removes an unused import.
lib/src/array/take_while.ts Removes an unused import.
lib/src/array/rotate.ts Removes an unused import.
lib/src/array/drop_while.ts Removes an unused import.
lib/src/array/fill.ts Removes an unused constant import.
.eslintrc Allows _-prefixed unused vars via varsIgnorePattern.

Comment thread lib/rollup.config.js
Comment thread lib/rollup.config.js
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@nev21 nev21 merged commit 34178c1 into nevware21:main May 25, 2026
14 checks passed
@nev21 nev21 added this to the 0.15.0 milestone May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] /*#__PURE__*/ annotations placed inside parens cause Rolldown/Vite 8 [INVALID_ANNOTATION] warnings and disable tree-shaking

4 participants