vitarx-router 4.0.0-beta.2 → 4.0.0-beta.3

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.
Files changed (65) hide show
  1. package/README.md +20 -3
  2. package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.d.ts +1 -1
  3. package/dist/core/router/manager.js +2 -0
  4. package/dist/file-router/config/index.d.ts +2 -1
  5. package/dist/file-router/config/index.js +2 -1
  6. package/dist/file-router/config/resolve.d.ts +26 -0
  7. package/dist/file-router/config/resolve.js +73 -0
  8. package/dist/file-router/{utils/validateOptions.d.ts → config/validate.d.ts} +10 -10
  9. package/dist/file-router/{utils/validateOptions.js → config/validate.js} +93 -96
  10. package/dist/file-router/constants.d.ts +12 -2
  11. package/dist/file-router/constants.js +13 -3
  12. package/dist/file-router/generator/generateRoutes.d.ts +18 -13
  13. package/dist/file-router/generator/generateRoutes.js +109 -68
  14. package/dist/file-router/generator/generateTypes.d.ts +3 -3
  15. package/dist/file-router/generator/generateTypes.js +36 -41
  16. package/dist/file-router/global.d.ts +1 -1
  17. package/dist/file-router/index.d.ts +130 -90
  18. package/dist/file-router/index.js +447 -134
  19. package/dist/file-router/macros/astValueExtractor.d.ts +1 -1
  20. package/dist/file-router/macros/definePage.d.ts +18 -3
  21. package/dist/file-router/macros/definePage.js +117 -39
  22. package/dist/file-router/parser/exportChecker.d.ts +4 -23
  23. package/dist/file-router/parser/exportChecker.js +36 -78
  24. package/dist/file-router/parser/filterUtils.d.ts +25 -0
  25. package/dist/file-router/parser/filterUtils.js +44 -0
  26. package/dist/file-router/parser/index.d.ts +2 -1
  27. package/dist/file-router/parser/index.js +2 -1
  28. package/dist/file-router/parser/parsePage.d.ts +21 -10
  29. package/dist/file-router/parser/parsePage.js +5 -195
  30. package/dist/file-router/types/hooks.d.ts +18 -0
  31. package/dist/file-router/types/index.d.ts +3 -0
  32. package/dist/file-router/types/index.js +1 -0
  33. package/dist/file-router/types/options.d.ts +138 -0
  34. package/dist/file-router/types/options.js +1 -0
  35. package/dist/file-router/types/route.d.ts +102 -0
  36. package/dist/file-router/types/route.js +1 -0
  37. package/dist/file-router/utils/fileReader.d.ts +11 -0
  38. package/dist/file-router/utils/fileReader.js +22 -0
  39. package/dist/file-router/utils/index.d.ts +3 -2
  40. package/dist/file-router/utils/index.js +3 -2
  41. package/dist/file-router/utils/logger.d.ts +2 -2
  42. package/dist/file-router/utils/logger.js +3 -3
  43. package/dist/file-router/utils/pathStrategy.d.ts +28 -0
  44. package/dist/file-router/utils/{namingStrategy.js → pathStrategy.js} +18 -28
  45. package/dist/file-router/utils/pathUtils.d.ts +31 -0
  46. package/dist/file-router/utils/pathUtils.js +53 -1
  47. package/dist/plugin-vite/index.d.ts +28 -9
  48. package/dist/plugin-vite/index.js +28 -55
  49. package/package.json +4 -4
  50. package/dist/file-router/config/configUtils.d.ts +0 -54
  51. package/dist/file-router/config/configUtils.js +0 -88
  52. package/dist/file-router/scanner/filterUtils.d.ts +0 -35
  53. package/dist/file-router/scanner/filterUtils.js +0 -188
  54. package/dist/file-router/scanner/index.d.ts +0 -8
  55. package/dist/file-router/scanner/index.js +0 -8
  56. package/dist/file-router/scanner/routeTreeBuilder.d.ts +0 -21
  57. package/dist/file-router/scanner/routeTreeBuilder.js +0 -312
  58. package/dist/file-router/scanner/scanPages.d.ts +0 -48
  59. package/dist/file-router/scanner/scanPages.js +0 -174
  60. package/dist/file-router/types.d.ts +0 -344
  61. package/dist/file-router/utils/namingStrategy.d.ts +0 -57
  62. /package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.js +0 -0
  63. /package/dist/{plugin-vite/auto-routes → auto-routes}/index.d.ts +0 -0
  64. /package/dist/{plugin-vite/auto-routes → auto-routes}/index.js +0 -0
  65. /package/dist/file-router/{types.js → types/hooks.js} +0 -0
package/README.md CHANGED
@@ -10,7 +10,7 @@ Vitarx 前端框架官方路由解决方案,提供声明式路由配置、导
10
10
  ## 特性
11
11
 
12
12
  - 🚀 **多种路由模式** - 支持 Hash、History、Memory 三种路由模式
13
- - 📁 **文件路由** - 基于 Vite 插件的文件系统路由自动生成
13
+ - 📁 **文件路由** - 基于 Vite 插件的文件系统路由自动生成,HMR 热更新支持
14
14
  - 🔒 **导航守卫** - 完整的路由守卫机制,支持权限控制
15
15
  - 🔄 **动态路由** - 支持动态参数、正则约束、可选参数
16
16
  - 📦 **懒加载** - 内置组件懒加载支持
@@ -403,7 +403,10 @@ import VitarxRouter from 'vitarx-router/vite'
403
403
  export default defineConfig({
404
404
  plugins: [
405
405
  VitarxRouter({
406
- pagesDir: 'src/pages'
406
+ pages: 'src/pages',
407
+ pathStrategy: 'kebab',
408
+ importMode: 'lazy',
409
+ dts: 'router.d.ts'
407
410
  })
408
411
  ]
409
412
  })
@@ -418,7 +421,21 @@ import { createRouter } from 'vitarx-router'
418
421
  export const router = createRouter({ routes })
419
422
  ```
420
423
 
421
- 详细配置请参考 [Vite 插件文档](src/plugin-vite/README.md)。
424
+ ### 文件路由配置选项
425
+
426
+ | 选项 | 类型 | 默认值 | 说明 |
427
+ |------------------|---------------------------------------|---------------|----------|
428
+ | `pages` | `PageSource \| readonly PageSource[]` | `'src/pages'` | 页面来源配置 |
429
+ | `pathStrategy` | `'kebab' \| 'lowercase' \| 'raw'` | `'kebab'` | 路径命名策略 |
430
+ | `importMode` | `'lazy' \| 'sync'` | `'lazy'` | 组件导入模式 |
431
+ | `injectImports` | `readonly string[]` | - | 自定义导入语句 |
432
+ | `dts` | `boolean \| string` | `false` | 类型声明文件配置 |
433
+ | `layoutFileName` | `string` | `'_layout'` | 布局文件名 |
434
+ | `configFileName` | `string` | `'_config'` | 分组配置文件名 |
435
+ | `transform` | `CodeTransformHook` | - | 代码转换钩子 |
436
+ | `extendRoute` | `ExtendRouteHook` | - | 路由扩展钩子 |
437
+
438
+ 详细配置请参考 [File Router 文档](src/file-router/README.md)。
422
439
 
423
440
  ## TypeScript 支持
424
441
 
@@ -1,4 +1,4 @@
1
- import type { Route, Router } from '../../core/index.js';
1
+ import type { Route, Router } from '../core/index.js';
2
2
  /**
3
3
  * 处理路由热更新函数
4
4
  *
@@ -200,6 +200,7 @@ export class RouteManager {
200
200
  const candidates = this.dynamicRoutes.get(length);
201
201
  if (candidates) {
202
202
  for (const { regex, route } of candidates) {
203
+ regex.lastIndex = 0;
203
204
  // 执行正则匹配
204
205
  const match = regex.exec(formattedPath);
205
206
  if (match) {
@@ -248,6 +249,7 @@ export class RouteManager {
248
249
  // 3. 动态路由:参数校验与序列化
249
250
  const resolvedParams = {};
250
251
  for (const paramDef of route.pattern) {
252
+ paramDef.regex.lastIndex = 0;
251
253
  const value = params[paramDef.name];
252
254
  const rawValue = String(value ?? ''); // 统一转字符串处理
253
255
  // 3.1 必填参数校验
@@ -3,4 +3,5 @@
3
3
  *
4
4
  * 导出所有配置相关功能,提供统一的访问入口。
5
5
  */
6
- export * from './configUtils.js';
6
+ export * from './resolve.js';
7
+ export * from './validate.js';
@@ -3,4 +3,5 @@
3
3
  *
4
4
  * 导出所有配置相关功能,提供统一的访问入口。
5
5
  */
6
- export * from './configUtils.js';
6
+ export * from './resolve.js';
7
+ export * from './validate.js';
@@ -0,0 +1,26 @@
1
+ import type { CodeTransformHook, ExtendRouteHook, FileRouterOptions, ImportMode, PageDirOptions, PathStrategy } from '../types/index.js';
2
+ export type PageDirConfig = Required<PageDirOptions>;
3
+ /**
4
+ * 规范化后的配置
5
+ */
6
+ export interface ResolvedConfig {
7
+ root: string;
8
+ pages: readonly PageDirConfig[];
9
+ pathStrategy: PathStrategy;
10
+ importMode: ImportMode;
11
+ injectImports: readonly string[];
12
+ dts: false | string;
13
+ layoutFileName: string;
14
+ configFileName: string;
15
+ transform?: CodeTransformHook;
16
+ extendRoute?: ExtendRouteHook;
17
+ }
18
+ /**
19
+ * 规范化文件路由配置
20
+ *
21
+ * 将用户提供的配置转换为内部统一格式。
22
+ *
23
+ * @param options - 用户提供的配置选项
24
+ * @returns - 规范化后的配置对象
25
+ */
26
+ export declare function resolveConfig(options: FileRouterOptions): ResolvedConfig;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @fileoverview 配置处理工具模块
3
+ *
4
+ * 提供页面目录配置的处理和文件检查功能。
5
+ * 与构建工具无关,可在任何 Node.js 环境中使用。
6
+ */
7
+ import { accessSync, constants } from 'node:fs';
8
+ import path from 'node:path';
9
+ import { DEFAULT_CONFIG_FILE, DEFAULT_DTS_FILE, DEFAULT_EXCLUDE, DEFAULT_INCLUDE, DEFAULT_LAYOUT_FILE, DEFAULT_PAGES_DIR } from '../constants.js';
10
+ const DEFAULT_PAGE_CONFIG = {
11
+ exclude: DEFAULT_EXCLUDE,
12
+ include: DEFAULT_INCLUDE,
13
+ prefix: '/',
14
+ group: false
15
+ };
16
+ /**
17
+ * 将 pages 配置规范化为 PageConfig 数组
18
+ *
19
+ * 支持四种输入格式:
20
+ * 1. 字符串:单个目录路径
21
+ * 2. 对象:单个目录配置
22
+ * 3. 字符串数组:多个目录路径
23
+ * 4. 对象数组:每个目录独立配置
24
+ *
25
+ * @param pages - 用户配置的 pages
26
+ * @param root - 项目根目录路径(用于解析相对路径)
27
+ * @returns - 规范化后的目录配置数组
28
+ */
29
+ function resolvePageConfigs(pages, root) {
30
+ const list = Array.isArray(pages) ? pages : [pages];
31
+ return list.map(page => {
32
+ const config = typeof page === 'string' ? { dir: page } : page;
33
+ const resolved = {
34
+ ...DEFAULT_PAGE_CONFIG,
35
+ ...config,
36
+ dir: path.isAbsolute(config.dir) ? config.dir : path.resolve(root, config.dir)
37
+ };
38
+ if (resolved.prefix !== '/' && !resolved.prefix.startsWith('/')) {
39
+ resolved.prefix = '/' + resolved.prefix;
40
+ }
41
+ try {
42
+ accessSync(resolved.dir, constants.R_OK | constants.W_OK);
43
+ }
44
+ catch (error) {
45
+ throw new Error(`File router: Pages directory "${resolved.dir}" does not exist or is not accessible`);
46
+ }
47
+ return resolved;
48
+ });
49
+ }
50
+ /**
51
+ * 规范化文件路由配置
52
+ *
53
+ * 将用户提供的配置转换为内部统一格式。
54
+ *
55
+ * @param options - 用户提供的配置选项
56
+ * @returns - 规范化后的配置对象
57
+ */
58
+ export function resolveConfig(options) {
59
+ const { dts = false, root = process.cwd(), pages = DEFAULT_PAGES_DIR, importMode = 'lazy', injectImports = [], pathStrategy = 'kebab', layoutFileName = DEFAULT_LAYOUT_FILE, configFileName = DEFAULT_CONFIG_FILE, transform, extendRoute } = options;
60
+ const resolvedPages = resolvePageConfigs(pages, root);
61
+ return {
62
+ dts: typeof dts === 'string' ? dts : dts ? DEFAULT_DTS_FILE : false,
63
+ root,
64
+ pages: resolvedPages,
65
+ importMode,
66
+ injectImports,
67
+ pathStrategy,
68
+ layoutFileName,
69
+ configFileName,
70
+ transform,
71
+ extendRoute
72
+ };
73
+ }
@@ -4,21 +4,21 @@
4
4
  * 提供文件路由配置选项的验证功能,确保用户提供的配置符合要求。
5
5
  * 当配置无效时抛出明确的错误信息,帮助用户快速定位问题。
6
6
  */
7
- import type { FileRouterOptions } from '../types.js';
7
+ import type { FileRouterOptions } from '../types/index.js';
8
8
  /**
9
9
  * 验证插件配置选项
10
10
  *
11
11
  * 验证顺序:
12
12
  * 1. root 配置
13
- * 2. prefix 配置
14
- * 3. pages 配置
15
- * 4. extensions 配置
16
- * 5. include 配置
17
- * 6. exclude 配置
18
- * 7. importMode 配置
19
- * 8. extendRoute 配置
20
- * 9. injectImports 配置
21
- * 10. namingStrategy 配置
13
+ * 2. pages 配置
14
+ * 3. pathStrategy 配置
15
+ * 4. importMode 配置
16
+ * 5. injectImports 配置
17
+ * 6. dts 配置
18
+ * 7. layoutFileName 配置
19
+ * 8. configFileName 配置
20
+ * 9. transform 配置
21
+ * 10. extendRoute 配置
22
22
  *
23
23
  * @param opts - 用户提供的配置选项
24
24
  * @throws {Error} 当配置无效时抛出错误
@@ -1,3 +1,9 @@
1
+ /**
2
+ * @fileoverview 配置验证工具模块
3
+ *
4
+ * 提供文件路由配置选项的验证功能,确保用户提供的配置符合要求。
5
+ * 当配置无效时抛出明确的错误信息,帮助用户快速定位问题。
6
+ */
1
7
  /**
2
8
  * 验证 pages 配置
3
9
  *
@@ -9,20 +15,20 @@ function validatePagesDir(opts) {
9
15
  return;
10
16
  if (typeof opts.pages === 'string') {
11
17
  if (opts.pages.trim() === '') {
12
- throw new Error('options.pagesDir 不能为空字符串');
18
+ throw new Error('options.pages 不能为空字符串');
13
19
  }
14
20
  return;
15
21
  }
16
22
  if (Array.isArray(opts.pages)) {
17
23
  if (opts.pages.length === 0) {
18
- throw new Error('options.pagesDir 数组不能为空');
24
+ throw new Error('options.pages 数组不能为空');
19
25
  }
20
26
  opts.pages.forEach((item, i) => {
21
27
  validatePagesDirItem(item, i);
22
28
  });
23
29
  return;
24
30
  }
25
- throw new Error('options.pagesDir 必须是字符串、字符串数组或对象数组');
31
+ throw new Error('options.pages 必须是字符串、字符串数组或对象数组');
26
32
  }
27
33
  /**
28
34
  * 验证 pages 数组中的单个项目
@@ -34,171 +40,162 @@ function validatePagesDir(opts) {
34
40
  function validatePagesDirItem(item, index) {
35
41
  if (typeof item === 'string') {
36
42
  if (item.trim() === '') {
37
- throw new Error(`options.pagesDir[${index}] 不能为空字符串`);
43
+ throw new Error(`options.pages[${index}] 不能为空字符串`);
38
44
  }
39
45
  return;
40
46
  }
41
47
  if (typeof item === 'object' && item !== null) {
42
48
  const config = item;
43
49
  if (!config.dir || typeof config.dir !== 'string' || config.dir.trim() === '') {
44
- throw new Error(`options.pagesDir[${index}].dir 必须是非空字符串`);
50
+ throw new Error(`options.pages[${index}].dir 必须是非空字符串`);
45
51
  }
46
52
  if (config.include !== undefined && !Array.isArray(config.include)) {
47
- throw new Error(`options.pagesDir[${index}].include 必须是数组`);
53
+ throw new Error(`options.pages[${index}].include 必须是数组`);
48
54
  }
49
55
  if (config.exclude !== undefined && !Array.isArray(config.exclude)) {
50
- throw new Error(`options.pagesDir[${index}].exclude 必须是数组`);
56
+ throw new Error(`options.pages[${index}].exclude 必须是数组`);
51
57
  }
52
58
  return;
53
59
  }
54
- throw new Error(`options.pagesDir[${index}] 必须是字符串或对象`);
60
+ throw new Error(`options.pages[${index}] 必须是字符串或对象`);
55
61
  }
56
62
  /**
57
- * 验证 extensions 配置
63
+ * 验证 root 配置
58
64
  *
59
65
  * @param opts - 配置选项
60
66
  * @throws {Error} 当配置无效时抛出错误
61
67
  */
62
- function validateExtensions(opts) {
63
- if (opts.extensions === undefined)
68
+ function validateRoot(opts) {
69
+ if (opts.root === undefined)
64
70
  return;
65
- if (!Array.isArray(opts.extensions)) {
66
- throw new Error('options.extensions 必须是数组');
71
+ if (typeof opts.root !== 'string') {
72
+ throw new Error('options.root 必须是字符串');
67
73
  }
68
- if (opts.extensions.length === 0) {
69
- throw new Error('options.extensions 数组不能为空');
74
+ if (opts.root.trim() === '') {
75
+ throw new Error('options.root 不能为空字符串');
70
76
  }
71
- opts.extensions.forEach((ext, i) => {
72
- if (typeof ext !== 'string') {
73
- throw new Error(`options.extensions[${i}] 必须是字符串`);
74
- }
75
- if (!ext.startsWith('.')) {
76
- throw new Error(`options.extensions[${i}] "${ext}" 必须以 "." 开头`);
77
- }
78
- });
79
77
  }
80
78
  /**
81
- * 验证 include 配置
79
+ * 验证 importMode 配置
82
80
  *
83
81
  * @param opts - 配置选项
84
82
  * @throws {Error} 当配置无效时抛出错误
85
83
  */
86
- function validateInclude(opts) {
87
- if (opts.include === undefined)
84
+ function validateImportMode(opts) {
85
+ if (opts.importMode === undefined)
88
86
  return;
89
- if (!Array.isArray(opts.include)) {
90
- throw new Error('options.include 必须是数组');
87
+ const validModes = ['lazy', 'sync'];
88
+ if (!validModes.includes(opts.importMode)) {
89
+ throw new Error(`options.importMode 必须是 'lazy' 或 'sync'`);
91
90
  }
92
- opts.include.forEach((item, i) => {
93
- if (typeof item !== 'string') {
94
- throw new Error(`options.include[${i}] 必须是字符串`);
95
- }
96
- });
97
91
  }
98
92
  /**
99
- * 验证 exclude 配置
93
+ * 验证 injectImports 配置
100
94
  *
101
95
  * @param opts - 配置选项
102
96
  * @throws {Error} 当配置无效时抛出错误
103
97
  */
104
- function validateExclude(opts) {
105
- if (opts.exclude === undefined)
98
+ function validateInjectImports(opts) {
99
+ if (opts.injectImports === undefined)
106
100
  return;
107
- if (!Array.isArray(opts.exclude)) {
108
- throw new Error('options.exclude 必须是数组');
101
+ if (!Array.isArray(opts.injectImports)) {
102
+ throw new Error('options.injectImports 必须是数组');
109
103
  }
110
- opts.exclude.forEach((item, i) => {
111
- if (typeof item !== 'string') {
112
- throw new Error(`options.exclude[${i}] 必须是字符串`);
104
+ opts.injectImports.forEach((imp, i) => {
105
+ if (typeof imp !== 'string') {
106
+ throw new Error(`options.injectImports[${i}] 必须是字符串`);
113
107
  }
114
108
  });
115
109
  }
116
110
  /**
117
- * 验证 root 配置
111
+ * 验证 namingStrategy 配置
118
112
  *
119
113
  * @param opts - 配置选项
120
114
  * @throws {Error} 当配置无效时抛出错误
121
115
  */
122
- function validateRoot(opts) {
123
- if (opts.root === undefined)
116
+ function validatePathStrategy(opts) {
117
+ if (opts.pathStrategy === undefined)
124
118
  return;
125
- if (typeof opts.root !== 'string') {
126
- throw new Error('options.root 必须是字符串');
127
- }
128
- if (opts.root.trim() === '') {
129
- throw new Error('options.root 不能为空字符串');
119
+ const validStrategies = ['kebab', 'lowercase', 'raw'];
120
+ if (!validStrategies.includes(opts.pathStrategy)) {
121
+ throw new Error(`options.pathStrategy 必须是 'kebab'、'lowercase' 或 'raw'`);
130
122
  }
131
123
  }
132
124
  /**
133
- * 验证 prefix 配置
125
+ * 验证 dts 配置
134
126
  *
135
127
  * @param opts - 配置选项
136
128
  * @throws {Error} 当配置无效时抛出错误
137
129
  */
138
- function validatePrefix(opts) {
139
- if (opts.prefix === undefined)
130
+ function validateDts(opts) {
131
+ if (opts.dts === undefined)
132
+ return;
133
+ if (typeof opts.dts === 'boolean')
134
+ return;
135
+ if (typeof opts.dts === 'string') {
136
+ if (opts.dts.trim() === '') {
137
+ throw new Error('options.dts 为字符串时不能为空字符串');
138
+ }
140
139
  return;
141
- if (typeof opts.prefix !== 'string') {
142
- throw new Error('options.prefix 必须是字符串');
143
140
  }
141
+ throw new Error('options.dts 必须是 boolean 或 string');
144
142
  }
145
143
  /**
146
- * 验证 importMode 配置
144
+ * 验证 layoutFileName 配置
147
145
  *
148
146
  * @param opts - 配置选项
149
147
  * @throws {Error} 当配置无效时抛出错误
150
148
  */
151
- function validateImportMode(opts) {
152
- if (opts.importMode === undefined)
149
+ function validateLayoutFileName(opts) {
150
+ if (opts.layoutFileName === undefined)
153
151
  return;
154
- const validModes = ['lazy', 'file'];
155
- if (!validModes.includes(opts.importMode)) {
156
- throw new Error(`options.importMode 必须是 'lazy' 或 'file'`);
152
+ if (typeof opts.layoutFileName !== 'string') {
153
+ throw new Error('options.layoutFileName 必须是字符串');
154
+ }
155
+ if (opts.layoutFileName.trim() === '') {
156
+ throw new Error('options.layoutFileName 不能为空字符串');
157
157
  }
158
158
  }
159
159
  /**
160
- * 验证 extendRoute 配置
160
+ * 验证 configFileName 配置
161
161
  *
162
162
  * @param opts - 配置选项
163
163
  * @throws {Error} 当配置无效时抛出错误
164
164
  */
165
- function validateExtendRoute(opts) {
166
- if (opts.extendRoute === undefined)
165
+ function validateConfigFileName(opts) {
166
+ if (opts.configFileName === undefined)
167
167
  return;
168
- if (typeof opts.extendRoute !== 'function') {
169
- throw new Error('options.extendRoute 必须是函数');
168
+ if (typeof opts.configFileName !== 'string') {
169
+ throw new Error('options.configFileName 必须是字符串');
170
+ }
171
+ if (opts.configFileName.trim() === '') {
172
+ throw new Error('options.configFileName 不能为空字符串');
170
173
  }
171
174
  }
172
175
  /**
173
- * 验证 injectImports 配置
176
+ * 验证 transform 配置
174
177
  *
175
178
  * @param opts - 配置选项
176
179
  * @throws {Error} 当配置无效时抛出错误
177
180
  */
178
- function validateInjectImports(opts) {
179
- if (opts.injectImports === undefined)
181
+ function validateTransform(opts) {
182
+ if (opts.transform === undefined)
180
183
  return;
181
- if (!Array.isArray(opts.injectImports)) {
182
- throw new Error('options.injectImports 必须是数组');
184
+ if (typeof opts.transform !== 'function') {
185
+ throw new Error('options.transform 必须是函数');
183
186
  }
184
- opts.injectImports.forEach((imp, i) => {
185
- if (typeof imp !== 'string') {
186
- throw new Error(`options.injectImports[${i}] 必须是字符串`);
187
- }
188
- });
189
187
  }
190
188
  /**
191
- * 验证 namingStrategy 配置
189
+ * 验证 extendRoute 配置
192
190
  *
193
191
  * @param opts - 配置选项
194
192
  * @throws {Error} 当配置无效时抛出错误
195
193
  */
196
- function validateNamingStrategy(opts) {
197
- if (opts.namingStrategy === undefined)
194
+ function validateExtendRoute(opts) {
195
+ if (opts.extendRoute === undefined)
198
196
  return;
199
- const validStrategies = ['kebab', 'lowercase', 'none'];
200
- if (!validStrategies.includes(opts.namingStrategy)) {
201
- throw new Error(`options.namingStrategy 必须是 'kebab'、'lowercase' 或 'none'`);
197
+ if (typeof opts.extendRoute !== 'function') {
198
+ throw new Error('options.extendRoute 必须是函数');
202
199
  }
203
200
  }
204
201
  /**
@@ -206,28 +203,28 @@ function validateNamingStrategy(opts) {
206
203
  *
207
204
  * 验证顺序:
208
205
  * 1. root 配置
209
- * 2. prefix 配置
210
- * 3. pages 配置
211
- * 4. extensions 配置
212
- * 5. include 配置
213
- * 6. exclude 配置
214
- * 7. importMode 配置
215
- * 8. extendRoute 配置
216
- * 9. injectImports 配置
217
- * 10. namingStrategy 配置
206
+ * 2. pages 配置
207
+ * 3. pathStrategy 配置
208
+ * 4. importMode 配置
209
+ * 5. injectImports 配置
210
+ * 6. dts 配置
211
+ * 7. layoutFileName 配置
212
+ * 8. configFileName 配置
213
+ * 9. transform 配置
214
+ * 10. extendRoute 配置
218
215
  *
219
216
  * @param opts - 用户提供的配置选项
220
217
  * @throws {Error} 当配置无效时抛出错误
221
218
  */
222
219
  export function validateOptions(opts) {
223
220
  validateRoot(opts);
224
- validatePrefix(opts);
225
221
  validatePagesDir(opts);
226
- validateExtensions(opts);
227
- validateInclude(opts);
228
- validateExclude(opts);
222
+ validatePathStrategy(opts);
229
223
  validateImportMode(opts);
230
- validateExtendRoute(opts);
231
224
  validateInjectImports(opts);
232
- validateNamingStrategy(opts);
225
+ validateDts(opts);
226
+ validateLayoutFileName(opts);
227
+ validateConfigFileName(opts);
228
+ validateTransform(opts);
229
+ validateExtendRoute(opts);
233
230
  }
@@ -3,9 +3,19 @@
3
3
  *
4
4
  * 定义文件路由的默认配置常量。
5
5
  */
6
- /** 支持的页面文件扩展名列表 */
7
- export declare const DEFAULT_EXTENSIONS: string[];
8
6
  /** 默认的页面目录路径 */
9
7
  export declare const DEFAULT_PAGES_DIR = "src/pages";
8
+ /** 默认的类型定义文件路径 */
9
+ export declare const DEFAULT_DTS_FILE = "router.d.ts";
10
10
  /** 默认的包含模式 */
11
11
  export declare const DEFAULT_INCLUDE: string[];
12
+ /** 默认的排除模式 */
13
+ export declare const DEFAULT_EXCLUDE: string[];
14
+ /**
15
+ * 布局文件名前缀
16
+ */
17
+ export declare const DEFAULT_LAYOUT_FILE = "_layout";
18
+ /**
19
+ * 配置文件名
20
+ */
21
+ export declare const DEFAULT_CONFIG_FILE = "_config";
@@ -3,9 +3,19 @@
3
3
  *
4
4
  * 定义文件路由的默认配置常量。
5
5
  */
6
- /** 支持的页面文件扩展名列表 */
7
- export const DEFAULT_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'];
8
6
  /** 默认的页面目录路径 */
9
7
  export const DEFAULT_PAGES_DIR = 'src/pages';
8
+ /** 默认的类型定义文件路径 */
9
+ export const DEFAULT_DTS_FILE = 'router.d.ts';
10
10
  /** 默认的包含模式 */
11
- export const DEFAULT_INCLUDE = ['**/*'];
11
+ export const DEFAULT_INCLUDE = ['**/*.{jsx,tsx}'];
12
+ /** 默认的排除模式 */
13
+ export const DEFAULT_EXCLUDE = ['**/node_modules/**', '**/dist/**', '**/.*'];
14
+ /**
15
+ * 布局文件名前缀
16
+ */
17
+ export const DEFAULT_LAYOUT_FILE = '_layout';
18
+ /**
19
+ * 配置文件名
20
+ */
21
+ export const DEFAULT_CONFIG_FILE = '_config';
@@ -4,17 +4,21 @@
4
4
  * 负责将解析后的页面信息转换为可执行的路由配置代码。
5
5
  * 与构建工具无关,可在任何 Node.js 环境中使用。
6
6
  */
7
- import type { ExtendRouteHook, ImportMode, NamingStrategy, ParsedPage } from '../types.js';
7
+ import type { ExtendRouteHook, ImportMode, ParsedNode, RouteNode } from '../types/index.js';
8
8
  /**
9
9
  * 路由生成选项
10
10
  */
11
11
  export interface GenerateRoutesOptions {
12
+ /**
13
+ * 是否生成类型代码
14
+ */
15
+ dts: boolean;
12
16
  /**
13
17
  * 组件导入模式
14
18
  * - `lazy`: 使用 lazy(() => import(...)) 懒加载组件
15
19
  * - `file`: 直接使用文件路径作为组件
16
20
  */
17
- importMode?: ImportMode;
21
+ importMode: ImportMode;
18
22
  /**
19
23
  * 路由扩展钩子
20
24
  * 在生成每个路由配置时调用,允许开发者自定义扩展路由配置
@@ -24,16 +28,17 @@ export interface GenerateRoutesOptions {
24
28
  * 自定义导入语句
25
29
  * 允许向虚拟模块注入自定义的导入语句
26
30
  */
27
- imports?: string[];
28
- /**
29
- * 路由命名策略
30
- * - `kebab`: 将驼峰命名转换为 kebab-case(默认)
31
- * - `lowercase`: 简单转换为小写
32
- * - `none`: 保持原始命名
33
- * @default 'kebab'
34
- */
35
- namingStrategy?: NamingStrategy;
31
+ imports?: readonly string[];
36
32
  }
33
+ export interface GenerateResult {
34
+ routes: RouteNode[];
35
+ code: string;
36
+ dts: string;
37
+ }
38
+ /**
39
+ * 将绝对路径转换为百分百唯一、合法的 import 变量名
40
+ */
41
+ export declare function pathToUniqueName(absolutePath: string): string;
37
42
  /**
38
43
  * 生成路由配置代码
39
44
  *
@@ -41,6 +46,6 @@ export interface GenerateRoutesOptions {
41
46
  *
42
47
  * @param pages - 解析后的页面列表
43
48
  * @param options - 路由生成选项
44
- * @returns Promise 包含可执行路由配置代码字符串
49
+ * @returns { GenerateResult } 包含routes、code、dts的路由配置结果
45
50
  */
46
- export declare function generateRoutes(pages: ParsedPage[], options?: GenerateRoutesOptions): Promise<string>;
51
+ export declare function generateRoutes(pages: ParsedNode[], options: GenerateRoutesOptions): GenerateResult;