Repository navigation
fix(web): decode percent-encoded route params in matchRoute - #25
Conversation
Applies decodeURIComponent() to each dynamic segment value so URLs like /coach/white%2Dcross correctly match lesson id white-cross instead of silently rendering LessonNotFound. Also exports matchRoute for direct testing, adds two tests covering percent-encoded and plain params, and fixes the ESLint parserOptions conflict that was causing router.spec.tsx to fail lint (project: ./tsconfig.json in the web config was overriding project: true from the shared base, forcing legacy program mode; removing the override lets the base projectService setting handle all files correctly). Closes #20 Co-authored-by: Titux Metal <TituxMetal@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes client-side route matching for percent-encoded dynamic segments (e.g. lesson IDs) by decoding route params during matchRoute, so encoded URLs correctly resolve to existing Coach lessons instead of rendering LessonNotFound. It also makes matchRoute directly testable and adjusts the web ESLint flat config to avoid overriding the shared TypeScript “project service mode” parser settings.
Changes:
- Decode dynamic route params via
decodeURIComponent()insidematchRouteand exportmatchRoutefor direct testing. - Add unit tests covering percent-encoded and plain dynamic params.
- Remove the web ESLint config override of
parserOptions.projectto restore the shared base config behavior.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| apps/web/src/lib/router.tsx | Export matchRoute and decode dynamic route params during route matching. |
| apps/web/src/lib/router.spec.tsx | Add direct matchRoute tests for encoded vs. plain params. |
| apps/web/eslint.config.mjs | Stop overriding parserOptions.project so the shared ESLint TypeScript settings apply. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@claude Please address the two Copilot review comments on this PR:
Follow project conventions: no semicolons, arrow functions only, named exports, |
|
Claude finished @TituxMetal's task in 2m 56s —— View job Addressing Copilot Review Comments
Changes made
try {
params[expected.slice(1)] = decodeURIComponent(actual)
} catch {
return null
}
it('returns null for a malformed percent-encoded param without throwing', () => {
const result = matchRoute({ '/coach/:id': render }, '/coach/%E0%A4')
expect(result).toBeNull()
})All pre-push checks green: |
Wrap decodeURIComponent in try/catch inside matchRoute so a URIError from a malformed segment (e.g. %E0%A4) returns null instead of crashing the router. Adds a regression test to cover this case. Co-authored-by: Titux Metal <TituxMetal@users.noreply.github.com>
Applies
decodeURIComponent()to each dynamic segment value inmatchRouteso URLs like/coach/white%2Dcrosscorrectly match lesson idwhite-crossinstead of silently rendering LessonNotFound.Also exports
matchRoutefor direct testing, adds two tests covering percent-encoded and plain params, and fixes a pre-existing ESLint failure inrouter.spec.tsxcaused by the web ESLint config overriding the shared base'sproject: true(project service mode) withproject: './tsconfig.json'(legacy program mode).Closes #20
Generated with Claude Code