Skip to content

Custom Route Resolvers #2415

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 40 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
474596f
feat: wip new matcher
posva Jun 25, 2024
745dabc
test: check parsed urls
posva Jun 25, 2024
3b893cf
chore: build location
posva Jun 25, 2024
f89a842
perf: parseURL minor improvements
posva Jun 26, 2024
38606b9
refactor: avoid double decoding
posva Jun 26, 2024
af7afb5
refactor: add fullPath
posva Jun 26, 2024
f26e919
chore: static path matcher
posva Jun 26, 2024
b7204fa
chore: error matches
posva Jun 26, 2024
684e970
test: static matcher
posva Jun 26, 2024
7cec10b
refactor: unused code
posva Jul 9, 2024
b0e0f0d
chore: ignore temp tsconfig
posva Dec 4, 2024
42bd1ac
test: better IM after hash
posva Dec 5, 2024
8204744
test: url parsing
posva Dec 5, 2024
ca95567
refactor: simplify parseURL
posva Dec 6, 2024
17bb729
chore: comment
posva Dec 6, 2024
f8cdff0
chore: comments
posva Dec 6, 2024
0d86f5a
refactor: renames and minor changes
posva Dec 6, 2024
c18e14f
refactor: simplify matcher interfaces
posva Dec 9, 2024
e1de9e6
refactor: remove unused code
posva Dec 9, 2024
00eef15
refactor: rename matcher-pattern
posva Dec 9, 2024
d061304
refactor: add methods needed by router
posva Dec 10, 2024
89035ea
feat: new dynamic path matcher
posva Dec 16, 2024
84ac19e
refactor: reorganize types and add initial experimental router
posva Dec 17, 2024
9af1441
chore: comments
posva Dec 23, 2024
e080bff
refactor: simplify router resolve
posva Dec 23, 2024
047858d
chore: wip encoding
posva Dec 24, 2024
a8e01d3
chore: small fix
posva Jan 7, 2025
0efc390
refactor: rename matcher to resolver
posva Jan 8, 2025
2d17e5b
chore: remove unused
posva Jan 8, 2025
1471a07
test: fix ts errors
posva Jan 8, 2025
9780e91
test: remove old matcher refs
posva Jan 8, 2025
800721f
chore: remove last ts errors
posva Jan 8, 2025
e9ba885
feat: support partial locations
posva Jan 8, 2025
3fe030a
chore: rename
posva Jan 9, 2025
b7b0bbf
feat: allow string in matcher resolve
posva Jan 9, 2025
a218b24
test: stricter no match test
posva Jan 9, 2025
ef995f4
feat: handle children
posva Jan 9, 2025
4b8ac59
refactor: simplify new resolver to be static
posva Jul 17, 2025
46aa26d
build: use tsdown
posva Jul 18, 2025
bce9486
docs: upgrade typedoc
posva Jul 18, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ local.log
_selenium-server.log
packages/*/LICENSE
tracing_output
tsconfig.vitest-temp.json
16 changes: 10 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@vue/router-root",
"private": true,
"packageManager": "pnpm@9.10.0",
"packageManager": "pnpm@10.13.1",
"type": "module",
"engines": {
"node": ">=20.9.0"
Expand Down Expand Up @@ -36,17 +36,17 @@
"brotli": "^1.3.3",
"chalk": "^5.4.1",
"enquirer": "^2.4.1",
"execa": "^9.5.2",
"execa": "^9.6.0",
"globby": "^14.1.0",
"lint-staged": "^15.5.1",
"minimist": "^1.2.8",
"p-series": "^3.0.0",
"prettier": "^3.5.3",
"semver": "^7.7.1",
"simple-git-hooks": "^2.13.0",
"typedoc": "^0.26.11",
"typedoc-plugin-markdown": "^4.2.10",
"typescript": "~5.6.3",
"typedoc": "^0.28.7",
"typedoc-plugin-markdown": "^4.7.0",
"typescript": "~5.8.3",
"vitest": "^2.1.9"
},
"simple-git-hooks": {
Expand All @@ -71,7 +71,11 @@
"@types/react",
"react-dom"
]
}
},
"onlyBuiltDependencies": [
"chromedriver",
"geckodriver"
]
},
"volta": {
"node": "20.11.1"
Expand Down
1 change: 1 addition & 0 deletions packages/docs/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.vitepress/cache
api
14 changes: 7 additions & 7 deletions packages/docs/.vitepress/config/en.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
import typedocSidebar from '../../api/typedoc-sidebar.json'

export const META_URL = 'https://router.vuejs.org'
export const META_TITLE = 'Vue Router'
Expand Down Expand Up @@ -53,6 +54,12 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
],

sidebar: {
'/api/': [
{
text: 'API',
items: typedocSidebar,
},
],
// catch-all fallback
'/': [
{
Expand Down Expand Up @@ -179,13 +186,6 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
],
},
],

'/api/': [
{
text: 'packages',
items: [{ text: 'vue-router', link: '/api/' }],
},
],
},
},
}
15 changes: 8 additions & 7 deletions packages/docs/.vitepress/config/zh.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
import typedocSidebar from '../../api/typedoc-sidebar.json'
// TODO: rework the typedoc sidebar to include the /zh/ prefix

export const META_URL = 'https://router.vuejs.org'
export const META_TITLE = 'Vue Router'
Expand Down Expand Up @@ -57,6 +59,12 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
],

sidebar: {
'/zh/api/': [
{
text: 'API',
items: typedocSidebar,
},
],
'/zh/': [
{
text: '设置',
Expand Down Expand Up @@ -186,13 +194,6 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
],
},
],

'/zh/api/': [
{
text: 'packages',
items: [{ text: 'vue-router', link: '/zh/api/' }],
},
],
},
},
}
Expand Down
7 changes: 4 additions & 3 deletions packages/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
"docs:preview": "vitepress preview ."
},
"dependencies": {
"simple-git": "^3.27.0",
"vitepress": "1.5.0",
"vitepress-translation-helper": "^0.2.1",
"simple-git": "^3.28.0",
"typedoc-vitepress-theme": "^1.1.2",
"vitepress": "1.6.3",
"vitepress-translation-helper": "^0.2.2",
"vue-router": "workspace:*"
}
}
16 changes: 11 additions & 5 deletions packages/docs/run-typedoc.mjs
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { createTypeDocApp } from './typedoc-markdown.mjs'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const __dirname = path.dirname(new URL(import.meta.url).pathname)

createTypeDocApp({
name: 'API Documentation',
textContentMappings: {
'title.indexPage': 'API Reference',
'title.memberPage': '{name}',
},
tsconfig: path.resolve(__dirname, './typedoc.tsconfig.json'),
// entryPointStrategy: 'packages',
categorizeByGroup: true,
githubPages: false,
disableSources: true, // some links are in node_modules and it's ugly
plugin: ['typedoc-plugin-markdown'],
readme: 'none',
indexFormat: 'table',
disableSources: true,
plugin: ['typedoc-plugin-markdown', 'typedoc-vitepress-theme'],
useCodeBlocks: true,
entryPoints: [path.resolve(__dirname, '../router/src/index.ts')],
}).then(app => app.build())
17 changes: 11 additions & 6 deletions packages/docs/typedoc-markdown.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// @ts-check
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { Application, PageEvent, TSConfigReader } from 'typedoc'
import { Application, TSConfigReader, PageEvent } from 'typedoc'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const __dirname = path.dirname(new URL(import.meta.url).pathname)

/** @satisfies {Partial<import('typedoc').TypeDocOptions & import('typedoc-plugin-markdown').PluginOptions>} */
const DEFAULT_OPTIONS = {
// disableOutputCheck: true,
cleanOutputDir: true,
excludeInternal: false,
readme: 'none',
Expand Down Expand Up @@ -72,7 +71,7 @@ export async function createTypeDocApp(config = {}) {
if (project) {
// Rendered docs
try {
await app.generateDocs(project, options.out)
await app.generateOutputs(project)
app.logger.info(`generated at ${options.out}.`)
} catch (error) {
app.logger.error(error)
Expand All @@ -88,6 +87,12 @@ export async function createTypeDocApp(config = {}) {
}
}

/**
* checks if a path exists
*
* @async
* @param {string} path
*/
async function exists(path) {
try {
await fs.access(path)
Expand All @@ -108,7 +113,7 @@ async function exists(path) {
*/
function prependYAML(contents, vars) {
return contents
.replace(/^/, `${toYAML(vars)}\n\n`)
.replace(/^/, toYAML(vars) + '\n\n')
.replace(/[\r\n]{3,}/g, '\n\n')
}

Expand Down
2 changes: 1 addition & 1 deletion packages/docs/typedoc.tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@
"removeComments": false,
"jsx": "preserve",
"lib": ["esnext", "dom"],
"types": ["node"]
"types": ["vitest", "node", "vite/client"]
}
}
12 changes: 6 additions & 6 deletions packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
"preview": "vite preview --port 4173"
},
"dependencies": {
"vue": "~3.5.13"
"vue": "~3.5.17"
},
"devDependencies": {
"@types/node": "^20.17.31",
"@vitejs/plugin-vue": "^5.2.3",
"@vue/compiler-sfc": "~3.5.13",
"@vue/tsconfig": "^0.6.0",
"vite": "^5.4.18",
"@vitejs/plugin-vue": "^5.2.4",
"@vue/compiler-sfc": "~3.5.17",
"@vue/tsconfig": "^0.7.0",
"vite": "^5.4.19",
"vue-router": "workspace:*",
"vue-tsc": "^2.2.10"
"vue-tsc": "^2.2.12"
}
}
103 changes: 99 additions & 4 deletions packages/router/__tests__/location.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,26 +134,121 @@ describe('parseURL', () => {
})
})

it('parses ? after the hash', () => {
it('correctly parses a ? after the hash', () => {
expect(parseURL('/foo#?a=one')).toEqual({
fullPath: '/foo#?a=one',
path: '/foo',
hash: '#?a=one',
query: {},
})
expect(parseURL('/foo/#?a=one')).toEqual({
fullPath: '/foo/#?a=one',
expect(parseURL('/foo/?a=two#?a=one')).toEqual({
fullPath: '/foo/?a=two#?a=one',
path: '/foo/',
hash: '#?a=one',
query: { a: 'two' },
})
})

it('works with empty query', () => {
expect(parseURL('/foo?#hash')).toEqual({
fullPath: '/foo?#hash',
path: '/foo',
hash: '#hash',
query: {},
})
expect(parseURL('/foo#hash')).toEqual({
fullPath: '/foo#hash',
path: '/foo',
hash: '#hash',
query: {},
})
expect(parseURL('/foo?')).toEqual({
fullPath: '/foo?',
path: '/foo',
hash: '',
query: {},
})
expect(parseURL('/foo')).toEqual({
fullPath: '/foo',
path: '/foo',
hash: '',
query: {},
})
})

it('works with empty hash', () => {
expect(parseURL('/foo#')).toEqual({
fullPath: '/foo#',
path: '/foo',
hash: '#',
query: {},
})
expect(parseURL('/foo?#')).toEqual({
fullPath: '/foo?#',
path: '/foo',
hash: '#',
query: {},
})
expect(parseURL('/foo')).toEqual({
fullPath: '/foo',
path: '/foo',
hash: '',
query: {},
})
})

it('works with a relative paths', () => {
expect(parseURL('foo', '/parent/bar')).toEqual({
fullPath: '/parent/foo',
path: '/parent/foo',
hash: '',
query: {},
})
expect(parseURL('./foo', '/parent/bar')).toEqual({
fullPath: '/parent/foo',
path: '/parent/foo',
hash: '',
query: {},
})
expect(parseURL('../foo', '/parent/bar')).toEqual({
fullPath: '/foo',
path: '/foo',
hash: '',
query: {},
})
// cannot go below root
expect(parseURL('../../foo', '/parent/bar')).toEqual({
fullPath: '/foo',
path: '/foo',
hash: '',
query: {},
})

expect(parseURL('', '/parent/bar')).toEqual({
fullPath: '/parent/bar',
path: '/parent/bar',
hash: '',
query: {},
})
expect(parseURL('#foo', '/parent/bar')).toEqual({
fullPath: '/parent/bar#foo',
path: '/parent/bar',
hash: '#foo',
query: {},
})
expect(parseURL('?o=o', '/parent/bar')).toEqual({
fullPath: '/parent/bar?o=o',
path: '/parent/bar',
hash: '',
query: { o: 'o' },
})
})

it('calls parseQuery', () => {
const parseQuery = vi.fn()
originalParseURL(parseQuery, '/?é=é&é=a')
expect(parseQuery).toHaveBeenCalledTimes(1)
expect(parseQuery).toHaveBeenCalledWith('é=é&é=a')
expect(parseQuery).toHaveBeenCalledWith('?é=é&é=a')
})
})

Expand Down
10 changes: 0 additions & 10 deletions packages/router/__tests__/matcher/pathRanking.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,9 @@ describe('Path ranking', () => {
return comparePathParserScore(
{
score: a,
re: /a/,
// @ts-expect-error
stringify: v => v,
// @ts-expect-error
parse: v => v,
keys: [],
},
{
score: b,
re: /a/,
stringify: v => v,
parse: v => v,
keys: [],
}
)
}
Expand Down
Loading