Files
Tim Neutkens c7a5a0954d Revert "Revert "refactor: write pages router export files directly to server/pages/"" (#93409)
Re-apply #92735

Before re-landing we need to investigate why the pages router middleware
i18n test fails when deployed.

Update: Investigation/fix:
https://github.com/vercel/next.js/pull/92735#issuecomment-4242978559

Reverts vercel/next.js#92733

---

Cursor plan based on the investigation Claude did:

# Fix i18n export orphan HTML files in server/pages/

## Problem

When i18n is configured, the export path map in `build/index.ts` creates
locale-prefixed entries for every page (e.g., `/en/home/a`,
`/fr/home/a`), but only deletes the non-locale entry from `defaultMap`
for SSG pages. For non-SSG static pages, the non-locale entry (e.g.,
`/home/a`) remains, causing the export worker to render and write orphan
HTML files directly to `server/pages/`.

These orphan files interfere with routing on deployment platforms (e.g.,
a `[teamId]/[slug].html` orphan matches rewritten paths that should
404).

## Root Cause

In [packages/next/src/build/index.ts](packages/next/src/build/index.ts)
at line 2988:

```2988:2991:packages/next/src/build/index.ts
                  if (isSsg) {
                    // remove non-locale prefixed variant from defaultMap
                    delete defaultMap[page]
                  }
```

The `if (isSsg)` guard means non-SSG pages keep their non-locale entry,
which gets rendered and written to `server/pages/` as an orphan file.

## Fix

**Single-line change** in
[packages/next/src/build/index.ts](packages/next/src/build/index.ts):
Remove the `if (isSsg)` guard so `delete defaultMap[page]` always runs
when i18n is enabled.

The locale-prefixed entries are already created in the loop above (line
2981), and they capture the original `page` value via
`defaultMap[page]?.page || page`. The non-locale entry is not needed
after that -- in the old staging-directory flow (`moveExportedPage`), it
was rendered but never moved to the final output directory.

**Safety verification:**

- `updatePagesManifestForExportedPage` (line 3754) is called based on
`combinedPages`, not the export map -- it still correctly replaces
non-locale manifest entries with locale-prefixed ones
- `pageDuration` tracking (line 3745) may return `undefined` for the
non-locale path, but this is typed `number | undefined` and used with
`|| 0` fallback at line 375 of `build/utils.ts` -- purely cosmetic
- SSG pages already have this exact behavior (delete from defaultMap),
so this change makes non-SSG consistent with SSG
- The `ssgPageDurations` invariant check (line 3737-3738) only runs for
SSG pages, which are unaffected

## Test

Add assertions to the existing i18n integration test at
[test/integration/i18n-support/test/index.test.ts](test/integration/i18n-support/test/index.test.ts)
to verify that non-locale-prefixed HTML orphans do not exist in
`server/pages/` after build. The test already has production mode
assertions checking locale-prefixed file existence and manifest
correctness (lines 81-99).
2026-05-26 16:50:38 +02:00

6 lines
113 B
JavaScript

import { NextResponse } from 'next/server'
export function middleware(request) {
return NextResponse.next()
}