## Summary
Enable the project-local TypeScript CLI checker by default during `next
build`, while preserving `experimental.useTypeScriptCli: false` as an
opt-out to the TypeScript compiler API. Update the TypeScript 7
guidance, diagnostics, tests, and documentation to match the new
default.
## Verification
- `pnpm exec jest --runTestsByPath test/unit/isolated/config.test.ts`
- Pre-commit lint-staged checks passed (Prettier and ESLint)
- Not run: `pnpm test-start-turbo
test/production/app-dir/typescript-cli/typescript-cli.test.ts` (isolated
fixture dependency installation was blocked by unavailable npm registry
access)
- Not run: `pnpm --filter=next types` (repository has pre-existing
unrelated TypeScript errors)
<!-- NEXT_JS_LLM -->
How long do you expect this to wait at worst (before failing)?
```js
await retry(async () => {
expect(await browser.elementById('never-appears').text()).toBe('x')
}, 15_000)
```
After this PR, the answer is 15 seconds. (Well, technically, 15 seconds
+ whatever last attempt is, so 20.)
But before this PR, the answer was 3 minutes.
## What?
There's a bug (IMO) in `retry`. Instead of waiting for the time you give
it, it counts tries.
For example, `retry(fn, 15_000)` reads as "keep trying for 15 seconds".
However, what it actually was doing is divide 15000 by the 500ms
interval and try 30 times, sleeping 500ms in between every try.
This is a problem because **it wasn't measuring the stall time of each
try itself.**
When a try is cheap, it doesn't matter. But a try that looks for an
element in the browser isn't cheap: if the element isn't there, the
selector waits 5 seconds before giving up.
In the example above, each try sits in the selector for its full 5
seconds, and there are 31 of these intervals, plus the 15 seconds of
sleeping in between. This ends up waiting 170103ms, i.e. three minutes
for a wait that says 15 seconds.
Jest kills the test at its own timeout long before that, and the actual
error gets thrown away. What's left is `Exceeded timeout of 120000 ms`,
which doesn't say which assertion never came true. In the one I measured
it was `page.waitForSelector: Timeout 5000ms exceeded`. My agent lost an
hour trying to make sense of this twice now.
## Fix
We count time now. After the fix, the retry watches the clock and stops
when its time is up. It still always makes at least one try. The
interval no longer has to divide the duration, because nothing counts
steps any more. If the last try is slow, we're gonna wait for that last
try to end (so the timeout is still not *strictly* respected) so that we
can show the error in the correct test.
Cheap waits behave exactly as before. In my local run, a try that had to
repeat cost 251ms at the median, 459ms at the worst. So the only calls
this changes are ones that were already failing, and those now fail with
their own error instead of a test timeout. One flaky dev test that used
to die at 120 seconds with nothing to show now fails in 19.5 seconds
saying `Failed to retry within 15000ms (waited 16022ms)`, next to nine
tests that take 2.2 seconds each.
I added some regression tests. Since it's a test helper, a unit test
seemed fine.
## What this means for existing tests
The catch is that a call whose tries are slow now gets less time than it
used to, and this helper has 2110 call sites, so CI is the real check.
It found one file: the hash scroll tests in `navigation.test.ts` wait
with the default 3 seconds, and each try there costs 150-300ms on a
runner, so they were really getting about 5. They need more than 3, so
they failed on two dev shards. I gave them 10.
If this is green, we can merge it in isolation from the rest of the
stack.
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
We were missing some test coverage for image optimization. This PR adds
tests for many missing image formats.
Additionally, this PR also ensures the sharp encoders match that
allowlist from detectContentType() such that future versions of sharp
that enable new encoders by default are not automatically enabled in
next/image.
Practically, there is no change to the end user because
detectContentType() runs before sharp so that is the true allowlist, but
this gives us a little more control to align the sharp allowlist as well
(since sharp can release new features in semver minor).
## Summary
Adds `experimental.devMemoryThresholdRestart`, defaulting to `true`, so
projects can set it to `false` to opt out of the development server
restart that occurs when V8 heap usage exceeds 80%. The option has no
effect on `next start`.
## Verification
- Focused config-loading and memory-threshold unit suites passed
- `pnpm --filter=next build` completed JavaScript compilation, then
failed on pre-existing `sharp` and `@vercel/detect-agent` declaration
errors unrelated to this change
<!-- NEXT_JS_LLM -->
- `detectContentType` test was too aggressive about asserting completion
in 1ms, let's give it several tries.
- We weren't correctly setting up pnpm overrides in tests, which breaks
things whenever canary comes out.
### What?
Fire-and-forget `setTimeout` calls in the Edge runtime sandbox (e.g.
from middleware) are tracked forever by Next.js's sandbox
`TimeoutsManager`, even after the timeout has run. In a long-lived
standalone/self-hosted server this makes tracked timeout resources grow
monotonically, producing step-like heap growth and eventual OOM.
### Why?
`TimeoutsManager.add()` pushes each timeout id into a `resources` array.
When a one-shot timeout fires naturally, `webSetTimeoutPolyfill`'s
`finally` clears the underlying Node timer (the workaround for
nodejs/node#53335) but never removes the id from the manager. The id is
dropped only if user code calls `clearTimeout(id)` or the whole module
context is torn down (`clearModuleContext`, introduced in #57235). The
natural-completion path — module context stays alive, timeout finishes
on its own — was missing, so ids accumulate for the lifetime of the
process.
### How?
- `TimeoutsManager.create()` now wraps the callback so the timeout
untracks its own id once it has run. The Node-timer workaround and the
`this`/args binding in `webSetTimeoutPolyfill` are preserved.
- Refactored the base `ResourceManager.remove()` into `untrack()` (stop
tracking) + `destroy()`, so natural completion can release tracking
without a redundant `clearTimeout`.
- Intervals are intentionally unchanged: they fire repeatedly and must
remain tracked until `clearInterval`/`removeAll`.
- Added a unit test covering natural release, no monotonic growth across
repeated rounds, the `clearTimeout`/`remove` path, callback `this`/args
binding, and that intervals stay tracked.
Verified: `pnpm --filter=next types` clean, prettier/eslint clean, and
the new unit test fails on the current behavior (reverting only the
release logic) and passes with the fix.
Fixes#95094
---
**Update:** this branch now lands @berry95's commit from #94794 first
(`Set`-based resource tracking, which also makes `untrack`/`remove`
O(1)), with the fix rebased on top.
---------
Co-authored-by: Berry Kim <wlsdud40@gmail.com>
Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
Recreation of https://github.com/vercel/next.js/pull/95370
### What's the problem?
A `POST` (or `PUT`/`PATCH`) request that passes through middleware
returning `NextResponse.next()` hangs indefinitely when the downstream
handler reads the body via Node's `Readable.toWeb()`. The request never
completes and eventually times out.
Reproduction: https://github.com/abir-taheer/next-js-readable-stream-bug
### Root cause
When middleware runs, `runMiddleware` clones the request body and later
calls `finalize()`, which grafts the buffered stream back onto the
original `IncomingMessage` via `replaceRequestBody()`
(`packages/next/src/server/body-streams.ts`). `replaceRequestBody`
copies the buffered stream's enumerable properties onto the request.
The buffered stream (`p2`) was a `PassThrough` — a `Duplex` — so its
writable-side internals (`_writableState` plus the enumerable `Writable`
methods like `write`/`end`) were copied onto the `IncomingMessage`.
Because that `_writableState.finished` is `false`, Node stream utilities
that inspect it — including `Readable.toWeb()`, which uses
`finished()`/end-of-stream detection — treat the request as a still-open
writable stream and wait forever.
`NextResponse.rewrite()` is unaffected (it builds a new internal request
and skips this path), and `GET`/`HEAD` requests are fine because there
is no body to clone.
### The fix
`p2` is only ever fed with `.push()`, so it never needs a writable side.
Making it a plain `Readable` instead of a `PassThrough` keeps the
finalized request a pure `Readable`, so `Readable.toWeb()` (and any
other duck-typing based on `_writableState`) behaves correctly. No
behavior change for the existing consumers, which only read the stream.
### Testing
Added `test/unit/body-streams.test.ts`, which drives the real clone →
`finalize()` flow and asserts the finalized request:
- is no longer writable (`_writableState` is `undefined`), and
- is fully consumable via `Readable.toWeb()` (this hangs before the
fix).
Fixes#95335
<!-- NEXT_JS_LLM_PR -->
Co-authored-by: UditDewan <udit.dewan21@gmail.com>
Co-authored-by: Baradhan-Madhu <26barum@gmail.com>
## Summary
- add `experimental.useTypeScriptCli` so projects can explicitly run
their local `tsc` during `next build`, including TypeScript 7 while the
legacy JavaScript API is unavailable
- preserve the TypeScript API backend as the default, with TypeScript
6-compatible dependency installation and actionable TypeScript 7
migration guidance
- load effective compiler metadata through `tsc --showConfig`, preserve
inherited path origins, stream native diagnostics, redirect incremental
state, and forward worker termination to the compiler process tree
- document the experimental behavior and cover TypeScript 6/7, both
bundlers, raw diagnostics, full-project checking, dependency selection,
and cleanup
## Demo
```
pnpm build
> cna@0.1.0 build /Users/timneutkens/projects/sandbox/cna
> next build
▲ Next.js 16.3.0-canary.81 (Turbopack)
✓ Running next.config.ts took 23ms
- Cache Components enabled
- Experiments (use with caution):
✓ useTypeScriptCli
Creating an optimized production build ...
✓ Compiled successfully in 3.9s
Running TypeScript ...
app/page.tsx:8:3 - error TS2322: Type 'string' is not assignable to type 'number'.
8 return count++ + "abc";
~~~~~~
Found 1 error in app/page.tsx:8
Failed to type check.
Next.js build worker exited with code: 1 and signal: null
ELIFECYCLE Command failed with exit code 1.
```
## Verification
- `pnpm test-start-turbo
test/production/app-dir/typescript-cli/typescript-cli.test.ts`
- `pnpm test-start-webpack
test/production/app-dir/typescript-cli/typescript-cli.test.ts`
- `pnpm test-start-turbo
test/production/ci-missing-typescript-deps/index.test.ts`
- `pnpm test-dev-turbo
test/development/typescript-native-preview/index.test.ts`
- `pnpm test-start-turbo test/production/typescript-basic/index.test.ts`
<!-- NEXT_JS_LLM_PR -->
Adds a global config option called `partialPrefetching` that changes the
behavior of `<Link prefetch={true}>`.
When Partial Prefetching is enabled, only Cache Components are included
in a prefetch response. Dynamic data is omitted. This is the same idea
as Partial Prerendering for initial page loads, now applied to client
navigations.
In the initial Cache Components release, Partial Prefetching was enabled
for any Link that did not explicitly set a `prefetch` prop. However, if
the `prefetch` prop was set to `true`, then the Link retained the old
behavior where the _entire_ page was prefetched, including dynamic data.
This was to ease the migration path for existing apps that already
relied on dynamic data being included in the prefetch.
For new apps that have Cache Components enabled from the start, it's
suggested to also enable `partialPrefetching`.
This PR also adds a per-segment opt-in: a route segment can set
`unstable_prefetch = 'partial'` to enable Partial Prefetching just for
that segment, so apps can migrate gradually before enabling it globally.
<!-- NEXT_JS_LLM_PR -->
### What?
Flips the default `experimental.instantInsights.validationLevel` from
`'manual-warning'` to `'warning'` so Cache Components apps get
instant-navigation validation across all pages by default.
### Why?
`'manual-warning'` only validates pages that explicitly export
`unstable_instant`, so apps see nothing unless they opt in. `'warning'`
is what users have been turning on manually to actually see the feature
(`v0`, `vercel-site`).
### Test coverage
- Unit: `instant-config-normalization.test.ts` pins the framework
default at `'warning'`.
- Integration: new `instant-validation-level-default/` fixture (no
`instantInsights` in config) asserts implicit dev validation fires, and
that build is unaffected.
### Collateral test fixtures
Tests whose intent is unrelated to instant validation
(router-autoscroll, owner-stack, hmr-iframe, next-image,
server-source-maps, etc.) now hit new redboxes/console warnings because
their fixtures incidentally use dynamic data. Each opts out with
`experimental: { instantInsights: { validationLevel: 'manual-warning' }
}` in `next.config`.
### Open question
With no `'off'` / `'info'` tier today, silencing Insights after this
change requires setting `validationLevel: 'manual-warning'`. Is that
acceptable? Should we inform of this anywhere?
<!-- NEXT_JS_LLM_PR -->
### What?
Defaults `experimental.useNodeStreams` to `true` while keeping the
experimental config flag and explicit `false` opt-out available. Removes
redundant dedicated node-stream CI jobs because the ordinary path
exercises Node streams after this change.
Depends on the standalone test compatibility update in #94347, which
intentionally remains outside the Node streams PR stack.
### Why?
Node streams can be exercised through the standard Node.js App Router
test path once they are the default, allowing a smaller rollout before
the larger follow-up that removes the flag and obsolete plumbing.
CI exposed one necessary compatibility fix for this intermediate state:
projects with a `next.config.*` received the new default after raw
experimental-feature processing, leaving the runtime stream selector on
the web implementation while compiled app code selected Node stream
helpers.
### How?
- Set `defaultConfig.experimental.useNodeStreams` to `true`.
- Synchronize `__NEXT_USE_NODE_STREAMS` from the fully resolved config,
including cached and standalone resolved configs, so defaults and
adapter modifications are reflected at runtime.
- Preserve the current public config/schema and explicit `experimental:
{ useNodeStreams: false }` opt-out; edge bundles continue to define the
stream flag as `false`.
- Remove the dedicated node-stream test jobs and now-unused manifest;
the affected streaming assertion compatibility changes are isolated in
#94347.
### Verification
Run before extracting the prerequisite assertion changes into #94347:
- `pnpm --filter=next types`
- `pnpm --filter=next build`
- `IS_WEBPACK_TEST=1 __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true pnpm
test-start-webpack
test/e2e/app-dir/ppr-root-param-fallback/ppr-root-param-fallback.test.ts`
- `IS_TURBOPACK_TEST=1 TURBOPACK_BUILD=1
__NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true pnpm test-start-turbo
test/e2e/app-dir/ppr-root-param-fallback/ppr-root-param-fallback.test.ts`
- `IS_WEBPACK_TEST=1 __NEXT_EXPERIMENTAL_STRICT_ROUTE_TYPES=true pnpm
test-start-webpack
test/e2e/app-dir/use-server-inserted-html/use-server-inserted-html.test.ts`
- `pnpm test-dev-webpack
test/e2e/app-dir/ppr-root-param-fallback/ppr-root-param-fallback.test.ts
test/e2e/app-dir/use-server-inserted-html/use-server-inserted-html.test.ts`
- `pnpm test-dev-turbo
test/e2e/app-dir/ppr-root-param-fallback/ppr-root-param-fallback.test.ts
test/e2e/app-dir/use-server-inserted-html/use-server-inserted-html.test.ts`
- `pnpm test-start-webpack
test/production/app-dir/use-node-streams-env-precedence/use-node-streams-env-precedence.test.ts`
- `pnpm test-start-webpack test/e2e/app-dir/app/standalone.test.ts`
<!-- NEXT_JS_LLM_PR -->
rootParams is now available by default. the flag is removed.
There are still intentional limitations. For instance rootParams cannot
be used in route handlers and Server Actions. The feature will be
expanded with some support for this in the future.
### 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.
Adds `experimental.instantInsights.validationLevel` so an app can opt
into instant validation for all routes by default.
There are two public levels
* `manual-warning`: currently the default. this mode won't enable
instant validation except where you add `export const unstable_instant =
true` into your Layouts, Pages, and Default files
* `warning`: This mode will enable instant validation for all Page and
Default files. You can override this with `export const unstable_instant
= false` in Layouts, Pages, and Default files to tune what navigations
are validated.
In addition this change disables build time validation.
There is a now undocumented way to get back to running build validation
with
* `experimental-error`
* `experimental-manual-error`
The build validation isn't ready for users because we need to overhaul
the setup where you provide cookies and other sample values so we can
run validation without a live session like we do in dev.
To further support this the two remaining object properties that are
also not ready for active use, `samples` and `from` are being updated to
`unstable_samples` and `unstable_from` respectively.
We will continue to iterate on build time validation but may remove
these APIs or alter their semantics as we figure out a more ergonomic
way to support this setup.
In anticipation of multiple levels the object form of `instant` can be
provided a level value `warning` or `experimental-error`. this latter
option is similarly undocumented so documented behavior will only cover
`warning` which is generally a synonym of true unless you are activating
error by default
## For Contributors
### Adding a feature
Adding a new ESLint rule to `eslint-plugin-next` reporting
`location.href = [relative URL]` or `location.assign([relative URL])`
usage.
For some reason, some LLM models prefer this incorrect pattern, and
avoid `redirect()` or `useRouter().push()` at all costs. But at least
many of them do respect ESLint, by adding a new rule we can enforce
correct behavior.
The error documentation and unit test cases are also added.
---------
Co-authored-by: Joseph <sephxd1234@gmail.com>
Co-authored-by: Benjamin Woodruff <benjamin.woodruff@vercel.com>
In a previous PR https://github.com/vercel/next.js/pull/88183, we added
`images.maximumResponseBody` but in only applied to external images.
This PR ensures the same config also applies to internal images.
## Summary
Generate type definitions for `next/root-params` from App Router root
layouts.
This adds a `root-params.d.ts` file under `.next/types` during build,
`next dev`, and `next typegen`, so imports from `next/root-params` get
accurate autocomplete and type checking.
## What Changed
- Collect root param metadata from layout routes in the route types
manifest
- Generate `.next/types/root-params.d.ts` for `next/root-params`
- Emit root param types in `next build/dev/typegen`
- Infer `undefined` only when a param is not present in every root
layout
- Model mixed root param shapes correctly as unions
- For example, `string | string[] | undefined` when the same param name
appears with different shapes across roots
- Remove stale `root-params.d.ts` when the feature is disabled or there
are no root params
## Why
`next/root-params` already works at runtime, but its types were not
tailored to the current app.
This change closes that gap by generating declarations from the app's
actual root layouts. Required params stay required for single-root apps,
while multi-root apps correctly expose params that may be missing or
have mixed segment shapes.
## Testing
- Added focused unit tests for root param type generation
- Added a typecheck e2e test for generated `root-params.d.ts`
### What?
Add `experimental.swcEnvOptions` to expose SWC's preset-env `env`
configuration options — including `mode`, `coreJs`, `include`,
`exclude`, `skip`, `shippedProposals`, `forceAllTransforms`, `debug`,
and `loose`.
### Why?
Currently Next.js only passes `env.targets` (derived from browserslist)
to SWC for **syntax downleveling**, but does not expose the polyfill
injection capabilities that SWC already supports. This means:
- Users who need automatic core-js polyfills (e.g.
`Array.prototype.at()`, `Promise.withResolvers()`, `Set` methods) have
no built-in way to get them.
- The only workarounds are importing `core-js` globally (which bloats
bundles significantly) or ejecting to Babel with `useBuiltIns: 'usage'`
(which sacrifices SWC's performance benefits).
- In the Babel era, Next.js supported this via `@babel/preset-env`'s
`useBuiltIns` (PR #10574). That capability was lost when Next.js
migrated to SWC.
### How?
A new `experimental.swcEnvOptions` config is added. Its properties are
spread into the `env` block that Next.js passes to SWC for client-side
compilation, alongside the existing browserslist-derived `targets`.
Server-side compilation is unaffected (always targets `node`).
The option surface mirrors [SWC's preset-env
docs](https://swc.rs/docs/configuration/supported-browsers) 1:1, keeping
it familiar and forward-compatible.
```js
// next.config.js
module.exports = {
experimental: {
swcEnvOptions: {
mode: 'usage',
coreJs: '3.38',
},
},
}
```
#### Changes:
config-shared.ts — type definition with JSDoc
config-schema.ts — zod validation
next-swc-loader.ts → swc/options.ts — plumb config into the SWC env
block
Unit tests (6 cases) + e2e test (dev & production)
Related issues #66562, #63104, #74978
Related discussion #46724
---------
Co-authored-by: Benjamin Woodruff <benjamin.woodruff@vercel.com>
This allows specifying a different deployment id for static immutable assets
- [x] rename `getImmutableAssetToken` to `getAssetToken` (because it can be non-immutable as well)
- [x] disable for webpack
Closes PACK-6534
For https://github.com/nextjs/adapter-vercel/pull/25
Part of PACK-6802
```
FAIL Turbopack test/unit/image-optimizer/lru-disk-eviction.test.ts
LRU disk eviction
√ should evict oldest entries on initialization (133 ms)
√ should evict old entries when new entries are set (124 ms)
× should promote entries on get() to prevent eviction (8 ms)
√ should return the same LRU instance on subsequent calls (1 ms)
√ should deduplicate concurrent init calls (2 ms)
√ should handle empty cache directory (2 ms)
√ should handle non-existent cache directory (2 ms)
● LRU disk eviction › should promote entries on get() to prevent eviction
EPERM: operation not permitted, rmdir 'C:\Users\ADMINI~1\AppData\Local\Temp\next-lru-test-dOXRaF\y'
```
Previously
```jsx
import img from "./x.png";
// x is "/_next/static/media/test.abc123.png?dpl=foo"
<Image id="static-unoptimized" src={x} unoptimized />
```
would lead to `/_next/static/media/test.abc123.png?dpl=foo&dpl=foo`
#90420 adds some related tests for the legacy loader as well
This PR adds an LRU disk cache so that reads and writes from the Image
Optimization API will evict old entries based on the value of
`images.maximumDiskCacheSize` configuration.
The LRU ensures that cache reads bump the entry to the top so that they
don't get evicted - only the least recently used entries get evicted.
When `next start` is run, if there is an existing disk cache the we will
replay the files in order to populate the LRU and respect
`images.maximumDiskCacheSize` if it was changed.
If no configuration is provided, default to 50% available disk space.
## Summary
Use `Buffer.indexOf` when available in `uint8array-helpers.ts` for
faster byte sequence scanning. Falls back to the existing manual loop on
non-Node environments.
## Test plan
- [ ] `pnpm test-unit test/unit/stream-utils/uint8array-helpers.test.ts`
passes
`NextURL` intentionally canonicalizes loopback hosts (127.x.x.x, [::1],
localhost) to localhost so that internal URL handling is consistent
across equivalent local origins.
However, we were applying a regex replacement to the entire URL before
parsing, which unintentionally rewrote loopback strings inside query
parameter values, eg `?redirect_uri=http%3A%2F%2F127.0.0.1...`. This is
unexpected for anyone that is explicitly passing through different
loopback values.
This fixes it by first parsing the URL with `new URL()`, and then only
apply the replacement logic to `parsed.hostname` when it is a loopback
host. This leaves search unchanged.
Fixes NEXT-4864
Fixes#89620
Fix `findClosestQuality` returning `0` when the `quality` prop on
`<Image>` is set to a low value (e.g. `quality={1}`), which causes the
image optimization server to reject the request with a 400 error.
### Cause
The `reduce` call in `findClosestQuality` used `0` as its initial
accumulator. Since `0` is not a valid quality value (must be 1-100), it
would win the "closest" comparison for any `quality <= 37` with the
default `qualities: [75]` config. This produced `q=0` in the image URL,
which the server rejects.
### Fix Description
Changed the `reduce` initializer from `0` to `config.qualities[0]`. This
is safe because the early return above already handles the empty array
case, so `config.qualities[0]` is always defined when reached.
Added a unit test for `quality=1` with `qualities=[75]` to verify it
returns `75` instead of `0`.
---------
Co-authored-by: Steven <steven@ceriously.com>
Original [PR](https://github.com/vercel/next.js/pull/88012/) has a bug
that attempted to validate on environment variables. Edited function to
not run validations on environment variables, only on the user provided
deployment id.
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:
## For Contributors
### Improving Documentation
- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide
### Fixing a bug
- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
### Adding a feature
- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
## For Maintainers
- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change
### What?
### Why?
### How?
Closes NEXT-
Fixes #
-->
---------
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
- Adds a `process.env.NEXT_DEPLOYMENT_ID` -> `globalThis.NEXT_DEPLOYMENT_ID` replacement
- On startup, `globalThis.NEXT_DEPLOYMENT_ID` is initialized to `process.env.NEXT_DEPLOYMENT_ID_COMPILE_TIME`
This prepares for https://github.com/vercel/next.js/pull/88761 to replace `process.env.NEXT_DEPLOYMENT_ID_COMPILE_TIME` with reading `<html data-dpl-id>` so that we can remove the env var inlining
However, all of this is only done for Turbopack, because we cannot forward `globalThis.NEXT_DEPLOYMENT_ID` to web workers with Webpack easily.
Based on our metrics, the P99.9 for a source image is 47 MB so we can
adjust the default setting to be much lower (by changing from from 300
MB to 50 MB) in favor of reducing memory.
Flagged behind `experimental.strictRouteTypes`
This fixes issues where cache profiles where not included or routes not validated if `.next` was excluded to squeeze out every bit of TypeScript performance.
It also fixes a redundant type-check since we have separate dev and prod dist folders. If you run `next dev` and (`next build` or `next typegen`), you'd type-check routes and links twice. Now `next-env.d.ts` only exists once where we decide which dist folder to type-check.
Going forward, new Next.js type-checking should be added to `next-env.d.ts`. That file is fully controlled by Next.js and churning it, does not churn users since it's not in version control.
`next-env.d.ts` will be moved into `.next` in a follow-up. Ideally independent of the dist folder i.e. independent of `next dev` vs `next build`.
Closes https://linear.app/vercel/issue/NXT-124/https://github.com/vercel/next.js/pull/87319 but flagged.
In a previous PR https://github.com/vercel/next.js/pull/73184 the `?dpl`
query string was added to local images from static imports but not to
images without the static import.
This was a subtle change based on malte's PR review comment here:
https://github.com/vercel/next.js/pull/73184#issuecomment-2499126751
However, I can't think of reason why we should differentiate these two
since they are both related to the current deployment (as opposed to
remote images which live outside the deployment).
Furthermore, this string matching is causing microfrontends to fail to
add the `?dpl` because they have a path prefix and thus fail skew
protection.
So this PR fixes both problems by loosening the string matching to look
for src images that start with `/`.
Note: new PR description written by @timneutkens
## What?
Improves the detection of Google Tag Manager vs Google Analytics.
As indicated by the author of this PR it was confusing that when you
have Google Tag Manager set up it gives you an error message saying
you're adding Google Analytics even though it's Tag Manager.
I've updated the PR to have a separate message for Google Tag Manager
and expanded the error message docs too.
It also mentioned `next/script` in a bunch of places even though the
error doc recommends `@next/third-parties`. I've updated all cases to
refer to `@next/third-parties` instead.
<details>
<summary>Previous PR description</summary>
Google has multiple products with similar names: the "Google Tag" and
"Google Tag Manager". Google Tag Manager uses `gtm.js` whereas the
"Google Tag" uses `gtag`. Google Analytics previously used
`analytics.js` (<2017) and is now delivered via the "Google Tag".
Google Tag: `www.googletagmanager.com/gtag/js`
Google Tag Manager: `www.googletagmanager.com/gtm.js`
The `next-script-for-ga.ts` rule's matching list
(`SUPPORTED_HTML_CONTENT_URLS`) includes `gtm.js` which means when a
developer adds Google Tag Manager to their NextJS website, they'll
receive the warning and be encouraged to swap from `gtm.js` to `gtag`. A
developer asleep at the wheel (...me) may think...
> I'm using Google Analytics via Google Tag Manager, and NextJS is
suggesting I swap `gtm.js` for `gtag` because it will improve
performance. `gtag` sounds like it is part of "Google Tag Manager" so I
am going to follow the instructions so that my website is faster.
However, `gtag` is **not** compatible with `gtm.js` (they're different
products with similar names and similar purposes) and so making this
change will cause very confusing Google Tag Manager behaviour. I am
suggesting that `gtm.js` is removed completely from the rule, because
anyone using `gtm.js` should not be following these instructions. A
separate rule could be added specifically for Google Tag Manager, that
does not refer to `gtag`.
</details>
---------
Co-authored-by: Tim Neutkens <tim@timneutkens.nl>