Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions packages/router/__tests__/matcher/pathParser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -865,6 +865,37 @@ describe('Path parser', () => {
})
})

it('repeatable catch all', () => {
matchParams('/:rest(.*)*/end', '/end', { rest: '' })
matchParams('/:rest(.*)*/end', '/a/end', { rest: ['a'] })
matchParams('/:rest(.*)*/end', '/a/b/c/end', { rest: ['a', 'b', 'c'] })
matchParams('/:rest(.*)*/end', '//end', { rest: '' })
matchParams('/:rest(.*)*/end', '/a/b/c/endx', null)
matchParams('/:rest(.*)+/end', '/a/b/c/end', { rest: ['a', 'b', 'c'] })
matchParams('/:rest(.*)+/end', '/end', null)
})

it('repeatable lazy catch all keeps per-segment captures', () => {
matchParams('/:rest(.*?)*/b/:other(.*)', '/a/b/c/b/d', {
rest: ['a', 'b', 'c'],
other: 'd',
})
matchParams('/:rest(.+?)*/b/:other(.*)', '/a/b/c/b/d', {
rest: ['a', 'b', 'c'],
other: 'd',
})
})

it('repeatable .-wildcard params do not produce an ambiguous repeat pattern', () => {
const pathParser = tokensToParser(tokenizePath('/:rest(.*)*/end'))
expect(pathParser.re.source).not.toContain('(?:\\/(?:.*))*')
// non `.` based or lazy repeatable params still need the repeat wrapper
const nonWildcard = tokensToParser(tokenizePath('/:rest([^/]*)*/end'))
expect(nonWildcard.re.source).toContain('(?:\\/(?:[^/]*))*')
const lazy = tokensToParser(tokenizePath('/:rest(.*?)*/end'))
expect(lazy.re.source).toContain('(?:\\/(?:.*?))*')
})

it('param multiple', () => {
matchParams('/:a-:b-:c', '/one-two-three', {
a: 'one',
Expand Down
6 changes: 5 additions & 1 deletion packages/router/src/matcher/pathParserRanker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,11 @@ export function tokensToParser(
}

// when we repeat we must take care of the repeating leading slash
let subPattern = repeatable ? `((?:${re})(?:/(?:${re}))*)` : `(${re})`
// Greedy dot wildcards match `/`; lazy variants need the repeat wrapper.
let subPattern =
repeatable && re !== '.+' && re !== '.*'
? `((?:${re})(?:/(?:${re}))*)`
: `(${re})`

// prepend the slash if we are starting a new segment
if (!tokenIndex)
Expand Down
Loading