npm.io
0.7.1 • Published 21h ago

@onoxm/vite-plugin-auto-router

Licence
MIT
Version
0.7.1
Deps
0
Size
41 kB
Vulns
0
Weekly
0

@onoxm/vite-plugin-auto-router

A Vite plugin for automatically generating React or Vue route files.

English | 中文

Features

  • Auto-generate route configuration, no manual maintenance needed
  • Supports both React and Vue frameworks
  • Convention-based routing, automatically mapped by directory structure
  • Supports dynamic route [id] syntax
  • home page path automatically converted to / (configurable)
  • __root__ page as root route container (configurable)
  • Configurable lazy loading and hot module replacement
  • Supports page-level configuration files
  • TypeScript type safety

Installation

npm install -D @onoxm/vite-plugin-auto-router

Usage Guide

Page Component Identification Rules

The plugin identifies page components based on the following rules:

React Project
  • Page components: Components with default export (export default) in the pages directory
  • Regular components: Components with named export (export const) in the pages directory
Vue Project
  • Page components:
    • Direct child components in the views directory (e.g., src/views/about.vue)
    • In nested directories, only index.vue is considered a page component (e.g., src/views/a/b/index.vue)
  • Regular components: Non-index.vue files in nested directories (e.g., src/views/a/b.vue)
React Project
Install Dependencies
npm install react-router
Configure Vite
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import autoRouter from '@onoxm/vite-plugin-auto-router/react'

export default defineConfig({
  plugins: [
    react(),
    autoRouter({
      onGenerated: async filePaths => {
        for (const filePath of filePaths) {
          console.log('Generated:', filePath)
        }
      }
    })
  ]
})
Directory Structure
src/
├── pages/
│   ├── index.tsx
│   ├── __root__.tsx
│   ├── 404.tsx
│   └── user/
│       ├── index.tsx
│       ├── index.config.ts
│       ├── [id].tsx
│       └── [id].config.ts
Special Pages
  • home page: Path automatically converted to /, used as home route
  • __root__ page: Used as root route container, wrapping all other routes
  • 404 or notfound page: Path automatically converted to /*, used as 404 route
Page Configuration

Inherits from React Router RouteObject, with the following modifications:

  • Removed: Component, element, children
  • Added: type?: 'single' | 'wrap'
type: 'single'

When type is set to single, the page component will be generated as an independent route:

// src/pages/user/index.config.ts
import { defineConfig } from '../../router/autoRouter'

export default defineConfig({
  type: 'single'
})

Generated route structure:

// src/router/autoRouter.tsx
import type { RouteObject } from 'react-router'
import Pages404 from './../pages/404.tsx'
import Pages from './../pages/index.tsx'
import PagesRoot from './../pages/__root__.tsx'
import PagesUser from './../pages/user/index.tsx'
import PagesUserId from './../pages/user/[id]/index.tsx'

type PageConfig = Partial<
  Omit<RouteObject, 'Component' | 'element' | 'children'> & {
    type?: 'single' | 'wrap'
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteObject[] = [
  {
    path: '/',
    element: <PagesRoot />,
    children: [
      {
        path: '/',
        element: <Pages />
      },
      {
        path: '/user',
        children: [
          {
            path: '',
            index: true,
            element: <PagesUser />
          },
          {
            path: ':id',
            children: [
              {
                path: '',
                index: true,
                action: async () => {},
                loader: async ({ params }) => await { params },
                element: <PagesUserId />
              }
            ]
          }
        ]
      }
    ]
  },
  {
    path: '/*',
    element: <Pages404 />
  }
]
type: 'wrap'

When type is set to wrap, the page component will act as a parent route container wrapping its child routes:

// src/pages/user/index.config.ts
import { defineConfig } from '../../router/autoRouter'

export default defineConfig({
  type: 'wrap'
})

Generated route structure:

// src/router/autoRouter.tsx
import type { RouteObject } from 'react-router'
import Pages404 from './../pages/404.tsx'
import Pages from './../pages/index.tsx'
import PagesRoot from './../pages/__root__.tsx'
import PagesUser from './../pages/user/index.tsx'
import PagesUserId from './../pages/user/[id]/index.tsx'

type PageConfig = Partial<
  Omit<RouteObject, 'Component' | 'element' | 'children'> & {
    type?: 'single' | 'wrap'
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteObject[] = [
  {
    path: '/',
    element: <PagesRoot />,
    children: [
      {
        path: '/',
        element: <Pages />
      },
      {
        path: '/user',
        element: <PagesUser />,
        children: [
          {
            path: ':id',
            children: [
              {
                path: '',
                index: true,
                action: async () => {},
                loader: async ({ params }) => await { params },
                element: <PagesUserId />
              }
            ]
          }
        ]
      }
    ]
  },
  {
    path: '/*',
    element: <Pages404 />
  }
]
Vue Project
Install Dependencies
npm install vue-router
Configure Vite
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import autoRouter from '@onoxm/vite-plugin-auto-router/vue'

export default defineConfig({
  plugins: [
    vue(),
    autoRouter({
      pagesDir: './src/views',
      configPattern: '/**/*.meta.ts',
      onGenerated: async filePaths => {
        for (const filePath of filePaths) {
          console.log('Generated:', filePath)
        }
      }
    })
  ]
})
Directory Structure
src/
├── views/
│   ├── 404.vue
│   ├── home/
│   │   ├── index.vue
│   │   └── index.meta.ts
│   └── user/
│       ├── index.vue
│       ├── index.meta.ts
│       ├── [id].vue
│       └── [id].meta.ts
Special Pages
  • home page: Path automatically converted to /, used as home route
  • __root__ page: Used as root route container, wrapping all other routes
  • 404 or notfound page: Path automatically converted to /:pathMatch(.*)*, used as 404 route
Page Configuration

Inherits from Vue Router RouteRecordRaw, with the following modifications:

  • Removed: component, children
  • Added: type?: 'single' | 'wrap'
type: 'single'

When type is set to single, the page component will be generated as an independent route:

// src/views/user/index.meta.ts
import { defineConfig } from '../../router/autoRouter'

export default defineConfig({
  type: 'single'
})

Generated route structure:

// src/router/autoRouter.ts
import type { RouteRecordRaw } from 'vue-router'
import Views404 from './../views/404.vue'
import ViewsHome from './../views/home/index.vue'
import ViewsUser from './../views/user/index.vue'
import ViewsUserId from './../views/user/[id]/index.vue'

type PageConfig = Partial<
  Omit<RouteRecordRaw, 'component' | 'children'> & {
    type?: 'single' | 'wrap'
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteRecordRaw[] = [
  {
    path: '/',
    children: [
      {
        path: '',
        name: 'home',
        component: ViewsHome
      }
    ]
  },
  {
    path: '/user',
    children: [
      {
        path: '',
        component: ViewsUser
      },
      {
        path: ':id',
        children: [
          {
            path: '',
            component: ViewsUserId
          }
        ]
      }
    ]
  },
  {
    path: '/:pathMatch(.*)*',
    children: [
      {
        path: '',
        component: Views404
      }
    ]
  }
]
type: 'wrap'

When type is set to wrap, the page component will act as a parent route container wrapping its child routes:

// src/views/user/index.meta.ts
import { defineConfig } from '../../router/autoRouter'

export default defineConfig({
  type: 'wrap'
})

Generated route structure:

// src/router/autoRouter.ts
import type { RouteRecordRaw } from 'vue-router'
import Views404 from './../views/404.vue'
import ViewsHome from './../views/home/index.vue'
import ViewsUser from './../views/user/index.vue'
import ViewsUserId from './../views/user/[id]/index.vue'

type PageConfig = Partial<
  Omit<RouteRecordRaw, 'component' | 'children'> & {
    type?: 'single' | 'wrap'
  }
>

export const defineConfig = (config: PageConfig) => config

export const routes: RouteRecordRaw[] = [
  {
    path: '/',
    children: [
      {
        path: '',
        name: 'home',
        component: ViewsHome
      }
    ]
  },
  {
    path: '/user',
    component: ViewsUser,
    children: [
      {
        path: ':id',
        children: [
          {
            path: '',
            component: ViewsUserId
          }
        ]
      }
    ]
  },
  {
    path: '/:pathMatch(.*)*',
    children: [
      {
        path: '',
        component: Views404
      }
    ]
  }
]

Configuration Options

Plugin Configuration
Option Type Default Description
framework 'react' | 'vue' 'react' Framework type
pagesDir string './src/pages' Pages directory
routesFile string undefined Generated route file path
keepHome boolean false Whether to keep home page path
keepRoot boolean false Whether to keep __root__ page path
lazy boolean true Whether to enable lazy loading
hmr boolean true Whether to enable/disable HMR
configPattern string /**/*.config.{js,ts} Config file format
onGenerated (filePaths: string[]) => Promise<void> | void undefined Callback after routes generated
Page Configuration
Option Type Default Description
type 'single' | 'wrap' 'single' Route type
path string undefined Route path, supports [currentPath] placeholder to reference current path
* any any Inherits from router config
path Usage Example

Use the [currentPath] placeholder to reference the current path when replacing:

// src/pages/user/index.config.ts
import { defineConfig } from '../../router/autoRouter'

export default defineConfig({
  // Replace /user with /users/v2
  path: '/users/v2'
})

// Or use the placeholder to add a suffix to the current path
export default defineConfig({
  // Replace /user with /user/v2
  path: '[currentPath]/v2'
})

License

MIT

Keywords