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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [10.0.0] - 2025-05-02

- Update dependency [path-to-regexp](https://www.npmjs.com/package/path-to-regexp) to version 8.2.0 from 6.2.0.
- Primarily inlcluds [major changes from 7.0](https://github.com/pillarjs/path-to-regexp/releases/tag/v7.0.0)

## [9.2.1] - 2024-11-22

- Enable `noPropertyAccessFromIndexSignature` and `noUncheckedIndexedAccess` checks ([#216](https://github.com/kriasoft/universal-router/pull/216))
Expand Down
972 changes: 669 additions & 303 deletions dist/universal-router-generate-urls.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router-generate-urls.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router-generate-urls.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router-generate-urls.min.js.map

Large diffs are not rendered by default.

1,161 changes: 724 additions & 437 deletions dist/universal-router-sync.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router-sync.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router-sync.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router-sync.min.js.map

Large diffs are not rendered by default.

1,167 changes: 727 additions & 440 deletions dist/universal-router.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/universal-router.min.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ const urlWithQueryString = generateUrls(router, {
stringifyQueryParams: (params) => new URLSearchParams(params).toString()
})

const params = { username: 'John', busy: 1 }
const params = { username: 'John', busy: '1' }
url('user', params) // => /base/user/John
urlWithQueryString('user', params) // => /base/user/John?busy=1
```
Expand Down
4 changes: 2 additions & 2 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import UniversalRouter from 'universal-router'
const routes = [
{ path: '/one', action: () => '<h1>Page One</h1>' },
{ path: '/two', action: () => '<h1>Page Two</h1>' },
{ path: '(.*)', action: () => '<h1>Not Found</h1>' }
{ path: '/*all', action: () => '<h1>Not Found</h1>' }
]

const router = new UniversalRouter(routes)
Expand Down Expand Up @@ -61,7 +61,7 @@ import UniversalRouter from 'universal-router'
const routes = [
{ path: '/one', action: () => <h1>Page One</h1> },
{ path: '/two', action: () => <h1>Page Two</h1> },
{ path: '(.*)', action: () => <h1>Not Found</h1> }
{ path: '/*all', action: () => <h1>Not Found</h1> }
]

const router = new UniversalRouter(routes)
Expand Down
22 changes: 13 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "universal-router",
"version": "9.2.1",
"version": "10.0.0",
"description": "Isomorphic router for JavaScript web applications",
"homepage": "https://www.kriasoft.com/universal-router/",
"repository": "kriasoft/universal-router",
Expand All @@ -23,7 +23,7 @@
"module": "module.js",
"types": "src/UniversalRouter.d.ts",
"dependencies": {
"path-to-regexp": "^6.2.0"
"path-to-regexp": "^8.2.0"
},
"devDependencies": {
"@babel/core": "7.14.6",
Expand Down
8 changes: 4 additions & 4 deletions src/UniversalRouter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ test('supports next() across multiple routes', async () => {
},
},
{
path: '(.*)',
path: '',
action(): void {
log.push(9)
},
Expand Down Expand Up @@ -316,7 +316,7 @@ test('supports next() across multiple routes', async () => {
},
},
{
path: '/*',
path: '/*all',
action(): void {
log.push(13)
},
Expand Down Expand Up @@ -622,7 +622,7 @@ test('decodes params correctly', async () => {
})

test('decodes repeated parameters correctly', async () => {
const router = new UniversalRouter({ path: '/:a+', action: (ctx): object => ctx.params })
const router = new UniversalRouter({ path: '/*a', action: (ctx): object => ctx.params })
await expect(router.resolve('/x%2Fy/z/%20/%AF')).resolves.toStrictEqual({
a: ['x/y', 'z', ' ', '%AF'],
})
Expand Down Expand Up @@ -672,7 +672,7 @@ test('matches 0 routes (6)', async () => {

test('matches 0 routes (7)', async () => {
const action: jest.Mock = jest.fn(() => true)
const route = { path: '/:a+', action, children: [] }
const route = { path: '/*a', action, children: [] }
await expect(new UniversalRouter(route).resolve('')).rejects.toThrow(/Route not found/)
expect(action.mock.calls.length).toBe(0)
})
Expand Down
12 changes: 7 additions & 5 deletions src/UniversalRouter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
Match,
MatchFunction,
ParseOptions,
TokensToRegexpOptions,
RegexpToFunctionOptions,
MatchOptions,
PathToRegexpOptions,
CompileOptions

Check failure on line 18 in src/UniversalRouter.ts

View workflow job for this annotation

GitHub Actions / build (14.x)

Insert `,`

Check failure on line 18 in src/UniversalRouter.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Insert `,`
} from 'path-to-regexp'

/**
Expand Down Expand Up @@ -80,7 +81,7 @@
/**
* A string, array of strings, or a regular expression. Defaults to an empty string.
*/
path?: Path
path?: Path | Path[]
/**
* A unique string that can be used to generate the route URL.
*/
Expand Down Expand Up @@ -123,8 +124,9 @@

export interface RouterOptions<R = any, C extends RouterContext = RouterContext>
extends ParseOptions,
TokensToRegexpOptions,
RegexpToFunctionOptions {
MatchOptions,
PathToRegexpOptions,
CompileOptions {
context?: C
baseUrl?: string
resolveRoute?: ResolveRoute<R, C>
Expand Down
8 changes: 4 additions & 4 deletions src/UniversalRouterSync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ test('supports next() across multiple routes', () => {
},
},
{
path: '(.*)',
path: '',
action(): void {
log.push(9)
},
Expand Down Expand Up @@ -316,7 +316,7 @@ test('supports next() across multiple routes', () => {
},
},
{
path: '/*',
path: '/*all',
action(): void {
log.push(13)
},
Expand Down Expand Up @@ -619,7 +619,7 @@ test('decodes params correctly', () => {
})

test('decodes repeated parameters correctly', () => {
const router = new UniversalRouter({ path: '/:a+', action: (ctx): object => ctx.params })
const router = new UniversalRouter({ path: '/*a', action: (ctx): object => ctx.params })
expect(router.resolve('/x%2Fy/z/%20/%AF')).toStrictEqual({
a: ['x/y', 'z', ' ', '%AF'],
})
Expand Down Expand Up @@ -669,7 +669,7 @@ test('matches 0 routes (6)', () => {

test('matches 0 routes (7)', () => {
const action: jest.Mock = jest.fn(() => true)
const route = { path: '/:a+', action, children: [] }
const route = { path: '/*a', action, children: [] }
expect(() => new UniversalRouter(route).resolve('')).toThrow(/Route not found/)
expect(action.mock.calls.length).toBe(0)
})
Expand Down
12 changes: 7 additions & 5 deletions src/UniversalRouterSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
Match,
MatchFunction,
ParseOptions,
TokensToRegexpOptions,
RegexpToFunctionOptions,
MatchOptions,
PathToRegexpOptions,
CompileOptions

Check failure on line 18 in src/UniversalRouterSync.ts

View workflow job for this annotation

GitHub Actions / build (14.x)

Insert `,`

Check failure on line 18 in src/UniversalRouterSync.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Insert `,`
} from 'path-to-regexp'

/**
Expand Down Expand Up @@ -79,7 +80,7 @@
/**
* A string, array of strings, or a regular expression. Defaults to an empty string.
*/
path?: Path
path?: Path | Path[]
/**
* A unique string that can be used to generate the route URL.
*/
Expand Down Expand Up @@ -124,8 +125,9 @@

export interface RouterOptions<R = any, C extends RouterContext = RouterContext>
extends ParseOptions,
TokensToRegexpOptions,
RegexpToFunctionOptions {
MatchOptions,
PathToRegexpOptions,
CompileOptions {
context?: C
baseUrl?: string
resolveRoute?: ResolveRoute<R, C>
Expand Down
25 changes: 12 additions & 13 deletions src/generateUrls.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ test('generates url for named routes', () => {
const router1 = new UniversalRouter({ path: '/:name', name: 'user' })
const url1 = generateUrls(router1)
expect(url1('user', { name: 'koistya' })).toBe('/koistya')
expect(() => url1('user')).toThrow(/Expected "name" to be a string/)
expect(() => url1('user')).toThrow(/Missing parameters: name/)

const router2 = new UniversalRouter({ path: '/user/:id', name: 'user' })
const url2 = generateUrls(router2)
expect(url2('user', { id: 123 })).toBe('/user/123')
expect(() => url2('user')).toThrow(/Expected "id" to be a string/)
expect(url2('user', { id: '123' })).toBe('/user/123')
expect(() => url2('user')).toThrow(/Missing parameters: id/)

const router3 = new UniversalRouter({ path: '/user/:id' })
const url3 = generateUrls(router3)
Expand All @@ -55,9 +55,9 @@ test('generates url for routes with array of paths', () => {
const url1 = generateUrls(router1)
expect(url1('user', { name: 'koistya' })).toBe('/koistya')

const router2 = new UniversalRouter({ path: ['/user/:id', /\/user\/(\d+)/i], name: 'user' })
const router2 = new UniversalRouter({ path: ['/user/:id', '/user/(\\d+)'], name: 'user' })
const url2 = generateUrls(router2)
expect(url2('user', { id: 123 })).toBe('/user/123')
expect(url2('user', { id: '123' })).toBe('/user/123')

const router3 = new UniversalRouter({ path: [], name: 'user' })
const url3 = generateUrls(router3)
Expand Down Expand Up @@ -85,7 +85,7 @@ test('generates url for nested routes', () => {
})
const url = generateUrls(router)
expect(url('a')).toBe('/')
expect(url('b', { x: 123 })).toBe('/b/123')
expect(url('b', { x: '123' })).toBe('/b/123')
expect(url('c', { x: 'i', y: 'j' })).toBe('/b/i/c/j')

if (Array.isArray(router.root.children)) {
Expand All @@ -103,7 +103,7 @@ test('respects baseUrl', () => {

const router2 = new UniversalRouter({ path: '/post/:id', name: 'post' }, options)
const url2 = generateUrls(router2)
expect(url2('post', { id: 12, x: 'y' })).toBe('/base/post/12')
expect(url2('post', { id: '12', x: 'y' })).toBe('/base/post/12')

const router3 = new UniversalRouter(
{
Expand Down Expand Up @@ -169,8 +169,7 @@ test('encodes params', () => {

const url = generateUrls(router)
const prettyUrl = generateUrls(router, {
encode(str, token) {
expect(token.name).toBe('user')
encode(str) {
return encodeURI(str).replace(
/[/?#]/g,
(c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`,
Expand All @@ -188,9 +187,9 @@ test('stringify query params (1)', () => {

const url = generateUrls(router, { stringifyQueryParams })

expect(url('user', { user: 'tj', busy: 1 })).toBe('/tj?qs')
expect(url('user', { user: 'tj', busy: '1' })).toBe('/tj?qs')
expect(stringifyQueryParams.mock.calls.length).toBe(1)
expect(stringifyQueryParams.mock.calls[0][0]).toEqual({ busy: 1 })
expect(stringifyQueryParams.mock.calls[0][0]).toEqual({ busy: '1' })
})

test('stringify query params (2)', () => {
Expand All @@ -199,9 +198,9 @@ test('stringify query params (2)', () => {

const url = generateUrls(router, { stringifyQueryParams })

expect(url('user', { username: 'tj', busy: 1 })).toBe('/user/tj')
expect(url('user', { username: 'tj', busy: '1' })).toBe('/user/tj')
expect(stringifyQueryParams.mock.calls.length).toBe(1)
expect(stringifyQueryParams.mock.calls[0][0]).toEqual({ busy: 1 })
expect(stringifyQueryParams.mock.calls[0][0]).toEqual({ busy: '1' })
})

test('stringify query params (3)', () => {
Expand Down
22 changes: 13 additions & 9 deletions src/generateUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@
* LICENSE.txt file in the root directory of this source tree.
*/

import {

Check failure on line 10 in src/generateUrls.ts

View workflow job for this annotation

GitHub Actions / build (14.x)

Replace `⏎··parse,⏎··ParseOptions,⏎··compile,⏎··CompileOptions,⏎··PathFunction,⏎` with `·parse,·ParseOptions,·compile,·CompileOptions,·PathFunction·`

Check failure on line 10 in src/generateUrls.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Replace `⏎··parse,⏎··ParseOptions,⏎··compile,⏎··CompileOptions,⏎··PathFunction,⏎` with `·parse,·ParseOptions,·compile,·CompileOptions,·PathFunction·`
parse,
ParseOptions,
tokensToFunction,
TokensToFunctionOptions,
compile,
CompileOptions,
PathFunction,
} from 'path-to-regexp'
import UniversalRouter, { Route, Routes } from './UniversalRouter'

export interface UrlParams {
[paramName: string]: string | number | (string | number)[]
[paramName: string]: string | string[]
}

export interface GenerateUrlsOptions extends ParseOptions, TokensToFunctionOptions {
export interface GenerateUrlsOptions extends ParseOptions, CompileOptions {
/**
* Add a query string to generated url based on unknown route params.
*/
Expand Down Expand Up @@ -113,12 +113,16 @@
rt = rt.parent
}
const tokens = parse(fullPath, opts)
const toPath = tokensToFunction(tokens, opts)
const toPath = compile(fullPath, opts)
const keys: Keys = Object.create(null)
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (token && typeof token !== 'string') {
keys[token.name] = true
for (let i = 0; i < tokens.tokens.length; i++) {
const token = tokens.tokens[i]
if (token && token.type !== 'text') {
if (token.type === 'group') {
keys[String(i)] = true
} else {
keys[token.name] = true
}
}
}
regexp = { toPath, keys }
Expand Down
Loading