Skip to content

route-pattern: PartPattern.{parse,variants,toString}#10935

Merged
pcattori merged 1 commit intopedro/route-patternfrom
pedro/part-pattern
Jan 12, 2026
Merged

route-pattern: PartPattern.{parse,variants,toString}#10935
pcattori merged 1 commit intopedro/route-patternfrom
pedro/part-pattern

Conversation

@pcattori
Copy link
Contributor

@pcattori pcattori commented Jan 8, 2026

Context

During the development of route-pattern, the design for the Route Pattern DSL changed many times. This wasn't unexpected, but it did mean that the matching APIs (ArrayMatcher, TrieMatcher, etc.) and parsing algorithms always had to "catch up" to the newest DSL changes. Over time, this led to some bolted-on bits and some gaps in the matching/parsing.

Additionally, the current ArrayMatcher and TrieMatcher aren't guaranteed to return the same "best" match. ArrayMatcher just bails as soon as it finds its first match and TrieMatcher does some ad-hoc scoring.

The goal moving forward is to spec out the parsing/matching (via unit tests + docs) and implement a simple, consistent ranking metric for ArrayMatcher, TrieMatcher, or any other matcher.

Here's my plan:

  • PartPattern.{parse,variants,toString} (this PR)
  • RoutePattern.{parse,join}
  • TrieMatcher.{add,matchAll} (w/o ranking)
  • Ranking + TrieMatcher.{match, matchAll}
  • ArrayMatcher.{add,match,matchAll}

PartPattern

As a first step towards this goal, this PR implements a new design for the parts of a Route Pattern that are allowed to use the pattern DSL. Today, that includes the protocol, hostname, and pathname, but in the future its likely that the protocol will be simplified to just accept the known network schemes (http, https, http(s), ws, wss, ws(s), ftp, ...).

Knowing that we want to eventually use PartPatterns within RoutePatterns to power a TrieMatcher, this new implementation aims for a simpler flat AST:

image

This AST representation has a couple benefits over the previous implementation:

Traversal is simpler and faster since its just a for-loop where you can decide to skip any optionals as you go. Specifically, variants (for construction trie branches) are trivial to implement.

Feature detection is simpler and faster. For example, does this part pattern have any params? Check ast.paramNames.length > 0? Any optionals? Check ast.optionals.size > 0. Etc.

Joining patterns is more efficient. Previously, RoutePattern.join reparsed the entire pathname on every invocation whereas with this AST, we can simple concatenate the two pathnames (adding a / text node between them as necessary) and quickly apply an offset to the second pattern's paramNames and optionals.

To be clear, the goal isn't necessary to be faster at parsing part patterns — that can come later with optimizations if needed — but to have a purpose-built structure for our DSL.

@pcattori pcattori force-pushed the pedro/part-pattern branch from effd092 to 75c1cad Compare January 9, 2026 16:14
@pcattori pcattori merged commit 0afa2f4 into pedro/route-pattern Jan 12, 2026
5 checks passed
@pcattori pcattori deleted the pedro/part-pattern branch January 12, 2026 19:02
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.

2 participants