Skip to content

fix(pages): route dotted dynamic paths in dev#2424

Merged
james-elicx merged 5 commits into
mainfrom
codex/fix-dotted-pages-routes
Jun 30, 2026
Merged

fix(pages): route dotted dynamic paths in dev#2424
james-elicx merged 5 commits into
mainfrom
codex/fix-dotted-pages-routes

Conversation

@james-elicx

@james-elicx james-elicx commented Jun 29, 2026

Copy link
Copy Markdown
Member

Summary

  • preserve Pages Router handling for dotted dynamic page and API route segments in dev
  • mirror downstream Pages i18n route matching during the dev dot-path preflight
  • reuse normalized dev request host/origin parsing across preflight, middleware, and image passthrough
  • keep unrelated file-looking requests delegated to Vite/static handling
  • add dev and production regression coverage for dotted dynamic Pages routes

Validation

  • vp check packages/vinext/src/index.ts tests/pages-router.test.ts
  • vp test run tests/pages-router.test.ts -t "dot-path|dotted dynamic"
  • vp test run tests/pages-router.test.ts -t "Pages Router dev dot-path i18n preflight"

Review

  • independent sub-agent review: no actionable findings
  • Bonk review loop: latest comments addressed

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@pkg-pr-new

pkg-pr-new Bot commented Jun 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2424
npm i https://pkg.pr.new/vinext@2424

commit: 9feb598

@github-actions

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
workers-cache preview production
web preview production

@ask-bonk

ask-bonk Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Working tree clean. Here's my review summary.

Review of PR #2424fix(pages): route dotted dynamic paths in dev

I reviewed the diff, traced the surrounding request lifecycle, verified the fixtures, ran the new tests, and ran vp check.

Verdict: LGTM — no blocking issues

The fix is correct and well-scoped. The dev middleware previously short-circuited to next() for any path containing a . (except .html), which incorrectly bounced legitimate dynamic Pages routes like /docs/release/v1.2 and /api/users/alpha.beta to Vite's static handler. The PR adds a preflight matchRoute against the page and API routers so dotted requests that actually match a route are kept in the Pages pipeline.

What I verified:

  • pagesRouter/apiRouter are cached (routeCache in routing/pages-router.ts), so the added Promise.all is a cache hit on the hot path — no perf regression, consistent with the AGENTS.md "reuse already-computed derivations" guidance.
  • pathname at line 4152 is already basePath-stripped and trailing-slash-canonicalized, so the preflight match operates on the correct path.
  • Fixtures back the tests: pages/docs/[...slug].tsx and pages/api/users/[id].ts produce exactly the asserted output.
  • The 4 new tests pass; vp check is clean.
  • Production parity: prod-server.ts has no equivalent dot-skip preflight (this was a dev-only gap), and the PR's two prod tests confirm prod already handled these correctly. Good defensive coverage.
  • Tests cite the relevant Next.js source (base-server.ts#getRouteMatchers, router-utils/filesystem.ts), matching the AGENTS.md requirement to anchor parity decisions.

Non-blocking observations

  1. i18n + dotted dynamic gap (minor): The preflight calls matchRoute(pathname, pageRoutes) directly, but the real matchPageRoute (index.ts:4305) first applies resolvePagesI18nRequest. For an i18n app, a locale-prefixed dotted dynamic path might fail the preflight and fall through to next(). This is not a regression (it's the prior behavior), but the fix is incomplete for that case. Worth a follow-up issue or a one-line code comment noting the preflight intentionally skips i18n resolution.

  2. Comment placement (nit): The pre-existing comment at index.ts:4139-4141 ("Skip requests for files with extensions…") now sits above the filePathMatchesRewrite block but describes the isFilePathRequest skip that moved a few lines down. Consider moving/expanding it to mention the new "unless it matches a Pages route" exception, so the intent stays obvious to future readers.

Neither blocks merge.

github run

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared 9feb598 against base c8aef02 using alternating same-runner rounds. Next.js was unchanged and skipped.

0 improved · 0 regressed · 6 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 124.0 KB 124.0 KB ⚫ 0.0%
Client entry size (gzip) vinext 118.8 KB 118.8 KB ⚫ -0.0%
Dev server cold start vinext 2.52 s 2.51 s ⚫ -0.5%
Production build time vinext 2.91 s 2.95 s ⚫ +1.4%
RSC entry closure size (gzip) vinext 95.2 KB 95.2 KB ⚫ -0.0%
Server bundle size (gzip) vinext 161.5 KB 161.5 KB ⚫ -0.0%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot 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.

Review of PR #2424fix(pages): route dotted dynamic paths in dev

I traced the dev request pipeline, verified parity with the real matchers, checked the fixtures, ran the new tests, and ran vp check.

Verdict: LGTM — no blocking issues

The fix is correct and well-scoped. The dev middleware previously short-circuited to next() for any path containing a . (except .html), incorrectly bouncing legitimate dotted dynamic Pages routes (e.g. /docs/release/v1.2, /api/users/alpha.beta) to Vite's static handler. The PR adds a preflight matchRoute against the page and API routers, keeping dotted requests that actually match a route in the Pages pipeline.

Notably, the second commit (7d35410) already closes the i18n gap that prior reviews of the first commit would have flagged: locale-prefixed dotted paths are now resolved through resolvePagesI18nRequest (pages) and stripI18nLocaleForApiRoute (api) before the preflight match.

What I verified

  • Parity with the real matchers. The preflight's page-route match (index.ts:4164-4173) mirrors the downstream matchPageRoute (index.ts:4321-4336) — both run resolvePagesI18nRequest(...).url. The preflight's API match via stripI18nLocaleForApiRoute(pathname, ...) (index.ts:4174) mirrors the pipeline's stripI18nLocaleForApiRoute(resolvedUrl, ...) (pages-request-pipeline.ts:513). The two sides stay in sync.
  • No perf regression. pagesRouter/apiRouter are cached by (pagesDir, extensions) (routeCache in routing/pages-router.ts:28), and the same routers are already invoked later (index.ts:4297, 4413). The added Promise.all is a cache hit on the hot path, consistent with the AGENTS.md "reuse already-computed derivations" guidance.
  • Hybrid app/pages safe. For dotted paths that match no Pages route, the preflight still calls next(), preserving the prior behavior so App Router RSC middleware can handle app-dir-only routes. No regression.
  • i18n redirect path intact. A non-prefixed dotted path that matches a dynamic route stays in the pipeline (preflight uses pathname unchanged when there's no prefix), so the locale redirect still fires.
  • Fixtures back the assertions. pages/docs/[...slug].tsx emits the Docs / Path: markup and pages/api/users/[id].ts returns { user: { id, name } } exactly as asserted.
  • Tests + check pass. All 6 new tests (dev dotted page/api, i18n locale-prefixed dotted page/api, and the 2 prod regression tests) pass. vp check packages/vinext/src/index.ts tests/pages-router.test.ts is clean (format, lint, types).
  • Prod parity. prod-server.ts had no dot-skip preflight (this was a dev-only gap), and the two new prod tests confirm prod already handled these correctly — good defensive coverage.
  • Tests cite the relevant Next.js sources (base-server.ts#getRouteMatchers, router-utils/filesystem.ts), matching the AGENTS.md parity-anchoring requirement.

Non-blocking observations

  1. Asymmetric i18n resolution between page and API preflight (intentional, but worth a note). pageRouteUrl goes through resolvePagesI18nRequest while apiRouteUrl goes through stripI18nLocaleForApiRoute. This is correct — each mirrors its respective downstream path — but a one-line comment explaining why the two differ would help future readers avoid "fixing" the asymmetry.

  2. requestHostname parsing (nit). The host split at index.ts:4160-4163 handles array/undefined host headers and strips the port. This duplicates the new URL(request.url).hostname derivation used by matchPageRoute (index.ts:4327). Both produce a bare hostname, so behavior is equivalent; consolidating the two into a shared helper would reduce drift risk if hostname handling changes later.

Neither blocks merge.

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review posted on PR #2424.

Summary

Verdict: LGTM — no blocking issues

PR #2424 fixes a dev-only bug where the Pages Router dev middleware short-circuited to next() for any path containing a . (except .html), incorrectly bouncing legitimate dotted dynamic routes like /docs/release/v1.2 and /api/users/alpha.beta to Vite's static handler. The fix adds a preflight matchRoute against the page and API routers.

What I verified:

  • Parity: The preflight's page/API matching mirrors the real downstream matchers (matchPageRoute and the pipeline's stripI18nLocaleForApiRoute).
  • No perf regression: Routers are cached by routeCache, so the added Promise.all is a hot-path cache hit.
  • i18n gap closed: The second commit (7d35410) — not reflected in the PR description — already handles locale-prefixed dotted paths via resolvePagesI18nRequest/stripI18nLocaleForApiRoute.
  • Hybrid app/pages safe: Non-matching dotted paths still next(), preserving App Router handling.
  • Tests + check pass: All 6 new tests pass; vp check is clean.

Two non-blocking nits: a comment explaining the intentional page-vs-API i18n resolution asymmetry, and consolidating the duplicated hostname derivation.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review of PR #2424fix(pages): route dotted dynamic paths in dev

I traced the dev request pipeline, verified parity with the downstream matchers, confirmed the public-file precedence path, ran the new tests, and ran vp check.

Verdict: LGTM — no blocking issues

The fix is correct and well-scoped. The dev Pages middleware previously short-circuited to next() for any path containing a . (except .html), incorrectly bouncing legitimate dotted dynamic routes (/docs/release/v1.2, /api/users/alpha.beta) to Vite's static handler. The PR adds a preflight matchRoute against the page and API routers, so dotted requests that actually match a route stay in the Pages pipeline. The follow-up commits close the i18n gap and clarify the comment.

What I verified

  • No public-file regression (the one I went looking for). My main concern was that keeping dotted paths in the pipeline could let a dynamic page render instead of serving a colliding public/ file (Next.js orders filesystem routes step 5 before dynamic routes step 7). This is not a regression: the Pages middleware is registered via server.middlewares.use(...) in configureServer (index.ts:3688/3816/4541), which runs after Vite's internal public/static middlewares. Real files under public/ are served by Vite before the request ever reaches this preflight, so only non-file dotted paths get here — and matching those to a dynamic route is the correct behavior. The dev serveFilesystemRoute("direct") adapter returning false (index.ts:4358-4367) is consistent with this.
  • App-only / no-pages apps are unaffected. The whole handler is gated by if (!hasPagesDir) return next(); (index.ts:3948), so app-router-only apps never run the preflight.
  • No perf regression. pagesRouter/apiRouter are cached by (pagesDir, extensions) via routeCache (routing/pages-router.ts), and the same routers are invoked later in the request (index.ts:4298, 4414). The added Promise.all is a hot-path cache hit — and only fires for dotted, non-rewrite requests.
  • Parity with the real matchers. Page preflight runs resolvePagesI18nRequest(...).url then matchRoute, mirroring matchPageRoute (index.ts:4322-4337). API preflight runs stripI18nLocaleForApiRoute(pathname, ...) then matchRoute, mirroring the pipeline's stripI18nLocaleForApiRoute(resolvedUrl, ...) (pages-request-pipeline.ts:513). Over-matching (page resolution applied to an /api/ path) only keeps the request in the pipeline, where it is then correctly routed — no incorrect outcome.
  • Hostname dedup is an improvement, not just a refactor. requestHost now picks req.headers.host[0] for array headers and is reused for requestOrigin, requestHostname, and mwOrigin (index.ts:4155-4159, 4222, 4255). The old http://${req.headers.host || "localhost"} would have joined an array host into http://a,b; the new code is more correct.
  • Tests + check pass. All new dev/i18n/prod tests pass (9 matching -t cases green), and vp check packages/vinext/src/index.ts tests/pages-router.test.ts is clean (format, lint, types). Tests anchor parity to the relevant Next.js sources.

Non-blocking observations

  1. API-preflight uses pre-middleware pathname, page side uses the resolved URL. The pipeline strips locale from resolvedUrl (post-middleware/rewrite) at line 513, while the preflight strips from the raw pathname. For the preflight gate this is intentionally permissive and fine — the gate only decides "stay in pipeline vs next()", and the pipeline re-resolves authoritatively. Worth keeping the existing comment that explains the page-vs-API asymmetry.
  2. requestHostname derivation duplicates new URL(request.url).hostname used by matchPageRoute (index.ts:4328). Equivalent output today; a shared helper would reduce drift risk if hostname parsing ever changes. Nit only.

Neither blocks merge.

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Posted my review to PR #2424. Summary:

Verdict: LGTM — no blocking issues

The fix is correct and well-scoped. I focused on the one thing prior reviews hadn't fully chased down: whether keeping dotted paths in the pipeline could regress public-file precedence.

Key findings:

  • No public-file regression. The Pages middleware runs after Vite's internal public/static middlewares, so real public/ files are served before reaching the preflight. Only non-file dotted paths get there, and matching those to a dynamic route is correct. This was the main risk and it's clean.
  • No perf regression — routers are cached (routeCache), the preflight only fires for dotted non-rewrite requests in pages apps, and it's a hot-path cache hit.
  • App-only apps unaffected — gated by if (!hasPagesDir) return next().
  • Parity verified — page preflight mirrors matchPageRoute's i18n resolution; API preflight mirrors the pipeline's stripI18nLocaleForApiRoute.
  • Hostname dedup is an improvement — array host headers now pick [0] instead of joining to http://a,b.
  • Tests + vp check pass (9 matching test cases green).

Two non-blocking nits (the intentional page-vs-API i18n asymmetry deserving its comment, and a duplicated hostname derivation that could be a shared helper). Neither blocks merge.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review of PR #2424fix(pages): route dotted dynamic paths in dev

I read the full diff (4 commits, including 875cca6 "share dev hostname parsing" which is not in the PR description), traced the dev request pipeline, verified parity with the downstream matchers, checked the fixtures, ran the new tests, and ran vp check.

Verdict: LGTM — no blocking issues

The fix is correct and well-scoped. The dev Pages middleware previously short-circuited to next() for any path containing a . (except .html), incorrectly bouncing legitimate dotted dynamic routes (/docs/release/v1.2, /api/users/alpha.beta) to Vite's static handler. The PR adds a preflight matchRoute against the page and API routers so dotted requests that actually match a route stay in the Pages pipeline.

What I verified

  • Parity with the real matchers, including query-string handling. The page preflight runs resolvePagesI18nRequest(...).url (index.ts:4168-4177), mirroring matchPageRoute (index.ts:4324-4333). The API preflight runs stripI18nLocaleForApiRoute(pathname, ...) (index.ts:4178), mirroring the pipeline's stripI18nLocaleForApiRoute(resolvedUrl, ...) (pages-request-pipeline.ts:513). matchRouteWithTrie strips the query string up front (route-matching.ts:50), so passing resolvePagesI18nRequest(...).url (may carry ?…) vs. the bare pathname produces identical matches — no off-by-query divergence.
  • Header-shape difference is safe. The preflight passes Node req.headers (lowercased Record) to resolvePagesI18nRequest, while matchPageRoute passes a Web Headers. readHeader (pages-i18n.ts:34-45) handles both shapes, so this is not a divergence.
  • Same route source, no drift. The preflight's pageRoutes come from pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher) — the identical (cached) call that produces devPageRoutes used later at index.ts:4334. The two sides cannot disagree.
  • No public-file regression. The Pages middleware is registered in the returned post-hook (server.middlewares.use, index.ts:4542), which runs after Vite's internal public/static middlewares. Real public/ files are served before the request reaches this preflight, so only non-file dotted paths get here.
  • App-only apps unaffected. Gated by if (!hasPagesDir) return next() (index.ts:3948).
  • Over-matching is benign. The preflight uses page-match || api-match and only decides "stay in pipeline vs next()"; the pipeline re-routes authoritatively, so applying page i18n resolution to an /api/ path can never produce an incorrect outcome.
  • No perf regression. pagesRouter/apiRouter are cached by (pagesDir, extensions) via routeCache, so the added Promise.all is a hot-path cache hit, and it only fires for dotted, non-rewrite requests.
  • Hostname dedup is a real improvement. requestHost now picks req.headers.host[0] for array headers and is reused for requestOrigin, requestHostname, and mwOrigin. The old http://${req.headers.host || "localhost"} would have joined an array host into http://a,b.
  • Dev/prod parity holds. prod-server.ts and the Cloudflare entry have no dot-skip preflight (this was a dev-only gap). The two new prod tests confirm prod already routed these correctly — good defensive coverage, no parity gap introduced.
  • Tests + check pass. All 6 new tests (2 dev, 2 i18n, 2 prod) pass; vp check packages/vinext/src/index.ts tests/pages-router.test.ts is clean (format, lint, types). Tests anchor parity to the relevant Next.js sources.

Non-blocking observations

  1. Pre-existing inconsistency now adjacent to the dedup (nit, not this PR's bug). Line 3990 still builds imageRequestUrl with http://${req.headers.host || "localhost"} — the exact pattern the PR just replaced for the main handler. It's outside the changed block and pre-existing, so not a regression, but a future cleanup could route it through the same requestHost helper for consistency.
  2. Page-vs-API i18n asymmetry deserves to keep its comment. The comment at index.ts:4166-4167 explaining why the page side uses full i18n resolution while the API side only strips a locale prefix is the thing that prevents a future reader from "fixing" the asymmetry. Keep it.

Neither blocks merge.

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Posted my review to PR #2424.

Verdict: LGTM — no blocking issues

The fix is correct and well-scoped. I reviewed all 4 commits (noting 875cca6 "share dev hostname parsing" isn't in the PR description), traced the dev request pipeline, and verified parity rather than re-confirming prior reviewers' summaries.

Key findings (things I independently chased down):

  • Query-string parity is soundmatchRouteWithTrie strips ?… up front, so the page preflight passing resolvePagesI18nRequest(...).url (which can carry a query) vs. the API preflight passing bare pathname produces identical matches. No off-by-query divergence.
  • Header-shape difference is safe — the preflight feeds Node req.headers (a Record) to resolvePagesI18nRequest while matchPageRoute feeds a Web Headers; readHeader handles both, so it's not a divergence.
  • Same route source — preflight pageRoutes and downstream devPageRoutes come from the identical cached pagesRouter(...) call, so they cannot drift.
  • No public-file / app-only / perf regressions — Pages middleware runs after Vite's static middlewares; gated by hasPagesDir; routers are cached.
  • Dev/prod parity holds — prod-server and the Cloudflare entry have no dot-skip; the new prod tests confirm prod already routed these correctly.
  • Tests + vp check pass — all 6 new tests green, check clean.

Two non-blocking nits: a pre-existing req.headers.host pattern at index.ts:3990 (the same one the PR just deduped, but outside scope), and keeping the page-vs-API i18n asymmetry comment so future readers don't "fix" it.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot 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.

Review of PR #2424fix(pages): route dotted dynamic paths in dev

I reviewed all 5 commits independently: read the full index.ts diff, traced the dev request pipeline, verified parity against the downstream matchers and the shared Pages pipeline, checked the fixtures, ran the new tests, and ran vp check.

Verdict: LGTM — no blocking issues

The fix is correct and well-scoped. The dev Pages middleware previously short-circuited to next() for any path containing a . (except .html), incorrectly bouncing legitimate dotted dynamic routes (/docs/release/v1.2, /api/users/alpha.beta) to Vite's static handler. The PR adds a preflight matchRoute against the page and API routers (index.ts:4161-4182) so dotted requests that actually match a route stay in the Pages pipeline.

What I verified

  • Same route source — no drift. The preflight's pageRoutes/apiRoutes come from the identical cached pagesRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher) / apiRouter(...) calls used later for devPageRoutes (index.ts:4299) and the API lookup (index.ts:4415). The two sides cannot disagree.
  • Page parity. Preflight runs resolvePagesI18nRequest(...).url (index.ts:4168-4177), mirroring the downstream matchPageRoute (index.ts:4323-4338). Confirmed against resolvePagesI18nRequest in pages-i18n.ts — for a non-prefixed /api/... path extractLocaleFromUrl returns the URL unchanged, so the page branch correctly fails to match and the API branch handles it.
  • API parity. Preflight runs stripI18nLocaleForApiRoute(pathname, ...) (index.ts:4178), mirroring the pipeline's stripI18nLocaleForApiRoute(resolvedUrl, ...) (pages-request-pipeline.ts:513). The preflight strips from the pre-middleware pathname while the pipeline strips from the post-middleware resolvedUrl; for this gate (stay-in-pipeline vs next()) the permissive direction is safe — the pipeline re-resolves authoritatively.
  • No public-file regression. The Pages middleware is registered inside the configureServer post-hook (return () => { server.middlewares.use(...) }, index.ts:3711/4542), which runs after Vite's internal public/static middlewares. Real public/ files are served before reaching the preflight, so only non-file dotted paths get here.
  • Hybrid app+pages safe. Dotted paths matching no Pages route still next() (index.ts:4183-4185), preserving App Router handling. App-only apps never reach the preflight (gated by hasPagesDir).
  • No perf regression. Routers are cached by (pagesDir, extensions) via routeCache, so the added Promise.all is a hot-path cache hit, and it only fires for dotted, non-rewrite requests.
  • Hostname dedup is a genuine improvement. requestHost now picks req.headers.host[0] for array headers and is reused for requestOrigin, requestHostname, and mwOrigin. The old http://${req.headers.host || "localhost"} would have joined an array host into http://a,b. getUrlHostname(requestOrigin) correctly strips any port, matching the downstream new URL(request.url).hostname.
  • Prior nit addressed. The dev image passthrough now uses the shared requestOrigin (index.ts:3995) instead of the old inline http://${req.headers.host || "localhost"} pattern flagged in earlier reviews.
  • Tests + check pass. All 9 matching tests pass (dev dotted page/api, i18n locale-prefixed dotted page/api, 2 prod regression tests); vp check packages/vinext/src/index.ts tests/pages-router.test.ts is clean (format, lint, types). Tests anchor parity to the relevant Next.js sources.

Non-blocking observations

  1. Page-vs-API i18n asymmetry is intentional and documented. The comment at index.ts:4166-4167 explains why the page side uses full i18n resolution while the API side only strips a locale prefix. Keep it — it's the thing that prevents a future reader from "fixing" the asymmetry.

No blockers. Ship it.

@ask-bonk

ask-bonk Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2424.

Verdict: LGTM — no blocking issues

The fix is correct and well-scoped. The dev Pages middleware previously short-circuited to next() for any dotted path (except .html), incorrectly bouncing legitimate dotted dynamic routes like /docs/release/v1.2 and /api/users/alpha.beta to Vite's static handler. The PR adds a preflight matchRoute against the page and API routers so dotted requests that actually match a route stay in the Pages pipeline.

What I independently verified:

  • Same cached route source for the preflight and downstream matchers — they can't drift.
  • Page parity with matchPageRoute (both run resolvePagesI18nRequest(...).url) and API parity with the pipeline's stripI18nLocaleForApiRoute.
  • No public-file regression — the Pages middleware runs in the configureServer post-hook, after Vite's static/public middlewares.
  • Hybrid app+pages and app-only apps unaffected (hasPagesDir gate, non-matching dotted paths still next()).
  • No perf regressionrouteCache makes the added Promise.all a hot-path cache hit.
  • Hostname dedup is a real correctness improvement — array host headers now pick [0] instead of joining to http://a,b; the previously-flagged image-passthrough nit was also addressed.
  • Tests + vp check pass — 9 matching tests green, check clean.

One non-blocking note: the page-vs-API i18n asymmetry is intentional and already has its explanatory comment — keep it.

github run

@james-elicx james-elicx marked this pull request as ready for review June 30, 2026 08:18
@james-elicx james-elicx merged commit c02f304 into main Jun 30, 2026
60 checks passed
@james-elicx james-elicx deleted the codex/fix-dotted-pages-routes branch June 30, 2026 08:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant