Skip to content

feat(routing): adopt roux 0.5 path syntax#155

Merged
medz merged 2 commits intomainfrom
feat/roux-0-5-path-syntax
Mar 11, 2026
Merged

feat(routing): adopt roux 0.5 path syntax#155
medz merged 2 commits intomainfrom
feat/roux-0-5-path-syntax

Conversation

@medz
Copy link
Copy Markdown
Owner

@medz medz commented Mar 11, 2026

Summary by CodeRabbit

  • New Features

    • Expressive file-route segment syntax: embedded params, regex, optional and repeated params, and single-segment wildcards.
    • Wildcard routing upgraded from single-level to recursive remainder matching (/**), enabling nested-path matches.
  • Documentation

    • Routing guide updated with examples, mapping notes, and expressive-segment reference.
  • Breaking / Migration

    • Manual route strings using /* must be updated to /**; re-run route tests after upgrading.

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 11, 2026

Deploy Preview for dart-spry canceled.

Name Link
🔨 Latest commit ad860bd
🔍 Latest deploy log https://app.netlify.com/projects/dart-spry/deploys/69b1c560a9633c00084e2783

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1fed72ed-3101-4913-bdc8-e9d5cd4ddde0

📥 Commits

Reviewing files that changed from the base of the PR and between 231673c and ad860bd.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • lib/src/builder/generator.dart
  • lib/src/builder/scanner.dart
  • test/fixtures/scanner/catch_all_index_dir/routes/docs/[...slug]/index.dart
  • test/fixtures/scanner/duplicate_param_names/routes/files/[name].[name].dart
  • test/fixtures/scanner/duplicate_param_names/routes/users/[id]/[id].dart
  • test/fixtures/scanner/non_terminal_catch_all/routes/[...slug]/tail.dart
  • test/generator_test.dart
  • test/scanner_test.dart
✅ Files skipped from review due to trivial changes (4)
  • test/fixtures/scanner/non_terminal_catch_all/routes/[...slug]/tail.dart
  • test/fixtures/scanner/duplicate_param_names/routes/users/[id]/[id].dart
  • test/fixtures/scanner/duplicate_param_names/routes/files/[name].[name].dart
  • CHANGELOG.md

📝 Walkthrough

Walkthrough

This PR upgrades the routing system to Roux 0.5.x, replacing single-segment wildcard paths (/*) with recursive wildcards (/**), and introduces expressive route segment syntax supporting embedded params, regex constraints, optional segments, repeated wildcards, and single-segment catch-alls. Scanner and generator components are refactored to parse and generate these new segment formats.

Changes

Cohort / File(s) Summary
Dependency & Release Notes
pubspec.yaml, CHANGELOG.md
Bumped roux from ^0.4.0 to ^0.5.0 and added Unreleased notes describing the switch from /* to /** remainder syntax and the new expressive file-route segment parsing.
Core Route Scanner Refactoring
lib/src/builder/scanner.dart
Major refactor: token-based per-segment parsing added (_ParsedSegment, _ParsedParam), new parsing helpers (_parseSegment, _parseParamToken, etc.), and _NormalizedPath updated to hasRemainderWildcard/isTerminalRemainderWildcard. Scope and catch-all semantics changed to use /**.
Wildcard Wrapper Generation
lib/src/builder/generator.dart
Refactored wildcard wrapper to compute local wildcard via event.params.get(name) ?? event.params.wildcard and construct Event with value-based conditional param insertion instead of inline conditionals.
Routing Documentation
sites/spry.medz.dev/guide/routing.md
Reworded "wildcard" to "remainder" matches and added an "Expressive segment syntax" section mapping filesystem-safe filenames to route segment forms (including examples like [...slug].dart/**:slug).
Test Fixtures for Expressive Syntax
test/fixtures/scanner/expressive/routes/**
Added multiple fixture files demonstrating expressive patterns: archive/[[...rest]].dart, assets/[...path+].dart, docs/[[section]].dart, files/[name].[ext].dart, posts/post-[id].json.get.dart, users/[_].dart, users/[id([0-9]+)].dart.
Route & Middleware Test Updates
test/app_test.dart, test/routing_test.dart
Updated tests to replace /* with /** (global and scoped middleware, error routes) to reflect recursive remainder semantics and adjusted expectations accordingly.
Generator & Scanner Tests
test/generator_test.dart, test/scanner_test.dart
Updated generator expectations for local wildcard extraction and multi-segment wildcard paths; added tests validating expressive segment parsing, duplicate param name rejection, and terminal-index catch-all behavior.
Misc Test Fixtures
test/fixtures/... (duplicate_param_names, non_terminal_catch_all, etc.)
Added small fixture files used by new scanner tests (placeholders with // fixture).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • medz/spry PR 152: Modifies scanner.dart and generator.dart around route segment parsing and wildcard generation; strongly related to the remainder-wildcard and expressive-segment changes in this PR.

Poem

🐰 Hop, hop—through folders I roam with glee,
/** spreads wide where /* used to be,
Tokens dance, params nest and play,
The scanner sings a brighter way,
A rabbit cheers: new routes set free!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adopting roux 0.5 path syntax with ** remainder matches, updated routing semantics, and new expressive segment syntax throughout the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/roux-0-5-path-syntax

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CHANGELOG.md`:
- Around line 3-4: The changelog entries fail to mark the path-syntax migration
as breaking: update the release notes to add a BREAKING change and a short
migration note explaining that the public string-path API changed from "/*" to
"/**" (affecting consumer code that constructs Spry, MiddlewareRoute, and
ErrorRoute instances manually); explicitly call out the symbols Spry,
MiddlewareRoute, ErrorRoute and instruct consumers to replace occurrences of
"/*" with "/**" in their route strings and to test route matching after
upgrading to roux 0.5.x.

In `@lib/src/builder/generator.dart`:
- Around line 81-82: The closure returned around the route parameter lookup
currently prefers event.params.wildcard before the named param, which causes
named captures (e.g., [...slug]) to be shadowed by the generic 'wildcard' key;
change the lookup in the returned function so it reads the named value first via
event.params.get(name) and only falls back to event.params.wildcard
(RouteParams.wildcard) if that returns null/empty, updating the logic in the
closure that computes final wildcard value.

In `@lib/src/builder/scanner.dart`:
- Around line 300-304: The code determines isTerminal and routeShape using
rawSegments including a trailing "index" file, so catch-all dirs like
[...slug]/index.dart are misclassified; before calling _parseSegment compute an
effectiveSegments list that strips a single trailing index file (match "index"
or filename with basename "index" like "index.dart") and then use isTerminal: i
== effectiveSegments.length - 1 and routeShape: effectiveSegments.join('/') when
calling _parseSegment (update the same logic at the other occurrence around
lines 311-313). Ensure you only strip one trailing index and leave other
segments unchanged.
- Around line 292-307: The scanner currently accumulates parsed.paramNames into
the route-local paramNames map but only checks for name collisions across
routes; update the loop that processes rawSegments so that after calling
_parseSegment and before adding parsed.paramNames into paramNames you detect
duplicates within the same normalized path: check for repeated names inside
parsed.paramNames and for any overlap between parsed.paramNames and the existing
paramNames for this route, and if found throw a scan/validation error (with a
clear message referencing the duplicate param) so routes like `[id]/[id].dart`
or `[name].[name].dart` are rejected.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34603120-94d0-433c-8f04-42afbd92b513

📥 Commits

Reviewing files that changed from the base of the PR and between 1c7481f and 231673c.

📒 Files selected for processing (16)
  • CHANGELOG.md
  • lib/src/builder/generator.dart
  • lib/src/builder/scanner.dart
  • pubspec.yaml
  • sites/spry.medz.dev/guide/routing.md
  • test/app_test.dart
  • test/fixtures/scanner/expressive/routes/archive/[[...rest]].dart
  • test/fixtures/scanner/expressive/routes/assets/[...path+].dart
  • test/fixtures/scanner/expressive/routes/docs/[[section]].dart
  • test/fixtures/scanner/expressive/routes/files/[name].[ext].dart
  • test/fixtures/scanner/expressive/routes/posts/post-[id].json.get.dart
  • test/fixtures/scanner/expressive/routes/users/[_].dart
  • test/fixtures/scanner/expressive/routes/users/[id([0-9]+)].dart
  • test/generator_test.dart
  • test/routing_test.dart
  • test/scanner_test.dart

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