56 Commits
Author SHA1 Message Date
Sam PoderandGitHub fec0a517fc [turbopack] Tree shake w/ bar['foo'] syntax for star imports (#96319)
This adds tree-shaking support for this syntax:

```javascript
import * as ns from './lib';
ns["foo"]
```

Instead of expanding the usage to `All` for this one, we can restrict
ourself to just "foo".

See attached fixture.
2026-07-29 10:42:08 -07:00
Sam PoderandGitHub b89bbd8277 Revert "Revert "[turbopack] Track re-exports in import_usage inside of compute_import_usage"" (#96315)
Reverts vercel/next.js#96311
2026-07-28 15:29:00 -07:00
Sam PoderandGitHub 02bbeaa84b [turbopack] Remove loader calls for dropped dynamic imports (#96284)
For modules that we were not using, that we're asynchronously loading,
we would still generate the loader but the file would not exist. This
meant building a Next.js app with:

```javascript
// pure.js — side-effect free: nothing observable happens on evaluation
export const version = '1.0.0'

// app/page.tsx
export default async function Page() {
  const {} = await import('../pure')   // ← nothing destructured out
  return <p>hello world</p>
}
```

Would error with:

```
▲ Next.js 16.3.0-canary.87 (Turbopack)
  Creating an optimized production build ...

FATAL: An unexpected Turbopack error occurred.

> Build error occurred
Error [TurbopackInternalError]: Failed to write app endpoint /page

Caused by:
- ModuleId not found for ident: [project]/pure.js [app-rsc] (ecmascript, async loader)

Debug info:
...
- Execution of PatternMapping::resolve_request failed
- ModuleId not found for ident: [project]/pure.js [app-rsc] (ecmascript, async loader)
```  

Now, we re-write those loaders to empty promises:

```diff
 async function evaluationOnly() {
-    const {} = await __turbopack_context__.A("…/pure.js [test] (ecmascript, async loader)");
+    const {} = await Promise.resolve({});
 }
 async function named() {
     const { used } = await __turbopack_context__.A("…/named.js [test] (ecmascript, async loader)");
     console.log(used);
 }
 async function pattern(locale) {
     const {} = await __turbopack_context__.f({
         "./locales/effectful.js": {
             id: ()=>"…/locales/effectful.js [test] (ecmascript, async loader)",
             module: ()=>__turbopack_context__.A("…/locales/effectful.js …")
         },
         "./locales/pure.js": {
-            id: ()=>"…/locales/pure.js [test] (ecmascript, async loader)",
-            module: ()=>__turbopack_context__.A("…/locales/pure.js …")
+            id: ()=>undefined,
+            module: ()=>Promise.resolve({})
         }
     }).import(`./locales/${locale}.js`);
 }
```

It turns `UnusedReferences` into a map between references and target
that can be dropped, where the target IDs are the unused targets. It
then passes the reference through to `pattern_mapping.rs` so it can
determine the `dropped_targets`. It needs to be target-specific as you
can have a situation (like above) where some targets are pure and some
aren't.
2026-07-28 14:37:08 -07:00
Sam PoderandGitHub dcf4607f8f Improve root detection to handle worktrees, more workspaces & stray lockfiles (#96159)
Watching too many files can hurt performance, and impacts how much work
Turbopack is doing. See for example the conversation around
https://github.com/vercel/next.js/pull/94597.

This PR handles the following four cases:

- Git worktrees: we can include a guard to prevent exiting a worktree
(by looking at the `.git` file).
- Git repository boundaries: we can include a guard to prevent us from
exiting a checked-out repository and watching the repository’s parent
directory.
    - We will log a warning if this happens.
- Empty / bogus `package.lock` files & `package-lock.json` files (and
equivalents using other package managers): if we spot a
`package-lock.json` file but it has no packages or a `package-lock.json`
w/o an associated `package.json` file. We can ignore them. This handles
the case of accidentally running `npm install` in any directory.
- Home directory detection: we can detect if we’re going to start
watching your home dir and rollback up if this is the case.
    - We will log a warning if this happens.
- If your Next config is in your home dir, we will still run your app
there as Next config is the furthest we can rollback
- NPM, Yarn, Lerna & Bun workspaces: based on the `workspaces` field in
the `package.json` or `lerna.json`

I would recommend using split-view to review. I also cleaned up the
`warnDuplicatedLockFiles()` function because it was a bit unnecessary to
repeat the full message.

Closes https://github.com/vercel/next.js/issues/92978
2026-07-28 13:35:17 -07:00
Sam PoderandGitHub 625dccbc4a Revert "[turbopack] Track re-exports in import_usage inside of compute_import_usage" (#96311)
Reverts vercel/next.js#95989
2026-07-28 20:29:13 +00:00
d61f16eed3 [turbopack] Treat Object.defineProperty(exports, …, { value: … }) as side-effect free (#96273)
Treat code like this:

```
Object.defineProperty(exports, '__esModule', { value: true });
exports.foo = 1;
```

As side-effect free. This is an extension of the work on
https://github.com/vercel/next.js/pull/94293 and will allow
https://github.com/vercel/next.js/pull/95718 to drop more dead requires.

---------

Co-authored-by: Benjamin Woodruff <[email protected]>
2026-07-28 11:40:29 -07:00
Sam PoderandGitHub 33a09d53dc [turbopack] Tree-shake module.exports = {...} (CJS) (#95996)
This PR builds on https://github.com/vercel/next.js/pull/95716 by adding
support tree-shaking exports exported using the `module.exports = {...}`
syntax
2026-07-27 00:26:22 -07:00
af3dad8390 [turbopack] Fix de-opt in getChunkRelativeUrl() (#96183)
Used Claude + this tool (https://github.com/vercel/next.js/pull/96158)
to look at Turbopack's runtime. With a large fixture with a lot of
imports, this part was bailing out:

```
{
  "kind": "deopt",
  "bailoutType": "deopt-eager",
  "reason": "wrong map",
  "functionName": "getChunkRelativeUrl",
  "original": {
    "source": "turbopack:///[turbopack]/browser/runtime/base/runtime-base.ts",
    "line": 486,
    "column": 11
  },
  "severity": "high"
}
```

After the fix, this went away.

---------

Co-authored-by: Luke Sandberg <[email protected]>
2026-07-26 23:25:39 -07:00
Sam PoderandGitHub 29b3966370 [turbopack] Import Webpack's tests for tree-shaking (#95811)
Imports the tests that Webpack uses for CJS tree-shaking. One test suite
is skipped `deep-reexports`, that's because we don't support that deep
of analysis at the moment and we just bail. WIP because after landing
this stack I will work on improving this.

I did have to make some edits to the tests, a summary is below (courtesy
of Claude):

```
1. Stripped ?query request suffixes (all cases).
Webpack uses require("./x?1") / ?2 to force distinct module instances. Turbopack shares one instance across require sites and aggregates usage, so the queries were removed (require("./x")). The behavioral assertions hold on a shared instance.

2. Distinct copies where per-instance semantics are genuinely required.
A few cases actually depend on separate instances, so we replaced the query with real duplicate files:
- mutate → module-1.js / module-2.js (so mutating one import doesn't leak into the other).
- reexports → add-to-counter-1/2/3.js (the counter must increment 3×).
- reexports-define-property → add-to-counter-1/2.js.

3. Removed __resourceQuery (webpack module-query magic — undefined in turbopack).
In reexports and reexports-define-property: require("./m" + __resourceQuery) → require("./m"), and the dynamic dispatch module reexport.js (require("./" + __resourceQuery.slice(1))) was split into static reexport-direct.js / reexport-indirect.js.

4. Commented out __webpack_exports_info__ / usedExports assertions.
Webpack exposes per-export usage introspection (x.usedExports, __webpack_exports_info__.x.used) that turbopack doesn't populate for CJS. Those specific assertions were commented (the real behavioral checks kept) in: require-member-access, require-member-access-deferred, require-destructuring, this-in-exported-function(-modern), reexport-require-binding, mutate, cjs-to-esm.

5. Case-specific rewrites.
- cjs-to-esm — dropped the require.cache[require.resolve(...)].exports comparison (webpack runtime introspection).
- namespace — changed expect(await import(x)).toBe(require(x)) (object identity) to assert the observable exports (ns.abc, ns.default); turbopack's import() yields an exotic Module namespace, never the same/deep-equal object as require().

6. mjs removed entirely — built on __resourceQuery + per-query instances + .js/.mjs interop divergence; can't even evaluate in turbopack and tests behavior turbopack doesn't share.
```

All tests should be passing.
2026-07-23 23:12:59 -07:00
Sam PoderandGitHub eb7beb5921 [turbopack] Drop dead require() calls (#95718)
This PR adds support for `turbopackRemoveUnusedImports` in CJS modules.
If modules are side-effect free and their imports are unused we do the
following re-writes:

```
require('./pure')          →   0;
console.log('x')               console.log('x')
```

```
const x = require('./pure')   →   const x = 0
```

```
const dead = require('./pure'), keep = 'v'   →   const dead = 0, keep = 'v'
```

```
const {} = require('./pure')   →   const {} = 0
```

```
if (cond) require('./pure')   →   if (cond) 0; 
```

SWC should then drop the 0.
2026-07-23 23:12:58 -07:00
Sam PoderandGitHub 1f04d8ff82 [turbopack] Track usage of modules imported with require() (#95717)
This PR introduces a new method `analyze_require_usage` that analyzes
how something imported with `require()` is used. It has two passes: one
catches simple things like `const { a } = require(...)` and
`require(...).foo`, the next one then handles if you import using `const
x = require(...)`.

This then gets populated into `export_usage_info`, this will expand the
set of exports we can drop through
https://github.com/vercel/next.js/pull/95716.
2026-07-23 23:12:58 -07:00
Sam PoderandGitHub 326fc90012 [turbopack] Track re-exports in import_usage inside of compute_import_usage (#95989)
Previously, code such as:

```
export { foo } from "bar"
```

Would contribute to a module's exports (we export foo).

However, it would not contribute to our tracking of imports from "bar".
Therefore we would fall back to the default import usage:


https://github.com/vercel/next.js/blob/c0a7b91becb33490bf412d02bbf09ebaa7216083/turbopack/crates/turbopack-ecmascript/src/references/exports.rs#L140

```rust
pub enum ImportUsage {
    #[default]
    TopLevel,
    Exports(FrozenSet<RcStr>),
}
```

We would therefore not tree-shake. This is rarely a problem with normal
imports / exports because they support following reexports and this is
enabled by default in Turbopack. However, async imports do not support
following reexports.

In https://github.com/vercel/next.js/issues/95698, the import of the
Viem chain definitions is an async-imported module:

<img width="796" height="81" alt="Screenshot 2026-07-20 at 5 47 43 pm"
src="https://github.com/user-attachments/assets/2fc46711-469f-4f28-8369-9c1ea01b808d"
/>

Closes https://github.com/vercel/next.js/issues/95698

I looked at the reproduction from the original issue, and this was the
result:


```
Client graph contains 11/712 viem chain definition files.
Not reproduced; missing 701 definitions.
```

Yay!
2026-07-23 17:34:53 -07:00
Sam PoderandGitHub 5b9d43b550 [turbopack] Fix deployment skew protection for component chunks (#96079)
Noticed this bug where we were assuming chunks were plain strings, which
may not always be the case given #95261. When putting together
https://github.com/react/react/pull/37095, I noticed this was also a
spot we were missing so this fixes that.

Almost all of this diff is a regression test.
2026-07-22 20:27:11 +00:00
Sam PoderandGitHub 3ed60553da Revert "[turbopack] Don't SSR on pages only navigated to through a soft nav (#95539)" (#96028)
Causes issues with `use client` it seems.
2026-07-21 21:03:04 +00:00
Sam PoderandGitHub 18123b3765 [turbopack] Tree-shake CJS exports that use the Object.defineProperty syntax (#95994)
This is a common format for transpilers to produce CJS code from ESM,
eg. this output:

```javascript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var x = { color: 'red' };
```

from
https://www.typescript-training.com/course/intermediate-v2/05-modules/.

This PR extends the work done in
https://github.com/vercel/next.js/pull/95716 to support this syntax.
2026-07-21 12:14:40 -07:00
Sam PoderandGitHub e90189b940 [turbopack] Rename turbopackTreeShaking to turbopackModuleFragments (#95978)
This has been causing a lot of confusion when contributors have been
debugging tree shaking, eg.
https://github.com/vercel/next.js/issues/95698 and the linked issues.
Turbopack's standard tree-shaking features are controlled through the
`turbopackRemoveUnusedExports` and `turbopackRemoveUnusedImports`
options which default to true.
2026-07-20 18:14:11 -07:00
Sam PoderandGitHub e71934a17f [turbopack] Drop unused exports from a CJS module (#95716)
This PR introduces two new things: a pass of the analyzer that
recognises exports from CJS modules (in the form of `exports.foo`, see
`analyze_cjs_exports`) and then a CodeGen that reads `export_usage_info`
to remove CJS exports that are not used.

This PR does not add anything additionally to `export_usage_info`, so
this means that we only trim from modules that are imported with `import
{ foo } from "bar"`. The CodeGen depends on the output of
`analyze_cjs_exports` for the locations of exports, whether it is safe
to drop exports (its pretty conservative), and also whether `__esModule`
is present.
2026-07-20 15:51:46 -07:00
Sam PoderandGitHub 42ccb2eede [turbopack] Clean up server_chunking_context (#95908)
https://github.com/vercel/next.js/pull/95539/changes#r3598998062
2026-07-17 21:44:26 +00:00
Sam PoderandGitHub 20f5809320 [turbopack] Don't SSR on pages only navigated to through a soft nav (#95539)
This PR essentially implements:
https://github.com/vercel/next.js/pull/95539#discussion_r3555050929.
Will add more a description soon.
2026-07-17 13:10:54 -07:00
Sam PoderandGitHub 153bf8ac5f [turbopack] Only ship pages-router routes in the client chunk-group bootstrap manifest (#94671)
This is a small performance thing Claude pointed out that I've added:

```
// Only the pages-router `route-loader` reads `__TURBOPACK_PAGE_BOOTSTRAP`. App routes
      // navigate via flight and never use it, so skip app entries to keep `_buildManifest.js`
      // (loaded on every page) small.
```
2026-07-16 00:35:23 -07:00
Sam PoderandGitHub f282e478b4 [turbopack] Inline the chunk group bootstrap in Next.js to drop the per-route runtime (#94666)
This PR builds on top of the other PRs in this stack, it is the PR that
switches to inlining the bootstrap code and dropping the runtime file
that each route had.
2026-07-16 00:35:23 -07:00
Sam PoderandGitHub b2e7c8bed0 [turbopack] Add chunk_group_bootstrap_params and the chunk-loading global to the build manifest (#94663)
This PR inserts the bootstrapping parameters into the build manifest so
they can then be inlined in
https://github.com/vercel/next.js/pull/94666.
2026-07-16 00:35:22 -07:00
Sam PoderandGitHub 856765a176 [turbopack] Add registerEntry() to handle inline bootstrapping (#94664)
In https://github.com/vercel/next.js/pull/94631, we started pushing
items to `globalThis["TURBOPACK"]` that were of a slightly different
shape. See the comment on
https://github.com/vercel/next.js/pull/94631#issue-4626372740.

This PR adds a new method `registerEntry()` that is able to take that
new shape (the object without the file name as an argument) and still
instantiate the module + load the other chunks.
2026-07-16 00:35:22 -07:00
Sam PoderandGitHub 6d9e6f0533 [turbopack] Add inline_chunk_group_bootstrap to BrowserChunkingContext and chunk_group_bootstrap_params to ChunkGroupResult (#94661)
We used to generate the runtime file for each `ChunkGroupResult` in
`evaluated_chunk_group()`, I've added `chunk_group_bootstrap_params`
which are the parameters that were unique to each chunk group. These are
inlined into each page's HTML in
https://github.com/vercel/next.js/pull/94666. They look like:

```javascript
{"otherChunks":["output/0_9x_turbopack-tests_tests_snapshot_basic-tree-shake_export-namespace_input_1mqgz2-._.js"],
"runtimeModuleIds":["[project]/turbopack/crates/turbopack-tests/tests/snapshot/basic-tree-shake/export-namespace/input/index.js [test] (ecmascript)"]}
```

Whether or not the inline-able code is generated is based on the
`inline_chunk_group_bootstrap` which is still false in all cases at the
moment but will be turned to true in
https://github.com/vercel/next.js/pull/94666.
2026-07-16 00:35:21 -07:00
Sam PoderandGitHub 6faa5536bf [turbopack] Create a chunk_group_bootstrap_params() function (#94631)
This method generates the params for this code (random example for
reference):

```javascript
(globalThis["TURBOPACK"] || (globalThis["TURBOPACK"] = [])).push([
    "output/1ece_tests_snapshot_basic-tree-shake_export-namespace_input_index_0tbr8qy.js",
    {"otherChunks":["output/0_9x_turbopack-tests_tests_snapshot_basic-tree-shake_export-namespace_input_1mqgz2-._.js"],"runtimeModuleIds":["[project]/turbopack/crates/turbopack-tests/tests/snapshot/basic-tree-shake/export-namespace/input/index.js [test] (ecmascript)"]}
]);
```
This is a pretty small change on its own (no functionality change), it
makes it a separate method so that it can be called in future PRs. I
will flag that the shape for inline-d bootstrap code will be slightly
different (further up the stack). Because it isn't a chunk itself it
does not have the first item in the array
(`output/1ece_tests_snapshot_basic-tree-shake_export-namespace_input_index_0tbr8qy.js`):

```javascript
(globalThis["TURBOPACK"] || (globalThis["TURBOPACK"] = [])).push({"otherChunks":["static/chunks/node_modules_react-dom_82bf80._.js","static/chunks/_4f9bce._.js","static/chunks/pages_index_tsx_5a9e._.js"],"runtimeModuleIds":[96168]});
```

This item only has `otherChunks` and `runtimeModuleIds`. New code is
added in https://github.com/vercel/next.js/pull/94664 to handle this
different shape.
2026-07-16 00:35:21 -07:00
Sam PoderGitHubBenjamin Woodruffvercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
8aefb1c71a [turbopack] Create a shared asset with browser runtime code (#94586)
The goal of this PR is to extract all the generic runtime code into one
asset that is shared by each route. This makes the per-route runtime
tiny, which will be removed in later PRs in this stack.

The diff is unfortunately massive, however, these are almost all because
of the snapshots.

---------

Co-authored-by: Benjamin Woodruff <[email protected]>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
2026-07-16 00:35:20 -07:00
Sam PoderandGitHub b7e3285893 [turbopack] Generate component chunks for each merged group to increase cache hits (#95261)
This is a WIP PR to attempt to achieve this:

<img width="653" height="291" alt="Screenshot 2026-06-28 at 11 53 17 pm"
src="https://github.com/user-attachments/assets/68a78003-b76c-40e2-bbea-ca581f468eef"
/>

This does involve emitting extra chunks, though in theory, they are
already being emitted.

I'm reusing the `module_chunks` field on `OutputChunkRuntimeInfo`:

```rust
/// List of paths of chunks containing individual modules that are part of
/// this chunk. This is useful for selectively loading modules from a chunk
/// without loading the whole chunk.
pub module_chunks: Option<ResolvedVc<OutputAssets>>,
```

This does cause larger build sizes:

| App | Client chunk files (baseline → PR) | Difference | Client chunk
bytes (baseline → PR) | Difference |
| --- | ---: | ---: | ---: | ---: |
| vercel-site | 3651 → 5245 | +1594 (43.7%) | 344.5 MiB → 418.4 MiB |
+73.9 MiB (21.5%) |
| vercel-marketing | 2242 → 2634 | +392 (17.5%) | 163.5 MiB → 180.7 MiB
| +17.2 MiB (10.5%) |
| **Total** | **5893 → 7879** | **+1986 (33.7%)** | **508.0 MiB → 599.1
MiB** | **+91.1 MiB (17.9%)** |
2026-07-15 15:57:11 -07:00
Sam PoderandGitHub 98c9754ad8 [turbopack] Tell agents not to mention next.config.js options (#95825)
Common piece of feedback I've gotten on my PRs. Would be nice to have it
in the `AGENTS.md` as I often use Claude to one shot the plumbing of a
flag.

though - maybe we should have a turbopack-specific AGENTS.md?
2026-07-15 11:57:45 -07:00
Sam PoderandGitHub f1185f1341 [turbopack] Switch make_production_chunks to use floats (#95749)
We've talked about making this switch, making this switch because
without it the diff for https://github.com/vercel/next.js/pull/95157 was
very difficult to read and follow.
2026-07-13 16:23:33 -07:00
Sam PoderandGitHub 4b1c74a3bc Fix support for custom-media-queries in LightningCSS (#95689)
Closes https://github.com/vercel/next.js/issues/95677. Added an e2e test
that roughly matches the original issue and tests that it works.
2026-07-10 19:07:17 +00:00
Sam PoderandGitHub 359d66817a [turbopack] Simplify service worker e2e tests (#95672)
This PR simplifies the way that we're preserving
`registration.updateViaCache` after the code-gen that rewrites the
options in the service worker registration
2026-07-10 10:50:03 +02:00
Sam PoderandGitHub 9317a54179 [docs] Update "Handling a Custom Service Worker" in the CRA migration docs (#95434)
This is a follow-up to https://github.com/vercel/next.js/pull/95431; I
think we can directly mention this in this guide to make people's lives
easier (the PWA guide is quite long for this small chunk of code)
2026-07-09 23:05:00 -07:00
Sam PoderGitHubvercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
affd86f0bf [docs] Update progressive web apps documentation for new service worker syntax (#95431)
We recently rolled out this new syntax for bundling service workers with
Turbopack:

```
await navigator.serviceWorker.register(new URL('../lib/service-workers/1.js', import.meta.url), ...)
```

This PR updates the documentation that previously mentioned service
workers.

---------

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
2026-07-09 23:05:00 -07:00
Sam PoderandGitHub c594b9fb4b [turbopack] Compile service workers registered from pages router pages (#95583)
Thanks @mischnic for catching this! I've added a test to ensure that
service workers work in pages router.
2026-07-09 16:26:18 -07:00
Sam PoderandGitHub 29bbdfbdee [turbopack] Output service workers to /_next/static/ (#95554)
This should fix deployment tests because this now re-uses Vercel's
support for static JS files
2026-07-09 15:17:50 -07:00
Sam PoderandGitHub 2e1eb8857d [turbopack] print Turbopack warnings after SSG (#95430)
Picked up this from "Turbopack Work Ideas 2026":

> 4. (S) Turbopack Warnings are before SSG Errors
>    - Solution: print Turbopack warnings after SSG

I think this is a pretty bare-bones fix for this - let me know if the
idea was to do something different!

See an example output:


```
✓ Running next.config.js took 6ms
▲ Next.js 16.3.0-canary.75 (Turbopack)

  Creating an optimized production build ...
✓ Compiled successfully in 889ms
  Running TypeScript ...
  Finished TypeScript in 38ms ...
  Collecting page data using 5 workers ...
  Generating static pages using 5 workers (0/4) ...
Error occurred prerendering page "/boom". Read more: https://nextjs.org/docs/messages/prerender-error
Error: boom during prerender
    at <unknown> (app/boom/page.js:2:9)
  1 | export default function Page() {
> 2 |   throw new Error("boom during prerender")
    |         ^
  3 | }
  4 | {
  digest: '1942497547'
}
Export encountered an error on /boom/page: /boom, exiting the build.
⨯ Next.js build worker exited with code: 1 and signal: null
Turbopack build encountered 1 warning:
./app/join-cwd.js:4:10
Warning: Dynamic filesystem access causes tracing of the whole project
  2 |
  3 | export default function (f) {
> 4 |   return path.join(process.cwd(), f)
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  5 | }
  6 |

Static analysis determined that this filesystem access causes the whole project to be traced and included in the output.
This is usually unintentional and leads to all source files (including the public folder) to be deployed as part of the server code.
This can slow down deployments or lead to failures when size limits are exceeded.
To resolve this, you can
- make sure they are statically scoped to some subfolder: path.join(process.cwd(), 'data', bar), or
- only use them in development, or
- add ignore comments: path.join(/*turbopackIgnore: true*/ process.cwd(), bar), or
- remove them.

Import trace:
  Server Component:
    ./app/join-cwd.js
    ./app/page.js
```
2026-07-08 10:15:33 -07:00
Sam PoderandGitHub bb9af37519 [turbopack] Rename rscEndpoint to rscHmrEndpoint (#95538)
I'm introducing a new endpoint that doesn't compile SSR when doing a
page navigation, I'd like to call it a `rscEndpoint` but currently this
name is taken. `rscEndpoint` appears to be used for detecting changes
for HMR so I've renamed it to `rscHmrEndpoint` so I can use that name
for the new `rscEndpoint`.
2026-07-07 18:19:40 -07:00
Sam PoderandGitHub b6ebce52f9 [skills] Update gh-stack skill (#95541)
Claude Code keeps trying to pass the `--draft` option to GitHub when I
use this stack. However, that option was removed. I've updated this
skill to match
https://github.com/github/gh-stack/blob/main/skills/gh-stack/SKILL.md
using `npx skills`
2026-07-06 18:54:57 -07:00
Sam PoderandGitHub 6065bf6fcf [turbopack] Add E2E tests for service workers (#94924)
This PR adds E2E test for the new service worker syntax introduced in
https://github.com/vercel/next.js/pull/94923. The tests do the
following:

`service-worker-multiple` tests that you can not have multiple service
workers with the same scope. this is because their file names would
clash; additionally, this is generally a bad practise.

`service-worker-scopes` tests that you register multiple service workers
with different scopes and they will be different files

`service-worker` tests that service workers work when registered using
the new syntax.
2026-07-06 08:57:17 -07:00
Sam PoderandGitHub 0d1dc0232e [turbopack] Discover service workers in the Turbopack analyzer (#94923)
This PR is the PR that "turns on" the service worker feature, this is
because in the analyzer we are now looking for code like:

```
await navigator.serviceWorker.register(new URL('../lib/service-workers/1.js', import.meta.url), ...)
```

And transforms it into registering the URL that Next.js will now serve
based on scope.

URL scheme is:

```
scope: "/" -> `sw.js`
scope: "/offline/mode" -> `sw-offline-mode.js`
```

etc.

It also inserts the `ServiceWorkerEntryModule`s so they can be
discovered by `next-api` (see
https://github.com/vercel/next.js/pull/94922).
2026-06-29 16:20:38 -07:00
Sam PoderandGitHub 38f75c76b5 [turbopack] Discover ServiceWorkerEntryModules in next-api and compile + serve those service workers (#94922)
This is the big change in the `next-` part of the repo to support this.
The code goes through the module graph to find any service workers that
need compiling and then compiles them. It also has the code changes to
`packages/next/src/server/lib/router-utils/filesystem.ts` that serve
these files on their respective routes.
2026-06-24 22:23:42 -07:00
Sam PoderandGitHub 032855e3b2 [turbopack] Create ServiceWorkerEntryModule and service_worker_chunk_filename (#94921)
This is what will be inserted by the analyzer into a page's module
graph: https://github.com/vercel/next.js/pull/94923. This allows
`next-api` to discover these modules (see
https://github.com/vercel/next.js/pull/94922) and compile them.
2026-06-24 22:15:43 -07:00
Sam PoderandGitHub d332405727 [turbopack] Create ServiceWorkerChunkingContextOptions in next-core (#94920)
Broke these changes out into a separate PR but these are needed later
down the stack in https://github.com/vercel/next.js/pull/94922 which
changes `next-api`.
2026-06-24 17:36:31 -07:00
b132f1dc0e Consider merging chunks when overlap == 1 (#95102)
See https://github.com/vercel/next.js/pull/95026/changes#r3462912703; as
discussed in office hours

---------

Co-authored-by: Tobias Koppers <[email protected]>
2026-06-23 16:43:58 -07:00
Sam PoderandGitHub e169a26fdf [turbopack] Sort modules in files by path not ID (#95065)
Sorting-by-ID was flagged as bad for g-zipping:

> This sorts by chunk item id.
> Tobias has been saying that this is bad, because it gzips worse
> We should be sort by path, so that similar modules stay together. E.g.
if you have many similar icon modules
> Sorting by chunk item results in random order

Switched to sorting by path!
2026-06-22 15:34:18 -07:00
Sam PoderandGitHub 47ad350d3a [turbopack] Sort modules in chunks to reduce duplicates (#94961)
# What
When building a chunk, lay out chunk_items in sorted order.

# Why
There are a number of ways turbopack can create chunk groups with
identical sets of chunk items, but different chunk parents or different
availability info. This means we cannot trivially deduplicate these
chunks. Heuristics inside of `make_production_chunks` means we also do
not always observe 'trivial merge opportunities' there either.

By canonically sorting these chunks we can ensure that we produce the
same content and thus select the same filenames.

See https://vercel.slack.com/archives/C09R44U5HQW/p1781814781879439, on
https://vercel.com/academy when switching to Turbopack, we started
shipping duplicate chunks that only differed by order. Now, module
ordering is fixed in chunks so that the file name hashes are consistent
and we don't ship duplicates.

For vercel-academy,

before:

| | Client JS | Client CSS | Client Source Maps | Server Bundled JS |
Server Unbundled | Server Source Maps |
| --------- | ------------------ | ------------------- |
------------------ | -------------------- | --------------------- |
-------------------- |
| **Total** | 41 files / 7.24 MB | 5 files / 580.57 KB | - | 286 files /
18.30 MB | 1700 files / 48.26 MB | 286 files / 78.63 MB |[4:54
PM]to:[4:54 PM]## Totals

after:

| | Client JS | Client CSS | Client Source Maps | Server Bundled JS |
Server Unbundled | Server Source Maps |
| --------- | ------------------ | ------------------- |
------------------ | -------------------- | --------------------- |
-------------------- |
| **Total** | 36 files / 4.43 MB | 5 files / 580.57 KB | - | 286 files /
18.30 MB | 1700 files / 48.26 MB | 286 files / 78.63 MB |
2026-06-19 00:07:59 -07:00
4b519bee13 [turbopack] Retry chunk loading on failure (#94918)
This is a trimmed down PR from
https://github.com/vercel/next.js/pull/88107, it achieves the same thing
(with much of the same code). However, it is Turbopack-only. This is
because the Webpack portions of that PR required patching React - we can
return to this in a different PR or I guess people could use a plugin?
(https://github.com/mattlewis92/webpack-retry-chunk-load-plugin)

---------

Co-authored-by: Jimmy Lai <[email protected]>
2026-06-18 10:00:01 -07:00
Sam PoderandGitHub e0c7efcd85 [turbopack] Update algebra in chunking algorithm (#94873)
Follow-up to conversation in the Turbopack office hours this week. I've
gone through and update that maths to match what the comment seemed to
be intending. The biggest change is the change from request = 3 to
request = 2 which trickles down to a lot of the equations.
2026-06-16 16:04:11 -07:00
d0f25a0ff1 [turbopack] Allocate Effects in an arena (#94614)
This is a follow-up PR to
https://github.com/vercel/next.js/pull/94297#discussion_r3351215197;
also adds an `append()` method to `BumpVec`.

---------

Co-authored-by: Benjamin Woodruff <[email protected]>
2026-06-15 22:08:41 -07:00
47ee8d7c9e Add a message to AGENTS.md about super:: imports. (#94733)
Co-authored-by: Benjamin Woodruff <[email protected]>
2026-06-15 22:09:12 +00:00
Sam PoderandGitHub 6d5dc2633d Move errorOnce imports into statements guarded by process.env.NODE_ENV === 'development' (#94782)
See https://github.com/vercel/next.js/pull/94781; cc @icyJoseph
2026-06-15 15:03:38 -07:00
Sam PoderandGitHub 588bb452fd Move warnOnce imports into statements guarded by process.env.NODE_ENV === 'development' (#94781)
The context is this message from @icyJoseph:

```
I wonder if you've also look a little at DCE? This module, https://nextjs.org/_next/static/immutable/chunks/2uxebeysomvt2.js ships every time...
```

https://nextjs.org/_next/static/immutable/chunks/2uxebeysomvt2.js
corresponds to this `warnOnce` helper:
https://github.com/vercel/next.js/blob/643e34a59183f90ae9d2db9a64673781316ce786/packages/next/src/shared/lib/utils/warn-once.ts#L1

That doesn't do anything because Turbopack appears to be attempting to
remove unused imports before doing dead-code removal. If the imports are
moved though into the `(process.env.NODE_ENV !== 'production')` guard,
this module won't get shipped into production. The patch to do this is
pretty small so this PR does that, I'm going to look into whether its
possible to reorder it so unused imports are looked at after dead code
is removed.
2026-06-15 11:54:51 -07:00
Sam PoderandGitHub da451d7593 [turbopack] Rename variables in path_join and add tests (#94625)
`results` became `segments` and `results_final` became `locked_prefix`,
the `segments` name I'm not too attached to, I'm just trying to
communicate that that vector stores the "other" parts of the path that
aren't fixed and can change. Meanwhile, the `locked_prefix` has to be at
the start - it contains variables we don't know the value of (and
anything that came before it) or also the starting "." or "..". I think
this is what "final" was originally trying to communicate.

I also had Claude generate tests for this function based on the
pre-existing tests in the `node` repo. There was some divergence related
to absolute paths if anyone is curious:

| Input | turbopack | node |
|---|---|---|
| `["", "foo"]` | `/foo` | `foo` |
| `["", "", "foo"]` | `/foo` | `foo` |
| `["", "..", "..", "/foo"]` | `/../../foo` | `../../foo` |
| `["/"]` | `` (empty) | `/` |
| `["/", "."]` | `` (empty) | `/` |
| `["/foo", "../../../bar"]` | `/../../bar` | `/bar` |
| `["/", ".."]` | `/..` | `/` |
| `["/", "..", ".."]` | `/../..` | `/` |
| `["", "."]` | `` (empty) | `.` |
| `[""]` | `` (empty) | `.` |
| `["", ""]` | `` (empty) | `.` |

I can follow-up and fix these as well if that'd be worthwhile, though I
think it was mentioned that we don't support absolute paths
2026-06-10 17:45:01 -07:00
Sam PoderandGitHub ef65efdfda Call register_assignment_scope() in visit_export_default_expr() and visit_export_default_decl() (#94581)
Talked IRL, we found that calling this in `visit_export_default_expr()`
cut down the `module-cost` benchmark on ESM execution by about 10ms
(approx. 35%).

Doing this unlocks a codegen optimization to avoid arrow functions, this
special case was needed to support `export default ...` which is a kind
of declaration all on its own.
2026-06-10 15:01:02 -07:00
Sam PoderGitHubvercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
539efa8cad [turbopack] Only ship top-level async support in the runtime when needed (#94376)
There's a lot of code in the runtime that enables top-level async
support. We may not always need to ship this. It makes up approximately
10% of the runtime. This PR separates this code into a separate module:
`async-module.ts`. This is included in the runtime based on this
condition:

```rust
!self.module_graph.async_module_info().await?.is_empty()
```

---------

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
2026-06-04 09:42:29 -07:00
Sam PoderandGitHub 418293859c Remove dead code from JsValue::Binary in is_truthy (#94165)
This is another thing flagged by @bgw's Claude:

```
  3. Dead duplicate match arm in is_truthy for StrictEqual (lines 2469–2494)

  ( … StrictEqual, Constant(a), Constant(b)) if a.is_value_type() => Some(a ==
  b),
  ( … StrictEqual, Constant(a), Constant(b)) if a.is_value_type() => { /* 
  same-type check */ }

  Identical pattern + guard. The second arm — the only one that handles
  cross-type equality conservatively — is unreachable. Looks like a half-applied
   fix.
``` 

I've removed the second arm with a double type check. Tests appear to
still pass and I can't think of a case where we'd hit the second arm.
2026-05-28 19:51:27 -07:00