[PR #996] [MERGED] refactor(metadata): share route pattern helpers #1009

Closed
opened 2026-05-06 13:11:32 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/996
Author: @NathanDrake2406
Created: 5/1/2026
Status: Merged
Merged: 5/1/2026
Merged by: @james-elicx

Base: mainHead: nathan/metadata-helper-dedupe


📝 Commits (3)

  • f4160f7 refactor(metadata): share route pattern helpers
  • 1899e97 fix(routing): preserve prototype-named route params
  • deea845 fix(routing): preserve literal intercept target segments

📊 Changes

8 files changed (+281 additions, -226 deletions)

View changed files

packages/vinext/src/routing/route-pattern.ts (+139 -0)
📝 packages/vinext/src/server/app-rsc-route-matching.ts (+3 -30)
📝 packages/vinext/src/server/file-based-metadata.ts (+20 -107)
📝 packages/vinext/src/server/metadata-route-build-data.ts (+2 -14)
📝 packages/vinext/src/server/metadata-routes.ts (+2 -41)
📝 packages/vinext/src/shims/metadata.tsx (+22 -34)
📝 tests/app-rsc-route-matching.test.ts (+24 -0)
tests/route-pattern.test.ts (+69 -0)

📄 Description

What this changes

This is a follow-up to James's review comments on #891 about duplicated metadata helper logic:

The PR adds a normal routing module, packages/vinext/src/routing/route-pattern.ts, that owns app-route segment pattern conversion, dynamic segment filling, and pattern matching. Metadata route build data, metadata route runtime matching, file-based metadata head injection, and App RSC intercept matching now consume that shared behavior instead of keeping local copies.

It also consolidates the duplicated icon descriptor normalization in both file-based-metadata.ts and metadata.tsx without changing observable metadata output.

Why

The codegen boundary should describe the app shape, while normal modules implement behavior. The previous #891 implementation already moved most runtime metadata work out of generated entries, but some generic route behavior still lived inside metadata-specific modules. That made the code harder to audit and made future routing parity fixes more likely to fork across metadata, App RSC, and shim paths.

Approach

  • Move route pattern helpers into routing/route-pattern.ts.
  • Reuse those helpers from metadata build data, metadata runtime matching, file-based metadata, and App RSC route matching.
  • Keep metadata-specific wrappers only where they still express metadata behavior. Generic segment parsing and matching now live outside metadata modules.
  • Collapse icon descriptor normalization to one list-normalization path per module, while preserving string, URL, descriptor, array, and icon-map inputs.

Next.js references

The shared helper is intentionally aligned with the same observable contracts Next uses in separate places:

Validation

  • vp test run tests/route-pattern.test.ts tests/metadata-route-build-data.test.ts tests/metadata-routes.test.ts tests/file-based-metadata.test.ts tests/app-rsc-route-matching.test.ts tests/shims.test.ts passed: 952 tests.
  • vp check passed: 1071 files formatted, 457 files checked with no warnings, lint errors, or type errors.
  • vp run vinext#build passed. The existing virtual-module unresolved import warnings are preserved build behavior.

Risks / follow-ups

The new matcher accepts catch-all pattern parts before a suffix, which metadata image routes need for generateImageMetadata() ID suffixes. App RSC intercept matching now uses the same matcher, but normal App route matching still goes through the route trie.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/996 **Author:** [@NathanDrake2406](https://github.com/NathanDrake2406) **Created:** 5/1/2026 **Status:** ✅ Merged **Merged:** 5/1/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `nathan/metadata-helper-dedupe` --- ### 📝 Commits (3) - [`f4160f7`](https://github.com/cloudflare/vinext/commit/f4160f746f31e1e7c7b9a38d0b5844e5de845d99) refactor(metadata): share route pattern helpers - [`1899e97`](https://github.com/cloudflare/vinext/commit/1899e979e70f62659cf8f85049ee711a3d1f679a) fix(routing): preserve prototype-named route params - [`deea845`](https://github.com/cloudflare/vinext/commit/deea845526111950fce91c16e717c5d88dbea9fc) fix(routing): preserve literal intercept target segments ### 📊 Changes **8 files changed** (+281 additions, -226 deletions) <details> <summary>View changed files</summary> ➕ `packages/vinext/src/routing/route-pattern.ts` (+139 -0) 📝 `packages/vinext/src/server/app-rsc-route-matching.ts` (+3 -30) 📝 `packages/vinext/src/server/file-based-metadata.ts` (+20 -107) 📝 `packages/vinext/src/server/metadata-route-build-data.ts` (+2 -14) 📝 `packages/vinext/src/server/metadata-routes.ts` (+2 -41) 📝 `packages/vinext/src/shims/metadata.tsx` (+22 -34) 📝 `tests/app-rsc-route-matching.test.ts` (+24 -0) ➕ `tests/route-pattern.test.ts` (+69 -0) </details> ### 📄 Description ## What this changes This is a follow-up to James's review comments on #891 about duplicated metadata helper logic: - [normalizeIconValue and normalizeIconDescriptor could probably be merged](https://github.com/cloudflare/vinext/pull/891#discussion_r3169851093) - [the same icon normalization logic could be reusable](https://github.com/cloudflare/vinext/pull/891#discussion_r3169864749) - [metadata route pattern conversion duplicated normalizeRoutePrefixPattern](https://github.com/cloudflare/vinext/pull/891#discussion_r3169950415) - [the repeated split/filter/map route pattern pipeline could be shared](https://github.com/cloudflare/vinext/pull/891#discussion_r3169957050) - [route patterns may want a generic internal format](https://github.com/cloudflare/vinext/pull/891#discussion_r3170026627) - [some metadata logic may be duplicated between server and shims](https://github.com/cloudflare/vinext/pull/891#discussion_r3170044468) The PR adds a normal routing module, `packages/vinext/src/routing/route-pattern.ts`, that owns app-route segment pattern conversion, dynamic segment filling, and pattern matching. Metadata route build data, metadata route runtime matching, file-based metadata head injection, and App RSC intercept matching now consume that shared behavior instead of keeping local copies. It also consolidates the duplicated icon descriptor normalization in both `file-based-metadata.ts` and `metadata.tsx` without changing observable metadata output. ## Why The codegen boundary should describe the app shape, while normal modules implement behavior. The previous #891 implementation already moved most runtime metadata work out of generated entries, but some generic route behavior still lived inside metadata-specific modules. That made the code harder to audit and made future routing parity fixes more likely to fork across metadata, App RSC, and shim paths. ## Approach - Move route pattern helpers into `routing/route-pattern.ts`. - Reuse those helpers from metadata build data, metadata runtime matching, file-based metadata, and App RSC route matching. - Keep metadata-specific wrappers only where they still express metadata behavior. Generic segment parsing and matching now live outside metadata modules. - Collapse icon descriptor normalization to one list-normalization path per module, while preserving string, URL, descriptor, array, and icon-map inputs. ## Next.js references The shared helper is intentionally aligned with the same observable contracts Next uses in separate places: - Next metadata route filling and static metadata segment normalization: [`packages/next/src/lib/metadata/get-metadata-route.ts`](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/packages/next/src/lib/metadata/get-metadata-route.ts#L61-L132) - Next dynamic path interpolation for optional and catch-all params: [`packages/next/src/server/server-utils.ts`](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/packages/next/src/server/server-utils.ts#L75-L107) - Next route matcher param extraction for repeat params: [`packages/next/src/shared/lib/router/utils/route-matcher.ts`](https://github.com/vercel/next.js/blob/ae61573e062e900050b8e6b24626e450accc4570/packages/next/src/shared/lib/router/utils/route-matcher.ts#L17-L49) ## Validation - `vp test run tests/route-pattern.test.ts tests/metadata-route-build-data.test.ts tests/metadata-routes.test.ts tests/file-based-metadata.test.ts tests/app-rsc-route-matching.test.ts tests/shims.test.ts` passed: 952 tests. - `vp check` passed: 1071 files formatted, 457 files checked with no warnings, lint errors, or type errors. - `vp run vinext#build` passed. The existing virtual-module unresolved import warnings are preserved build behavior. ## Risks / follow-ups The new matcher accepts catch-all pattern parts before a suffix, which metadata image routes need for `generateImageMetadata()` ID suffixes. App RSC intercept matching now uses the same matcher, but normal App route matching still goes through the route trie. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:11:32 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#1009
No description provided.