Commit Graph

6 Commits

Author SHA1 Message Date
Divanshu Chauhan (divkix) 2b9ff64019 fix(routing): preserve dynamic route params key order (#1609)
App Router dynamic-route params were being returned in reversed key
order (deepest segment first) because the recursive trie matcher
mutated result.params on the way back up the call stack.

Fix by collecting param entries into a shared stack during forward
traversal, then materializing the params object from that stack at
the terminal node. This preserves declaration order for Object.keys().

- Shared buildParams helper extracted to routing/utils.ts to unify
code between route-trie.ts and app-optimistic-routing.ts.
- Added tests verifying key order for [category]/[id], [a]/[b]/[c],
mixed static+dynamic, and backtracking-to-catch-all scenarios.

Fixes #1537
2026-05-27 09:41:37 +01:00
Nathan Nguyen 6c786d69a9 fix(routing): decode matched params with decodeURIComponent (#1049)
* fix(routing): decode matched params with decodeURIComponent

Vinext's segment-safe normalization preserves encoded delimiters (%2F) for
correct route matching, but the captured param values were left undecoded.
This caused dynamic params to return "a%2Fb" instead of "a/b", diverging
from Next.js which decodes at the route-matcher level.

Add decodeURIComponent pass in trieMatch and matchRoutePattern — the two
route matching entry points — so all downstream consumers (pages, route
handlers, metadata, useParams) receive decoded param values.

Mirrors Next.js: packages/next/src/shared/lib/router/utils/route-matcher.ts L25-27

* test(app-rsc-route-matching): update param decode assertion to match new behavior
2026-05-04 18:58:48 +01:00
Nathan Nguyen 1e4c175ee4 fix(routing): omit empty optional catch-all params (#987)
Optional catch-all routes currently materialize the missing segment list as an empty array. Next.js leaves the param key absent when the optional catch-all capture group does not match, so pages and app routes can observe a different params shape at the zero-segment path.

The route trie treated zero remaining segments as a captured value. The fix keeps the route match while returning an empty params object for that zero-segment branch, and applies the same normalization to standalone app RSC pattern matching.

Tests cover shared trie matching, app RSC matching, app route discovery, pages route discovery, and hyphenated optional catch-all params.
2026-04-30 10:24:51 +01:00
James Anderson a83be594f9 chore: turn on lint rule to prefer type over interface (#716)
* chore: turn on lint rule to prefer `type` over `interface`

* fix vite-hmr.d.ts
2026-03-29 22:09:39 +01:00
Stephen Zhou c17d6941be chore: migrate to vite plus (#535)
* chore: migrate to vite plus

* Disable typeAware and typeCheck

* Update CI

* Fix CI

* Fix test

* Clean

* Run test with vp

* Try revert

* react: false In test

* Fix test

* Revert "Try revert"

This reverts commit 009da10473.

* Update

* Update

* Try revert ci changes

* revert

* Run vp migrate

* Disable typeAware and typeCheck for now

* Better resolve for test

* Use vp dev instead of vite

* Update expect

* Fix NormalizeManifestModuleId

* Try increase timeout

* Update to use vp

* Try new check

* Bring back npx vp

* Migrate CI

* Make next-intl resolvable

* Update

* Update

* Update
2026-03-15 10:50:13 +00:00
Divanshu Chauhan (divkix) 20f08bdd86 perf: replace O(n) linear route matching with radix trie (#442)
* perf: replace O(n) linear route matching with radix trie

Every request triggered 3-6 linear scans over the full route list, calling
matchPattern() for every route until a match. For apps with many routes
this means thousands of comparisons per request.

This replaces the linear scan with a trie-based O(depth) lookup in all
4 codebases that share the matching logic:

- pages-router.ts (scanner module, dev time)
- app-router.ts (scanner module, dev time)
- app-rsc-entry.ts (generated entry, runtime)
- pages-server-entry.ts (generated entry, runtime)

The trie enforces correct precedence at each node via traversal order:
static > dynamic > catch-all > optional catch-all. Recursive DFS with
backtracking ensures dead-end branches fall through to alternatives.

matchPattern is retained only in app-rsc-entry.ts for the intercept
lookup (typically 0-5 entries, not worth trie-ifying).

* test: update entry-templates snapshot and assertion for trie-based matching

The server entry no longer inlines matchPattern — it imports the trie
module instead. Update the explicit assertion to check for buildRouteTrie
and trieMatch, and regenerate all entry template snapshots.

* fix: address review comments on trie-based route matching

- Correct "radix trie" → "trie (prefix tree)" in doc comment (no path compression)
- Remove unused `routes` parameter from `matchRoute` in app-rsc-entry
- Replace stream-of-consciousness test comment with concise explanation

* fix: update matchRoute regex in test to match new 1-arg signature

The test was asserting the old `matchRoute(url, routes)` signature in
the generated code. Updated to match the new `matchRoute(url)` signature
after the unused `routes` parameter was removed.
2026-03-11 16:41:21 +00:00