Skip to content

Commit 20521b0

Browse files
babu-chposva
andauthored
fix(matcher): finalize param token before processing escaped colon (#2654)
Co-authored-by: Eduardo San Martin Morote <posva13@gmail.com>
1 parent a6289cd commit 20521b0

2 files changed

Lines changed: 148 additions & 7 deletions

File tree

packages/router/__tests__/matcher/pathParser.spec.ts

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,132 @@ describe('Path parser', () => {
3030
])
3131
})
3232

33+
it('escapes non-colon char after param', () => {
34+
expect(tokenizePath('/:foo\\-abc')).toEqual([
35+
[
36+
{
37+
type: TokenType.Param,
38+
value: 'foo',
39+
regexp: '',
40+
repeatable: false,
41+
optional: false,
42+
},
43+
{ type: TokenType.Static, value: '-abc' },
44+
],
45+
])
46+
})
47+
48+
it('escapes ) inside custom re to allow nested groups', () => {
49+
expect(tokenizePath('/:id((?:a-[^/]+\\)?)')).toEqual([
50+
[
51+
{
52+
type: TokenType.Param,
53+
value: 'id',
54+
regexp: '(?:a-[^/]+)?',
55+
repeatable: false,
56+
optional: false,
57+
},
58+
],
59+
])
60+
})
61+
62+
it('escapes + after empty custom re', () => {
63+
expect(tokenizePath('/:p()\\+')).toEqual([
64+
[
65+
{
66+
type: TokenType.Param,
67+
value: 'p',
68+
regexp: '',
69+
repeatable: false,
70+
optional: false,
71+
},
72+
{ type: TokenType.Static, value: '+' },
73+
],
74+
])
75+
})
76+
77+
it('escapes + after param', () => {
78+
expect(tokenizePath('/:p\\+')).toEqual([
79+
[
80+
{
81+
type: TokenType.Param,
82+
value: 'p',
83+
regexp: '',
84+
repeatable: false,
85+
optional: false,
86+
},
87+
{ type: TokenType.Static, value: '+' },
88+
],
89+
])
90+
})
91+
92+
it('escapes : after optional param with custom re', () => {
93+
expect(tokenizePath('/:foo([^:]+)?\\:abc')).toEqual([
94+
[
95+
{
96+
type: TokenType.Param,
97+
value: 'foo',
98+
regexp: '[^:]+',
99+
repeatable: false,
100+
optional: true,
101+
},
102+
{ type: TokenType.Static, value: ':abc' },
103+
],
104+
])
105+
})
106+
107+
it('escapes : after param', () => {
108+
expect(tokenizePath('/:foo\\:abc')).toEqual([
109+
[
110+
{
111+
type: TokenType.Param,
112+
value: 'foo',
113+
regexp: '',
114+
repeatable: false,
115+
optional: false,
116+
},
117+
{ type: TokenType.Static, value: ':abc' },
118+
],
119+
])
120+
})
121+
122+
it('escapes : after param with custom re', () => {
123+
expect(tokenizePath('/:foo([^:]+)\\:abc')).toEqual([
124+
[
125+
{
126+
type: TokenType.Param,
127+
value: 'foo',
128+
regexp: '[^:]+',
129+
repeatable: false,
130+
optional: false,
131+
},
132+
{ type: TokenType.Static, value: ':abc' },
133+
],
134+
])
135+
})
136+
137+
it('escapes : between two params', () => {
138+
expect(tokenizePath('/:foo([^:]+)\\::bar')).toEqual([
139+
[
140+
{
141+
type: TokenType.Param,
142+
value: 'foo',
143+
regexp: '[^:]+',
144+
repeatable: false,
145+
optional: false,
146+
},
147+
{ type: TokenType.Static, value: ':' },
148+
{
149+
type: TokenType.Param,
150+
value: 'bar',
151+
regexp: '',
152+
repeatable: false,
153+
optional: false,
154+
},
155+
],
156+
])
157+
})
158+
33159
// not sure how useful this is and if it's worth supporting because of the
34160
// cost to support the ranking as well
35161
it.skip('groups', () => {
@@ -808,6 +934,24 @@ describe('Path parser', () => {
808934
})
809935
})
810936

937+
it('param followed by escaped colon and static', () => {
938+
matchParams('/:foo\\:abc', '/section:abc', { foo: 'section' })
939+
matchParams('/:foo\\:abc', '/sectionabc', null)
940+
})
941+
942+
it('param with custom re followed by escaped colon and another param', () => {
943+
matchParams('/:foo([^:]+)\\::bar', '/section:aaabbbccc', {
944+
foo: 'section',
945+
bar: 'aaabbbccc',
946+
})
947+
})
948+
949+
it('escaped + becomes part of static path, not a repeat modifier', () => {
950+
matchParams('/:p\\+', '/abc+', { p: 'abc' })
951+
matchParams('/:p\\+', '/abc', null)
952+
matchParams('/:p()\\+', '/abc+', { p: 'abc' })
953+
})
954+
811955
// end of parsing urls
812956
})
813957

packages/router/src/matcher/pathTokenizer.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,12 @@ export function tokenizePath(path: string): Array<Token[]> {
118118
while (i < path.length) {
119119
char = path[i++]
120120

121-
if (char === '\\' && state !== TokenizerState.ParamRegExp) {
122-
previousState = state
123-
state = TokenizerState.EscapeNext
124-
continue
125-
}
126-
127121
switch (state) {
128122
case TokenizerState.Static:
129-
if (char === '/') {
123+
if (char === '\\') {
124+
previousState = state
125+
state = TokenizerState.EscapeNext
126+
} else if (char === '/') {
130127
if (buffer) {
131128
consumeBuffer()
132129
}

0 commit comments

Comments
 (0)