feat: migrate from unplugin-vue-router to vue-router v5 (#563)

* feat: migrate from unplugin-vue-router to vue-router v5

* chore: update lock

* fix: resolve CI failures for vue-router v5 migration

- Fix ESLint import order in vite.config.ts
- Fix readonly routes type mismatch in src/main.ts by spreading routes array
- Upgrade vite to v7, vite-ssg to v28, beasties to v0.4, and other deps
- Update CI node matrix from [18.x, 20.x] to [20.x, 22.x]

* chore: upgrade vite-plugin-pwa and vitest

* fix: bump Node.js version to 22 for Iterator helpers support

vue-router v5 uses Iterator helpers (e.g. `seen.values().filter()`)
which require Node.js >= 22. Bump NODE_VERSION in netlify.toml from
20 to 22 to fix the Netlify build failure.
This commit is contained in:
云游君 authored and GitHub committed 2026-02-25 16:36:23 +08:00
1 parent abf64f7dd4
commit f648eecd1d
12 files changed
+2402 -1435

No files matched your search

+1 -1
View File
@@ -47,7 +47,7 @@ jobs:
strategy: strategy:
matrix: matrix:
node-version: [18.x, 20.x] node-version: [20.x, 22.x]
os: [ubuntu-latest] os: [ubuntu-latest]
fail-fast: false fail-fast: false
+1 -1
View File
@@ -3,7 +3,7 @@ publish = "dist"
command = "pnpm run build" command = "pnpm run build"
[build.environment] [build.environment]
NODE_VERSION = "20" NODE_VERSION = "22"
[[redirects]] [[redirects]]
from = "/*" from = "/*"
-1
View File
@@ -56,7 +56,6 @@
"unplugin-vue-components": "catalog:build", "unplugin-vue-components": "catalog:build",
"unplugin-vue-macros": "catalog:build", "unplugin-vue-macros": "catalog:build",
"unplugin-vue-markdown": "catalog:build", "unplugin-vue-markdown": "catalog:build",
"unplugin-vue-router": "catalog:build",
"vite": "catalog:build", "vite": "catalog:build",
"vite-bundle-visualizer": "catalog:build", "vite-bundle-visualizer": "catalog:build",
"vite-plugin-inspect": "catalog:build", "vite-plugin-inspect": "catalog:build",
+2256 -1380
View File
File diff suppressed because it is too large. Load diff
+14 -15
View File
@@ -5,32 +5,31 @@ catalogs:
build: build:
'@intlify/unplugin-vue-i18n': ^6.0.5 '@intlify/unplugin-vue-i18n': ^6.0.5
'@shikijs/markdown-it': ^3.2.1 '@shikijs/markdown-it': ^3.2.1
'@unocss/eslint-config': ^66.1.0-beta.7 '@unocss/eslint-config': ^66.6.0
'@vitejs/plugin-vue': ^5.2.3 '@vitejs/plugin-vue': ^6.0.4
beasties: ^0.2.0 beasties: ^0.4.1
markdown-it-link-attributes: ^4.0.1 markdown-it-link-attributes: ^4.0.1
rollup: ^4.37.0 rollup: ^4.37.0
shiki: ^3.2.1 shiki: ^3.2.1
unocss: ^66.1.0-beta.7 unocss: ^66.6.0
unplugin: ^2.2.2 unplugin: ^2.2.2
unplugin-auto-import: ^19.1.2 unplugin-auto-import: ^19.1.2
unplugin-vue-components: ^28.4.1 unplugin-vue-components: ^28.4.1
unplugin-vue-macros: ^2.14.5 unplugin-vue-macros: ^2.14.5
unplugin-vue-markdown: ^28.3.1 unplugin-vue-markdown: ^28.3.1
unplugin-vue-router: ^0.12.0 vite: ^7.3.1
vite: ^6.2.3
vite-bundle-visualizer: ^1.2.1 vite-bundle-visualizer: ^1.2.1
vite-plugin-inspect: ^11.0.0 vite-plugin-inspect: ^11.0.0
vite-plugin-pwa: ^0.21.2 vite-plugin-pwa: ^1.2.0
vite-plugin-vue-devtools: ^7.7.2 vite-plugin-vue-devtools: ^7.7.2
vite-plugin-vue-layouts: ^0.11.0 vite-plugin-vue-layouts: ^0.11.0
vite-ssg: ^26.0.0 vite-ssg: ^28.3.0
vite-ssg-sitemap: ^0.8.1 vite-ssg-sitemap: ^0.10.0
dev: dev:
'@antfu/eslint-config': ^4.11.0 '@antfu/eslint-config': ^4.11.0
'@iconify-json/carbon': ^1.2.8 '@iconify-json/carbon': ^1.2.8
'@vue-macros/volar': ^3.0.0-beta.7 '@vue-macros/volar': ^3.1.2
'@vue/test-utils': ^2.4.6 '@vue/test-utils': ^2.4.6
cypress: ^14.2.1 cypress: ^14.2.1
cypress-vite: ^1.6.0 cypress-vite: ^1.6.0
@@ -42,18 +41,18 @@ catalogs:
simple-git-hooks: ^2.12.1 simple-git-hooks: ^2.12.1
taze: ^19.0.2 taze: ^19.0.2
typescript: ^5.8.2 typescript: ^5.8.2
vitest: ^3.0.9 vitest: ^4.0.18
vue-tsc: ^2.2.8 vue-tsc: ^3.2.4
frontend: frontend:
'@unhead/vue': ^2.0.2 '@unhead/vue': ^2.0.2
'@unocss/reset': ^66.1.0-beta.7 '@unocss/reset': ^66.1.0-beta.7
'@vueuse/core': ^13.0.0 '@vueuse/core': ^13.0.0
nprogress: ^0.2.0 nprogress: ^0.2.0
pinia: ^3.0.1 pinia: ^3.0.4
vue: ^3.5.13 vue: ^3.5.28
vue-i18n: ^11.1.2 vue-i18n: ^11.1.2
vue-router: ^4.5.0 vue-router: ^5.0.3
types: types:
'@types/markdown-it-link-attributes': ^3.0.5 '@types/markdown-it-link-attributes': ^3.0.5
+2 -3
View File
@@ -31,12 +31,10 @@ declare global {
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent'] const defineComponent: typeof import('vue')['defineComponent']
const defineLoader: typeof import('vue-router/auto')['defineLoader'] const definePage: typeof import('vue-router/experimental')['definePage']
const definePage: typeof import('unplugin-vue-router/runtime')['definePage']
const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
const effectScope: typeof import('vue')['effectScope'] const effectScope: typeof import('vue')['effectScope']
const extendRef: typeof import('@vueuse/core')['extendRef'] const extendRef: typeof import('@vueuse/core')['extendRef']
const getActiveHead: typeof import('@unhead/vue')['getActiveHead']
const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope'] const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h'] const h: typeof import('vue')['h']
@@ -346,6 +344,7 @@ declare module 'vue' {
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']> readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']> readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']> readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly definePage: UnwrapRef<typeof import('vue-router/experimental')['definePage']>
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']> readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']> readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']> readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
+1
View File
@@ -2,6 +2,7 @@
// @ts-nocheck // @ts-nocheck
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {} export {}
/* prettier-ignore */ /* prettier-ignore */
+1 -1
View File
@@ -13,7 +13,7 @@ import 'uno.css'
export const createApp = ViteSSG( export const createApp = ViteSSG(
App, App,
{ {
routes: setupLayouts(routes), routes: setupLayouts([...routes]),
base: import.meta.env.BASE_URL, base: import.meta.env.BASE_URL,
}, },
(ctx) => { (ctx) => {
+121
View File
@@ -0,0 +1,121 @@
/* eslint-disable */
/* prettier-ignore */
// oxfmt-ignore
// @ts-nocheck
// noinspection ES6UnusedImports
// Generated by vue-router. !! DO NOT MODIFY THIS FILE !!
// It's recommended to commit this file.
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry.
import type {
RouteRecordInfo,
ParamValue,
ParamValueOneOrMore,
ParamValueZeroOrMore,
ParamValueZeroOrOne,
} from 'vue-router'
declare module 'vue-router' {
interface TypesConfig {
ParamParsers: never
}
}
declare module 'vue-router/auto-routes' {
/**
* Route name map generated by vue-router
*/
export interface RouteNamedMap {
'/': RouteRecordInfo<
'/',
'/',
Record<never, never>,
Record<never, never>,
| never
>,
'/[...all]': RouteRecordInfo<
'/[...all]',
'/:all(.*)',
{ all: ParamValue<true> },
{ all: ParamValue<false> },
| never
>,
'/about': RouteRecordInfo<
'/about',
'/about',
Record<never, never>,
Record<never, never>,
| never
>,
'/hi/[name]': RouteRecordInfo<
'/hi/[name]',
'/hi/:name',
{ name: ParamValue<true> },
{ name: ParamValue<false> },
| never
>,
'/README': RouteRecordInfo<
'/README',
'/README',
Record<never, never>,
Record<never, never>,
| never
>,
}
/**
* Route file to route info map by vue-router.
* Used by the \`sfc-typed-router\` Volar plugin to automatically type \`useRoute()\`.
*
* Each key is a file path relative to the project root with 2 properties:
* - routes: union of route names of the possible routes when in this page (passed to useRoute<...>())
* - views: names of nested views (can be passed to <RouterView name="...">)
*
* @internal
*/
export interface _RouteFileInfoMap {
'src/pages/index.vue': {
routes:
| '/'
views:
| never
}
'src/pages/[...all].vue': {
routes:
| '/[...all]'
views:
| never
}
'src/pages/about.md': {
routes:
| '/about'
views:
| never
}
'src/pages/hi/[name].vue': {
routes:
| '/hi/[name]'
views:
| never
}
'src/pages/README.md': {
routes:
| '/README'
views:
| never
}
}
/**
* Get a union of possible route names in a certain route component file.
* Used by the \`sfc-typed-router\` Volar plugin to automatically type \`useRoute()\`.
*
* @internal
*/
export type _RouteNamesForFilePath<FilePath extends string> =
_RouteFileInfoMap extends Record<FilePath, infer Info>
? Info['routes']
: keyof RouteNamedMap
}
export {}
-27
View File
@@ -1,27 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-router. ‼️ DO NOT MODIFY THIS FILE ‼️
// It's recommended to commit this file.
// Make sure to add this file to your tsconfig.json file as an "includes" or "files" entry.
declare module 'vue-router/auto-routes' {
import type {
RouteRecordInfo,
ParamValue,
ParamValueOneOrMore,
ParamValueZeroOrMore,
ParamValueZeroOrOne,
} from 'vue-router'
/**
* Route name map generated by unplugin-vue-router
*/
export interface RouteNamedMap {
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
'/[...all]': RouteRecordInfo<'/[...all]', '/:all(.*)', { all: ParamValue<true> }, { all: ParamValue<false> }>,
'/about': RouteRecordInfo<'/about', '/about', Record<never, never>, Record<never, never>>,
'/hi/[name]': RouteRecordInfo<'/hi/[name]', '/hi/:name', { name: ParamValue<true> }, { name: ParamValue<false> }>,
'/README': RouteRecordInfo<'/README', '/README', Record<never, never>, Record<never, never>>,
}
}
+1 -2
View File
@@ -15,8 +15,7 @@
"vite/client", "vite/client",
"vite-plugin-vue-layouts/client", "vite-plugin-vue-layouts/client",
"vite-plugin-pwa/client", "vite-plugin-pwa/client",
"unplugin-vue-macros/macros-global", "unplugin-vue-macros/macros-global"
"unplugin-vue-router/client"
], ],
"allowJs": true, "allowJs": true,
"strict": true, "strict": true,
+4 -4
View File
@@ -9,13 +9,13 @@ import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite' import Components from 'unplugin-vue-components/vite'
import VueMacros from 'unplugin-vue-macros/vite' import VueMacros from 'unplugin-vue-macros/vite'
import Markdown from 'unplugin-vue-markdown/vite' import Markdown from 'unplugin-vue-markdown/vite'
import { VueRouterAutoImports } from 'unplugin-vue-router'
import VueRouter from 'unplugin-vue-router/vite'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import { VitePWA } from 'vite-plugin-pwa' import { VitePWA } from 'vite-plugin-pwa'
import VueDevTools from 'vite-plugin-vue-devtools' import VueDevTools from 'vite-plugin-vue-devtools'
import Layouts from 'vite-plugin-vue-layouts' import Layouts from 'vite-plugin-vue-layouts'
import generateSitemap from 'vite-ssg-sitemap' import generateSitemap from 'vite-ssg-sitemap'
import { VueRouterAutoImports } from 'vue-router/unplugin'
import VueRouter from 'vue-router/vite'
import 'vitest/config' import 'vitest/config'
export default defineConfig({ export default defineConfig({
@@ -26,10 +26,10 @@ export default defineConfig({
}, },
plugins: [ plugins: [
// https://github.com/posva/unplugin-vue-router // https://github.com/vuejs/router
VueRouter({ VueRouter({
extensions: ['.vue', '.md'], extensions: ['.vue', '.md'],
dts: 'src/typed-router.d.ts', dts: 'src/route-map.d.ts',
}), }),
VueMacros({ VueMacros({