Visitar URL original
fix: detect proxy.ts correctly with compound pageExtensions by bmorros94 · Pull Request #93246 · vercel/next.js · GitHub
Skip to content

fix: detect proxy.ts correctly with compound pageExtensions - #93246

Merged
mischnic merged 13 commits into
vercel:canaryfrom
bmorros94:fix/proxy-page-extensions-detection
Sep 14, 2026
Merged

mischnic merged 13 commits into
vercel:canaryfrom
bmorros94:fix/proxy-page-extensions-detection

Conversation

@bmorros94

@bmorros94 bmorros94 commented Apr 25, 2026 •

Copy link
Copy Markdown
Contributor

Maintainer status

  • Current with canary as of 2026-05-18; this branch includes a clean merge from latest origin/canary.
  • Lightweight checks pass; full fork workflows are still action_required until a maintainer approves CI.
  • No unresolved review threads or failing jobs are reported after the refresh.
  • Review focus: shared convention-file basename extraction for proxy, middleware, and instrumentation under compound pageExtensions; TS and Rust paths use the same rule.

Summary

Fixes #85648
Fixes #86303
Fixes #91600
Fixes #85646
Related to #86122
Fixes #92342
Closes #92934

When pageExtensions is set to compound extensions like ['page.ts', 'page.tsx'], proxy files must be named proxy.page.ts. However, the proxy detection logic used file_stem() (Rust/Turbopack) and path.parse().name (JS/webpack), both of which only strip the last extension — so proxy.page.ts becomes proxy.page instead of proxy, and the proxy is never detected.

Root cause: path.parse('proxy.page.ts').name returns 'proxy.page', not 'proxy'. Same issue with Rust's file_stem() which uses rsplit_once('.').

Fix: Use file_name().split('.')[0] (JS) / file_name().split('.').next() (Rust) to extract the first segment before any dot. This correctly returns 'proxy' for both proxy.ts and proxy.page.ts.

The same fileBaseName extraction is also used for middleware and instrumentation convention file detection, fixing compound pageExtensions for those as well.

Changes

  • Turbopack (Rust): crates/next-api/src/project.rs (2 locations) + crates/next-api/src/middleware.rs (1 location)
  • Webpack (JS): packages/next/src/build/index.ts (build-time detection) + packages/next/src/server/lib/router-utils/setup-dev-bundler.ts (dev-time detection)
  • E2e tests:
    • proxy-page-extensions/ — proxy + instrumentation with compound extensions
    • middleware-page-extensions/ — middleware with compound extensions (separate fixture because the build refuses both proxy.* and middleware.* simultaneously)

Verified locally

Mode Bundler Result
Dev Turbopack PASS (5/5)
Dev Webpack PASS (5/5)
Production (build+start) Turbopack PASS (5/5)
Production (build+start) Webpack PASS (5/5)

Existing proxy test suites also verified (proxy-runtime-nodejs, proxy-with-middleware, proxy-missing-export, proxy-runtime) — no regressions.

Test plan

  • proxy-page-extensions.test.ts covers proxy header injection, page render through proxy, and instrumentation.page.ts:register() running
  • middleware-page-extensions.test.ts covers middleware header injection and page render through middleware
  • All 5 cases pass in dev/turbopack, dev/webpack, start/turbopack, start/webpack
  • Existing proxy-runtime-nodejs tests pass (dev/webpack, dev/turbopack, production/webpack)
  • CI passes on all existing proxy tests

@github-actions github-actions Bot added tests Turbopack Related to Turbopack with Next.js. type: next labels Apr 25, 2026
@bmorros94
bmorros94 marked this pull request as ready for review April 25, 2026 19:13
@bmorros94
bmorros94 force-pushed the fix/proxy-page-extensions-detection branch 2 times, most recently from 22512c2 to c785334 Compare April 25, 2026 20:13
When `pageExtensions` includes compound extensions like `["page.ts"]`,
the proxy file becomes `proxy.page.ts`. The existing `file_stem()` check
uses `rsplit_once('.')` which only strips the last extension, yielding
`"proxy.page"` instead of `"proxy"`, causing proxy detection to fail.

Fix: use `file_name().split('.').next()` to extract the base name before
the first dot.

Fixes vercel#85648
Ref vercel#86122
Verifies that proxy.page.ts is correctly detected and executed when
pageExtensions includes compound extensions like ["page.ts"].

Ref vercel#85648
Ref vercel#86303
Apply the same fix as the Turbopack/Rust side: use split('.')[0] instead
of path.parse().name to extract the convention file basename. Handles
compound pageExtensions like 'page.ts' where proxy.page.ts yields
path.parse().name === 'proxy.page' instead of 'proxy'.

Fixes both build-time detection (build/index.ts) and dev-time detection
(setup-dev-bundler.ts).

Fixes vercel#86303
Ref vercel#86122
The original PR description claimed the same fileBaseName extraction is used
for `middleware` and `instrumentation` convention file detection, but the
test fixture only exercised `proxy.page.ts`. Add coverage for the other two
to lock in the fix end-to-end.

  * Add `instrumentation.page.ts` to the existing `proxy-page-extensions`
    fixture and assert its `register()` runs (visible via cliOutput).
  * Add a separate `middleware-page-extensions` fixture so middleware can
    be detected without colliding with proxy (the build refuses both).

Verified locally in dev/turbopack, dev/webpack, start/turbopack, and
start/webpack — 5/5 tests pass in each mode.
@bmorros94
bmorros94 force-pushed the fix/proxy-page-extensions-detection branch from baa986b to 515a938 Compare April 28, 2026 00:52
bmorros94 and others added 4 commits April 27, 2026 21:57
…helper

The "first dot segment" rule used to detect proxy/middleware/instrumentation
files when pageExtensions includes compound entries (e.g. `page.ts`) was
open-coded in five sites — two in TypeScript (build/index.ts:1259 and
setup-dev-bundler.ts:475) and three in Rust (next-api/src/middleware.rs:86
and project.rs:1855/2031). Each TS site carried the same 4-line comment
verbatim; each Rust site repeated `file_name().split('.').next() == Some(...)`.

Hoist into:
- `packages/next/src/build/get-convention-file-base-name.ts` (TS)
- `crates/next-api/src/path_utils.rs` (Rust)

Behavior unchanged. Future contributors no longer need to rediscover the
rule from a comment scattered across two languages, and a future change to
the matching semantics only needs to touch one site per language.

Note: other `file_stem()` callers in `crates/next-*` (notably the
`is_layout` checks at app.rs:944 and app_client_references_chunks.rs:189)
have the same latent compound-pageExtensions assumption. They are out of
scope for this PR — they affect module-graph propagation, not routing
correctness for proxy detection — but should be audited as a follow-up.
…s, tighten existing assertions

New fixtures:
- `proxy-mixed-page-extensions/`: pageExtensions = ['page.tsx', 'tsx']
  (compound + simple coexisting). Locks in that the compound rule
  recognizes `proxy.page.tsx` when a simple entry is also present, and
  that simple-extension routes (`app/page.tsx`) keep working alongside it.
- `proxy-and-middleware-conflict/`: both `proxy.page.ts` and
  `middleware.page.ts` present. Asserts the dual-file error
  ("are detected. Please use \"./proxy.page.ts\" only.") fires in both
  dev and start modes — previously the throw sites at build/index.ts:1285
  and setup-dev-bundler.ts:491 were unreachable on canary and so untested.

Existing test improvements:
- `middleware-page-extensions.test.ts`: assert the deprecation warnOnce
  ("middleware" file convention is deprecated) appears in cliOutput.
  Same rationale — previously unreachable on canary.
- `proxy-page-extensions.test.ts`:
  - Set `x-proxy-ran` response header in `proxy.page.ts` and assert it
    in the "render the page through the proxy" test. Previously the
    test asserted only the page body, which would render even with a
    broken proxy (the test name overstated coverage).
  - Drop the misleading `await next.fetch('/')` and accompanying comment
    in the instrumentation test — `register()` runs at server boot,
    not on first request, so the fetch is decorative.
…nsions-detection

# Conflicts:
#	crates/next-api/src/middleware.rs
Copilot AI review requested due to automatic review settings May 9, 2026 04:32
@bmorros94

Copy link
Copy Markdown
Contributor Author

Merged latest canary into this branch and resolved the middleware.rs conflict while preserving the compound pageExtensions proxy detection fix. Could a maintainer please approve CI and review when you have a chance?

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes root-level convention file detection (proxy, middleware, instrumentation) when pageExtensions includes compound extensions (e.g. page.ts, page.tsx) by extracting the convention base name from the first filename segment (before any .), aligning webpack/dev and Turbopack behavior.

Changes:

  • Add shared helpers to derive convention base names in both TypeScript (webpack/dev) and Rust (Turbopack).
  • Update build-time and dev-time convention scanning to use the new base-name logic.
  • Add e2e fixtures/tests covering proxy, middleware, instrumentation, mixed extensions, and proxy+middleware conflict behavior.

Reviewed changes

Copilot reviewed 31 out of 31 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/next/src/build/get-convention-file-base-name.ts New helper to compute convention base name from the first .-separated segment.
packages/next/src/build/index.ts Build-time root convention detection now uses the helper to correctly classify proxy.* / middleware.* / instrumentation.* with compound extensions.
packages/next/src/server/lib/router-utils/setup-dev-bundler.ts Dev-time convention detection now uses the helper to correctly classify proxy.* / middleware.* with compound extensions.
crates/next-api/src/path_utils.rs New Rust helper mirroring the TS logic for Turbopack convention base-name extraction.
crates/next-api/src/project.rs Turbopack project path now uses convention_file_base_name instead of file_stem() for proxy detection.
crates/next-api/src/middleware.rs Turbopack middleware endpoint now uses convention_file_base_name for proxy detection.
crates/next-api/src/lib.rs Registers the new path_utils module.
test/e2e/app-dir/proxy-page-extensions/next.config.js E2E fixture configuring compound pageExtensions.
test/e2e/app-dir/proxy-page-extensions/proxy.page.ts E2E fixture proxy file named with a compound extension.
test/e2e/app-dir/proxy-page-extensions/instrumentation.page.ts E2E fixture instrumentation hook with compound extension.
test/e2e/app-dir/proxy-page-extensions/app/page.page.tsx App Router page using compound extension.
test/e2e/app-dir/proxy-page-extensions/app/layout.page.tsx App Router layout using compound extension.
test/e2e/app-dir/proxy-page-extensions/app/headers/page.page.tsx Route used to assert proxy-injected request headers.
test/e2e/app-dir/proxy-page-extensions/proxy-page-extensions.test.ts E2E coverage for proxy execution, response header evidence, and instrumentation detection.
test/e2e/app-dir/middleware-page-extensions/next.config.js E2E fixture configuring compound pageExtensions for middleware convention.
test/e2e/app-dir/middleware-page-extensions/middleware.page.ts E2E fixture middleware file named with a compound extension.
test/e2e/app-dir/middleware-page-extensions/app/page.page.tsx App Router page using compound extension for middleware fixture.
test/e2e/app-dir/middleware-page-extensions/app/layout.page.tsx App Router layout using compound extension for middleware fixture.
test/e2e/app-dir/middleware-page-extensions/app/headers/page.page.tsx Route used to assert middleware-injected request headers.
test/e2e/app-dir/middleware-page-extensions/middleware-page-extensions.test.ts E2E coverage for middleware execution and deprecation warning.
test/e2e/app-dir/proxy-mixed-page-extensions/next.config.js E2E fixture mixing compound and simple pageExtensions.
test/e2e/app-dir/proxy-mixed-page-extensions/proxy.page.tsx E2E fixture proxy file that must still be detected under mixed extensions.
test/e2e/app-dir/proxy-mixed-page-extensions/app/page.tsx App Router page using the simple extension entry.
test/e2e/app-dir/proxy-mixed-page-extensions/app/layout.tsx App Router layout using the simple extension entry.
test/e2e/app-dir/proxy-mixed-page-extensions/proxy-mixed-page-extensions.test.ts E2E coverage ensuring proxy.page.tsx detection works when extensions are mixed.
test/e2e/app-dir/proxy-and-middleware-conflict/next.config.js E2E fixture configuring compound pageExtensions for conflict scenario.
test/e2e/app-dir/proxy-and-middleware-conflict/proxy.page.ts E2E fixture proxy file for conflict test.
test/e2e/app-dir/proxy-and-middleware-conflict/middleware.page.ts E2E fixture middleware file for conflict test.
test/e2e/app-dir/proxy-and-middleware-conflict/app/page.page.tsx App Router page (should not render due to conflict).
test/e2e/app-dir/proxy-and-middleware-conflict/app/layout.page.tsx App Router layout for conflict fixture.
test/e2e/app-dir/proxy-and-middleware-conflict/proxy-and-middleware-conflict.test.ts E2E coverage asserting the conflict error message when both conventions exist.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@bmorros94

Copy link
Copy Markdown
Contributor Author

Refreshed this branch against latest canary today (2026-05-18) and pushed the clean merge. It is still waiting on maintainer CI approval/review. Thanks.

@PhilMcVay

Copy link
Copy Markdown

Any maintainer able to take a look at this? It's been sat here for a while.

It would be great to get this merged and published as it includes some long awaited fixes for projects using the proxy.ts file alongside a pageExtensions config setup. 🙏

@MauriceArikoglu

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor

Tests Passed

Commit: e177ed3

@mischnic
mischnic merged commit 000390a into vercel:canary Sep 14, 2026
315 of 320 checks passed
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Sep 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

locked tests Turbopack Related to Turbopack with Next.js. type: next

Projects

None yet

5 participants