npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jl-org/vite-auto-route

v2.0.1

Published

自动生成 Vite 路由配置,支持 Vue、React 嵌套路由

Readme

Vite auto route

基于 Vite 文件系统结构自动生成路由配置的工具

核心规则

动态路由参数

使用方括号语法定义动态参数:

| 文件路径 | 生成的路由路径 | 说明 | |---------|--------------|------| | [id]/page.tsx | /:id | 必选参数 | | [id$]/page.tsx | /:id? | 可选参数(以 $ 结尾) | | [...slug]/page.tsx | /:slug* | 捕获所有参数(以 ... 开头) |


React Router 使用示例

示例项目

import { genRoutes } from '@jl-org/vite-auto-route'
import { createBrowserRouter } from 'react-router'
import Index from '@/views'

export const pages = genRoutes({
  globComponentsImport: () => import.meta.glob('/src/views/**/page.tsx'),
  indexFileName: '/page.tsx',
  routerPathFolder: '/src/views',
  pathPrefix: /^\/src\/views/,
  customizeRoute: (context) => (route) => {
    return {
      path: route.path,
      Component: lazy(route.component)
      // ... anything you want
    }
  },
  extendRoutes: (routes) => {
    routes.push({
      path: '/',
      Component: Index,
    } as any)
    return routes
  }
})

export const router = createBrowserRouter(pages)

Vue Router 使用示例

import { genRoutes } from '@jl-org/vite-auto-route'
import { createRouter, createWebHistory } from 'vue-router'
import Index from '../views/index.vue'

/** 拿到 /src/views 下所有 index.vue 作为路由 */
const views = genRoutes({
  globComponentsImport: () => import.meta.glob('/src/views/**/page.vue'),
  indexFileName: '/page.vue',
  routerPathFolder: '/src/views',
  pathPrefix: /^\/src\/views/,
})
/** 拿到 /src/components 下所有 Test.vue 作为路由 */
const components = genRoutes({
  globComponentsImport: () => import.meta.glob('/src/components/**/Test.vue'),
  indexFileName: '/Test.vue',
  routerPathFolder: '/src/components',
  pathPrefix: /^\/src\/components/,
})

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      component: Index,
    },
    ...views,
    ...components,
  ],
})

高级配置

路由自定义选项

genRoutes 提供了三个配置选项,用于在不同阶段自定义路由生成:

1. customizeRoute - 简单字段修改(推荐)

执行顺序:最早,在 transformRoute 之前执行

适用场景:

  • 添加或修改路由字段(如 middlewaresmeta
  • 基于路径、名称等简单条件进行字段设置
  • 不需要过滤或拆分路由的场景

特点:

  • 只能修改单个路由,不能过滤或拆分
  • 返回类型固定为 FileSystemRoute
  • API 设计更简洁,适合简单场景
export const pages = genRoutes({
  globComponentsImport: () => import.meta.glob('/src/views/**/page.tsx'),
  indexFileName: '/page.tsx',
  routerPathFolder: '/src/views',
  pathPrefix: /^\/src\/views/,
  customizeRoute: (context) => {
    return (route) => {
      if (route.path !== '/') {
        return {
          ...route,
          // ... anything you want
        }
      }

      return route
    }
  },
})

2. transformRoute - 复杂转换逻辑

执行顺序:中间,在 customizeRoute 之后执行

适用场景:

  • 需要过滤掉某些路由(返回 null
  • 需要将一个路由拆分成多个路由(返回数组)
  • 需要复杂的转换逻辑
  • 需要基于路由结构进行深度修改

特点:

  • 可以返回 null 来过滤路由
  • 可以返回数组来拆分路由
  • 功能更强大,适合复杂场景
const routes = genRoutes({
  globComponentsImport: () => import.meta.glob('/src/views/**/page.tsx'),
  indexFileName: '/page.tsx',
  routerPathFolder: '/src/views',
  pathPrefix: /^\/src\/views/,
  transformRoute: (route, context) => {
    // 过滤掉某些路由
    if (route.path.startsWith('/internal')) {
      return null
    }
    // 将一个路由拆分成多个
    if (route.path === '/multi') {
      return [
        { ...route, path: '/multi/a' },
        { ...route, path: '/multi/b' }
      ]
    }
    return route
  },
})

3. extendRoutes - 全局操作

执行顺序:最后,在所有路由转换完成后执行

适用场景:

  • 添加全局路由(如 404 页面、错误页面)
  • 对路由数组进行排序
  • 全局级别的路由处理
  • 需要访问完整路由树的场景

特点:

  • 接收整个路由数组,可以添加、删除、重新排序
  • 在单个路由转换完成后执行
  • 适合全局级别的操作
const routes = genRoutes({
  globComponentsImport: () => import.meta.glob('/src/views/**/page.tsx'),
  indexFileName: '/page.tsx',
  routerPathFolder: '/src/views',
  pathPrefix: /^\/src\/views/,
  extendRoutes: (routes) => {
    // 添加 404 路由
    routes.push({
      path: '*',
      name: 'notFound',
      component: () => import('./views/404'),
      children: [],
      rawPath: '',
      segments: [],
      rawSegments: [],
    })
    // 对路由进行排序
    return routes.sort((a, b) => a.path.localeCompare(b.path))
  },
})

执行顺序

三个配置选项的执行顺序如下:

customizeRoute → transformRoute → extendRoutes

它们可以同时使用,按上述顺序执行,不会冲突。

自定义路由名称

使用 resolveRouteName 可以自定义路由名称的生成策略:

const routes = genRoutes({
  globComponentsImport: () => import.meta.glob('/src/views/**/page.tsx'),
  indexFileName: '/page.tsx',
  routerPathFolder: '/src/views',
  pathPrefix: /^\/src\/views/,
  resolveRouteName: (context) => {
    // 自定义名称生成逻辑
    if (context.isRoot) {
      return 'root'
    }
    return context.segments
      .map(s => s.replace(/[:?*]/g, ''))
      .join('-')
  },
})

文件树示例

src/views/
├── index.tsx                    # 布局组件(不参与路由生成)
├── test/
│   ├── page.tsx                 # /test
│   ├── nested/
│   │   ├── page.tsx             # /test/nested
│   │   └── deep/
│   │       ├── page.tsx         # /test/nested/deep
│   │       └── [id]/
│   │           └── page.tsx     # /test/nested/deep/:id
│   ├── param/
│   │   └── [id]/
│   │       └── page.tsx         # /test/param/:id
│   └── optional/
│       ├── page.tsx             # /test/optional
│       └── [optional$]/
│           └── page.tsx         # /test/optional/:optional?

生成的路由结构

基于上述文件树,生成的路由配置如下:

[
  {
    path: '/test',
    name: 'test',
    component: () => import('/src/views/test/page.tsx'),
    children: [
      {
        path: '/test/nested',
        name: 'testNested',
        component: () => import('/src/views/test/nested/page.tsx'),
        children: [
          {
            path: '/test/nested/deep',
            name: 'testNestedDeep',
            component: () => import('/src/views/test/nested/deep/page.tsx'),
            children: [
              {
                path: '/test/nested/deep/:id',
                name: 'testNestedDeepId',
                component: () => import('/src/views/test/nested/deep/[id]/page.tsx'),
                children: []
              }
            ]
          }
        ]
      },
      {
        path: '/test/param/:id',
        name: 'testParamId',
        component: () => import('/src/views/test/param/[id]/page.tsx'),
        children: []
      },
      {
        path: '/test/optional',
        name: 'testOptional',
        component: () => import('/src/views/test/optional/page.tsx'),
        children: [
          {
            path: '/test/optional/:optional?',
            name: 'testOptionalOptional',
            component: () => import('/src/views/test/optional/[optional$]/page.tsx'),
            children: []
          }
        ]
      }
    ]
  }
]