vite-plugin-generoutes 1.1.0 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  A Vite plugin that automatically generates Vue router configuration based on file system.
4
4
 
5
+ ### 📋 Prerequisites
6
+
7
+ - **Node.js**: `20.12.0` or higher
8
+
5
9
  ### ✨ Features
6
10
 
7
11
  - 📁 File-system based routing
@@ -10,6 +14,7 @@ A Vite plugin that automatically generates Vue router configuration based on fil
10
14
  - 🎨 Customizable route configuration
11
15
  - 🧩 Support for route metadata via `defineOptions`
12
16
  - 🚦 Route redirection support
17
+ - 🖼️ Layout-based route grouping
13
18
 
14
19
  ### 📦 Installation
15
20
 
@@ -48,6 +53,7 @@ export default defineConfig({
48
53
  | Option | Type | Default | Description |
49
54
  | --------------- | ---------- | ---------------- | ----------------------------------------------------------------------------------------------------------------- |
50
55
  | `pagesFolder` | `string` | `'src/pages'` | Path to pages folder |
56
+ | `layoutsFolder` | `string` | `'src/layouts'` | Path to layouts folder |
51
57
  | `ignoreFolders` | `string[]` | `['components']` | Folders to ignore when generating routes |
52
58
  | `routesPath` | `string` | Auto-detected | Path to generated routes file. Auto-detected based on `tsconfig.json` presence (`.ts` if exists, otherwise `.js`) |
53
59
  | `nested` | `boolean` | `false` | Whether to generate nested routes |
@@ -115,7 +121,70 @@ defineOptions({
115
121
  </script>
116
122
  ```
117
123
 
118
- ### 🚀 Complete Example
124
+ ### �️ Layout Routes
125
+
126
+ Routes are automatically grouped by their `meta.layout` property and wrapped with a parent layout route:
127
+
128
+ - Routes with `meta.layout: false` will **not** be wrapped with a layout
129
+ - Routes without `meta.layout` will use the `'default'` layout by default
130
+ - Routes with `meta.layout: 'xxx'` will use the corresponding layout component from `layoutsFolder`
131
+
132
+ ```vue
133
+ <!-- src/pages/login.vue - No layout wrapper -->
134
+ <script setup>
135
+ defineOptions({
136
+ name: 'Login',
137
+ meta: {
138
+ layout: false
139
+ }
140
+ })
141
+ </script>
142
+ ```
143
+
144
+ ```vue
145
+ <!-- src/pages/home.vue - Uses 'admin' layout -->
146
+ <script setup>
147
+ defineOptions({
148
+ name: 'Home',
149
+ meta: {
150
+ layout: 'admin'
151
+ }
152
+ })
153
+ </script>
154
+ ```
155
+
156
+ **Generated route structure example:**
157
+
158
+ ```javascript
159
+ [
160
+ // Routes with layout: false are not wrapped
161
+ {
162
+ name: 'Login',
163
+ path: '/login',
164
+ component: () => import('/src/pages/login.vue'),
165
+ meta: { layout: false }
166
+ },
167
+ // Routes are grouped by layout
168
+ {
169
+ name: 'LAYOUT_DEFAULT',
170
+ path: '/__layout_default__',
171
+ component: () => import('/src/layouts/default.vue'),
172
+ children: [
173
+ { name: 'Index', path: '/' }
174
+ ]
175
+ },
176
+ {
177
+ name: 'LAYOUT_ADMIN',
178
+ path: '/__layout_admin__',
179
+ component: () => import('/src/layouts/admin.vue'),
180
+ children: [
181
+ { name: 'Home', path: '/home' }
182
+ ]
183
+ }
184
+ ]
185
+ ```
186
+
187
+ ### �🚀 Complete Example
119
188
 
120
189
  ```javascript
121
190
  import vue from '@vitejs/plugin-vue'
package/README.zh_CN.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  一个基于文件系统自动生成Vue路由配置的Vite插件。
4
4
 
5
+ ### 📋 前置要求
6
+
7
+ - **Node.js**: `20.12.0` 或更高版本
8
+
5
9
  ### ✨ 特性
6
10
 
7
11
  - 📁 基于文件系统的路由生成
@@ -10,6 +14,7 @@
10
14
  - 🎨 可自定义路由配置
11
15
  - 🧩 支持通过`defineOptions`设置路由元数据
12
16
  - 🚦 支持路由重定向
17
+ - 🖼️ 支持按布局分组路由
13
18
 
14
19
  ### 📦 安装
15
20
 
@@ -48,6 +53,7 @@ export default defineConfig({
48
53
  | 选项 | 类型 | 默认值 | 描述 |
49
54
  | --------------- | ---------- | ---------------- | ----------------------------------------------------------------------------------------- |
50
55
  | `pagesFolder` | `string` | `'src/pages'` | 页面文件夹路径 |
56
+ | `layoutsFolder` | `string` | `'src/layouts'` | 布局组件文件夹路径 |
51
57
  | `ignoreFolders` | `string[]` | `['components']` | 生成路由时忽略的文件夹 |
52
58
  | `routesPath` | `string` | 自动检测 | 生成的路由文件路径,根据 `tsconfig.json` 是否存在自动检测(存在则为 `.ts`,否则为 `.js`) |
53
59
  | `nested` | `boolean` | `false` | 是否生成嵌套路由 |
@@ -115,7 +121,70 @@ defineOptions({
115
121
  </script>
116
122
  ```
117
123
 
118
- ### 🚀 完整示例
124
+ ### �️ 布局路由
125
+
126
+ 路由会根据 `meta.layout` 属性自动分组,并包裹在对应的布局父级路由中:
127
+
128
+ - 设置 `meta.layout: false` 的路由**不会**被布局包裹
129
+ - 未设置 `meta.layout` 的路由默认使用 `'default'` 布局
130
+ - 设置 `meta.layout: 'xxx'` 的路由会使用 `layoutsFolder` 中对应的布局组件
131
+
132
+ ```vue
133
+ <!-- src/pages/login.vue - 不使用布局包裹 -->
134
+ <script setup>
135
+ defineOptions({
136
+ name: 'Login',
137
+ meta: {
138
+ layout: false
139
+ }
140
+ })
141
+ </script>
142
+ ```
143
+
144
+ ```vue
145
+ <!-- src/pages/home.vue - 使用 'admin' 布局 -->
146
+ <script setup>
147
+ defineOptions({
148
+ name: 'Home',
149
+ meta: {
150
+ layout: 'admin'
151
+ }
152
+ })
153
+ </script>
154
+ ```
155
+
156
+ **生成的路由结构示例:**
157
+
158
+ ```javascript
159
+ [
160
+ // layout: false 的路由不会被包裹
161
+ {
162
+ name: 'Login',
163
+ path: '/login',
164
+ component: () => import('/src/pages/login.vue'),
165
+ meta: { layout: false }
166
+ },
167
+ // 路由按布局分组
168
+ {
169
+ name: 'LAYOUT_DEFAULT',
170
+ path: '/__layout_default__',
171
+ component: () => import('/src/layouts/default.vue'),
172
+ children: [
173
+ { name: 'Index', path: '/' }
174
+ ]
175
+ },
176
+ {
177
+ name: 'LAYOUT_ADMIN',
178
+ path: '/__layout_admin__',
179
+ component: () => import('/src/layouts/admin.vue'),
180
+ children: [
181
+ { name: 'Home', path: '/home' }
182
+ ]
183
+ }
184
+ ]
185
+ ```
186
+
187
+ ### �🚀 完整示例
119
188
 
120
189
  ```javascript
121
190
  import vue from '@vitejs/plugin-vue'
package/dist/index.d.ts CHANGED
@@ -12,6 +12,8 @@ interface RouteMeta {
12
12
  requiresAuth?: boolean;
13
13
  /** Whether the route is enabled */
14
14
  enabled?: boolean;
15
+ /** Layout component name, false to disable layout wrapping */
16
+ layout?: string | false;
15
17
  /** Custom properties */
16
18
  [key: string]: unknown;
17
19
  }
@@ -69,6 +71,12 @@ interface Options {
69
71
  * @default src/pages
70
72
  */
71
73
  pagesFolder: string;
74
+ /**
75
+ * layouts folder
76
+ *
77
+ * @default src/layouts
78
+ */
79
+ layoutsFolder: string;
72
80
  /**
73
81
  * ignore folders, ignore these folders when generating routes
74
82
  *
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // src/index.ts
2
2
  import path from "path";
3
+ import { styleText } from "util";
3
4
  import { debounce, slash } from "@antfu/utils";
4
5
  import { parse } from "@vue/compiler-sfc";
5
- import chalk from "chalk";
6
6
  import fs from "fs-extra";
7
7
  import { globSync } from "glob";
8
8
  import prettier from "prettier";
@@ -66,6 +66,7 @@ function VitePluginGeneroutes(options = {}) {
66
66
  let routesPath;
67
67
  let isTypeScript;
68
68
  const pagesFolder = options.pagesFolder || "src/pages";
69
+ const layoutsFolder = options.layoutsFolder || "src/layouts";
69
70
  const ignoreFolders = options.ignoreFolders || ["components"];
70
71
  const nested = options.nested || false;
71
72
  const defineOptionsCache = /* @__PURE__ */ new Map();
@@ -100,10 +101,37 @@ function VitePluginGeneroutes(options = {}) {
100
101
  console.warn(`Warning: Duplicate names found in routes: ${duplicateNames.join(", ")}`);
101
102
  if (duplicatePaths.length)
102
103
  console.warn(`Warning: Duplicate paths found in routes: ${duplicatePaths.join(", ")}`);
104
+ const processedRoutes = wrapRoutesWithLayout(nested ? convertToTree(routes) : routes);
103
105
  return {
104
- routes: nested ? convertToTree(routes) : routes
106
+ routes: processedRoutes
105
107
  };
106
108
  }
109
+ function wrapRoutesWithLayout(routes) {
110
+ const layoutGroups = /* @__PURE__ */ new Map();
111
+ const noLayoutRoutes = [];
112
+ for (const route of routes) {
113
+ const layout = route.meta?.layout;
114
+ if (layout === false) {
115
+ noLayoutRoutes.push(route);
116
+ } else {
117
+ const layoutName = layout || "default";
118
+ const group = layoutGroups.get(layoutName);
119
+ if (group) {
120
+ group.push(route);
121
+ } else {
122
+ layoutGroups.set(layoutName, [route]);
123
+ }
124
+ }
125
+ }
126
+ const layoutRoutes = Array.from(layoutGroups, ([layoutName, children]) => ({
127
+ name: `LAYOUT_${layoutName.toUpperCase()}`,
128
+ path: `/__layout_${layoutName}__`,
129
+ component: `@@layout@@/${layoutsFolder}/${layoutName}.vue@@`,
130
+ meta: {},
131
+ children
132
+ }));
133
+ return [...noLayoutRoutes, ...layoutRoutes];
134
+ }
107
135
  async function writerRoutesFile(isInit = false) {
108
136
  const { routes } = generateMenusAndRoutes();
109
137
  const typeDefinitions = isTypeScript ? `
@@ -148,12 +176,13 @@ export type GeneratedRoute = RouteRecordRaw & {
148
176
  export const routes${routesType} = ${JSON.stringify(routes, null, 2)}
149
177
  `;
150
178
  routesStr = routesStr.replace(/"##(.*)##"/g, (_, p1) => `async () => ({...(await import('${p1.split("@@")[0]}')).default, name: '${p1.split("@@")[1]}'})`);
179
+ routesStr = routesStr.replace(/"@@layout@@(.*)@@"/g, (_, p1) => `() => import('${p1}')`);
151
180
  const parser = isTypeScript ? "typescript" : "babel";
152
181
  routesStr = await prettier.format(routesStr, { parser, semi: false, singleQuote: true });
153
182
  const filePath = path.resolve(rootDir, routesPath);
154
183
  await fs.ensureDir(path.dirname(filePath));
155
184
  fs.writeFileSync(filePath, routesStr);
156
- console.log(` \u2705 ${isInit ? "routes generated:" : "routes updated:"} ${chalk.cyan(routesPath)}`);
185
+ console.log(` \u2705 ${isInit ? "routes generated:" : "routes updated:"} ${styleText("cyanBright", routesPath)}`);
157
186
  }
158
187
  const debounceWriter = debounce(500, writerRoutesFile);
159
188
  return {
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "vite-plugin-generoutes",
3
3
  "type": "module",
4
- "version": "1.1.0",
4
+ "version": "2.0.0-beta.1",
5
+ "packageManager": "pnpm@10.27.0",
5
6
  "description": "A Vite plugin that generate routes based on the file structure, supports dynamic routes, and supports custom meta data for each route.",
6
7
  "author": "Ronnie Zhang <zclzone@outlook.com>",
7
8
  "license": "MIT",
@@ -33,6 +34,17 @@
33
34
  "files": [
34
35
  "dist"
35
36
  ],
37
+ "scripts": {
38
+ "start": "tsup --watch",
39
+ "build": "tsup",
40
+ "lint": "eslint .",
41
+ "prepublishOnly": "nr build",
42
+ "release": "bumpp && npm publish",
43
+ "test": "vitest",
44
+ "typecheck": "tsc --noEmit",
45
+ "prepare": "simple-git-hooks",
46
+ "up:deps": "taze major -I"
47
+ },
36
48
  "peerDependencies": {
37
49
  "vite": ">=6.1.0",
38
50
  "vue-router": ">=4.0.0"
@@ -40,7 +52,6 @@
40
52
  "dependencies": {
41
53
  "@antfu/utils": "^9.3.0",
42
54
  "@vue/compiler-sfc": "^3.5.26",
43
- "chalk": "^5.6.2",
44
55
  "fs-extra": "^11.3.3",
45
56
  "glob": "^13.0.0",
46
57
  "prettier": "^3.7.4"
@@ -68,14 +79,5 @@
68
79
  },
69
80
  "lint-staged": {
70
81
  "*": "eslint --fix"
71
- },
72
- "scripts": {
73
- "start": "tsup --watch",
74
- "build": "tsup",
75
- "lint": "eslint .",
76
- "release": "bumpp && npm publish",
77
- "test": "vitest",
78
- "typecheck": "tsc --noEmit",
79
- "up:deps": "taze major -I"
80
82
  }
81
- }
83
+ }