vercel / vercel/next.js

Turbopack production build eliminates entire function body of a `/* @__PURE__ */`-wrapped self-invoking function, including its side effects

Open
#97,369 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Turbopack
Dominant language
JavaScript
Stars
142k
Forks
32.4k
Avg merge
2d 14h
Merged PRs (30d)
351

Description

Link to the code that reproduces this issue

https://github.com/dexterouschen/turbopack-pure-annotation-dce-repro

To Reproduce

npm install, then npm run build && npm run start, open localhost:3000, watch "Tick count" stay at 0 forever. Compare with npm run dev — same page, counter climbs continuously.

Current vs. Expected behavior

Expected: the "Tick count" counter climbs continuously (~hundreds/sec) via a requestAnimationFrame loop started in a useEffect.

Actual: in a production build only, the counter never increments past 0. Inspecting .next/static/chunks/*.js shows the entire startTicking(setCount) call — including the callback invocation and the requestAnimationFrame scheduling — compiled away to useEffect(() => () => window.cancelAnimationFrame(0), []).

The triggering pattern is a self-invoking named function expression passed through a /* @__PURE__ */-annotated name-preserving helper (the same shape esbuild/tsup emit with keepNames, and shipped as-is by npm packages like @radix-ui/react-scroll-area). The minifier appears to extend the @__PURE__ annotation's scope from the helper call it directly precedes to the separate, subsequent invocation of that call's return value — even though invoking the returned function has clear, necessary side effects (mutating closure state, invoking a caller-supplied callback, scheduling further work).

Provide environment information
Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 25.6.0: Sat Jul 11 15:25:34 PDT 2026; root:xnu-12377.161.13~4/RELEASE_ARM64_T6020
  Available memory (MB): 16384
  Available CPU cores: 10
Binaries:
  Node: 26.7.0
  npm: 11.19.0
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 16.3.1 // Latest available version is detected (16.3.1).
  eslint-config-next: N/A
  react: 19.2.8
  react-dom: 19.2.8
  typescript: 7.0.2
Next.js Config:
  output: N/A
Which area(s) are affected? (Select all that apply)

Turbopack

Which stage(s) are affected? (Select all that apply)

next build (local)

Additional context

This was discovered via a real-world regression: @radix-ui/react-scroll-area@1.2.18's addUnlinkedScrollListener (in dist/index.mjs) uses this exact pattern to drive live scrollbar-thumb position tracking during scroll. In a Next.js 16 + Turbopack production build, the same elimination causes the scrollbar thumb to freeze while the user is actively scrolling — it only updates once per fresh "listener registration" (e.g. on mouse re-enter, or after a scroll pause/resume), which is exactly the "increments once, then freezes" behavior this minimal repro demonstrates. We worked around it locally with a pnpm patch that restructures the function to avoid the IIFE + adjacent-annotation shape (happy to share the patch diff if useful for a fix).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked turbopack-pure-annotation-dce-repro repository: run npm install, compare npm run build && npm run start with npm run dev, and inspect the generated .next/static/chunks/*.js output. Trace how the annotated helper and subsequent self-invoking function are handled during the Turbopack production build. Done means the production counter keeps incrementing and the necessary callback and requestAnimationFrame side effects remain present.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, nextjs, react
Domain
build-system, compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.