There have been breaking changes to the AST:
- `BlockStmtOrExpr` is now `ArrowFunctionBody`
- function bodies are `FunctionBody` instead of `BlockStmt`
- JSX text literals are `Wtf8Atom`, instead of `Atom`
Under Cache Components, a dynamic route whose `generateStaticParams`
returns an empty array is intentionally an error, but it surfaced badly:
in development the request failed with an uncaught error that left a
blank screen and a 500, and a production build printed the error without
a stack. The cause was that `throwEmptyGenerateStaticParamsError`
deliberately discarded the stack, because the throw happens in framework
code in `buildAppStaticPaths` after the user's function has already
returned, so there was no user frame to point at.
This change adds an SWC transform that gives the error a stack anchored
at the user's code. For a `page`, `layout`, or `default` file that
exports `generateStaticParams`, it emits a
`__next_create_empty_gsp_error` factory whose `new Error` is span-mapped
back to the source. It keys off the export rather than any declaration
named `generateStaticParams`, so it covers `export function`/`export
const`, `export { x as generateStaticParams }`, and `export {
generateStaticParams } from '...'`, while ignoring a same-named local
helper that is never exported. The anchor is the most specific
available: the `return []` literal when it is unambiguously the
function's only return, the declaration, or the export statement when
the body lives in another module. The transform is registered for both
bundlers, gated on Cache Components and excluded from the edge runtime,
mirroring the existing `debug_instant_stack` wiring.
At runtime the factory is attached to the segment as
`createEmptyParamsError` in `collectSegments`, and `buildAppStaticPaths`
throws it when it detects an empty result. Because the error now
originates in the page module rather than framework code, development
surfaces a proper redbox with a meaningful stack and production fails
the build with a stacked CLI error. The custom
`EmptyGenerateStaticParamsError` name has been dropped since it only
added noise to the logs. A few export forms, notably a wildcard `export
*` re-export, can't be detected from the page module alone, so when the
factory is absent the helper falls back to throwing the message with its
natural stack; those framework frames are ignore-listed in the output
but stay available for debugging.
The build-mode error does not yet include a code frame, because
`generateStaticParams` runs in the page-data worker, which lacks the
code-frame support that the prerender export worker has. That is left
for a follow-up. A new end-to-end test covers the redbox in development
for both a literal and a computed empty array, and asserts the per-route
build errors in production using `--debug-build-paths` so each route is
isolated. Transform fixtures cover the literal, computed,
preceding-statement, multiple-return, aliased-export, re-export, and
not-exported cases.
The transform output for 'use cache' uses `Object.defineProperty` and
`Array.prototype.slice`. In theory, it's possible for a module to shadow
those names and break it:
```ts
// i want to suffer!
const Array = {}
const Object = {}
async function cached() {
"use cache"
...
}
```
We should defensively make sure that our transform's output refers to
the actual Object/Array builtins, not whatever's in scope for that name.
We can do this by using an unresolved syntax context for the
identifiers. This is what we do for `require()` elsewhere:
https://github.com/vercel/next.js/blob/6805af48baf1128e6b2a9e8f1752ee5982f02c60/crates/next-custom-transforms/src/transforms/track_dynamic_imports.rs#L119-L122
Using `quote_ident!(unresolved_ctxt, "Array")` will cause SWC to rename
any local redefinitions of `Array` to something that doesn't clash
(which can be seen in the added snapshots).
In #86014 we added a validation for exporting object and array literals
from `'use cache'` files. This was a bit too restrictive, and
short-sighted, as it would prevent exporting common things like metadata
or viewport objects from page and layout files, if the file uses a `'use
cache'` top-level directive.
Previously, in a `'use cache'` module, we only compiled statically-known
exported functions and arrow expressions to cache functions. Now, we
also compile re-exports and exports of values that might be functions.
This includes exports like:
- `export { getData } from './data'` - re-exporting from another module
- `export const aliased = getData` - exporting an imported identifier
- `const Page = withSlug(...); export default Page` - exporting the
result of a function call
These cases can't be statically verified as functions at compile time,
so we generate runtime wrappers with `typeof === "function"` checks
(simplified):
```js
let $$RSC_SERVER_CACHE_getData = getData;
if (typeof getData === "function") {
$$RSC_SERVER_CACHE_getData = React.cache(function() {
return cache("default", "...", 0, getData, arguments);
});
registerServerReference($$RSC_SERVER_CACHE_getData, "...", null);
Object.defineProperty($$RSC_SERVER_CACHE_getData, "name", { value: "getData" });
}
export { $$RSC_SERVER_CACHE_getData as getData };
```
This ensures that all function exports from `'use cache'` modules are
properly cached, while non-function exports pass through unchanged.
This approach comes with some limitations compared to statically-known
function exports:
1. **No compile-time type checking:** We can't verify that these exports
are actually functions. Non-function exports will be passed through
as-is at runtime.
2. **No async enforcement:** We can't enforce that functions must be
async (as we do for statically-known functions). Synchronous functions
are supported, but will lie about their type signature. They become
async after being wrapped as cache functions, even though their original
signature was synchronous.
closes NAR-196
With this PR we are restructuring how the Next.js compiler transforms server function modules (`'use server'` or `'use cache'` directive at the top of the file).
The workaround for skipping the registration of exported `'use cache'` functions is removed. All exported server functions are now properly registered during visitor traversal, which fixes incorrect server reference information bytes in server reference IDs in some circumstances (see #86292 for example).
We're also adding support for string literal exports, e.g.:
```js
'use server'
async function foo() {}
export { foo as '📙' }
```
Upgrade swc from 45 to 48
Based on #85540
- In the AST, strings now contain a `Wtf8Atom` (= not utf8), while `Ident`s keep using the utf8 `Atom`.
- swc's comment printing logic also changed slighted, moving some comments around
- ~~Some comments are getting lost along the way and/or attached to the wrong line. This also broke server actions which rely on a `__next_internal_client_entry_do_not_use__` at the very top~~
When a module does not include any `'use server'` or `'use cache'` closures that use variables from their outer scope, the imports for `encryptActionBoundArgs` and `decryptActionBoundArgs` are unnecessary.
> [!TIP]
> Review with hidden whitespace changes.
When functions exported from a module with a top-level `'use cache'`
directive are imported into client modules, the compiler should allow
`cacheLife` and `cacheTag` usage within those functions.
This PR updates the `react_server_components` transform to properly
track module directives (`'use client'`, `'use server'`, `'use cache'`)
and skip client graph validation for modules with `'use cache'`,
identical to how modules with `'use server'` are already handled. Those
modules are transformed anyway for the client graph by the
`server_actions` transform, which is applied after the
`react_server_components` transform, in which their implementation is
replaced by server function references.
closes NAR-498
This PR fixes a slight performance regression introduced in #85519 where
inline async function expressions were created for each unique set of
arguments passed to a `'use cache'` function.
For a function like this:
```js
export async function getCachedData(id) {
'use cache';
return fetch(`/api/data/${id}`);
}
```
we were previously generating (simplified):
```js
export var $$RSC_SERVER_CACHE_0 = cache("default", "0815", 0, async function(id) {
return fetch(`/api/data/${id}`);
});
```
In #85519, we restructured this to ensure proper stack frames in error
messages (simplified):
```js
export var $$RSC_SERVER_CACHE_0 = React.cache(function getCachedData() {
return cache("default", "0815", 0, async function(id) {
return fetch(`/api/data/${id}`);
}, arguments);
});
```
However, this introduced a small performance issue: the inner async
function expression was passed inline to the cache wrapper. While
`React.cache` memoizes the outer function, each unique set of arguments
would cause a new inner function object to be allocated.
Now we're generating (simplified):
```js
const $$RSC_SERVER_CACHE_0_INNER = async function getCachedData(id) {
return fetch(`/api/data/${id}`);
};
export var $$RSC_SERVER_CACHE_0 = React.cache(function getCachedData() {
return cache("default", "0815", 0, $$RSC_SERVER_CACHE_0_INNER, arguments);
});
```
The cache implementation is now hoisted to module scope with an `_INNER`
suffix as a const declaration with a named function expression. The
function name is preserved to ensure proper stack traces in dev. This
eliminates the repeated function allocations while preserving the
improvements from #85519.
When passing server actions or nested `'use cache'` functions inside of
cached components as props to client components, we need to make sure
that those are registered as server references, even when restoring the
parent component from the cache, e.g. during the resume of a partially
static shell. Otherwise, React would throw a runtime error while trying
to serialize the props.
<details>
<summary>Example</summary>
```tsx
import { connection } from 'next/server'
import { Suspense } from 'react'
export default function Page() {
return (
<div>
<Suspense fallback={<h1>Loading...</h1>}>
<Dynamic />
</Suspense>
<CachedForm />
</div>
)
}
const Dynamic = async () => {
await connection()
return <h1>Dynamic</h1>
}
async function CachedForm() {
'use cache'
return (
<form
action={async () => {
'use server'
console.log('Hello, World!')
}}
>
<button>Submit</button>
</form>
)
}
```
</details>
Previously, for inline server functions, the Next.js compiler placed the
`registerServerReference` calls where the server function was originally
declared. When the enclosing function was restored from a cache, this
call was skipped and the reference was not registered, leading to the
serialization error. To fix it, we can hoist the
`registerServerReference` call into the module scope, where the
reference itself also has been hoisted to. For simplicity, we're doing
this now generally, regardless of whether the server function is inline
or top-level.
Note: Since `registerServerReference` uses `Object.defineProperties` to
mutate the given reference, we don't need to assign the result to
anything. We already did this for exported functions of a module with a
top-level `'use server'` directive.
closes NAR-167
When a server function passes a closure to a method on a closed-over
value (e.g. an array), we must correctly track the value itself so that
it's included in the bound arguments.
For example, in this snippet, `list` must be bound to the server action:
```js
export function Component({ list }) {
return (
<form
action={async () => {
'use server'
console.log(list.find((x) => !!x))
}}
>
<button>submit</button>
</form>
)
}
```
This use case used to work but was accidentally broken by #73189.
fixes#79608fixes#77247
closes NAR-8
_Relanding #79151, now ignoring TS AST nodes when checking whether all
exported module items are async functions._
This fixes an error where we incorrectly showed the following compiler
error when using Turbopack:
```sh
Ecmascript file had an error
> 1 | 'use cache'
| ^^^^^^^^^^^
2 |
3 | import { useStuff } from './client-module'
4 |
The "use cache" directive must be at the top of the file.
```
The directive was regarded as not being at the top of the file, because
the React transform (`EcmascriptInputTransform::React`) injects a
variable declaration for React Refresh (e.g. `var _s =
__turbopack_context__.k.signature`) at the top of the file, before the
`"use cache"` directive.
We can fix this by ensuring that the server action transform (should
really be called "server _function_ transform") runs first.
As a consequence, the transform now needs to be able to parse JSX AST
nodes, which was already explicitly implemented.
closes NAR-130
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:
## For Contributors
### Improving Documentation
- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide
### Adding or Updating Examples
- The "examples guidelines" are followed from our contributing doc
https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md
- Make sure the linting passes by running `pnpm build && pnpm lint`. See
https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md
### Fixing a bug
- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
### Adding a feature
- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
## For Maintainers
- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change
### What?
### Why?
### How?
Closes NEXT-
Fixes #
-->
This PR introduces some extra tracking which makes us treat
`import(...)` like a call to a cached function. Thanks to this, `await
import(...)` will no longer cause dynamicity errors in `dynamicIO`.
The motivation is the same as allowing `fs.readFileSync` -- if something
is available on the server at prerender time, we don't consider it IO.
Fixes#72589Closes#75132
### Implementation notes
The tracking is implemented via an SWC transform
(`track_dynamic_imports.ts`) that turns `import(...)` into
`trackDynamicImport(import(...))`.
`trackDynamicImport(promise)` tracks the promise globally, without using
`workUnitStore.cacheSignal`. The prospective render subscribes to
pending modules using `trackPendingModules(cacheSignal)`, which causes
the prospective render to wait for all `import()`s to finish before
proceeding to the final render. The mechanism is analogous to `'use
cache'`, but the "result" of an `import()` is stored *in the module
cache* instead; when we invoke the `import()` again in the actual
prerender, it will resolve at microtask-speed, like we need it to.
The transform is enabled for all modules that run server-side, both RSC
and SSR, because the prerender runs both. This also includes route
handlers, because we also use a `cacheSignal` when prerendering those.
Notably, we also instrument `import()` in `node_modules` to account for
libraries that do lazy initialization.
I've also had to adjust the prospective client render in PPR mode to
wait for `cacheSignal.cacheReady()` - otherwise, it wouldn't wait for
`import()`s to resolve before doing the final client render, which'd
subsequently cause a dynamicity error.
(we didn't need to wait for `cacheSignal` before, because all cached
promises would've already been awaited in the server prerender)
Finally, we no longer need `warmFlightResponse`, because
`trackPendingModules` already makes the `cacheSignal` wait for all
loading chunks to finish, so we don't need to do it ahead of time.
The type checking introduced in #75935 wasn't actually failing the
build, because `find ... -exec ...` seems to eat non-zero exit codes.
This is fixed by using `xargs` instead.
Also fixes some files that were failing typechecking but no one noticed
due to the above bug.
Closes PACK-4074
We really need to look into getting e2e tests for correct sourcemaps: PACK-4142
Fix Turbopack server action tree shaking by explicitly splitting up that module
```js
/* __next_internal_action_entry_do_not_use__ {"00c18c215a6b7cdc64bf709f3a714ffdef1bf9651d":"default","70e10665baac148856374b2789aceb970f66fec33e":"myAction"} */ import { createServerReference, callServer, findSourceMapURL } from "private-next-rsc-action-client-wrapper";
export var foo = /*#__PURE__*/ createServerReference("70e10665baac148856374b2789aceb970f66fec33e", callServer, void 0, findSourceMapURL, "myAction");
export var bar = /*#__PURE__*/ createServerReference("00c18c215a6b7cdc64bf709f3a714ffdef1bf9651d", callServer, void 0, findSourceMapURL, "default");
```
->
```js
"use turbopack no side effects";
export { foo } from "data:text/javascript,%2F%2A%20__next_internal_action_entry_do_not_use__%20%7B%2200ab21efdafbe611287bc25c0462b1e0510d13e48b%22%3A%22foo%22%7D%20%2A%2F%22use%20turbopack%20no%20side%20effects%22%3Bimport%7BcreateServerReference%2CcallServer%2CfindSourceMapURL%7Dfrom%22private-next-rsc-action-client-wrapper%22%3Bexport%20var%20foo%3D%2F%2A%23__PURE__%2A%2FcreateServerReference%28%2200ab21efdafbe611287bc25c0462b1e0510d13e48b%22%2CcallServer%2Cvoid%200%2CfindSourceMapURL%2C%22foo%22%29%3B";
export { bar } from "data:text/javascript,%2F%2A%20__next_internal_action_entry_do_not_use__%20%7B%2200ac840dcaf5e8197cb02b7f3a43c119b7a770b272%22%3A%22bar%22%7D%20%2A%2F%22use%20turbopack%20no%20side%20effects%22%3Bimport%7BcreateServerReference%2CcallServer%2CfindSourceMapURL%7Dfrom%22private-next-rsc-action-client-wrapper%22%3Bexport%20var%20bar%3D%2F%2A%23__PURE__%2A%2FcreateServerReference%28%2200ac840dcaf5e8197cb02b7f3a43c119b7a770b272%22%2CcallServer%2Cvoid%200%2CfindSourceMapURL%2C%22bar%22%29%3B";
````
- [x] Keep [sourcemaps](https://github.com/vercel/next.js/blob/canary/test/development/app-dir/source-mapping/README.md#server-actions)
### What?
Remove magic comments for disabling export merging in the advanced tree shaking of turbopack.
### Why?
Turbopack magic comments for tree shaking + server actions are not required anymore since https://github.com/vercel/next.js/pull/76938.
### What?
Fix the span of the turbopack tree shaking magic comment.
### Why?
Previously it was printed on strange position.
### How?
Closes NAR-91
Closes PACK-4087
This PR changes the server action generated code a bit to work around a
typescript bug and remove some false positives we got while
typechecking: https://github.com/microsoft/TypeScript/issues/61165
The trick is that typescript seems to look for *exactly*
`Object.defineProperty(obj, 'literal', { value: ... })`, and changing
any part of the expression bypasses the bug, so we can just use
`Object['defineProperty']` instead.
This PR attempts to add typechecking to the server actions tests
fixtures in order to add an extra layer of validation against compiler
bugs. (there's a couple!). Unfortunately the tests were pretty
loosey-goosey around undefined variables, so there's a lot of noise in
adding missing imports for `Button` etc. i tried to call out the
interesting changes via PR comments.
Note that this is a bit limited, because we often have to rely on
typescript inferring some sensible types for the outputs -- see e.g. the
issue with `24/output.js` mentioned below. In case typescript can't
infer sensible types for something, we can just exclude that file from
checking. this seems to be pretty rare, so i think it's okay.
We should also definitely add typechecking to the other fixture folders,
but this PR is already big, so i want to do that piecemeal.
### TODO
- [x] invoke the typechecker in CI
- added a `check-compiler-fixtures` script that runs as part of
`types-and-precompiled` (open to feedback, maybe this isn't the right
place?)
- [x] figure out how to make `tsc` error on
`crates/next-custom-transforms/tests/fixture/server-actions/server-graph/51/output.js`,
where we're referencing a non-existent variable `$$RSC_SERVER_ACTION_0`.
right now, it emits no errors
- this is a compiler bug, and i want to catch bugs like this in the
future
- solution: https://github.com/vercel/next.js/pull/75944
- [x] figure out why `tsc` is complaining about a type mismatch in
`crates/next-custom-transforms/tests/fixture/server-actions/server-graph/24/output.js`
- we can work around this by loosening the types for
`registerServerReference`, but i'd rather we didn't have to do that
- this seems to be a bizarre TS behavior where `fn.bind(...)` makes all
the params optional in JS files. ignoring this for now
The issues with the current export statement validation for app router
pages are documented in the added fixtures, see also the inline comments
in the diff.
In the SWC transform we're now using the span of the original `"use cache"` function for the generated cache wrapper call, e.g. so that the captured timeout error stack can be source mapped correctly.
This also fixes the wrongly placed `/*#__TURBOPACK_DISABLE_EXPORT_MERGING__*/` marker for the generated
cache wrapper statements.
**Before:**
<img width="1496" alt="404215955-c354d1f5-4aeb-4f35-9385-a9531219dc4c" src="https://github.com/user-attachments/assets/34d1863a-d557-419f-8477-577bd759341b" />
**After:**
<img width="1496" alt="404215977-a0157fad-a1f8-409a-b9b9-9709e07f7c05" src="https://github.com/user-attachments/assets/f00b7776-6674-4714-b425-d0de994abbff" />
Closes NDX-675
In #73059 we added build-time checks for the forbidden usage of `this`
and `arguments` in server functions. A nested function expression is
however allowed to use these expressions.
fixes#74181
To add a bit more context: `this` is forbidden in server actions because
the Next.js compiler hoists inline server actions out of their original
location into the module scope, so that they can be imported and invoked
by the action handler. Due to this hoisting, the `this` context gets
lost and is not available to the server action.
To prevent surprising runtime errors for such cases, we emit a build
error to provide feedback to developers as early as possible.
However, nested function declarations or function expressions create a
new `this` context, and in those it's allowed to access `this` and
`arguments`.
For consistency, and to prevent surprises when refactoring server
actions, we apply the same rules for module-level server actions.
- Before, there was a separate AST traversal that collected `next-dynamic` imports
- Now, we use transitions (which are inserted by the existing next-custom-transform) to tell Turbopack about the next-dynamic imports
- Unlike Webpack (where the react-loadable identifiers are `path/to/parent-module -> ./imported-client`), Turbopack just uses the module id: `[project]/path/to/imported-client.js [app-client] (ecmascript, next/dynamic entry)`
- the `next-dynamic` transition only inserts the marker module (i.e. for the SSR `import()`)
- the `next-dynamic-client` transition inserts the marker module and changes the module context (used to get the client module name)
- Now, we use the correct `AvailabilityInfo` for the next/dynamic chunks to preload, and they are not explicitly emitted. If they are for some reason not the same chunks that are actually emitted for the dynamic import, you get a hard error. (Previously, they didn't use the same `AvailabilityInfo` as the real async chunks, so they usually bundled another copy of React, and also had a different chunk name hash)
- For Turbopack, react-loadable manifests are now scoped per page instead of globally
When a server function closes over another function (without a server
directive), React will fail to serialize the function. For server-side
usages (e.g. a `"use cache"` function that's called during rendering),
we did show the error in the dev overlay, but without a useful stack and
without an error source. For client-side usages (i.e. a server function
passed to a client component) we didn't show an error at all initially,
and only after triggering the server function the error was shown.
With this PR, we're now showing a proper error in the dev overlay for
both cases:
<img width="537" alt="Screenshot 2024-12-03 at 11 56 30"
src="https://github.com/user-attachments/assets/0af4b120-07c2-4266-8d5c-0b29a89abc6d">
For the client-side usage, we need to lean on the console replaying
feature to make this work.
In addition, the compiler now also tracks closed-over function
declarations, and not only arrow expressions.
For functions or arrow expressions that are not passed in as
arguments/props, but are declared in the parent scope of the server
function, we could even emit a build error as a follow-up PR.
With this PR, we are allowing _static_ class methods to be annotated with `"use cache"` or `"use server"`.
Class _instance_ methods on the other hand are not allowed as server functions.
This is a follow-up fix for #72969. Method or function properties of exported objects in `"use cache"` or `"use server"` files must not be compiled into server functions, unless they have an inline server directive.
### What?
Enable single-incoming-edge optimization for module fragments with `ModulePart::Export`.
### Why?
It will reduce the number of modules greatly.
### How?
Closes PACK-3481
With this PR, we are now allowing `"use server"` and `"use cache"` to be
used in method properties, e.g.:
```js
export const obj = {
async foo() {
'use cache'
return Math.random()
},
async bar() {
'use server'
console.log(42)
},
}
```
Or, more realistically, something like this:
```js
export const api = {
product: {
async fetch() {
'use cache'
return fetch('https://example.com').then((res) => res.json())
},
},
}
```
Supporting this pattern is mostly done for convenience; it allows users
to group server functions into a common object, using method properties.
Notably, we will not support the usage of `this` and `super`, for which
we will add build errors in a follow-up PR.
By adding `visit_mut_function` we can deduplicate the code that's common between `visit_mut_fn_expr` and `visit_mut_fn_decl`. And more importantly, this prepares us for handling the transform of method props, which will also use `visit_mut_function`.
As a positive side effect this brings two small improvements with it:
- always highlight the whole function when `async` is missing, not only the identifier
- assign a function name to an anonymous arrow function expression in a variable declaration
Considering the bound args param in the server reference information byte is required for a client-side optimization of omitting unused arguments to work properly when a server action or `"use cache"` function uses closed-over variables.
The server reference information byte is not used yet, so this PR should not change any observable behavior.
This is a pure refactoring without any behavioral changes. It will allow us to more easily incorporate the presence of the bound args param into the server reference information byte.