mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
codex/ledgers-react-sync
1409 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
944e45484e |
Add nextConfig.instrumentationClientInject (#93785)
This allows injecting client bootstrap code using only next.config.js (without writing to any user source files). |
||
|
|
75d2ed53f7 |
turbopack: emit assets before duplicate check in emit_assets (#93875)
### What?
Reworks `emit_assets` in `crates/next-core/src/emit.rs` so that:
- The actual `emit()` / `emit_rebase()` write runs **before**
`check_duplicates()`.
- Per-duplicate diff checks inside `check_duplicates` run in parallel.
- At most one `EmitConflictIssue` is emitted per asset path, even when
multiple duplicates conflict.
- The conflict-issue emission is wrapped in a `turbo_tasks::function`
and reported via `as_side_effect()`.
### Why?
Under eventual consistency, all children might be removed from
`emit_assets`. This causes all `content()` and `emit()` tasks being
inactive and keeping the stale value (which is potentially an error).
On recomputation of `emit_assets()` this only makes very few `content()`
tasks active, as tasks are still in a stale error state and make
`emit_assets()` exit early. So it takes N recomputations of
`emit_assets()` to be up-to-date, where N is the number of duplicate
assets. This can slow down builds drastically and did cause build time
up to 30min.
Emitting the asset first ensures the file is written to disk even if the
subsequent duplicate diagnostic crashes under eventual consistency.
Doing the diff checks in parallel and folding conflicts into a single
issue keeps the diagnostic path cheap and avoids noisy duplicate output
for the same path. Tracking the issue emission as a turbo-tasks side
effect means it is properly attributed to a task instead of being
emitted ad-hoc from `emit_assets`.
### How?
- Swap the order in both branches of `emit_assets` (server-side `emit`
and client-side `emit_rebase`) so the write happens first, then
`check_duplicates` runs as a diagnostic afterwards. A comment is left
explaining the ordering requirement.
- `check_duplicates` now returns `Result<()>` instead of returning the
picked asset. The caller picks `assets.first()` directly, since
`check_duplicates` only validated equivalence — it didn't choose a
different asset.
- The inner loop in `check_duplicates` uses `iter.map(async |next| { ...
}).try_flat_join().await?` to run the diff comparisons in parallel and
collect conflict details. `ext` is computed once before the loop and
cloned into each task.
- Only the first conflict from the collected list is reported, instead
of one issue per conflicting asset.
- The `EmitConflictIssue` emission is moved into a local
`#[turbo_tasks::function] fn emit_conflict_issue(...)` invoked via
`.as_side_effect().await?`, so the issue is tracked as a task side
effect.
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
e2fca6c66a |
[Turbopack] Add graph-based CSS chunking algorithm behind experimental.cssChunking: "graph" (#93606)
### What?
Adds an alternative CSS chunking algorithm to Turbopack, opted into via:
```js
// next.config.js
module.exports = {
experimental: {
cssChunking: 'graph',
// or, with explicit cost overrides:
// cssChunking: { type: 'graph', requestCost: 20_000, moduleFactorCost: 1 },
},
}
```
The new algorithm is **off by default** — Turbopack still uses the
existing
"loose"/dependencies algorithm unless this flag is set, so this PR is a
pure
addition for users that don't opt in.
While we were here, the `experimental.cssChunking` shape was also
generalized
so every existing string accepts an object form too:
| Value | Bundler | Notes |
|---|---|---|
| `true` / `'loose'` / `{ type: 'loose' }` | both | default
heuristic-based chunking |
| `'strict'` / `{ type: 'strict' }` | webpack | unchanged |
| `false` | webpack | unchanged (one chunk per CSS module) |
| `'graph'` / `{ type: 'graph', requestCost?, moduleFactorCost? }` |
Turbopack | new |
Cross-bundler combinations are rejected at config-validation time:
- `'graph'` with webpack throws.
- `'strict'` and `false` with Turbopack throw.
### Why?
The existing Turbopack CSS chunker (loose / dependencies) is good at
preserving CSS ordering but doesn't share chunks across pages well —
every
page tends to load its own chunk per CSS module, which scales poorly for
apps
with many pages and shared component libraries.
The new "graph" algorithm models the per-chunk-group CSS ordering as a
weighted DAG over modules, then greedily merges adjacent runs in the
global
topological order whenever the merge reduces total cost. The cost model
charges every CSS request and overshipped byte, with two tunable knobs
(`requestCost` and `moduleFactorCost`).
**Trade-off vs. the loose default.** With the default cost parameters
(`requestCost: 20_000`, `moduleFactorCost: 1`) the graph algorithm
typically
ships **less CSS per chunk group at the cost of more requests** than the
loose algorithm. The cost model is tuned to avoid overshipping unrelated
CSS
into pages that don't need it; on apps where the loose algorithm was
already
collapsing a lot into one big chunk that some pages didn't actually use,
the
graph algorithm will split it. Apps that prefer fewer requests can raise
`requestCost`; apps that prefer less overshipping can raise
`moduleFactorCost`.
This is opt-in and Turbopack-only because:
- The cost model is sensitive to per-app properties (number of pages,
size
distribution of CSS modules, …) — keeping it experimental gives us room
to
tune defaults from real usage.
- Webpack already has its own `CssChunkingPlugin` and `'strict'` mode
that
cover the equivalent design space; we don't want to fork that.
### Performance
Measured on `vercel.com` (the full graph algorithm spans
`create_graph → make_acyclic → linearize → split_into_chunks →
assemble`):
- **~3s** end-to-end for the synchronous chunking pipeline on a
realistic
production input.
Implementation choices that matter for that throughput:
- Tarjan SCC uses `Vec<u32>` / `Vec<bool>` scratch arrays indexed by
`NodeIndex` — no hashing on `indices` / `lowlinks` / `on_stack`.
- `make_acyclic` batches multiple cuts per SCC pass by seeding
successive
short-cycle searches at the previous cut's target, only re-running
Tarjan
when no further cycle is reachable from the seed.
- `find_short_cycle` is a bidirectional Dijkstra over a `BinaryHeap`
with
predecessor pointers (no path cloning) and skips its refinement loop for
trivial 2-cycles.
- `split_into_chunks` picks the next merge from a `BinaryHeap` keyed on
the
cost delta instead of an O(N) linear scan per merge.
- `chunk_cost` reads a once-built `module_to_groups` inverse index
instead of
scanning every chunk group on every call; the GlobalStyle leakage check
uses binary search on the inverse index rather than scanning each
group's
module list.
### How?
#### Module layout (`turbopack/crates/turbopack-core/src/module_graph/`)
The two algorithms are deliberately split so neither imports from the
other:
- `style_groups/` — algorithm-neutral output types (`StyleGroups`,
`StyleItemInfo`, `make_style_groups`). Both algorithms produce these.
- `style_groups_loose/` — the existing ("loose") algorithm plus the
shared
config types (`StyleGroupsAlgorithm`, `StyleGroupsConfig`,
`F32TaskInput`).
- `style_groups_graph/` — the new algorithm. Pure Rust, no `Vc`, with
`petgraph::DiGraph` plus a thin `SubgraphView` wrapper and a small
`ReadonlyGraph` trait that lets the same pipeline run against either a
`&DiGraph` or a filtered view of one SCC.
#### Algorithm
```text
create_graph → make_acyclic → linearize → split_into_chunks → assemble batches
```
1. **`create_graph`** — for each chunk group, every `(later, earlier)`
pair
inside the group's CSS-module list becomes an edge `later → earlier`
(weight 1, accumulated). Heavy edges = strong co-occurrence.
2. **`make_acyclic`** — co-occurrence almost always introduces cycles;
each
multi-node SCC has its lowest-weight cycle edge cut until the graph is a
DAG.
3. **`linearize`** — Kahn-style topological sort with a tie-break on
edge
weight, so strongly co-occurring modules end up adjacent in the global
order.
4. **`split_into_chunks`** — greedy bottom-up merger over the global
order.
At every active split point we score the merge as
`cost(merged) - cost(left) - cost(right)`, take the most-negative score
from a min-heap, and repeat until no merge would reduce cost.
`max_chunk_size` and "global CSS must not leak into unrelated chunk
groups" are enforced as `+infinity` cost.
The cost model is:
```text
cost_per_group(chunk, group)
= chunk_size
+ (chunk_size / group_total_size) * module_factor_cost
+ request_cost
```
summed over the chunk groups that load the chunk.
#### Wiring
- `StyleGroups::shared_chunk_items` is a
`FxIndexMap<ChunkItemWithAsyncModuleInfo, StyleItemInfo>` where
`StyleItemInfo { order: Option<u32>, batch: Option<…> }`. The graph
algorithm fills `order` so `style_production.rs` can stable-sort chunks
globally; the legacy algorithm leaves `order = None`, which makes the
sort
a no-op for it. `flatten_and_sort` returns the `StyleItemInfo`
references
alongside each chunk item so the per-item loop doesn't re-query the map.
- A new `StyleGroupsAlgorithm` enum on `ChunkingConfig` selects the
algorithm at chunking time; `ModuleGraph::style_groups` dispatches
to either `compute_style_groups` (existing) or
`compute_style_groups_graph`
(new).
- `next-core` exposes `NextConfig::css_chunking() ->
Vc<CssChunkingAlgorithm>`
resolving the JS `experimental.cssChunking` to the Rust enum, with cost
defaults applied (`requestCost: 20_000`, `moduleFactorCost: 1`). All
three
chunking-context constructors (`next_client`, `next_edge`,
`next_server`) thread it through.
#### Configuration
- `experimental.cssChunking` zod schema accepts the new shapes; cost
params
are `z.number().nonnegative().finite().optional()`.
- `config-shared.ts` exports a `CssChunkingConfig` type alias and a
`resolveCssChunkingMode(value)` helper that normalizes any input to one
of `'off' | 'loose' | 'strict' | 'graph'`. Both `webpack-config.ts`
(plugin
wiring) and `config.ts` (bundler-compat validation) use the helper.
- New `errors.json` entries for the three bundler-compatibility
validation
errors (E1193 graph-on-webpack, E1194 strict-on-Turbopack, E1195
false-on-Turbopack).
#### Tests
- 53 Rust unit tests in `style_groups_graph/tests.rs` cover
`create_graph`,
Tarjan SCC, `find_short_cycle` (bidirectional Dijkstra), `make_acyclic`,
`linearize`, `split_into_chunks`, and end-to-end pipeline scenarios.
- `test/e2e/app-dir/css-order/css-order.test.ts` is parametrised over
`[label, value]` pairs. The Turbopack matrix now includes `'graph'` and
an
object-form `{ type: 'graph', requestCost: 1, moduleFactorCost: 1 }` in
addition to the existing default. Per-page expectations grew a
`requests`
object encoding distinct request counts for `loose` and `graph` where
they
differ.
- A new `sandwich` e2e fixture (`/sandwich/a`, `/sandwich/b`) exercises
the
case where two pages share a leading and trailing chunk around a unique
middle stylesheet — including a global stylesheet that the algorithm
must
not leak into unrelated chunk groups. The graph algorithm hits the
optimal
3 chunks per page on this fixture; loose mode falls short.
#### Documentation
- `ExperimentalConfig.cssChunking` JSDoc describes every accepted shape
and
what each cost knob does.
- The `style_groups_graph` module-level docs describe the pipeline, cost
model and constraints with diagrams.
Closes NEXT-
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: v-work-app[bot] <262237222+v-work-app[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
|
||
|
|
5d0dab5fc6 |
[turbopack] Enforce root attribute for strongly consistent reads and collectibles (#93114)
### What? Replaces the silent "make root node" promotion in the turbo-tasks backend with a panic that enforces tasks to already have the `root` attribute before performing strongly consistent reads, reading task collectibles, or removing collectibles. ### Why? Previously, the backend would silently promote any non-root task to a root node (aggregation number `u32::MAX`) when these operations were requested. This masked incorrect task configuration — tasks that needed root-level aggregation weren't explicitly declared as such, making it harder to reason about the aggregation graph and hiding potential performance issues from implicit promotions. ### How? **Backend enforcement (3 locations):** - **Strongly consistent read** (`backend/mod.rs`): Checks `NativeFunction.is_root` on the target task. If it's a persistent task without `root`, panics with both the target and reader task descriptions. - **Read task collectibles** (`backend/mod.rs`): Same check when reading collectibles from a task. - **Remove collectibles** (`operation/update_collectible.rs`): Same check when removing collectibles (count < 0). All three locations drop the task guard before panicking to avoid deadlocking with `debug_get_task_description`. **Macro support for `root` on methods:** - `value_impl_macro.rs`: Now reads the `root` attribute from `#[turbo_tasks::function(root)]` on inherent impl and trait impl methods (previously hardcoded to `false`). - `value_trait_macro.rs`: Same for trait default methods. **`root` attribute additions:** - All `#[turbo_tasks::function(operation)]` in test files, benchmarks, and fuzz code - Production operations in `crates/next-api/` that are read with strong consistency - Regular functions and methods in tests that use `.strongly_consistent()` - Trait methods in `value_impl` and `value_trait` blocks used with strong consistency **New test:** - `non_root_task_panic.rs`: Verifies the panic fires when attempting a strongly consistent read on a non-root operation task. Captures the panic from the worker thread via a panic hook since the panic propagates as a channel error to the test thread. <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9a48c22c17 |
Turbopack: expose hashes of source files to adapters (#93539)
- [x] fix tests
- [ ] add experimental flag and only enable in adapter? or is this cheap
enough to just always do?
- [x] disable for webpack
Closes PACK-6541
For https://github.com/nextjs/adapter-vercel/pull/24
Newer version of https://github.com/vercel/next.js/pull/89534
- Add a hashes mapping to the NFT file
- Forward that information to the adapter via `assetsHashes` for
functions
Basically zero-cost:
```
commit ad728b15a6431a6372eea3d3d766b328569811b4 (HEAD -> mischnic/server-paths-manifest-2, origin/mischnic/server-paths-manifest-2)
pnpm next build 351.22s user, 51.55s system, 811% cpu, 49.654 total
pnpm next build 354.52s user, 53.39s system, 837% cpu, 48.706 total
pnpm next build 344.02s user, 63.62s system, 749% cpu, 54.368 total
pnpm next build 353.74s user, 54.25s system, 833% cpu, 48.970 total
commit
|
||
|
|
7f90cce674 |
Extend instant error overlay to metadata, viewport, and sync IO errors (#93287)
### What? Extends the card-based instant error overlay (#92638) to metadata, viewport, and sync IO errors, and updates the matching build/CLI messages to a consistent structured format. ### Why? After the blocking-route redesign, metadata, viewport, and sync IO errors still used the old prose format with no visual fix guidance. ### Demo - Metadata: [runtime](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/12-cookies-in-metadata) · [dynamic](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/13-fetch-in-metadata) - Viewport: [runtime](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/14-cookies-in-viewport) · [dynamic](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/15-fetch-in-viewport) - Sync IO: [Math.random()](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/38-math-random-no-instant) · [Date.now()](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/39-date-now-no-instant) · [crypto.randomUUID()](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/40-crypto-random-no-instant) - Sync IO in Client: [Math.random()](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/44-client-math-random-no-suspense) · [Date.now()](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/43-client-date-no-suspense) · [crypto.randomUUID()](https://error-messages-overhaul-ibsl.labs.vercel.dev/scenario/45-client-crypto-no-suspense) - [Fix overview (all cards)](https://error-messages-overhaul-ibsl.labs.vercel.dev/fix-overview) ### How? **Overlay** - New `dynamic-metadata`, `dynamic-viewport`, and `sync-io` error types in `errors.tsx`, all rendered through `InstantRuntimeError` with kind-specific fix cards - `isSyncIOError()` detects sync IO via the docs URL pattern, mirroring `isRuntimeVariant()` **Build & CLI messages** - Headlines aligned to "Next.js encountered..." across all error families - `blocking-route-messages.ts`: metadata/viewport errors restructured from prose to "Ways to fix this:" bullets - Sync IO messages extracted into `sync-io-messages.ts`, mirroring `blocking-route-messages.ts`. Helpers renamed to `createSyncIOError` / `createSyncIORuntimeError` / `createSyncIOClientError` to match the `create*Error` pattern |
||
|
|
8141dcf12e |
Convert test/integration to isolated tests (#93247)
### What?
Converts every test under `test/integration/` to an isolated test
running through `nextTestSetup` (under `test/e2e/`, `test/production/`,
`test/development/`, or `test/unit/`), then deletes `test/integration/`
along with the legacy CI orchestration that was specific to it.
- `test/integration/` removed entirely (~327 test suites)
- New isolated suites added across the existing folders:
- `test/e2e/` — 175
- `test/production/` — 130
- `test/development/` — 43
- `test/unit/` — 1
- `.github/workflows/build_and_test.yml` and `run-tests.js` no longer
have any `integration` branches
- `nextTestSetup` gained a `baseUrl` option on `next.browser()` so a
small number of tests that drive their own proxy/static-export server
can keep using `next.browser(...)` instead of importing `next-webdriver`
directly
### Why?
`test/integration/` predated `nextTestSetup` and ran tests directly
against the source checkout via custom helpers (`launchApp`,
`nextBuild`, `nextStart`, `runNextCommand`, `webdriver`, `fetchViaHTTP`,
…). Each suite hand-rolled its own dev/start/build orchestration,
fixture mutation, and process management.
The isolated test model used by the rest of the repo gives each suite an
isolated working directory containing a packed `next.tgz` install, a
uniform `next.start()` / `next.build()` / `next.fetch()` /
`next.browser()` API, and the same lifecycle for dev, start, and deploy
modes — so a single set of assertions covers all three. Deploy-mode
skips and per-feature gates are expressed declaratively
(`skipDeployment`, `disableAutoSkewProtection`, `if (skipped) return`)
instead of branching on `process.env`.
Removing `test/integration/` lets us:
- Delete the bespoke orchestration code in the CI workflow and
`run-tests.js`
- Run every converted suite consistently in dev, start, and deploy modes
(where applicable)
- Reproduce every test locally with the same `pnpm
test-{dev,start}-{turbo,webpack}` commands; no separate `integration`
path
- Open the door to running `test/production` against deployments in the
future (the converted suites already declare `skipDeployment` so they
can be flipped on)
### How?
Mechanical conversion per suite, with targeted clean-ups:
1. **Per-suite conversion.** Each
`test/integration/<name>/test/index.test.{js,ts}` was rewritten into a
single `<name>.test.ts` under the right folder based on what the
original exercised:
- `launchApp` / dev-only assertions → `test/development/`
- `nextBuild` + `nextStart` / start-only assertions → `test/production/`
- Both → `test/e2e/`
- The one pure jsdom render check (`link-without-router`) → `test/unit/`
2. **API mapping.** Custom helpers were replaced by `nextTestSetup`
equivalents: `launchApp` → `next.start()`, `nextBuild` → `next.build()`,
`runNextCommand` → `next.runCommand`, `fetchViaHTTP` → `next.fetch`,
`webdriver(...)` → `next.browser(...)`. Fixture mutations switched from
raw `fs.writeFile`/`fs.rename` to `next.patchFile` (with the 3-arg
`runWithTempContent` callback when the change has a defined scope) and
`next.deleteFile`.
3. **Deploy-mode handling.** Suites that can't run in deploy mode (use
`patchFile` / `next.build()` / depend on local CLI output) declare
`skipDeployment: true` and early-return on the `skipped` boolean. Suites
where Vercel's edge mutates URLs (`&dpl=`, immutable assets) declare
`disableAutoSkewProtection: true`.
4. **`next.browser({ baseUrl })`.** A handful of tests
(`prerender-export`, `cdn-cache-busting`, `preload-viewport`, both
`react-virtualized` suites) need to drive a separate server (a
static-export server or an `http-proxy` instance) rather than the
Next.js process. Instead of importing `next-webdriver` directly, those
tests now pass `{ baseUrl: <port|url> }` to `next.browser()`. For the
proxy cases, the proxy was moved into `server.js` inside the fixture and
`http-proxy` declared via the `dependencies` option of `nextTestSetup`,
so the test runs with a fully isolated dependency graph.
5. **CI clean-up.** With `test/integration` gone, the `test
integration*` jobs and `integration-tests-manifest`-related logic in
`.github/workflows/build_and_test.yml` were removed, and `run-tests.js`
no longer has the `integration` test-folder branch.
6. **Validation.** The PR was iterated against multiple full CI runs;
the remaining failures on the latest run are pre-existing flakes
(segment-cache 60s `act` timeouts in turbopack-prod) or transient
infrastructure issues unrelated to the conversion.
|
||
|
|
554b7fe3b0 |
Turbopack: don't generate next-server.js.nft.json with adapters (#93684)
Adapters don't read these files. Also added a snapshot test similar to the existing ones to not regress on function size |
||
|
|
e8f8f498e8 |
[turbopack] fix feature usage telemetry (#93100)
## Report Turbopack feature-usage telemetry
Turbopack never reported `NEXT_BUILD_FEATURE_USAGE` telemetry for production builds. This PR wires it up and fixes a correctness bug in how the counts were computed, then cleans up the API surface that carried them across the napi boundary.
### Changes
- **JS**: `turbopackBuild()` now records `EVENT_BUILD_FEATURE_USAGE` events after `writeAllEntrypointsToDisk` via a new `eventBuildFeatureUsageFromTurbopackDiagnostics` helper. Dev is out of scope — webpack's `TelemetryPlugin` is `!dev && isClient` too.
- **Rust**: aligned feature names with the JS `EventBuildFeatureUsage['featureName']` union — SWC triple is now `swc/target/<triple>`; dropped `persistentCaching` (redundant with `turbopackFileSystemCache`) and `turbotrace: false` (hardcoded).
### Correctness fix: count unique importers, not resolves
Previously feature-usage counts for module imports (`next/image`, `next/font/google`, …) were computed from a `BeforeResolvePlugin` that emitted one event per resolve. Turbopack caches resolves, so the emission fired at most **once per unique request** — the count was effectively `1` for every feature that was imported anywhere. Webpack's equivalent counts unique importing modules via `moduleGraph.getIncomingConnections(module).size`.
This PR replaces the resolve-plugin emission with a single whole-app module-graph traversal on `Project`. For each tracked feature, we accumulate the set of unique parent modules of each matching node (mirroring webpack's "unique origin modules" semantics). Fonts are matched against their synthesized `/target.css?…` virtual modules produced by the SWC font-loader transform — matching webpack's `FEATURE_MODULE_REGEXP_MAP` approach. Paths are matched via `phf_map!` tables in `next_telemetry.rs`.
### Incidental simplifications
While in here, the `Diagnostic` collectibles subsystem got right-sized and then removed entirely, since feature usage was its only consumer:
- `Project::project_feature_usage()` returns a structured `Vc<ProjectFeatureUsageSummary>` instead of emitting diagnostics. Surfaced to JS as a dedicated `project.featureUsage(): Promise<BuildFeatureUsage[]>` napi method, called once at build's end.
- `TurbopackResult<T>` loses its `diagnostics: BuildFeatureUsage[]` field — it's now just `{ result, issues }`. Every napi result type and ~10 construction sites are correspondingly simpler.
- Deleted `turbopack_core::diagnostics` entirely (`Diagnostic` trait, `DiagnosticExt`, `DiagnosticContextExt`, `CapturedDiagnostics`, `PlainBuildFeatureUsage`). Deleted `FeatureUsageTelemetry`, `ModuleFeatureReportResolvePlugin`, `get_diagnostics()` aggregation, the `feature_usage`/`diagnostics` fields on `AllWrittenEntrypointsWithIssues`/`OperationResult`/`EntrypointsWithIssues`/`WrittenEndpointWithIssues`/`HmrUpdateWithIssues`/`HmrChunkNamesWithIssues`/`EndpointIssuesAndDiags`/`WriteAnalyzeResult`, and the defensive `drop_collectibles::<Box<dyn Diagnostic>>()` scrub in `entrypoints_without_collectibles_operation`.
Feature-usage telemetry now flows as a plain return value end-to-end: `Project::project_feature_usage()` → napi `projectFeatureUsage()` → JS `project.featureUsage()` → `telemetry.record()`. No collectibles, no peeking, no emission-as-side-effect.
### Tests
Un-skipped four previously webpack-only integration tests in `test/integration/telemetry/test/config.test.ts`: `image/script/dynamic`, `next/legacy/image`, `transpilePackages`, and middleware options. All pass under Turbopack. The remaining three skipped tests (`swc` flags, `@vercel/og`, `useCache`) cover features Turbopack doesn't emit yet — left skipped with TODOs.
Added unit test for the helper at `packages/next/src/telemetry/events/build.test.ts`. Updated the Turbopack `next-rs-api` snapshot to reflect the new diagnostic shape.
<!-- NEXT_JS_LLM_PR -->
|
||
|
|
d81d5ab7df |
Proof of concept: task eviction after snapshot for turbo-tasks-backend (#91790)
> **Note:** This is a **proof of concept** implementation. It is not yet ready for production use. ## Summary Implements memory eviction for the turbo-tasks engine. After a persistence snapshot completes, tasks that are safe to remove are evicted from in-memory storage and transparently restored from disk on next access. ### Eviction levels - **Full eviction**: Entire task removed from the in-memory map (restored from disk on access). Only possible when the task has no meaningful transient state (and other state is already on disk) - **DataAndMeta eviction**: Both data and meta categories cleared, but the task stays in the map to preserve transient state (e.g. `current_session_clean`, aggregated session-clean counts). - **DataOnly eviction**: Only data-category fields cleared; meta (graph structure, output, dirty state) stays in memory. - **MetaOnly eviction**: Only meta-category fields cleared; data stays in memory. Data and meta evictability are computed independently — if one category is modified but the other is clean, the clean category can still be dropped. Eviction is gated behind `BackendOptions::evict_after_snapshot` (off by default), and can be enabled in Next.js via the `TURBO_ENGINE_EVICT_AFTER_SNAPSHOT=1` env var for testing. ## Key changes - **Orthogonal eviction decision tree** (`storage_schema.rs`): Data and meta evictability are computed independently. Full eviction additionally requires no meaningful transient state (session-clean flags, aggregated session-clean counts). Replaces the previous sequential bail-out approach which was too aggressive on full eviction (losing transient session state on leaf tasks) and not aggressive enough on partial eviction (blocking all eviction when only one category was modified). - **`drop_partial()` codegen** (`task_storage_macro.rs`): New generated methods to drop data - **`restore_from_*()` codegen changes** (`task_storage_macro.rs`): New semantics for merging persistent data from the backend with transient data stored in memory. - **`task_cache` moved into `Storage`** (`storage.rs`): The `CachedTaskType → TaskId` deduplication map was previously a separate field on `TurboTasksBackendInner`. It is now owned by `Storage` so eviction can remove entries when a task is fully evicted. Because `task_cache` is a pure performance cache (entries are re-populated by `task_by_type()` on miss once the task type is persisted to backing storage), evicting entries is safe. After bulk eviction the map is shrunk when it is less than half full. - **Parallel shard eviction** (`storage.rs`): Eviction iterates all storage shards in parallel after snapshot, applying the appropriate eviction level per task. Each shard is shrunk after bulk eviction to reclaim slack capacity. - In principle this is O(N) work to scan, but because each pass drops >98% of tasks there isn't wasted work and the logic is fast, taking <100ms for even the largest applications. ## Design notes - **SessionDependent tasks**: SessionDependent tasks can still be evicted but if `current_session_clean` is set we prevent full eviction to avoid rechecking. Within a session the file-watchers are responsible for invalidations after setting `current_session_clean`. ## Known limitations (proof of concept) - No LRU or access-frequency tracking — all eligible tasks are evicted on every snapshot cycle - No memory pressure feedback — eviction runs on a timer, not in response to actual memory pressure - Only runs after snapshotting which tends to be a high point in memory - Future work will explore interleaving this logic with snapshotting to trim the peak <!-- NEXT_JS_LLM_PR --> |
||
|
|
f0c1ffc44e |
Remove ineffective turbo-tasks (#91341)
## Remove ineffective turbo-tasks
Identifies and removes turbo-tasks functions where the task overhead exceeds the value they provide. Each turbo-task carries ~4-6μs execution overhead per miss and ~200-500ns per cache hit, plus allocations and bookkeeping.
### What?
Removes 22 `#[turbo_tasks::function]` implementations across resolve plugins, chunk items, and resolve-result helpers — converting them to plain methods or inlining their work. Changes fall into a few buckets:
- **ResolvePlugin condition handling** (`AfterResolvePluginCondition::matches`, `BeforeResolvePluginCondition::matches`, `after_resolve_condition`, `before_resolve_condition`): conditions now store the resolved `Glob` as a `ReadRef<Glob>` on the plugin struct at construction, so `matches` is a pure sync function and the per-plugin `*_resolve_condition` getters are trivial field reads (no longer turbo-tasks). The `after_resolve` / `before_resolve` hooks themselves stay as `#[turbo_tasks::function]` — they synthesize virtual sources/modules and need memoization on `(self, lookup_path, reference_type, request)` to avoid distinct cells producing duplicate module-graph idents.
- The basic theory here is that the right level of caching is at `resolve` and at the hook bodies themselves, not the conditions or condition getters.
- `AfterResolvePluginCondition` and `BeforeResolvePluginCondition` are marked `serialization = "none"` because `ReadRef` cannot be persisted; plugin construction is cheap enough to re-derive on restore.
- **ChunkItem trait methods** (`chunking_context`, `ty`, `content_with_async_module_info`): returned constants or simple field reads, zero cache hits and no `.await` calls (no invalidation value).
- **ResolveResult / ModuleResolveResult helpers** (`primary_modules`, `first_module`, `first_source`, `primary_sources`, `is_unresolvable`, `primary_output_assets`): simple iterators over already-resolved data; converted to plain methods. Added a `Duplicate(usize)` variant to `ModuleResolveResultItem` to handle dedup at construction time instead of in a separate task.
- The basic idea here is that it is reasonable to consume `ResolveResult/ModuleResolveResult` monolithically, and we get little to no benefit from fine grained access. e.g. `is_unresolved()` in theory that is a valuable turbotask, but since it rarely changes but generally if we change how we resolve an import then we have to regenerate code, so saving a few boolean conditions is unlikely to be very valuable.
- Misc: `EcmascriptModuleAsset::analyze`, `is_types_resolving_enabled`, `next_server::resolve::condition`.
### Impact (vercel-site build, dev first-compile)
| Metric | Before | After | Δ |
|---|---:|---:|---:|
| Total cache hits | 30,885,827 | 29,201,314 | −1,684,513 |
| Total cache misses | 6,473,123 | 5,953,626 | **−519,497** |
| Overall hit rate | 82.67% | 83.06% | +0.39 pp |
| Registered task functions | 1,294 | 1,272 | −22 |
The 22 removed tasks were collectively responsible for ~519K misses per build — each miss previously paying the full execution overhead. Most of the work from `EcmascriptModuleAsset::analyze` naturally migrated into `analyze_ecmascript_module` (the task it was wrapping; +129K hits there).
### On-disk cache size (persistent caching)
Each removed task also stops allocating cache cells on disk. Measured on the same vercel-site build with `.next/cache/turbopack` (persistent cache enabled):
| | Size |
|---|---:|
| canary | 2.56 GiB |
| this branch | 2.46 GiB |
| **saved** | **~100 MiB (−3.81%)** |
### Build-time wall clock and peak memory
Ran `pnpm next build --experimental-build-mode=compile` 5 times on each branch
**Peak RSS — clear reduction:**
| | canary | branch | Δ |
|---|---:|---:|---:|
| min | 19.18 GiB | 18.94 GiB | |
| **median** | **19.22 GiB** | **19.01 GiB** | **−217 MiB (−1.10%)** |
| mean | 19.21 GiB | 19.02 GiB | −199 MiB (−1.01%) |
| max | 19.23 GiB | 19.13 GiB | |
Every branch run has lower RSS than every canary run — the distributions don't overlap. Welch's t = −6.03.
**Wall time — no measurable change:**
| | canary | branch | Δ |
|---|---:|---:|---:|
| min | 62.03s | 60.78s | |
| **median** | **62.61s** | **62.65s** | **+0.04s (+0.06%)** |
| mean | 62.83s | 63.80s | +0.96s (+1.53%) |
| max | 64.25s | 68.23s | |
| stddev | 0.84s | 3.42s | |
Median is flat. The mean difference is within noise (Welch's t = +0.61, n = 5). Branch run-to-run variance is higher — one 68.23s outlier pulls the mean up — so this is neither a regression nor a measurable speedup at this sample size.
<!-- NEXT_JS_LLM_PR -->
|
||
|
|
8f132ea984 |
Fix "type: module" in project dir when using standalone or adapters (#93612)
### What?
Add `.next/package.json` (the distDir commonjs boundary marker) to the
`required-server-files.json` manifest.
### Why?
`next build` writes `.next/package.json` with `{"type": "commonjs"}` so
that everything in `.next/**` is loaded as CJS even when the user's
project `package.json` has `"type": "module"`. Without that file, Node
walks up to the project `package.json` when loading
`.next/server/**/*.js` and tries to evaluate the compiled server bundles
as ESM, which fails at runtime.
This file is necessary on disk in every server deployment artifact (the
standalone bundle, every adapter Node function), but it was not declared
in `required-server-files.json`. As a result, adapters consuming
`requiredServerFiles.files` did not include the boundary file.
`.next/package.json` is conceptually a required server file: any
deployment that wants to run `.next/server/**/*.js` correctly under a
`"type": "module"` project must ship it. Declaring it in the manifest is
the right place to express that contract once for every consumer.
This error case would only happen if the project package.json gets
included into the Node.js functions. For example when file tracing
analysis (node-file-trace) decides it needs to include it (i.e. glob
patterns or such).
### How?
Add `'package.json'` to the `requiredServerFilesManifest.files` array in
`packages/next/src/build/index.ts`. Every entry in that array is joined
with `config.distDir`, so this resolves to `.next/package.json`.
Both existing consumers pick it up automatically with no further
changes:
- `packages/next/src/build/adapter/build-complete.ts` iterates
`requiredServerFiles` into `sharedNodeAssets`, which is merged into
every Node function's `output.assets` regardless of bundler. The Vercel
adapter (and any other adapter) gets the file for free.
- `copyTracedFiles` in `packages/next/src/build/utils.ts` iterates
`requiredServerFiles.files` and copies each entry into
`.next/standalone/<relative>`, so the standalone output now contains
`.next/standalone/.next/package.json`.
### Tests
- `test/production/required-server-files-package-boundary/` — a focused,
non-standalone test that builds a `"type": "module"` app and asserts:
- `.next/package.json` is `{"type": "commonjs"}` on disk
- `.next/required-server-files.json` lists `.next/package.json`
- `test/production/standalone-mode/type-module/` — extended with a
`pages/dynamic.js` that uses `getServerSideProps`. The test now:
- Asserts `.next/standalone/.next/package.json` exists with `{"type":
"commonjs"}`
- Boots the standalone `server.js` and fetches `/dynamic`, which forces
Node to actually evaluate `.next/server/pages/dynamic.js` at runtime —
the exact code path the boundary file makes work
|
||
|
|
defac920cc |
feat(turbopack): add chunkLoadingGlobal config option (#93488)
### What?
Adds a new `chunkLoadingGlobal` option to the `turbopack` config in
`next.config.js` that overrides the global variable name used for
Turbopack chunk loading (defaults to `TURBOPACK`).
```js
// next.config.js
module.exports = {
turbopack: {
chunkLoadingGlobal: 'myApp',
},
}
```
### Why?
When multiple Turbopack-built apps run on the same page (e.g. horizontal
micro-frontends), they each try to use `globalThis.TURBOPACK` which
causes conflicts. This option allows each app to use its own isolated
global, mirroring webpack's `output.chunkLoadingGlobal`.
The `chunk_loading_global` method was already wired into Turbopack in
#88790 — this PR exposes it via Next.js config.
A reason is
[vercel/remote-components#239](https://github.com/vercel/remote-components/pull/239),
which uses this option to eliminate four brittle `globalThis.TURBOPACK`
regex rename transforms in the remote-components runtime. Those
transforms target Turbopack's internal variable naming and have already
required updates as Turbopack's output format evolved. With
`chunkLoadingGlobal` set at build time, Turbopack handles the renaming
natively and those transforms become unnecessary.
### How?
- `config-shared.ts` / `config-schema.ts` — adds `chunkLoadingGlobal?:
string` to `TurbopackOptions` with Zod validation
- `next_config.rs` — adds the field to `TurbopackConfig` and a
`turbopack_chunk_loading_global()` accessor
- `next_client/context.rs` — threads it through
`ClientChunkingContextOptions` and calls
`builder.chunk_loading_global()` when set
- `project.rs` — wires the config into the client chunking context
options
Only applies to the browser/client chunking context — server and edge
runtimes don't have this concept.
### Test plan
- `pnpm test-start-turbo
test/production/app-dir/turbopack-chunk-loading-global/turbopack-chunk-loading-global.test.ts`
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com>
|
||
|
|
505771a20d |
Use Next.js version as Turbopack persistent cache versioning key (#93605)
### What?
The Turbopack persistent cache directory under
`.next/cache/turbopack/<version>/` is now versioned by the Next.js
package version concatenated with the git short SHA, instead of `git
describe`.
Example: `v16.0.1-canary.13-94e9fa6`
### Why?
The previous version key came from `VERGEN_GIT_DESCRIBE` (`git describe
--match 'v[0-9]' --dirty`), which depends on having an annotated `v*`
tag reachable from the build commit. That ties the cache version to
local git tag state and breaks down for builds against forks, shallow
clones, or branches without a recent matching tag.
The Next.js package version from `packages/next/package.json` is the
value users actually care about for cache compatibility, and it is
already plumbed through to the Rust side as
`ProjectOptions::next_version` (sourced from
`process.env.__NEXT_VERSION`). Combining it with the git short SHA keeps
per-commit cache uniqueness while removing the dependency on git tags.
### How?
**Rust (`crates/next-napi-bindings`)**
- `build.rs`: also emit `VERGEN_GIT_SHA` (short form) via
`vergen_gitcl`. `VERGEN_GIT_DESCRIBE` is still emitted and is still used
for the bug-report URL / panic log.
- `src/next_api/turbopack_ctx.rs`:
- New `cache_describe(next_version)` returns
`format!("v{next_version}-{}", env!("VERGEN_GIT_SHA"))`.
- `git_version_info` now takes the pre-built `describe` string instead
of reading `VERGEN_GIT_DESCRIBE` itself. `dirty` handling is unchanged
(still derived from `VERGEN_GIT_DIRTY`, still suppressed in CI), so
`handle_db_versioning`'s dirty-repo behavior is preserved.
- `create_turbo_tasks` takes `next_version: &str` and threads it
through.
- `src/next_api/project.rs`:
- `project_new` passes `options.next_version` into `create_turbo_tasks`.
No new field is needed on `NapiProjectOptions` — the `nextVersion` field
already exists and is already populated by all three call sites.
- `turbopack_database_compact` (the napi `databaseCompact` function used
by `next-post-build`) gains a `next_version: String` parameter so
post-build compaction targets the same versioned directory.
**JS (`packages/next`)**
- `src/build/swc/types.ts`, `generated-native.d.ts`, `index.ts`: update
the `databaseCompact(path, nextVersion)` signature on both the native
and WASM-fallback paths.
- `src/cli/next-post-build.ts`: passes `process.env.__NEXT_VERSION`
through to `bindings.turbo.databaseCompact`.
`turbopack/crates/turbopack-cli` (the standalone Turbopack CLI) is
intentionally left on `VERGEN_GIT_DESCRIBE` — it is not tied to a
Next.js package version.
---------
Co-authored-by: v-work-app[bot] <262237222+v-work-app[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
|
||
|
|
c06d94ba22 |
Turbopack: Match proxy matchers with webpack implementation (#93594)
## What? Applies https://github.com/vercel/next.js/commit/d166096c399c4fc4e09cd2d1bf26dca6579a855d for canary and handles the Turbopack-side implementation for `middleware-manifest.json` which is used for edge runtime middleware.ts --------- Co-authored-by: Zack Tanner <1939140+ztanner@users.noreply.github.com> |
||
|
|
f088c3d407 |
Turbopack: Fix middleware matcher suffix (#93590)
## What? The Turbopack path incorrectly included a double backslash instead of one backslash in the regex matcher. |
||
|
|
ead8013f88 |
Turbopack: more strict vergen setup (#93541)
Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com> Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com> |
||
|
|
b785969e83 |
Order loader tree imports by tree depth (#93537)
### What? Reorder the imports emitted by the app-page loader-tree builder so that they are grouped by their depth in the loader tree (shallow segments first, deep segments last) instead of the somewhat arbitrary order produced by recursive walking. ### Why? The loader tree is built by walking the app router tree recursively, and each segment contributes a few `require(...)` declarations (layout, error, loading, page, metadata, ...) to a shared list of imports that is later concatenated into the generated entry module. The order of that list is what ends up in the bundle, and walking the tree recursively interleaves segments at different depths in a way that depends on traversal order rather than on the structure of the route. For chunking and readability we want a more predictable order: *outer (shallower) layouts/segments before inner (deeper) ones*, with imports inside the same depth keeping their original relative order. This gives downstream chunking a more useful signal. ### How? In `crates/next-core/src/base_loader_tree.rs`: - `BaseLoaderTreeBuilder::imports` is now `Vec<(u32, RcStr)>` instead of `Vec<RcStr>`. The `u32` is a sort key (the depth at which the import was produced). - `create_module_tuple_code` takes a new `position: u32` argument and stores it alongside the generated `require` line. In `crates/next-core/src/app_page_loader_tree.rs`: - `walk_tree` takes a new `depth: u32` argument. The initial call from `build()` passes `0`. - `depth` is threaded through `write_modules_entry`, `write_metadata`, `write_metadata_items`, `write_metadata_item`, and `write_static_metadata_item`, so the three other places that push directly into `self.base.imports` (dynamic image metadata, static metadata items, and their alt-text companions) also tag their entries with the current depth. - When recursing into `parallel_routes`, depth is incremented only for the `"children"` slot. Named parallel routes (e.g. `@modal`) are sibling slots of the same segment and therefore stay at the same depth. - In `AppPageLoaderTreeBuilder::build`, the collected `(depth, import)` pairs are stable-sorted by depth (`sort_by_key`, which is stable in Rust, preserving the original relative order within each depth) and then stripped back to `Vec<RcStr>` before being placed on `AppPageLoaderTreeModule.imports`. The public type of `AppPageLoaderTreeModule.imports` (`Vec<RcStr>`) is unchanged, so consumers in `crates/next-core/src/next_app/app_page_entry.rs` need no adjustments. Co-authored-by: v-work-app[bot] <262237222+v-work-app[bot]@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com> |
||
|
|
0f38c522bc |
Turbopack: simplify asset ident constructors (#93213)
### What?
Removes the per-method turbo-task constructors on `AssetIdent` (`from_path`, `with_query`, `with_fragment`, `with_modifier`, `with_part`, `with_path`, `with_layer`, `with_content_type`, `with_asset`, `rename_as`, and `path`). Each of those was its own cached task that returned a small projection or a one-field-changed copy. They are now plain Rust builder methods on the owned value, with a single `into_vc()` at the end of the chain that goes through the existing cached `new_inner` constructor.
Call sites that previously chained `Vc` methods now look like:
```rust
module
.ident()
.owned()
.await?
.with_modifier(rcstr!("async loader"))
.into_vc()
```
### Why?
These constructors were tiny "projection" turbo-tasks that paid the cost of a task lookup, cell allocation, and dependency tracking but whose cache layer didn't meaningfully prevent recomputation. The trade-off is invalidation semantics:
- **Before:** a caller doing `module.ident().path()` depended on the cached `path()` projection. If the source `AssetIdent` changed but its `.path` field was unchanged (e.g. a new modifier was added), `path()` re-ran, returned the same `FileSystemPath` cell, and the caller did not re-run.
- **After:** the same caller does `module.ident().await?.path` and depends directly on the `AssetIdent` cell. Any change to the ident (modifier, query, layer, …) invalidates the caller, even if the path is unchanged.
In practice this is rarely a real loss: when an ident changes, the `Module` typically changes too, and the dependent task was going to re-run anyway. `new_inner` already deduplicates structurally-equal idents, so the wrappers were paying overhead per call without buying meaningful invalidation isolation.
Measured on a `vercel-site` build via `NEXT_TURBOPACK_TASK_STATISTICS` and `turbopack/scripts/analyze_cache_effectiveness.py`:
| Task | canary (hits / misses) | this branch |
| --------------------------------- | ---------------------- | ----------- |
| `AssetIdent::path` | 778,273 / 98,018 | removed |
| `AssetIdent::with_modifier` | 27,895 / 22,801 | removed |
| `AssetIdent::from_path` | 2,954 / 29,650 | removed |
| `AssetIdent::with_part` | 2 / 5,440 | removed |
| `AssetIdent::with_layer` | 7 / 4,356 | removed |
| `AssetIdent::rename_as` | 4,969 / 2,269 | removed |
| `AssetIdent::with_query` | 0 / 521 | removed |
| `AssetIdent::with_content_type` | 0 / 79 | removed |
| `AssetIdent::new_inner` | 628 / 129,777 | 29,213 / 120,650 |
Aggregate over the whole build:
- Total cached tasks: 1,300 → 1,292
- Total task invocations: 39,361,186 → 38,208,036 (~1.15M fewer lookups)
- Total cache misses: 6,812,198 → 6,639,937 (~172k fewer)
- Overall hit rate: 82.7% → 82.6% (essentially unchanged)
`new_inner` absorbs the construction work that used to be split across the wrappers. Four upstream tasks gained +519 cache hits each (`EsmAssetReference::resolve_reference`, `ReferencedAsset::from_resolve_result`, `NextServerUtilityModule::ident`, `NodeJsChunkingContext::chunk_item_id_strategy`); no task gained any new misses.
### How?
- `AssetIdent::from_path` and the `with_*` methods are now plain `&mut self`/`self`-by-value builder methods on the struct itself, not `#[turbo_tasks::function]`s.
- A new `AssetIdent::into_vc(self)` finalizes the builder by going through the still-cached `new_inner`.
- `AssetIdent::path()` is removed; callers use `.path` on an owned `AssetIdent`.
- All call sites across `turbopack-*` and `next-*` crates are updated. Most go from `ident.with_modifier(m)` (returning `Vc`) to `ident.owned().await?.with_modifier(m).into_vc()`.
- A follow-up commit removes a few `.clone()`s introduced in the conversion that aren't needed once lifetimes are bound to a local.
### Follow-ups (out of scope)
While migrating call sites, two pre-existing entry builders surfaced as candidates for cleanup. Not addressed here, but worth noting:
- `get_app_page_entry` (`crates/next-core/src/next_app/app_page_entry.rs`) replaces the *content* of the source returned by `load_next_js_template` (prefixing imports onto `result.build()`) but reuses the template's `ident` with a `?page=...` query suffix as a disambiguator. The new `VirtualSource` ends up with content from one place and an ident chain pointing at another. A cleaner shape would be to mint a fresh ident from the page path, since the caller already knows what it's building.
- `create_page_ssr_entry_module` (`crates/next-pages/page_entry.rs`) has the same shape on the instrumentation-conflict branch: it appends `export const register = hoist(...)` to the template content and constructs a `VirtualSource` with the original `source.ident()` unchanged. Lower-frequency than the app-page case (fires at most once per build), but the ident still misrepresents the constructed content.
<!-- NEXT_JS_LLM_PR -->
|
||
|
|
3d4df34575 |
Turbopack: make rcstr! macro expansion const (#93516)
Brought to you by ``` destructor of `std::option::Option<turbo_rcstr::RcStr>` cannot be evaluated at compile-time ``` and ``` cannot perform conditionally-const deref coercion on `std::mem::ManuallyDrop<std::option::Option<RcStr>>` in constants ``` |
||
|
|
26afa89200 |
Replace Lazy<FxHashMap> static lookups with phf::Map (#93096)
## What Trivial change: LazyLock<FxHashMap<...>> -> phf::Map. |
||
|
|
908717af2d |
Drop once_cell from all direct crate deps (#93095)
## What Purely mechanical change from once_cell to stdlib primitives. --------- Co-authored-by: Luke Sandberg <lukesandberg@users.noreply.github.com> |
||
|
|
62f0d46774 | Only log partytown setup error once (#93378) | ||
|
|
668094edce |
Turbopack: add experimental.turbopackWorkerAssetPrefix to keep Workers same-origin when assetPrefix is a CDN (#93271)
### What? Adds `experimental.turbopackWorkerAssetPrefix` — a Turbopack counterpart to webpack's [`output.workerPublicPath`](https://webpack.js.org/configuration/output/#outputworkerpublicpath). Opt-in, off by default, fully backward compatible. ```js // next.config.js module.exports = { assetPrefix: 'https://cdn.example.com', experimental: { // Same shape as `assetPrefix`: bare prefix, no trailing slash, no `/_next`. // `/_next/` is appended automatically. `''` → same-origin `/_next/...`. turbopackWorkerAssetPrefix: '', }, } ``` ### Why? When `assetPrefix` points to a cross-origin CDN (common production setup), Turbopack emits Worker URLs under that CDN origin. Browsers reject cross-origin `new Worker(url)` with `SecurityError`, breaking every feature that uses `new Worker(new URL('./worker.ts', import.meta.url))` — image decoders, WebP/GIF renderers, compression, OffscreenCanvas, etc. webpack solved this with `output.workerPublicPath`. Turbopack ignores the `webpack()` callback in `next.config.js`, so the existing same-origin workaround silently breaks when projects switch to Turbopack. Full design doc, alternatives considered, related issues (#88602, #92676, #74621, #29468): **#93044** Repro repo (`pnpm dev` reproduces, `pnpm dev:webpack` works): https://github.com/GuinsooRocky/next-turbopack-worker-cdn-repro ### How? Threads a new optional `turbopack_worker_asset_prefix: Option<RcStr>` from `ExperimentalConfig` → `BrowserChunkingContext` → the JS runtime, where `createWorker` reads it via a new `WORKER_BASE_PATH` global emitted alongside the existing `CHUNK_BASE_PATH`. When unset, behavior is identical to today. Semantics, modeled on `assetPrefix`: - **Bare prefix.** `/_next/` is appended automatically (same as `computed_asset_prefix`); the user supplies just the origin (or empty string for same-origin). - **`undefined` vs `''`.** `undefined` falls back to the regular chunk base path; `''` is a literal empty prefix that resolves to same-origin `/_next/...`. The runtime distinguishes the two by injecting a JS `null` literal vs a quoted string, and uses `WORKER_BASE_PATH ?? CHUNK_BASE_PATH` (not `||`). - **Applies to entrypoint and module chunks.** The Turbopack worker bootstrap rejects cross-origin module chunks (`Refusing to load script from foreign origin`), so the override has to cover both — overriding only the entrypoint would leave the worker unable to load any chunks under a cross-origin `assetPrefix`. Touchpoints: - `packages/next/src/server/config-{shared,schema}.ts` — public option + zod under `experimental` - `crates/next-{api,core}/...` — wiring through to `ClientChunkingContextOptions`; `NextConfig::turbopack_worker_asset_prefix()` resolves to `Some("<prefix>/_next/")` or `None` - `turbopack/crates/turbopack-browser/src/chunking_context.rs` — new field + builder + getter - `turbopack/crates/turbopack-ecmascript-runtime/src/browser_runtime.rs` — emit `WORKER_BASE_PATH` declaration as `null` or a quoted string - `turbopack/.../runtime-base.ts` — `createWorker` resolves `workerBasePath` and uses it for both the entrypoint URL and module-chunk URLs delivered via params - `test/e2e/turbopack-worker-asset-prefix/` — e2e fixture using real cross-origin (`localhost` page, `127.0.0.1` `assetPrefix`); intercepts `new Worker()` to assert on the URL the runtime helper resolved. Three cases: no override (cross-origin URL → `SecurityError`), explicit override origin, and `''` (relative `/_next/...`). - `docs/01-app/03-api-reference/08-turbopack.mdx` — option reference - `telemetry/events/version.ts` — `useTurbopackWorkerAssetPrefix: boolean` (set on any explicit value, including empty string) ### Notes - Opening as **Draft** to invite API-shape feedback before going to review. Per the contributing guide, the design discussion is at #93044. - All checklist items the PR template requires for a feature are covered: e2e tests, docs, telemetry, no error-link path applies. cc @sokra Closes #93044 <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com> |
||
|
|
8384791ee4 |
Turbopack: also add edge count to trace (#93314)
<!-- 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 ### Signed commits - This repository requires verified commit signatures on protected branches. - If this pull request is blocked for unsigned commits, re-sign the commits and force-push the branch. - A `Signed-off-by` line in the commit message is not enough. ## 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 # --> |
||
|
|
28c9eefab2 |
Turbopack: iter_reachable_modules (#93315)
Add `iter_reachable_modules` and `iter_reachable_nodes`. They actually respect unused references |
||
|
|
7df1a40bb7 |
Turbopack: cheaper check in map_next_dynamic (#93283)
Currently doesn't have a measureable performance impact. Attempt the cheap downcast first and only do the ident reading and comparison in the uncommon case |
||
|
|
65542fa028 |
Reduce allocations in the next_debug_instant_stack transform (#93222)
This was allocating a regex on every transform, we can hoist that regex up to avoid the work I noticed this as a major source of temporary allocations in a dev benchmark also avoid a bunch of tiny string copies |
||
|
|
f68f70b044 |
Recognize super(...) messages in Error subclass constructors (#93182)
The error-code SWC plugin at `crates/next-error-code-swc-plugin` previously only transformed `new Error(msg)` call sites where the constructor identifier was in the hard-coded `is_error_class_name` list. Subclasses not in that list — for example `UseCacheTimeoutError`, which defines its message inside the constructor via `super('...')` and is instantiated with no arguments — went undetected, so their instances carried no `__NEXT_ERROR_CODE` at all.
The plugin now also visits class declarations and expressions. When it sees `class X extends Y` where `Y` is one of the recognized Error classes, it finds the first top-level `super(arg)` call inside the constructor body, resolves the argument to a string via the existing `stringify_new_error_arg` logic (which handles string literals, template literals, string concatenations, and resolved identifier bindings), and inserts an `Object.defineProperty(this, '__NEXT_ERROR_CODE', ...)` statement immediately after the `super` call. Messages that aren't in `errors.json` yet are emitted to `cwd/.errors/<hash>.json` and consolidated by the existing `check-error-codes` build step, exactly as the call-site path already does. Classes whose `super` is nested inside an `if` or `try`, or whose only argument is a spread (`super(...args)`), are left alone.
Classes that are already in `is_error_class_name` (for example `InvariantError`) now get a code assigned at both their call sites and inside their constructor body. The call-site `Object.defineProperty` runs after the constructor and therefore takes precedence at runtime, so observable behavior at those sites is unchanged. The constructor injection is defensive for any future instantiation that the call-site pass doesn't cover.
`packages/next/errors.json` gains nine entries for super messages in existing Error subclasses that the plugin now captures for the first time.
|
||
|
|
3cb77c844b |
Turbopack: unify ModuleGraph constructors (#93203)
We have options and variable length lists. Why do we need a separate function for every combination? |
||
|
|
5d3232fb2b |
[rust-deps] Update tracing (#93194)
### What? Updates the `tracing` crate and its related dependencies to their latest versions: - `tracing`: 0.1.37 → 0.1.44 - `tracing-attributes`: 0.1.30 → 0.1.31 - `tracing-core`: 0.1.34 → 0.1.36 ### Why? Keep Rust dependencies up to date with security patches and performance improvements. ### How? Updated the version specification in `Cargo.toml` and regenerated `Cargo.lock`. <!-- NEXT_JS_LLM_PR --> |
||
|
|
b91cff03ad |
feat(turbopack): add NEXT_TURBOPACK_TRACING_PATH to control trace output location (#93084)
### What? Adds a new environment variable `NEXT_TURBOPACK_TRACING_PATH` that lets users specify the full output path (including filename) for the Turbopack trace file. ### Why? Some users need to control where the `trace-turbopack` file is written — for example, to redirect it outside the project directory, to a shared volume, or to a custom filename. Previously the path was hardcoded to `<project>/.next-profiles/trace-turbopack` with no override mechanism. ### How? When `NEXT_TURBOPACK_TRACING` is set (tracing is enabled) and `NEXT_TURBOPACK_TRACING_PATH` is also set, the trace is written to the specified path instead of the default: - **Absolute paths** are used as-is. - **Relative paths** are resolved against the process CWD. - The **parent directory is auto-created** recursively (equivalent to `mkdir -p`), so pointing to a non-existent subdirectory works without manual setup. - Setting `NEXT_TURBOPACK_TRACING_PATH` alone (without `NEXT_TURBOPACK_TRACING`) has no effect — tracing is not enabled by it. - The "Trace output will be written to …" message reflects the resolved path. The change is confined to `crates/next-napi-bindings/src/next_api/project.rs`. **Example:** ```sh NEXT_TURBOPACK_TRACING=overview \ NEXT_TURBOPACK_TRACING_PATH=/tmp/my-project-trace \ next dev ``` <!-- NEXT_JS_LLM_PR --> Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
82b320addf |
[ci]: switch to GitHub runners (#93164)
## Summary Switch CI from self-hosted runners to GitHub-hosted runners and fix the follow-up issues that showed up once the hosted jobs were exercised. ## Changes - Move Linux workflows onto `ubuntu-latest-16-core-oss`, Windows workflows onto `windows-latest-8-core-oss`, and the native mac release lane onto `macos-15-intel`. - Update the reusable build workflow to work in hosted environments by relying on `runner.os` instead of label string matching, and by removing the hardcoded `/home/runner` `fnm` path. - Make Turbo/sccache configuration explicit for hosted runners: - unify on `vtest314-next-adapter-e2e-tests` - pass `TURBO_TOKEN` via workflow env/secrets instead of assuming runner-level env - switch Turbo cache mode to `local:rw,remote:rw` so jobs still have local cache behavior when remote cache is unavailable - Update the `sccache` action defaults/docs to use the hosted-runner secret setup (`TURBO_TOKEN`) instead of the old self-hosted token wiring. - Make Datadog reporting fail open so missing `DATA_DOG_API_KEY` does not block CI. - Fix the CLI reserved-port test so it asserts Next’s own `-p 1` validation instead of failing earlier on hosted Linux due to privileged-port binding. - Replace the live `musl.cc` dependency in `native-builder.Dockerfile` with musl sysroots imported from GHCR-hosted `rust-musl-cross` images, keeping the existing `/opt/*-cross` layout used by the Linux native build scripts. - Fix the macOS native build workaround s so it applies to all *-apple-darwin targets based on the Cargo target OS rather than the host architecture, which avoids napi-build injecting the unsupported -Wl linker arg when @next/swc is linked with rust-lld on hosted mac runners. Test Plan: - This PR's CI - This [build_and_deploy](https://github.com/vercel/next.js/actions/runs/24865715983/job/72801225722) job running on every arch |
||
|
|
d069c2e163 |
[turbopack] Unify Cell Storage (#92974)
## What
Tightens the value-type persistence API and sets the table for a future eviction policy. Two user-visible changes on the `#[turbo_tasks::value(...)]` macro:
- **`serialization = "none"` → `serialization = "skip"`** — imperative ("skip persisting") instead of descriptive. Making it clear that it isn't that we are missing a feature but rather that we are choosing to not persist (of course persisting might be impossible but that is generally rare)
- **New `evict = "always" | "last" | "never"` attribute** — replaces the old overloaded `"none"` semantic. Only valid with `serialization = "skip"`. Defaults to `"always"`.
Internally this collapses `persistent_cell_data` + `transient_cell_data` into one `cell_data` map and replaces the old `bincode: Option<(enc, dec)>` field with a four-variant `ValueTypePersistence` enum. Eviction machinery itself is a follow-up PR; this PR just gives each value type a precise, queryable persistence/eviction descriptor.
## Why break out a new parameter?
`serialization = "none"` on canary conflated three different intents:
1. **Cheap recomputable outputs** (SWC ASTs, codegen `Rope`s) — fine to evict, recompute is cheap.
2. **Expensive recomputable outputs** (WASM modules, Node process pools) — re-derivable but costly.
3. **Session-scoped state** (`State<>` cells, `Arc<Mutex<_>>` dedup histories) — can't be recomputed without losing accumulated mutations.
They all produced identical runtime behavior (stored in transient_cell_data), so eviction can't tell them apart. The fix is two orthogonal attributes:
```rust
// A cheap skip — default evict = "always"
#[turbo_tasks::value(serialization = "skip")]
// Expensive recompute — evict last under pressure
#[turbo_tasks::value(serialization = "skip", evict = "last")]
// Session-scoped state — never evict
#[turbo_tasks::value(serialization = "skip", evict = "never")]
```
The macro rejects `evict` on any other `serialization` mode.
## `ValueTypePersistence` enum
Replaces `ValueType.bincode: Option<(enc, dec)>`:
```rust
pub enum ValueTypePersistence {
Persistable(AnyEncodeFn, AnyDecodeFn<SharedReference>), // auto, custom
SkipPersist { expensive: bool }, // skip (+ evict = last)
HashOnly, // hash
SessionStateful, // skip + evict = never
}
```
The existing `"hash"` mode gets its own `HashOnly` variant rather than being folded into `SkipPersist`, which lets the backend gate its hash-writing and hash-comparison paths precisely.
## Unified `cell_data` storage
`persistent_cell_data: AutoMap<CellId, TypedSharedReference>` + `transient_cell_data: AutoMap<CellId, SharedReference>` collapse into `cell_data: CellData`. `CellData` is a newtype over `AutoMap<CellId, SharedReference>` with a custom bincode impl that filters non-`Persistable` entries at encode time. This removes the `is_serializable_cell_content: bool` parameter that was threading through ~14 read/write call sites.
Uses `SharedReference` instead of `TypedSharedReference` — `CellId` already carries the `ValueTypeId`.
## Annotation sweep
All prior `serialization = "none"` sites move to either `serialization = "skip", evict = "never"` or `serialization = "skip", evict = "last"` based on a per-site audit. Summary:
**`evict = "last"` (6 sites)** — re-derivable but expensive:
- `SwcPluginModule`, `EvaluatePool`, `ChildProcessPool`, `WorkerThreadPool`, `EffectInstance`, `Effects`
**`evict = "never"` (2 sites)** — interior-mutable state accumulated across the session:
- `ConsoleUi` (`Arc<Mutex<SeenIssues>>`), `VersionState` (`State<VersionRef>` with HMR invalidators)
The distinguishing rule: `evict = "never"` only when the value holds interior mutability accumulated across the session. Everything else can be re-derived (possibly expensively) by re-running the producing task.
## Follow-ups (separate PRs)
- Wire an eviction policy that consumes `ValueTypePersistence` — respects `SessionStateful` (never evict), prefers cheap `SkipPersist` over `expensive: true` ones. Either as part of #91790 or afterwards depending on when things land
<!-- NEXT_JS_LLM_PR -->
|
||
|
|
8ddc1cdbdd |
Use OIDC for CodSpeed (#93059)
https://codspeed.io/docs/integrations/ci/github-actions/configuration#oidc-recommended |
||
|
|
7873aeaa51 |
Perf: Simplify static metadata handling (#92630)
### What? Avoid running the generic `fillMetadataSegment(..., true)` path for static metadata routes. This PR adds a dedicated static metadata URL resolver, uses it from the existing TypeScript static call sites, and precomputes static metadata URLs in Turbopack loader tree codegen instead of waiting on `props.params` at runtime. ### Why? Static metadata files like `favicon.ico`, `icon.png`, and other static metadata images do not need request-time dynamic path interpolation. The old path reused logic that was designed to also support dynamic metadata routes, which meant extra route-regex work even when the final URL was already statically known apart from the existing `-` placeholder and route-group / parallel-route suffix semantics. ### How? - Added a static-only metadata URL helper in `get-metadata-route.ts` that: - normalizes app path segments, - replaces dynamic and catch-all segments with `-`, - preserves the existing metadata filename suffix logic for route groups and parallel routes. - Kept `fillMetadataSegment()` for dynamic metadata routes, but short-circuited the `isStatic` path to the new helper. - Switched TypeScript static metadata call sites to use the static helper directly: - build tree display path generation, - dev bundler static metadata mapping, - webpack static metadata image loader output. - Updated Turbopack static metadata codegen to emit the final static URL string instead of calling `fillMetadataSegment(..., true)` with `await props.params`. - Added focused coverage for: - root favicon, - static metadata under dynamic parents, - route-group / parallel-route suffix behavior, - unchanged dynamic metadata interpolation. Closes NEXT- Fixes # <!-- NEXT_JS_LLM_PR --> |
||
|
|
1de67d8b34 |
Refactor trace server sort to SortMode enum (execution order / value / name) (#92873)
### What?
Replaces the boolean `sort` flag in the trace server with a `SortMode`
enum supporting three modes:
- **`ExecutionOrder`** — no sorting, spans appear in natural/execution
order (previous `false`)
- **`Value`** — sort by corrected duration descending (previous `true`)
- **`Name`** — sort alphabetically by span name (title), then by
category
### Why?
The boolean `sort` only allowed toggling duration-based sorting on/off.
Adding alphabetical sorting by name makes it easier to find duplicate
spans and locate a specific span by name in the trace viewer. The enum
is extensible for future sort modes without breaking changes.
### How?
**Rust (`turbopack-trace-server`):**
- Added `SortMode { ExecutionOrder, Value, Name }` enum to both `lib.rs`
(for the query API) and `viewer.rs` (for the WebSocket viewer).
- `ViewMode` variants changed from `sorted: bool` to `sort_mode:
SortMode`.
- `sort_children()` now returns `SortMode` instead of `bool`; all 10
sorting branches in `viewer.rs` converted from `if/else` to a 3-arm
`match`.
- `Name` sort uses `nice_name()` — sorts by title first, then category,
both ascending.
- `SpanEventRef::SelfTime` and `SpanGraphEventRef::SelfTime` (which have
no name) sort as `("", "")` under `Name` mode.
**WebSocket protocol (backward compatible):**
- `-sorted` suffix → `SortMode::Value` (existing clients unaffected)
- `-sorted-by-value` suffix → `SortMode::Value`
- `-sorted-by-name` suffix → `SortMode::Name`
- no suffix → `SortMode::ExecutionOrder`
**napi bindings (`next-napi-bindings`):**
- `TraceQueryOptions.sort` changed from `Option<bool>` to
`Option<String>` (`"value"` | `"name"` | omit).
**TypeScript:**
- `generated-native.d.ts`: `sort?: 'value' | 'name'`
- MCP tool schema: `z.enum(['value', 'name']).optional()`
- CLI: `--sort <mode>` with `.choices(['value', 'name'])` replacing the
boolean `--sort` flag
- Tests updated; added new test cases for `sort: 'name'` and `--sort
name`
<!-- NEXT_JS_LLM_PR -->
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
|
||
|
|
a226531900 |
Support type cast expressions in page static info extractor (#92837)
So that ```ts export const runtime = "node" as const export const runtime = "node" as string export const runtime = "node" satisfies string // was already working ``` works |
||
|
|
26cfeb8531 | Allow overriding outputHashSalt in modifyConfig (#92856) | ||
|
|
f49722285b |
turbopack: cache TransformPlugin in narrow-scoped turbo-tasks functions (#92842)
### What?
Refactor all usages of
`EcmascriptInputTransform::Plugin(ResolvedVc::cell(Box::new(...) as _))`
across `next-core` and `turbopack-tests` so that `TransformPlugin` cells
are created inside dedicated `#[turbo_tasks::function]` functions rather
than inline at call sites.
Additionally:
- Introduce a `JsonValue` newtype wrapping `serde_json::Value` that
implements `TaskInput`, enabling the SWC wasm plugin list to be passed
through a turbo-tasks function boundary and properly cached.
- Replace the `bool` roundtrip in `next_strip_page_exports` with an
`ExportFilterInput` enum that derives `TaskInput`, mirroring
`ExportFilter` exhaustively (so a new upstream variant is a compile
error, not a silent fallback).
- Derive `TaskInput` on `ActionsTransform` and pass it directly to the
cached function instead of converting to `is_server: bool` at the call
site.
- Replace all `.expect("... config must exist")` panics in option-gated
plugin functions (`emotion`, `styled_components`,
`react_remove_properties`, `remove_console`, `relay`) with
`.context(...)?` for proper error propagation.
- Add `// TODO: use get_ecma_transform_rule instead` comments to the ~10
transform functions that manually inline the same `ModuleRule::new` +
`ExtendEcmascriptTransforms` pattern that `get_ecma_transform_rule`
abstracts.
### Why?
`TransformPlugin` is not serializable and not comparable. When a
`TransformPlugin` is `cell`ed inline (i.e.
`ResolvedVc::cell(Box::new(...) as _)`) inside a turbo-tasks function, a
new cell is created on every invocation of the enclosing function,
because the framework has no way to detect that the value is the same as
before. This causes every task that depends on the `TransformPlugin`
cell to be invalidated unnecessarily.
By moving the `Vc::cell(...)` call into its own narrow-scoped
`#[turbo_tasks::function]`, turbo-tasks can cache the cell by the
function's inputs. If the inputs haven't changed, the function won't
re-run, the existing cell is reused, and downstream tasks are not
invalidated.
### How?
Each inline `ResolvedVc::cell(Box::new(SomeTransformer { ... }) as _)`
is replaced with:
```rust
some_transform_plugin(args).to_resolved().await?
#[turbo_tasks::function]
fn some_transform_plugin(args: ...) -> Vc<TransformPlugin> {
Vc::cell(Box::new(SomeTransformer { ... }) as Box<dyn CustomTransformer + Send + Sync>)
}
```
Where a cached function needs to store a `ResolvedVc` in the resulting
transformer struct, the parameter is declared as `ResolvedVc<T>`
directly in the `#[turbo_tasks::function]` signature. The turbo_tasks
macro rewrites `ResolvedVc<T>` → `Vc<T>` in the external call-site
signature, and the call site passes a dereferenced `*resolved_vc`. This
avoids a redundant `.to_resolved().await?` inside the function body.
For the SWC wasm plugin case, `serde_json::Value` (used for per-plugin
config) doesn't implement `Hash` or `TaskInput`. A `JsonValue` newtype
is introduced with:
- `#[bincode(with = "turbo_bincode::serde_self_describing")]` for
serialization
- Manual `Hash` impl that hashes the JSON string representation
- Manual `TaskInput` impl (`is_transient = false` since the type
contains no `Vc`s)
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Niklas Mischkulnig <mischnic@users.noreply.github.com>
|
||
|
|
3f17705979 |
Turbopack: properly set NODE_ENV for SWC plugins (#92579)
### What? Closes https://github.com/vercel/next.js/pull/92650 Closes https://github.com/vercel/next.js/issues/92547 When Turbopack runs SWC transform plugins, it passes a `TransformPluginMetadataContext` that includes the current `NODE_ENV`. Previously this value was hardcoded to `"development"` regardless of the actual build mode, causing SWC plugins to misbehave during production builds. ### Why? `SwcPluginMetadataContext` is used by SWC plugins (e.g. `babel-plugin-styled-components`, `@swc/plugin-emotion`) to conditionally produce development vs. production output — for instance, injecting display names and data attributes only in development. With `NODE_ENV` always set to `"development"`, plugins could never produce their optimised production output, resulting in larger bundles and missing minification in `next build`. Fixes https://github.com/vercel/next.js/issues/92547 ### How? `NODE_ENV` is now derived from the `process.env.NODE_ENV` compile-time define that is already part of `CompileTimeInfo` (the same source of truth used for dead-code elimination). The resolution happens in `EcmascriptModuleAsset::parse()` via a new shared helper `node_env_from_compile_time_info()` in `parse.rs`, and the value is stored on `TransformContext` so every `CustomTransformer` implementation can read it from `ctx.node_env` without needing access to the broader compilation context. Key design decisions: - **`TransformContext`** is the right place: it is the per-file context passed to every `CustomTransformer::transform()` call, so storing `node_env` there follows the existing pattern and avoids coupling the plugin struct to the build mode. - **Webpack analysis path**: `compile_time_info` is now threaded through `webpack_runtime()`, `module_references()`, and the webpack reference structs (`WebpackModuleAsset`, `WebpackChunkAssetReference`, `WebpackEntryAssetReference`, `WebpackRuntimeAssetReference`) so that webpack-bundled files with SWC plugin transforms also receive the correct `NODE_ENV`. - **`segment_config.rs`**: passes a placeholder but includes a comment explaining it is irrelevant since `EcmascriptInputTransforms::empty()` means no transforms run. <!-- NEXT_JS_LLM_PR --> |
||
|
|
836fa63fa4 |
Stop using deprecated baseUrl (#92653)
|
||
|
|
b9cd44892c |
Add TurboMalloc::thread_park() to flush and collect on thread park (#92804)
### What? Adds `TurboMalloc::thread_park()` — a new method on `TurboMalloc` that calls `libmimalloc_sys::mi_collect(false)` to process mimalloc's lazy free lists when a Tokio worker thread parks. Registers it as an `on_thread_park` callback in every Tokio runtime that already registers `on_thread_stop`: - `turbopack-cli/src/main.rs` - `turbopack-cli/benches/small_apps.rs` - `next-napi-bindings/src/lib.rs` - `next-build-test/src/main.rs` - `turbo-tasks-backend/fuzz/src/graph.rs` ### Why? mimalloc uses deferred freeing: memory freed by one thread is not reclaimed immediately but is queued until that thread performs N more allocations. Tokio worker threads that are parked (waiting for work) don't allocate, so deferred frees accumulate indefinitely — memory is never returned to the OS until the thread wakes up and does real work. Calling `mi_collect(false)` on park processes the lazy free lists, allowing mimalloc to reclaim that memory, while avoiding the syscalls and globally synchronized operations that `mi_collect(true)` would trigger. ### How? - `thread_park()` only calls `mi_collect(false)` — it intentionally does **not** call `flush()`. `flush()` transfers the thread-local allocation counter to the global atomic and resets it to zero, which would violate the trace server's assumption that per-thread counters are monotonically increasing. Counter sync remains exclusively in `thread_stop()`. - `thread_park()` is feature-gated behind `cfg(all(feature = "custom_allocator", not(target_family = "wasm")))` and is a no-op otherwise. - `libmimalloc-sys` is added as a direct optional dependency under `[target.'cfg(not(target_family = "wasm"))'.dependencies]` (it was already a transitive dep via `mimalloc`) to access `mi_collect` directly, since `mimalloc`'s `use ffi::*` is not a public re-export. - In each `on_thread_park` callback, `TurboMalloc::thread_park()` is called **after** the existing SWC atom store GC, so atoms freed by that GC pass are also eligible for reclamation in the same collection. <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
3bbbb32fa9 |
Enable wasm plugins on windows arm (#92544)
Not because we should, but because we can. Previously we disabled this due to an incompatibility between windows arm and wasmer, however we recently switched to wasmtime which appears to work. So simplify the CI configuration and the rust feature flags https://github.com/vercel/next.js/actions/runs/24271405735 is a build-and-deploy.yml run and this is the [windows arm build](https://github.com/vercel/next.js/actions/runs/24271405735/job/70877042113) |
||
|
|
ac6d9932a6 |
Add unstable_prefetch route segment config and decouple runtime prefetching from unstable_instant (#92754)
Allows you to opt into runtime prefetching without coupling it to the way you configure instant validation. We do not intend to allow shipping runtime prefetching without opting into instant validation unless you specifically disable the validation because the cost of the runtime prefetching is potentially high but the way you configure validation is likely going to be pulling in lots of test code and we want to make it easier to discern that the validation code is not going to be bundled into production builds so separating the config is helpful. Another reason to separate the config is that we expect that eventually most prefetching is configured globally and you do not need to opt specific segments into a different prefetching strategy. Improved client component error messages to accurately describe the constraint: these are route segment configs that require a Server Component module, not exports that are forbidden. |
||
|
|
d141874063 | Bump TypeScript to 6.0 (#91257) | ||
|
|
5ff5ae217b |
feat(trace-server): add query CLI and MCP API to turbopack-trace-server (#92030)
### What? Adds a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) HTTP server and a companion CLI to the Turbopack trace server (`next internal trace`), so AI agents and developers can explore trace data from a build or dev session incrementally through a structured tool API. Two new user-facing features: 1. **`--mcp-port` flag on `next internal trace`** — starts an MCP-over-HTTP endpoint (at `/mcp`) alongside the existing WebSocket trace viewer. 2. **`next internal query-trace`** — a standalone CLI command that queries a running trace server from the terminal, using the same MCP endpoint under the hood. ### Why? Trace files from Turbopack builds can be very large. An agent querying raw trace data would be overwhelmed by tens of thousands of spans. The MCP API exposes the data incrementally: - **Pagination** (20 spans per page) prevents context overflow. - **Aggregation** groups repeated spans by name (same logic as the WebSocket viewer's graph), so agents see one entry per span type with count/total/avg statistics rather than thousands of identical entries. - **Drill-down** lets agents pick a span ID from one response and fetch its children in the next call. - **Search** filters spans by name/args using `SpanRef::search` (full subtree search index, same as the WebSocket viewer). - **Sort** orders by corrected (wall-clock) duration to surface the slowest spans first. - **Output format** — `outputType: "json"` for machine-readable structured data, `"markdown"` (default) for human-readable rendering. ### How? #### Rust (`turbopack/crates/turbopack-trace-server/src/lib.rs`) - `start_turbopack_trace_server_non_blocking(path, port) -> Arc<StoreContainer>` — starts the reader and WebSocket server on background threads and returns the store handle immediately (previously the function blocked forever). - `QueryOptions` — struct with `parent`, `aggregated`, `sort`, `search`, `page` fields. - `query_spans(store, opts) -> QueryResult` — queries up to 20 spans per page. In aggregated mode it uses the existing `SpanGraphRef` graph logic (the same grouping the WebSocket viewer uses). Waits up to 10s for the store to finish initial data loading on the first call. - Helper functions extracted: `paginate()`, `format_span_name()`, `build_span_id()` to avoid duplication between aggregated and raw code paths. #### Span ID format IDs encode both the span type and the navigation path: | Span kind | Leaf format | Example full path | |---|---|---| | Raw span | `<index>` (decimal) | `a1-a5-20` | | Aggregated span | `a<first-span-index>` | `a1-a5-a34` | Segments are joined by `-` as the caller drills deeper. `resolve_span_by_id` only needs the last segment to look up the underlying store index. #### NAPI bridge (`crates/next-napi-bindings/src/turbo_trace_server.rs`) - `TraceServerHandle` — opaque NAPI class wrapping `Arc<StoreContainer>`. - `startTurbopackTraceServerHandle(path, port)` — calls the non-blocking Rust function. - `queryTraceSpans(handle, options)` — calls `query_spans` and returns a plain JS object. - Doc comments on `SpanInfo` fields clarify that `cpu_duration`/`corrected_duration` hold the first example span's values for aggregated groups (while `total_*` fields hold group totals). #### TypeScript (`packages/next/src/...`) - `generated-native.d.ts` / `types.ts` / `index.ts` — type declarations, native wrappers, and WASM stubs. - `next.ts` — adds `--mcp-port <port>` to `next internal trace` (defaults to `5748`); registers `next internal query-trace` subcommand with `--json` flag. - `turbo-trace-server.ts` — rewrites the CLI handler to: 1. Start the WebSocket trace viewer server non-blocking (was blocking). 2. Start an HTTP server at `/mcp` running MCP `StreamableHTTPServerTransport` (stateless — one transport per request, `sessionIdGenerator: undefined`). 3. `renderSpanMarkdown()` — extracted helper that renders a single span as markdown. 4. Error handling: `server.on('error')` for `EADDRINUSE`, `try/catch` around `loadBindings()` and `startTurbopackTraceServerHandle()`. - `query-trace.ts` — new CLI command that POSTs JSON-RPC to the MCP endpoint and prints the response. On connection failure, shows instructions to start `next internal trace` first. Supports `--json` flag for structured output. #### `next internal query-trace` CLI ``` Usage: next internal query-trace [options] Options: --port <port> MCP port of the running trace server. Defaults to 5748. --parent <parent> Span ID to enumerate children of. Omit for root level. --no-aggregated Disable aggregation of spans by name (aggregated by default). --sort Sort results by corrected duration descending (default: false). --search <search> Substring filter on span name/category. --json Output as JSON instead of markdown. --page <page> Page number (1-based, default 1). -h, --help Displays this message. ``` #### Startup output When `next internal trace` starts with `--mcp-port`: ``` Turbopack trace server started. View trace at https://trace.nextjs.org?port=5747 Query this trace from the command line: next internal query-trace --help Alternatively, connect an MCP client to http://127.0.0.1:5748/mcp ``` #### Markdown output format (per span) ```markdown ### `<name>` (ID: `<id>`) - CPU Duration: … - Corrected Duration: … - Start (relative to parent): … - End (relative to parent): … **Attributes:** - `key`: value ``` For aggregated spans with count > 1, totals and averages are shown first, then one example span's raw data. ### Tests **E2E test (`test/e2e/turbopack-trace-server-query/turbopack-trace-server.test.ts`)** Uses `nextTestSetup` with `env: { NEXT_TURBOPACK_TRACING: '1' }` to produce a real trace file. Spawns `next internal trace <file> --mcp-port <port>`, waits for the MCP server to be ready, then runs both MCP HTTP and CLI queries: - Root listing (markdown format) - Aggregation mode - Pagination - Drill-down by span ID (using JSON output to extract IDs) - Search with a real span name match and a non-matching term - Sort by duration - JSON output format (`outputType: 'json'`) - CLI: `--sort`, `--search`, `--no-aggregated`, `--parent`, `--json` - Error path: connection failure message with instructions Turbopack-only (skips via `if (!isTurbopack) return` at describe level). The error-path test runs regardless of bundler. ### Usage ```bash # Build with tracing NEXT_TURBOPACK_TRACING=1 next build # Start the trace viewer + MCP server next internal trace .next-profiles/trace-turbopack --mcp-port 5748 # Query from CLI next internal query-trace --sort next internal query-trace --parent a1 --sort next internal query-trace --search "turbo_tasks::function" --page 2 next internal query-trace --no-aggregated next internal query-trace --json # Or connect an MCP client to http://127.0.0.1:5748/mcp ``` <!-- NEXT_JS_LLM_PR --> --------- Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
1efbcb244f |
TURBOPACK: support crsossOriginLoading (#92070)
Support: https://nextjs.org/docs/pages/api-reference/config/next-config-js/crossOrigin#options for turbopack. |
||
|
|
cc5bbaad05 | fix(server-actions): skip type-only ExportNamed in pre-pass to prevent phantom registerServerReference (#92494) | ||
|
|
a05035f3c7 |
Delete some dead turbo task functions (#92596)
Removes a handful of dead code discovered while exploring deletion of the `ValueDefault` trait. That larger change turned out not to be worth it from a binary size perspective, so it was backed out — but the dead code found along the way is worth keeping. **Removed:** - `impl ValueDefault for VersionedContentMap` (not tagged with `#[turbo_tasks::value_impl]`, never a turbo-tasks singleton) - `impl ValueDefault for Body` in `turbopack-dev-server` (same — plain Rust impl, no callers via `Vc::default()`) - `EmotionTransformConfig::default_private()` and `impl ValueDefault for EmotionTransformConfig` (unused) - `StyledComponentsTransformConfig::default_private()` and `impl ValueDefault for StyledComponentsTransformConfig` (unused) - `EmittedEvaluatePoolAssetsWithEffects` struct in `evaluate.rs` (defined but never referenced) - `OutputAsset::size_bytes()` default trait method (no call sites) <!-- NEXT_JS_LLM_PR --> |