This implements some simple static analysis to determine if modules are side effect free based on their top level module evaluation items.
The rules are simple
* declarations ✅
* literals ✅
* property access ✅ (potentially controversal but consistent with other bundlers)
* function calls ❌ (unless part of an builtin allowlist, e.g. `Object.keys`)
imports are obviously the difficult part, for that reason we can evaluate a module to `SideEffectful` `SideEffectFree` or `ModuleEvaluationIsSideEffectFree`. A later step will leverage a graph analysis step to determine of transitive dependencies of ModuleEvaluationIsSideEffectFree modules are also side effect free and upgrade it.
So for right now the impact is minor. There is also an experimental option to disable it in case it causes trouble this can help us debug
I tested the performance on vercel site and as expected it isn't really observable
with side effect analysis disabled
```
~/projects/front/apps/vercel-site (main *)$ hyperfine -p 'rm -rf .next' -w 2 -r 20 'pnpm next build --turbopack --experimental-build-mode=compile'
Benchmark 1: pnpm next build --turbopack --experimental-build-mode=compile
Time (mean ± σ): 48.017 s ± 1.696 s [User: 319.651 s, System: 44.273 s]
Range (min … max): 44.855 s … 50.600 s 20 runs
```
With it enabled.
```
~/projects/front/apps/vercel-site (main *)$ hyperfine -p 'rm -rf .next' -w 2 -r 20 'pnpm next build --turbopack --experimental-build-mode=compile'
Benchmark 1: pnpm next build --turbopack --experimental-build-mode=compile
Time (mean ± σ): 48.038 s ± 2.337 s [User: 320.261 s, System: 45.434 s]
Range (min … max): 44.156 s … 52.774 s 20 runs
```
so if there is a regression it is well within the noise.
https://github.com/vercel/next.js/pull/86338 switches cell storage to use bincode, but it uses a compatibility shim for storing `TaskInput`s using `serde` + `pot`. This PR completes the migration by switching `TaskInput` to use `bincode` as well.
## Benchmark Summary
`next build` is consistently faster across a variety of repositories when persistent caching is enabled, and the resulting cache size is consistently smaller.

`cargo build` runs only slightly faster, but there's potential for improving this further by removing unused `serde` macro derives.
## Benchmarking Notes
Performance measurements (aside from cargo build) are taken using `next build`. This should have proportional benefits for `next dev` too, but `next build` is more reproducible and easier to measure. `canary` refers to `a8f7b5467485647def4c0f4479a0a83bc4a3673a`.
These numbers are taken with persistent caching enabled with an empty cache, so it's the worst-case scenario for the serialization codepath. We don't care nearly as much about the performance of deserialization because it's less common.
## `cargo build -p next-swc-napi`
This makes builds _slightly_ faster. This branch doesn't clean up the serde derives yet, so there's a bunch of unused macro expansion here, so I think this number is actually pretty good.
```
~/next.js $ hyperfine -w 1 -r 5 -p 'git co a8f7b54674 && cargo clean' -p 'git co bgw/bincode-task-inputs && cargo clean' 'cargo build -p next-swc-napi' 'cargo build -p next-swc-napi'
Benchmark 1: cargo build -p next-swc-napi
Time (mean ± σ): 237.476 s ± 1.420 s [User: 2001.820 s, System: 84.957 s]
Range (min … max): 235.294 s … 238.862 s 5 runs
Benchmark 2: cargo build -p next-swc-napi
Time (mean ± σ): 232.366 s ± 1.549 s [User: 1945.355 s, System: 83.940 s]
Range (min … max): 230.550 s … 234.485 s 5 runs
Summary
cargo build -p next-swc-napi ran
1.02 ± 0.01 times faster than cargo build -p next-swc-napi
```
## next-site
Using `~/front/apps/next-site` on macos with a M2 Pro. Nothing else was running on the machine.
```
hyperfine -p 'rm -rf .next' -w 2 -r 20 'TURBOPACK_PERSISTENT_CACHE=1 TURBO_ENGINE_IGNORE_DIRTY=1 pnpm next build --turbopack --experimental-build-mode=compile'
du -sh .next/cache/turbopack/
du -s .next/cache/turbopack/
```
#### Canary
```
Benchmark 1: TURBOPACK_PERSISTENT_CACHE=1 TURBO_ENGINE_IGNORE_DIRTY=1 pnpm next build --turbopack --experimental-build-mode=compile
Time (mean ± σ): 14.999 s ± 0.258 s [User: 84.137 s, System: 8.683 s]
Range (min … max): 14.400 s … 15.509 s 20 runs
```
```
855M .next/cache/turbopack/
1751640 .next/cache/turbopack/ (Note: macos measures size in 512B blocks)
```
#### This Branch
```
Benchmark 1: TURBOPACK_PERSISTENT_CACHE=1 TURBO_ENGINE_IGNORE_DIRTY=1 pnpm next build --turbopack --experimental-build-mode=compile
Time (mean ± σ): 14.106 s ± 0.255 s [User: 79.976 s, System: 8.421 s]
Range (min … max): 13.829 s … 14.939 s 20 runs
```
```
767M .next/cache/turbopack/
1571072 .next/cache/turbopack/ (Note: macos measures size in 512B blocks)
```
## vercel-site
Using `~/front/apps/vercel-site` on macos with a M2 Pro. Nothing else was running on the machine.
```
hyperfine -p 'rm -rf .next' -w 2 -r 20 'TURBOPACK_PERSISTENT_CACHE=1 TURBO_ENGINE_IGNORE_DIRTY=1 pnpm next build --turbopack --experimental-build-mode=compile'
du -sh .next/cache/turbopack/
du -s .next/cache/turbopack/
```
#### Canary
```
Benchmark 1: TURBOPACK_PERSISTENT_CACHE=1 TURBO_ENGINE_IGNORE_DIRTY=1 pnpm next build --turbopack --experimental-build-mode=compile
Time (mean ± σ): 93.274 s ± 1.630 s [User: 671.120 s, System: 57.888 s]
Range (min … max): 91.111 s … 96.881 s 10 runs
```
```
4.3G .next/cache/turbopack/
9002568 .next/cache/turbopack/ (Note: macos measures size in 512B blocks)
```
#### This Branch
```
Benchmark 1: TURBOPACK_PERSISTENT_CACHE=1 TURBO_ENGINE_IGNORE_DIRTY=1 pnpm next build --turbopack --experimental-build-mode=compile
Time (mean ± σ): 88.694 s ± 0.963 s [User: 633.801 s, System: 54.576 s]
Range (min … max): 87.357 s … 90.037 s 10 runs
```
```
3.7G .next/cache/turbopack/
7788472 .next/cache/turbopack/ (Note: macos measures size in 512B blocks)
```
## shadcn/ui
Using a low-noise Linux machine: https://github.com/bgw/benchmark-scripts/
```
diff --git a/apps/v4/next.config.mjs b/apps/v4/next.config.mjs
index 7fa0f012..d1137d01 100644
--- a/apps/v4/next.config.mjs
+++ b/apps/v4/next.config.mjs
@@ -27,6 +27,7 @@ const nextConfig = {
},
experimental: {
turbopackFileSystemCacheForDev: true,
+ turbopackFileSystemCacheForBuild: true,
},
redirects() {
return [
```
```
cd ~/shadcn-ui/apps/v4
hyperfine -p 'rm -rf .next' -w 2 'pnpm next build --turbopack --experimental-build-mode=compile'
du -sh .next/cache/turbopack/
du -s .next/cache/turbopack/
```
#### Canary
```
Benchmark 1: pnpm next build --turbopack --experimental-build-mode=compile
Time (mean ± σ): 62.772 s ± 0.939 s [User: 163.153 s, System: 11.170 s]
Range (min … max): 60.739 s … 64.220 s 10 runs
```
```
596M .next/cache/turbopack/
609636 .next/cache/turbopack/ (Note: Linux measures size in 1024B blocks)
```
#### This Branch
```
Benchmark 1: pnpm next build --turbopack --experimental-build-mode=compile
Time (mean ± σ): 59.017 s ± 1.014 s [User: 151.359 s, System: 9.978 s]
Range (min … max): 57.124 s … 60.343 s 10 runs
```
```
491M .next/cache/turbopack/
502240 .next/cache/turbopack/ (Note: Linux measures size in 1024B blocks)
```
A separate manifest (`required-server-files.js`, like `_buildManifest.js`) so that we the bundler doesn't have to inline the whole config.
The Node.js runtime routes already get their nextConfig at runtime by reading `required-server-files.js`.
Closes https://github.com/vercel/next.js/pull/86630
Context for `initializeCacheHandlers`: https://github.com/vercel/next.js/pull/76130
This PR switches `turbo-tasks-backend` to use `bincode` instead of serde for cell contents. To keep PR size manageable, `TaskInput`s are still serialized using serde and a compatibility shim. The next PR fixes that.
See https://github.com/vercel/next.js/pull/86631 for benchmark numbers.
It looks like bincode should reduce the filesystem cache size by ~10% versus pot, because its not a self-describing format. This attempts to add the `bincode::Encode`/`bincode::Decode` traits everywhere, so that we can use those instead of serde.
## Why not use bincode's serde compatibility feature?
- Unfortunately, serde has some bugs/incompatibilities/footguns with non-self-describing formats. `bincode`'s traits avoid this by not exposing features that would be incompatible with a self-describing format. https://docs.rs/bincode/latest/bincode/serde/index.html#known-issues
- `bincode::Encode`/`Decode` are much simpler traits than serde's equivalents, so we'll probably get some rustc build performance and turbopack binary size benefits from using the bincode versions. In cases where we do have to implement these traits by hand, it's much easier than implementing the serde equivalents.
- I'd like to kill `erased-serde` anyways because we're probably paying some performance cost from the large amounts of dynamic dispatch it performs, and we're not getting any benefits from it if we only ever serialize to a single format.
## Forks of Upstream Crates
- I've forked `bincode` here: https://github.com/bgw/bincode/commits/bgw/patches/
- I submitted a patch via email to the maintainer via sourcehut
- Upstream: https://git.sr.ht/~stygianentity/bincode
- Fork: https://git.sr.ht/~bgw/bincode
- This adds a few extra derive features and fixes type bounds on `PhantomData`.
- The type bounds on `PhantomData` aren't very important, and we could work around it in our code.
- We could just fork the derive crate if upstream doesn't want to accept the patches.
- And `virtue` here: https://github.com/bgw/virtue/commit/e386f35cf7f22d04a4db32231904cbe5dc52c01d
- I've submitted a PR to `virtue` here: https://github.com/bincode-org/virtue/pull/94
- This fixes a bug in the parsing of const enums and generics with defaults.
- We don't hit this very often as most of our types are not generics, so we could work around it in our code if upstream doesn't want to accept the patch.
## Other Notes
- Depends on a patched version of the `bincode` and `virtue` crates to fix a few bugs and add a few new features. I'll work on upstreaming these changes.
- This implements `BorrowDecode` everywhere because the `bincode::Decode` derive macro requires it. I plan to add an attribute in my bincode fork to allow disabling this, and then we can get rid of all of the `BorrowDecode` impls. We don't want/need `BorrowDecode` for turbo task values because everything needs to be owned anyways.
I don't think these specific re-exports are too problematic, but we've moved away from the pattern of a few top-level crates re-exporting all of the rest of turbopack because in some cases it can make compilation performance much worse in the way that it structures the crate dependency graph.
Re-exports of "internal" crates can be useful in controlling what parts of the API is and isn't "public", but these re-exports aren't really doing that.
I'm debating whether this should be `Option<BindingUsage>` instead, so that we can detect if you built the graph without the binding_usage but then still try to enable `remove_unused_exports` (which would now silently noop and never tree shaking anything).
### What?
While idle only snapshot once. Start snapshotting again when idle has been left.
Place persisting spans on top level.
Fix some missing spans.
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.
This `.into()` hid the cell creation, we have already removed `.into()` as a shorthand for `Vc::cell`, but this was a manual version of that.
It could certainly be more ergonomic though:
```rust
AssetContent::file(FileContent::Content(File::from(some_str)).cell()),
```
- `OptionStringifiedSourceMap` was always a strange type
- We can now use the `FileContent` to not clone the Rope in the `SourceMapAsset::content`, which would end up duplicating sourcemaps in the cache.
The difference is barely measureable though...
```
less tasks:
Time (mean ± σ): 5.429 s ± 0.099 s [User: 28.337 s, System: 3.512 s]
canary:
Time (mean ± σ): 5.547 s ± 0.082 s [User: 28.730 s, System: 3.480 s]
```
(#86375)
Closes PACK-5936
This alleviates a long-standing painpoint of `serverExternalPackages`. If you are using
`some-utility` which uses `lodash`, and you have to make `lodash` a `serverExternalPackages`, then
you'd have to add the correct version of `lodash` to your application's dependencies in package.json.
This is not very ergonomic as it leaks the internal implementation of your direct dependencies (you
shoudln't have to care about their transitive dependencies). And it's easy to run into a situation
where it's impossible to satisfy all dependencies, if you'd have to add two conflicting versions of `lodash` for example.
Instead, Turbopack now emits symlinks like
```
.next/node_modules/lodash-98e63531e9336dd0@ -> ../../../../node_modules/.pnpm/lodash@4.17.20/node_modules/lodash
.next/node_modules/lodash-ee4fa714b6d81ca3@ -> ../../../../node_modules/.pnpm/lodash@3.10.1/node_modules/lodash
```
.so that `.next/server/chunks/29adj2sd9.js` can then contain a `require("lodash-98e63531e9336dd0")`
and alway get the correct version (and without the user having to declare them).
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
Alias "unused import removal"
Closes PACK-5782
Performance:
```
* 58fe2e2e377d877f51f91f50770310d2bbf20987 - WIP: enable by default
pnpm next build --experimental-build-mode=compile > ~/Downloads/log.txt 2>&1 380.01s user 53.74s system 691% cpu 1:02.76 total
pnpm next build --experimental-build-mode=compile > ~/Downloads/log.txt 2>&1 384.99s user 51.00s system 708% cpu 1:01.57 total
pnpm next build --experimental-build-mode=compile > ~/Downloads/log.txt 2>&1 381.52s user 46.86s system 711% cpu 1:00.21 total
vs canary:
* f6c2d49132 - Turbopack: add task_id_details feature (#84970)
pnpm next build --experimental-build-mode=compile > ~/Downloads/log.txt 2>&1 374.87s user 48.47s system 760% cpu 55.662 total
pnpm next build --experimental-build-mode=compile > ~/Downloads/log.txt 2>&1 379.18s user 50.27s system 700% cpu 1:01.28 total
pnpm next build --experimental-build-mode=compile > ~/Downloads/log.txt 2>&1 370.39s user 45.93s system 726% cpu 57.305 total
pnpm next build --experimental-build-mode=compile > ~/Downloads/log.txt 2>&1 372.70s user 46.24s system 717% cpu 58.364 total
```
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 '📙' }
```
### What?
* add support for multiple options for chunk suffix
* Use FromScriptSrc chunk suffix to avoid depending on deployment id
* fix passing chunk suffix to WebWorkers initial chunks
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~~
### What?
fix the import chain showing only modules relevant to the current route
fix module graph being merged by path. It's keyed by identifier now.
also add more info to import chain, e. g. layer info. and more details in tooltip.
Turbopack: remove Asset supertrait from Module trait. Modules don't have content
remove unneeded Asset implementation
<!-- 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
### 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 #
-->
fixup
Turbopack: disable input source maps for analyze
<!-- 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
### 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 #
-->
<!-- 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
### 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 the `ServerReferenceExport` struct to replace `(Ident, Atom, Atom)` tuples for tracking exported server references. It also replaces the `Vec<(Atom, Atom)>` `export_actions` with an `FxIndexMap` for `reference_ids_by_export_name`, enabling more efficient lookups in upstack PRs. There are no functional changes.
For small routes, it can appear like the polyfill chunk takes up a sizable amount of data, but `polyfill-nomodule.js` is only sent to legacy browsers. This makes it clearer.
This PR enhances the bundle analyzer by adding visual indicators for polyfill chunks, and noting they're conditionally sent to modern or legacy browsers. The changes include:
- Adds a source map to polyfill chunks (for the large pre-bundled input only)
- Visually distinguished polyfill chunks in the treemap by lightening their color
- Added a "Polyfill" badge (other badges coming soon!) with text when viewing polyfill chunk details
<img width="388" height="227" alt="image" src="https://github.com/user-attachments/assets/bcebfba6-1e10-459a-9364-b6b8197cd3df" />
Fixes PACK-5826
### What?
Allow to configure nested vs flat async chunking with a new config option.
```
experimental: {
turbopackClientSideNestedAsyncChunking: false,
turbopackServerSideNestedAsyncChunking: true,
}
```
This adds an interactive Treemap-style bundle analyzer app to the Next.js codebase and writes a copy to disk when `next build --experimental-analyze` is run.
* It's a separate app in `apps/bundle-analyzer` as `@next/bundle-analyzer-ui` that uses `output: 'export'` to export a purely static Next.js app
* These static resources are copied into Next.js's published `dist` directory in the `next` package itself. Users never depend on `@next/bundle-analyzer-ui`
* When users run `next build --experimental-analyze`, these static resources are copied into the user's `.next/diagnostics/analyze` along with the data payloads needed to visualize the user's app
In an upcoming PR, we'll add `next experimental-analyze` that will create analysis files without build artifacts, as well as optionally start a static file server.
Test Plan: Ran `next build --experimental-analyze` in a test app and ran a static file server to run it.
### What?
Adds a new mode for processing async imports (e. g. `import("...")`).
* Nested async chunking (the existing mode): Each async import creates a new availability scope so already available modules are not included again. This leads to exploring all paths in the application to create the most efficient chunk groups. The time complexity grows with the number of paths.
* Flat async chunking: Only the first async import creates a new availability scope. This makes all async imports have the same available modules (per page), no matter how deep their are nested in the application. This allows to reuse chunk groups. The time complexity is linear to the number of async imports.
The nested async chunking has a benefit when download size matters. Flat async chunking has better compile time and often less total bundle size. Therefore nested async chunking is only enabled in production for client-side assets.
### What?
Refactor the OutputAssets of chunk groups to not eagerly return all referenced output assets from async loaders, but instead return a list of references that can be followed to get the full list.
That solves a problem that will happen when the async loaders create cycles in the output graph.
### What?
Introduces a null-rendering default component for interception routes to
prevent 404 responses when children slots are missing.
### Why?
When rendering interception routes like `(.)[id]`, there's a segment
mismatch where the interception route lacks a children slot that the
base `[id]` route has.
**The Bug:**
Previously, the framework injected a 404 page into the missing default
slot for the children route. On platforms like Vercel, this caused full
404 RSC responses because the HTTP status code has semantic meaning for
route behavior when using cache components. This resulted in the
"unknown segment value fallback page" issue.
**Impact:**
- RSC requests for interception routes would return 404 status codes
- Platform routing behavior was incorrectly triggered by these 404
responses
- Only affects pages rendering with cache components
### How?
- Created new `default-null.tsx` builtin component that renders `null`
- Updated Rust code (`app_structure.rs`) to detect interception routes
and use the null default
- Updated webpack loader (`next-app-loader/index.ts`) to check
`isInterceptionRouteAppPath()` and inject the null default
- Removed test-specific default component that was working around this
bug
- The old `page.tsx` is still used during client navigation, so there's
no visible change to users
This ensures minimal RSC payload changes and prevents 404 status codes
while maintaining existing client-side behavior.
NAR-483
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.
We had two implementations of various traversals: first for the SingleModuleGraph, and then also the more general version that can cross module graphs. Let's just use the second for everything (with just a single graph in the case of `SingleModuleGraph`)
This turned out to be a much bigger change than I had anticipated, but this was long overdue anyway.
For some operations, we need the module graph (e.g. determining unused exports, and for the future inner graph tree shaking).
Previously, it was constructed inside of the evaluation machinery.