diff --git a/src/router.js b/src/router.js index 02e68b0..80e1f53 100644 --- a/src/router.js +++ b/src/router.js @@ -75,7 +75,7 @@ export const exec = (url, route, matches = {}) => { if (!m && param == val) continue; // /foo/* match if (!m && val && flag == '*') { - matches.rest = '/' + url.slice(i).map(decodeURIComponent).join('/'); + matches.rest = '/' + url.slice(i).join('/'); break; } // segment mismatch / missing required field: diff --git a/test/node/router-match.test.js b/test/node/router-match.test.js index 266a79e..ba90952 100644 --- a/test/node/router-match.test.js +++ b/test/node/router-match.test.js @@ -116,6 +116,11 @@ test('Handles leading/trailing slashes', () => { }); }); +test('Percent-encoded characters in rest are not decoded', () => { + const result = execPath('/nested/child/%25', '/nested/*'); + assert.equal(result, { path: '/nested/child/%25', params: {}, query: {}, rest: '/child/%25' }); +}); + test('should not overwrite existing properties', () => { const result = execPath('/foo/bar', '/:path/:query', { path: '/custom-path' }); assert.equal(result, { diff --git a/test/router.test.js b/test/router.test.js index de12b91..349e957 100644 --- a/test/router.test.js +++ b/test/router.test.js @@ -927,6 +927,35 @@ describe('Router', () => { expect(params).to.deep.include({ id: 'bar' }); }); + it('should not double-decode percent-encoded characters in nested routes', async () => { + let route; + const Inner = () => ( + + { + route = useRoute(); + return null; + }} + /> + + ); + + render( + + + + + + , + scratch + ); + + scratch.querySelector('a[href="/nested/child/%25"]').click(); + await sleep(1); + expect(route).to.deep.include({ params: { id: '%' } }); + }); + it('should replace the current URL', async () => { const pushState = sinon.spy(history, 'pushState'); const replaceState = sinon.spy(history, 'replaceState');