Skip to content

fix(babel): do not change "use server" and "use cache" code - #77

Merged
mark1russell7 merged 3 commits into
mainfrom
fix/preset-server-directives
Nov 15, 2026
Merged

mark1russell7 merged 3 commits into
mainfrom
fix/preset-server-directives

Conversation

@mark1russell7

@mark1russell7 mark1russell7 commented Nov 15, 2026 •

Copy link
Copy Markdown
Owner

Fixes the first problem of #69, and documents the answers to the other questions.

The bug

Next.js accepts only native async functions in a "use server" or "use cache" file or function, and it forbids this and arguments there. The preset changed these functions into plain functions that call coroutine with this and arguments. Next.js then stopped the build. This happened in the setup of the guide (babel.config.js), and also with a Turbopack rule that gives only the browser files to the preset: in the browser bundle, Next.js compiles a "use server" file into references to the server, and it checks the functions first.

The fix

  • src/babel/server-code.ts (new): isServerDirective ("use server", "use cache", "use cache: <kind>", the same forms that Next.js reads) and withServerCodeSkipped(). Each visitor function of a wrapped plugin does nothing on server code: a program with a server directive, or a path in a function with one.
  • The preset wraps each plugin after the first one. The guard does not use path.skip(), because all the plugins of a Babel pass share one traversal: a skip would also stop next/babel, @babel/preset-typescript and the other presets in that code. A test pins this.
  • The plugins with their own traversal (bind-generators, name-coroutines, static-members, async-generator-resources, and the inner traversal of @babel/plugin-transform-async-generator-functions through withRuntimeSkipped) skip server functions in that traversal, where a skip stops only that plugin.
  • A text check (/use (?:server|cache)/) for each file keeps the ancestor walk off the files without these words.
  • The Vite plugin uses the preset, so Vite RSC setups get the same fix.

Tests

  • src/babel/server-code.test.ts: file directives (both quote styles, "use cache: remote"), inline server actions in a transformed page function, inline "use cache: private" next to a transformed function, nested generators, async generators, await using and static class fields in a server function, server methods, a late string that is not a directive, "use client", another plugin in the same traversal, transformFromAst without the source text, and each visitor form of withServerCodeSkipped.
  • test/bundlers (CI only):
    • The Next.js page now calls a server action (app/actions.js), which calls a module with an inline "use cache" function next to a plain async function (app/cached.js). All Next.js cases enable cacheComponents.
    • New case next-turbopack-rule: no Babel config. A turbopack.rules rule gives the browser files with async code (a content condition) to babel-loader with only the preset, also in node_modules, plus the resolveAlias of the pnpm section of the guide.
    • Each Next.js build logs its time, for a rough comparison of the setups.
    • The server action found a second problem, outside the preset: with webpack, next/babel compiles the browser code with @babel/preset-env, and Next.js gives it no targets. preset-env then changes the async functions of a server action to ES5 (_asyncToGenerator, regenerator), and Next.js stops the build, also without this library. The fixture babel.config.js now gives the default browsers of Next.js 16 (chrome 111, edge 111, firefox 111, safari 16.4) as preset-env targets, and the guide shows the same.

Docs

  • The Next.js section of the build tools guide:
    • It has a table of the three setups, and makes the Turbopack rule the main setup.
    • It says that Server Action and "use cache" code is skipped.
    • It adds a pnpm table: which settings let a dependency find the runtime, and the alias.
    • It adds a performance section: the content condition and the costs.
    • It keeps the Babel configuration file (for webpack) and the SWC browserslist route.
  • The README table, the transform concept page and the API page mention the skip.

Version

0.3.2. PATCH: a bug fix before 1.0.0 (the preset no longer changes server code that Next.js rejects). There is no new API. The merge starts release.yml.

🤖 Generated with Claude Code

mark1russell7 and others added 3 commits November 14, 2026 19:48
Next.js accepts only native async functions in a "use server" or
"use cache" file or function, without this and arguments. The preset
changed them, and Next.js stopped the build (issue #69).

Each plugin of the preset after the first does nothing on server code:
a program or a function with "use server", "use cache" or
"use cache: <kind>". The guard does not skip the shared traversal, so
the other presets of the configuration still change that code. The own
traversals of the plugins skip the server functions.

The Next.js fixture of the build tool test now starts a server action
and a "use cache" function, and a new case builds the Turbopack rule of
the guide. The guide makes that rule the main Next.js setup, and tells
the pnpm settings, the alias and the costs.

0.3.2, PATCH: a bug fix before 1.0.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With webpack, next/babel compiles the browser code with @babel/preset-env,
and Next.js gives it no targets. preset-env then changes the async
functions of a server action, and Next.js stops the build ("Server Actions
must be async functions"), also without the preset of this library. The
fixture and the guide now give the default browsers of Next.js 16.

The test of withServerCodeSkipped no longer gives noScope to a plugin
visitor (Babel refuses it there), and counts each identifier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e name

The case next-turbopack-rule and its fixture folder had the same name, so
the case folder was the fixture folder, and the copy failed ("src and dest
cannot be the same"). The fixture is now fixture/next-rule.

Also fix two STE findings in the CHANGELOG and the guide.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mark1russell7
mark1russell7 merged commit 254946b into main Nov 15, 2026
31 checks passed
@mark1russell7
mark1russell7 deleted the fix/preset-server-directives branch November 15, 2026 01:16
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.

1 participant