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

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 (93) hide show
  1. package/README.md +42 -17
  2. package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.d.ts +1 -1
  3. package/dist/components/RouterView.js +5 -4
  4. package/dist/core/common/constant.d.ts +5 -6
  5. package/dist/core/common/constant.js +5 -6
  6. package/dist/core/common/utils.js +2 -1
  7. package/dist/core/router/checkOptions.d.ts +11 -0
  8. package/dist/core/router/checkOptions.js +119 -0
  9. package/dist/core/router/manager.js +27 -23
  10. package/dist/core/router/router.d.ts +154 -1
  11. package/dist/core/router/router.js +303 -230
  12. package/dist/core/router/web.d.ts +13 -0
  13. package/dist/core/router/web.js +35 -4
  14. package/dist/core/shared/link.d.ts +7 -0
  15. package/dist/core/shared/link.js +11 -8
  16. package/dist/core/shared/route.js +1 -2
  17. package/dist/core/shared/router.d.ts +3 -3
  18. package/dist/core/shared/router.js +7 -4
  19. package/dist/core/types/options.d.ts +2 -0
  20. package/dist/file-router/config/index.d.ts +2 -1
  21. package/dist/file-router/config/index.js +2 -1
  22. package/dist/file-router/config/resolve.d.ts +43 -0
  23. package/dist/file-router/config/resolve.js +69 -0
  24. package/dist/file-router/{utils/validateOptions.d.ts → config/validate.d.ts} +11 -10
  25. package/dist/file-router/config/validate.js +280 -0
  26. package/dist/file-router/constants.d.ts +12 -2
  27. package/dist/file-router/constants.js +13 -3
  28. package/dist/file-router/generator/generateRoutes.d.ts +44 -13
  29. package/dist/file-router/generator/generateRoutes.js +159 -80
  30. package/dist/file-router/generator/generateTypes.d.ts +3 -29
  31. package/dist/file-router/generator/generateTypes.js +36 -41
  32. package/dist/file-router/global.d.ts +1 -1
  33. package/dist/file-router/index.d.ts +224 -90
  34. package/dist/file-router/index.js +571 -135
  35. package/dist/file-router/macros/astValueExtractor.d.ts +1 -1
  36. package/dist/file-router/macros/astValueExtractor.js +27 -7
  37. package/dist/file-router/macros/definePage.d.ts +20 -3
  38. package/dist/file-router/macros/definePage.js +120 -40
  39. package/dist/file-router/parser/exportChecker.d.ts +4 -23
  40. package/dist/file-router/parser/exportChecker.js +38 -79
  41. package/dist/file-router/parser/filterUtils.d.ts +25 -0
  42. package/dist/file-router/parser/filterUtils.js +43 -0
  43. package/dist/file-router/parser/index.d.ts +2 -1
  44. package/dist/file-router/parser/index.js +2 -1
  45. package/dist/file-router/parser/parsePage.d.ts +56 -9
  46. package/dist/file-router/parser/parsePage.js +194 -172
  47. package/dist/file-router/parser/routePath.d.ts +22 -0
  48. package/dist/file-router/parser/routePath.js +74 -0
  49. package/dist/file-router/types/hooks.d.ts +52 -0
  50. package/dist/file-router/types/index.d.ts +3 -0
  51. package/dist/file-router/types/index.js +1 -0
  52. package/dist/file-router/types/options.d.ts +279 -0
  53. package/dist/file-router/types/options.js +1 -0
  54. package/dist/file-router/types/route.d.ts +114 -0
  55. package/dist/file-router/types/route.js +1 -0
  56. package/dist/file-router/utils/fileReader.d.ts +11 -0
  57. package/dist/file-router/utils/fileReader.js +22 -0
  58. package/dist/file-router/utils/findRoute.d.ts +8 -0
  59. package/dist/file-router/utils/findRoute.js +22 -0
  60. package/dist/file-router/utils/index.d.ts +4 -2
  61. package/dist/file-router/utils/index.js +4 -2
  62. package/dist/file-router/utils/logger.d.ts +6 -6
  63. package/dist/file-router/utils/logger.js +44 -4
  64. package/dist/file-router/utils/pathStrategy.d.ts +28 -0
  65. package/dist/file-router/utils/{namingStrategy.js → pathStrategy.js} +18 -28
  66. package/dist/file-router/utils/pathUtils.d.ts +31 -0
  67. package/dist/file-router/utils/pathUtils.js +53 -1
  68. package/dist/plugin-vite/constant.d.ts +9 -0
  69. package/dist/plugin-vite/constant.js +9 -0
  70. package/dist/plugin-vite/index.d.ts +4 -24
  71. package/dist/plugin-vite/index.js +4 -94
  72. package/dist/plugin-vite/plugin.d.ts +86 -0
  73. package/dist/plugin-vite/plugin.js +181 -0
  74. package/dist/plugin-vite/watcher.d.ts +15 -0
  75. package/dist/plugin-vite/watcher.js +65 -0
  76. package/package.json +9 -7
  77. package/dist/file-router/config/configUtils.d.ts +0 -54
  78. package/dist/file-router/config/configUtils.js +0 -88
  79. package/dist/file-router/scanner/filterUtils.d.ts +0 -35
  80. package/dist/file-router/scanner/filterUtils.js +0 -188
  81. package/dist/file-router/scanner/index.d.ts +0 -8
  82. package/dist/file-router/scanner/index.js +0 -8
  83. package/dist/file-router/scanner/routeTreeBuilder.d.ts +0 -21
  84. package/dist/file-router/scanner/routeTreeBuilder.js +0 -312
  85. package/dist/file-router/scanner/scanPages.d.ts +0 -48
  86. package/dist/file-router/scanner/scanPages.js +0 -174
  87. package/dist/file-router/types.d.ts +0 -344
  88. package/dist/file-router/utils/namingStrategy.d.ts +0 -57
  89. package/dist/file-router/utils/validateOptions.js +0 -233
  90. /package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.js +0 -0
  91. /package/dist/{plugin-vite/auto-routes → auto-routes}/index.d.ts +0 -0
  92. /package/dist/{plugin-vite/auto-routes → auto-routes}/index.js +0 -0
  93. /package/dist/file-router/{types.js → types/hooks.js} +0 -0
@@ -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,36 +4,67 @@
4
4
  * 负责将解析后的页面信息转换为可执行的路由配置代码。
5
5
  * 与构建工具无关,可在任何 Node.js 环境中使用。
6
6
  */
7
- import type { ExtendRouteHook, ImportMode, NamingStrategy, ParsedPage } from '../types.js';
7
+ import type { BeforeWriteRoutesHook, ExtendRouteHook, ImportMode, RouteNode, ScanNode } 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
  * 在生成每个路由配置时调用,允许开发者自定义扩展路由配置
21
25
  */
22
26
  extendRoute?: ExtendRouteHook;
23
27
  /**
24
- * 自定义导入语句
25
- * 允许向虚拟模块注入自定义的导入语句
28
+ * 路由写入前钩子
29
+ * 在生成路由代码前调用,允许开发者自定义路由配置
26
30
  */
27
- imports?: string[];
31
+ beforeWriteRoutes?: BeforeWriteRoutesHook;
28
32
  /**
29
- * 路由命名策略
30
- * - `kebab`: 将驼峰命名转换为 kebab-case(默认)
31
- * - `lowercase`: 简单转换为小写
32
- * - `none`: 保持原始命名
33
- * @default 'kebab'
33
+ * 自定义导入语句
34
+ * 允许向虚拟模块注入自定义的导入语句
34
35
  */
35
- namingStrategy?: NamingStrategy;
36
+ imports?: readonly string[];
36
37
  }
38
+ export interface GenerateResult {
39
+ routes: RouteNode[];
40
+ code: string;
41
+ dts: string;
42
+ }
43
+ /**
44
+ * 根据绝对路径生成一个唯一的名称
45
+ *
46
+ * @param absolutePath
47
+ */
48
+ export declare function pathToUniqueName(absolutePath: string): string;
49
+ /**
50
+ * 构建解析后的路由配置列表
51
+ *
52
+ * @param pages - 页面列表
53
+ * @param extendRoute - 路由扩展钩子
54
+ * @param parent - 父路由
55
+ * @returns 路由配置列表
56
+ */
57
+ export declare function buildRoutes(pages: Iterable<ScanNode>, extendRoute?: ExtendRouteHook, parent?: RouteNode): RouteNode[];
58
+ /**
59
+ * 生成路由代码字符串
60
+ *
61
+ * @param routes - 路由配置列表
62
+ * @param importMode - 导入模式
63
+ * @param customImports - 自定义导入语句
64
+ * @param indent - 缩进字符串
65
+ * @returns {string} 完整的路由模块代码
66
+ */
67
+ export declare function generateRoutesCode(routes: RouteNode[], importMode?: ImportMode, customImports?: readonly string[], indent?: string): string;
37
68
  /**
38
69
  * 生成路由配置代码
39
70
  *
@@ -41,6 +72,6 @@ export interface GenerateRoutesOptions {
41
72
  *
42
73
  * @param pages - 解析后的页面列表
43
74
  * @param options - 路由生成选项
44
- * @returns Promise 包含可执行路由配置代码字符串
75
+ * @returns { GenerateResult } 包含routes、code、dts的路由配置结果
45
76
  */
46
- export declare function generateRoutes(pages: ParsedPage[], options?: GenerateRoutesOptions): Promise<string>;
77
+ export declare function generateRoutes(pages: ScanNode[], options: GenerateRoutesOptions): GenerateResult;
@@ -1,59 +1,111 @@
1
+ /**
2
+ * @fileoverview 路由代码生成模块
3
+ *
4
+ * 负责将解析后的页面信息转换为可执行的路由配置代码。
5
+ * 与构建工具无关,可在任何 Node.js 环境中使用。
6
+ */
7
+ import { createHash } from 'node:crypto';
8
+ import { normalizeRoutePath } from '../utils/index.js';
9
+ import { generateDtsCode } from './generateTypes.js';
10
+ const IMPORT_QUOTE_REGEX = /from\s+(['"])([^'"]*)\1/g;
11
+ const IMPORT_NORMALIZE_REGEX = /\s+/g;
12
+ /**
13
+ * 规范化导入语句
14
+ *
15
+ * @param statement
16
+ */
17
+ function normalizeImportStatement(statement) {
18
+ let result = statement.trim();
19
+ result = result.replace(IMPORT_QUOTE_REGEX, 'from "$2"');
20
+ result = result.replace(IMPORT_NORMALIZE_REGEX, ' ');
21
+ result = result.replace(/import\s*\{/g, 'import {');
22
+ result = result.replace(/}\s*from/g, '} from');
23
+ return result;
24
+ }
25
+ /**
26
+ * 根据绝对路径生成一个唯一的名称
27
+ *
28
+ * @param absolutePath
29
+ */
30
+ export function pathToUniqueName(absolutePath) {
31
+ // md5 碰撞概率在现实场景中为 0,且生成速度极快
32
+ const hash = createHash('md5').update(absolutePath).digest('hex');
33
+ // 加上 '_' 前缀,因为 hash 结果可能是数字开头(如 '1a2b3c...'),
34
+ // 数字开头的标识符在 JS 中不能直接作为变量名。
35
+ return `_${hash}`;
36
+ }
37
+ /**
38
+ * 生成路由名称
39
+ *
40
+ * @returns 路由名称
41
+ */
42
+ function generateRouteName(fullPath) {
43
+ return (fullPath
44
+ // 1. 去除首尾斜杠
45
+ .replace(/^\/+|\/+$/g, '')
46
+ // 2. 直接删除花括号和问号(不替换为-,保护静态路径的连续中划线)
47
+ .replace(/[{}?]/g, '')
48
+ // 3. 仅将斜杠替换为中划线
49
+ .replace(/\//g, '-'));
50
+ }
1
51
  /**
2
52
  * 构建单个路由配置
3
53
  *
4
54
  * @param page - 页面信息
5
- * @param options - 生成选项
55
+ * @param parent
56
+ * @param extendRoute - 路由扩展钩子
6
57
  * @returns 解析后的路由配置
7
58
  */
8
- async function buildResolvedRoute(page, options) {
9
- const { extendRoute } = options;
59
+ function buildRouteNode(page, extendRoute, parent) {
10
60
  // 创建基础路由配置
11
61
  const route = {
12
- path: page.path
62
+ isGroup: page.isGroup,
63
+ filePath: page.filePath,
64
+ path: page.path,
65
+ fullPath: normalizeRoutePath(parent ? `${parent.fullPath}/${page.path}` : page.path)
13
66
  };
14
67
  // 处理组件配置
15
- if (page.filePath && page.filePath.trim() !== '') {
16
- if (page.namedViews) {
17
- // 处理命名视图
18
- const components = {
19
- default: JSON.stringify(page.filePath)
20
- };
21
- for (const [viewName, viewPath] of Object.entries(page.namedViews)) {
22
- components[viewName] = JSON.stringify(viewPath);
23
- }
24
- route.component = components;
68
+ if (page.components) {
69
+ // 处理命名视图
70
+ const components = {
71
+ default: JSON.stringify(page.filePath)
72
+ };
73
+ for (const [viewName, viewPath] of Object.entries(page.components)) {
74
+ components[viewName] = viewPath;
25
75
  }
26
- else {
27
- // 处理普通组件
28
- route.component = JSON.stringify(page.filePath);
29
- }
30
- }
31
- // 处理其他路由配置
32
- if (page.meta && Object.keys(page.meta).length > 0) {
33
- route.meta = page.meta;
34
- }
35
- if (page.pattern && Object.keys(page.pattern).length > 0) {
36
- route.pattern = page.pattern;
76
+ route.component = components;
37
77
  }
38
- if (page.redirect !== undefined) {
39
- route.redirect = page.redirect;
40
- }
41
- if (page.alias !== undefined) {
42
- route.alias = page.alias;
78
+ // 处理路由配置
79
+ if (page.options) {
80
+ const { name, meta, pattern, redirect, alias } = page.options;
81
+ if (name?.trim()) {
82
+ route.name = name;
83
+ }
84
+ // 处理其他路由配置
85
+ if (meta && Object.keys(meta).length > 0) {
86
+ route.meta = { ...meta };
87
+ }
88
+ if (pattern && Object.keys(pattern).length > 0) {
89
+ route.pattern = { ...pattern };
90
+ }
91
+ if (redirect !== undefined) {
92
+ route.redirect = typeof redirect === 'object' ? { ...redirect } : redirect;
93
+ }
94
+ if (alias !== undefined) {
95
+ route.alias = Array.isArray(alias) ? Array.from(alias) : alias;
96
+ }
43
97
  }
44
- if (page.children.length > 0) {
98
+ // 应用路由扩展钩子
99
+ if (extendRoute)
100
+ extendRoute(route);
101
+ // 处理子路由
102
+ if (page.children && page.children.size > 0) {
45
103
  // 递归处理子路由
46
- route.children = await buildResolvedRoutes(page.children, options);
104
+ route.children = buildRoutes(page.children.values(), extendRoute, route);
47
105
  }
48
- // 为叶子路由或重定向路由添加名称
49
- if (page.children.length === 0 || page.redirect !== undefined) {
50
- route.name = page.name;
51
- }
52
- // 应用路由扩展钩子
53
- if (extendRoute) {
54
- const result = await extendRoute(route);
55
- if (result)
56
- return result;
106
+ // 动态路由不存在name时生成一个name
107
+ if (!route.name && route.fullPath.includes('{')) {
108
+ route.name = generateRouteName(route.fullPath);
57
109
  }
58
110
  return route;
59
111
  }
@@ -61,33 +113,59 @@ async function buildResolvedRoute(page, options) {
61
113
  * 构建解析后的路由配置列表
62
114
  *
63
115
  * @param pages - 页面列表
64
- * @param options - 生成选项
116
+ * @param extendRoute - 路由扩展钩子
117
+ * @param parent - 父路由
65
118
  * @returns 路由配置列表
66
119
  */
67
- async function buildResolvedRoutes(pages, options) {
120
+ export function buildRoutes(pages, extendRoute, parent) {
68
121
  const routes = [];
69
122
  for (const page of pages) {
70
- const route = await buildResolvedRoute(page, options);
123
+ const route = buildRouteNode(page, extendRoute, parent);
71
124
  routes.push(route);
72
125
  }
73
126
  return routes;
74
127
  }
128
+ /**
129
+ * 解析组件导入表达式
130
+ *
131
+ * @param file - 组件文件路径
132
+ * @param importPath - JSON.stringify 后的文件路径
133
+ * @param mode - 解析后的导入模式('lazy' | 'sync' | 自定义表达式)
134
+ * @param importLines - 导入语句集合
135
+ * @returns 组件表达式代码
136
+ */
137
+ function resolveComponentExpr(file, importPath, mode, importLines) {
138
+ if (mode === 'sync') {
139
+ const expr = pathToUniqueName(file);
140
+ importLines.add(normalizeImportStatement(`import ${expr} from ${importPath}`));
141
+ return expr;
142
+ }
143
+ if (mode === 'lazy') {
144
+ importLines.add(normalizeImportStatement(`import { lazy } from 'vitarx'`));
145
+ return `lazy(() => import(${importPath}))`;
146
+ }
147
+ return mode;
148
+ }
75
149
  /**
76
150
  * 格式化组件表达式
77
151
  *
78
152
  * @param component - 组件路径或命名视图映射
79
153
  * @param importMode - 导入模式
154
+ * @param importLines - 导入语句集合
80
155
  * @returns 格式化后的组件表达式代码
81
156
  */
82
- function formatComponent(component, importMode) {
83
- if (typeof component === 'string') {
84
- // 处理单个组件
85
- return importMode === 'file' ? component : `lazy(() => import(${component}))`;
86
- }
87
- // 处理命名视图
88
- const entries = Object.entries(component).map(([name, value]) => {
89
- const expr = importMode === 'file' ? value : `lazy(() => import(${value}))`;
90
- return `${name}: ${expr}`;
157
+ function formatComponent(component, importMode, importLines) {
158
+ const entries = Object.entries(component).map(([name, file]) => {
159
+ const importPath = JSON.stringify(file);
160
+ const mode = typeof importMode === 'function'
161
+ ? importMode({
162
+ importPath,
163
+ filePath: file,
164
+ addImport: statement => importLines.add(normalizeImportStatement(statement))
165
+ })
166
+ : importMode;
167
+ const expr = resolveComponentExpr(file, importPath, mode, importLines);
168
+ return `${JSON.stringify(name)}: ${expr}`;
91
169
  });
92
170
  return `{ ${entries.join(', ')} }`;
93
171
  }
@@ -110,9 +188,10 @@ function generatePatternCode(pattern) {
110
188
  * @param indent - 缩进字符串
111
189
  * @param isLast - 是否为最后一个元素
112
190
  * @param importMode - 导入模式
191
+ * @param importLines - 导入语句集合
113
192
  * @returns 代码行数组
114
193
  */
115
- function generateRouteCode(route, indent, isLast, importMode) {
194
+ function generateRouteCode(route, indent, isLast, importMode, importLines) {
116
195
  const lines = [];
117
196
  const comma = isLast ? '' : ',';
118
197
  // 开始路由对象
@@ -126,7 +205,7 @@ function generateRouteCode(route, indent, isLast, importMode) {
126
205
  // 添加组件配置
127
206
  if (route.component) {
128
207
  lines[lines.length - 1] += ',';
129
- lines.push(`${indent} component: ${formatComponent(route.component, importMode)}`);
208
+ lines.push(`${indent} component: ${formatComponent(route.component, importMode, importLines)}`);
130
209
  }
131
210
  // 添加 meta 配置
132
211
  if (route.meta) {
@@ -155,7 +234,7 @@ function generateRouteCode(route, indent, isLast, importMode) {
155
234
  for (let i = 0; i < route.children.length; i++) {
156
235
  const child = route.children[i];
157
236
  // 递归生成子路由代码
158
- lines.push(...generateRouteCode(child, indent + ' ', i === route.children.length - 1, importMode));
237
+ lines.push(...generateRouteCode(child, indent + ' ', i === route.children.length - 1, importMode, importLines));
159
238
  }
160
239
  lines.push(`${indent} ]`);
161
240
  }
@@ -170,32 +249,25 @@ function generateRouteCode(route, indent, isLast, importMode) {
170
249
  * @param importMode - 导入模式
171
250
  * @param customImports - 自定义导入语句
172
251
  * @param indent - 缩进字符串
173
- * @returns 完整的路由模块代码
252
+ * @returns {string} 完整的路由模块代码
174
253
  */
175
- function generateRoutesCode(routes, importMode = 'lazy', customImports, indent = ' ') {
176
- const lines = [];
177
- // 添加 lazy 导入(如果需要)
178
- if (importMode === 'lazy') {
179
- lines.push(`import { lazy } from 'vitarx'`);
180
- }
181
- // 添加自定义导入语句
254
+ export function generateRoutesCode(routes, importMode = 'lazy', customImports, indent = ' ') {
255
+ const importLines = new Set();
256
+ const codeLines = [];
182
257
  if (customImports && customImports.length > 0) {
183
- for (const imp of customImports) {
184
- lines.push(imp);
185
- }
186
- }
187
- // 添加空行
188
- if (importMode === 'lazy' || (customImports && customImports.length > 0)) {
189
- lines.push('');
258
+ customImports.forEach(imp => importLines.add(normalizeImportStatement(imp)));
190
259
  }
191
- // 生成路由数组
192
- lines.push('export default [');
260
+ codeLines.push('export default [');
193
261
  for (let i = 0; i < routes.length; i++) {
194
262
  const route = routes[i];
195
- lines.push(...generateRouteCode(route, indent, i === routes.length - 1, importMode));
263
+ codeLines.push(...generateRouteCode(route, indent, i === routes.length - 1, importMode, importLines));
196
264
  }
197
- lines.push(']');
198
- return lines.join('\n');
265
+ codeLines.push(']');
266
+ const allImports = Array.from(importLines.values());
267
+ if (allImports.length > 0) {
268
+ allImports.push('');
269
+ }
270
+ return allImports.concat(codeLines).join('\n');
199
271
  }
200
272
  /**
201
273
  * 生成路由配置代码
@@ -204,11 +276,18 @@ function generateRoutesCode(routes, importMode = 'lazy', customImports, indent =
204
276
  *
205
277
  * @param pages - 解析后的页面列表
206
278
  * @param options - 路由生成选项
207
- * @returns Promise 包含可执行路由配置代码字符串
279
+ * @returns { GenerateResult } 包含routes、code、dts的路由配置结果
208
280
  */
209
- export async function generateRoutes(pages, options = {}) {
281
+ export function generateRoutes(pages, options) {
210
282
  // 构建解析后的路由配置
211
- const routes = await buildResolvedRoutes(pages, options);
283
+ let routes = buildRoutes(pages, options.extendRoute);
284
+ if (options.beforeWriteRoutes) {
285
+ const result = options.beforeWriteRoutes(routes);
286
+ if (Array.isArray(result))
287
+ routes = result;
288
+ }
212
289
  // 生成最终的路由代码
213
- return generateRoutesCode(routes, options.importMode, options.imports);
290
+ const code = generateRoutesCode(routes, options.importMode, options.imports);
291
+ const dts = options.dts ? generateDtsCode(routes) : '';
292
+ return { routes, code, dts };
214
293
  }
@@ -4,37 +4,11 @@
4
4
  * 负责生成路由的 TypeScript 类型定义,用于提供 IDE 类型提示和编译时类型检查。
5
5
  * 与构建工具无关,可在任何 Node.js 环境中使用。
6
6
  */
7
- import type { ParsedPage } from '../types.js';
8
- /**
9
- * 路由索引映射表
10
- *
11
- * 将路由名称和路径映射到对应的类型信息。
12
- * 支持通过名称或路径两种方式进行类型安全的导航。
13
- *
14
- * @example
15
- * ```typescript
16
- * // 生成的类型示例
17
- * interface RouteIndexMap {
18
- * 'home': {},
19
- * '/': {},
20
- * 'user-id': { params: { id: string } },
21
- * '/user/{id}': { params: { id: string } }
22
- * }
23
- *
24
- * // 使用示例
25
- * router.push({ index: 'user-id', params: { id: '123' } })
26
- * ```
27
- */
28
- export interface ParsedRouteIndexMap {
29
- [key: string]: {
30
- /** 动态参数类型映射 */
31
- params?: Record<string, string | number>;
32
- };
33
- }
7
+ import type { RouteNode } from '../types/index.js';
34
8
  /**
35
9
  * 生成完整的 .d.ts 声明文件内容
36
10
  *
37
- * @param pages - 解析后的页面列表
11
+ * @param routes - 解析后的页面列表
38
12
  * @returns 完整的 .d.ts 文件内容
39
13
  */
40
- export declare function generateFullDtsFile(pages: ParsedPage[]): string;
14
+ export declare function generateDtsCode(routes: RouteNode[]): string;
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @fileoverview 类型生成模块
3
+ *
4
+ * 负责生成路由的 TypeScript 类型定义,用于提供 IDE 类型提示和编译时类型检查。
5
+ * 与构建工具无关,可在任何 Node.js 环境中使用。
6
+ */
7
+ import { hasDynamicPath } from '../utils/index.js';
1
8
  /**
2
9
  * 从路径中提取动态参数
3
10
  *
@@ -13,26 +20,16 @@ function extractParamsFromPath(path) {
13
20
  }
14
21
  return params;
15
22
  }
16
- /**
17
- * 检查路径是否包含动态参数
18
- *
19
- * @param path - 路由路径
20
- * @returns 是否包含动态参数
21
- */
22
- function hasDynamicParams(path) {
23
- return /\{[^}]+}/.test(path);
24
- }
25
23
  /**
26
24
  * 判断路由是否需要注册
27
25
  *
28
- * @param page - 页面信息
29
- * @returns 是否需要注册
26
+ * @param route - 路由信息
27
+ * @returns {boolean} 是否需要注册
30
28
  */
31
- function shouldRegisterRoute(page) {
32
- const hasComponent = page.filePath && page.filePath.trim() !== '';
33
- const hasChildren = page.children.length > 0;
34
- const hasRedirect = page.redirect !== undefined;
35
- return (hasComponent && !hasChildren) || hasRedirect;
29
+ function shouldRegisterRoute(route) {
30
+ const isGroup = route.children?.length;
31
+ const hasRedirect = route.redirect !== undefined;
32
+ return !isGroup || hasRedirect;
36
33
  }
37
34
  /**
38
35
  * 解析 alias 路径
@@ -74,49 +71,47 @@ function formatRouteIndexEntry(key, value) {
74
71
  * 递归处理单个页面及其子页面
75
72
  *
76
73
  * @param map - 路由索引映射表
77
- * @param page - 页面信息
74
+ * @param route - 页面信息
78
75
  * @param parentFullPath - 父级完整路径
79
76
  */
80
- function processPage(map, page, parentFullPath = '') {
81
- if (shouldRegisterRoute(page)) {
82
- const params = extractParamsFromPath(page.path);
77
+ function processPage(map, route, parentFullPath = '') {
78
+ const currentPath = route.fullPath;
79
+ if (shouldRegisterRoute(route)) {
80
+ const params = extractParamsFromPath(route.fullPath);
83
81
  const entry = params.length > 0
84
82
  ? { params: params.reduce((acc, p) => ({ ...acc, [p]: 'string | number' }), {}) }
85
83
  : {};
86
- map[page.name] = entry;
87
- if (!hasDynamicParams(page.path)) {
88
- let fullPath = page.path;
89
- if (page.path === '' && parentFullPath) {
90
- fullPath = parentFullPath;
91
- }
92
- if (fullPath) {
93
- map[fullPath] = entry;
94
- }
84
+ // 命名路由
85
+ if (route.name)
86
+ map[route.name] = entry;
87
+ if (!hasDynamicPath(currentPath)) {
88
+ map[currentPath] = entry;
95
89
  }
96
- if (page.alias) {
97
- const aliases = Array.isArray(page.alias) ? page.alias : [page.alias];
90
+ if (route.alias) {
91
+ const aliases = Array.isArray(route.alias) ? route.alias : [route.alias];
98
92
  for (const alias of aliases) {
99
93
  const aliasPath = resolveAliasPath(alias, parentFullPath);
100
- if (aliasPath && !hasDynamicParams(aliasPath)) {
94
+ if (aliasPath && !hasDynamicPath(aliasPath)) {
101
95
  map[aliasPath] = entry;
102
96
  }
103
97
  }
104
98
  }
105
99
  }
106
- const currentFullPath = page.path === '' ? parentFullPath : page.path || parentFullPath;
107
- for (const child of page.children) {
108
- processPage(map, child, currentFullPath);
100
+ if (route.children) {
101
+ for (const child of route.children) {
102
+ processPage(map, child, currentPath);
103
+ }
109
104
  }
110
105
  }
111
106
  /**
112
107
  * 构建路由索引映射表
113
108
  *
114
- * @param pages - 页面列表
109
+ * @param routes - 页面列表
115
110
  * @returns 路由索引映射表
116
111
  */
117
- function buildRouteIndexMap(pages) {
112
+ function buildRouteIndexMap(routes) {
118
113
  const map = {};
119
- for (const page of pages) {
114
+ for (const page of routes) {
120
115
  processPage(map, page);
121
116
  }
122
117
  return map;
@@ -124,12 +119,12 @@ function buildRouteIndexMap(pages) {
124
119
  /**
125
120
  * 生成完整的 .d.ts 声明文件内容
126
121
  *
127
- * @param pages - 解析后的页面列表
122
+ * @param routes - 解析后的页面列表
128
123
  * @returns 完整的 .d.ts 文件内容
129
124
  */
130
- export function generateFullDtsFile(pages) {
125
+ export function generateDtsCode(routes) {
131
126
  const moduleName = 'vitarx-router';
132
- const indexMap = buildRouteIndexMap(pages);
127
+ const indexMap = buildRouteIndexMap(routes);
133
128
  const lines = [];
134
129
  lines.push('/// <reference types="vitarx-router/global" />');
135
130
  lines.push('/* eslint-disable */');
@@ -19,7 +19,7 @@
19
19
  * /// <reference types="vitarx-router/global" />
20
20
  * ```
21
21
  */
22
- import type { PageOptions } from './types.js';
22
+ import type { PageOptions } from './types/index.js';
23
23
  declare global {
24
24
  /**
25
25
  * 文件路由全局宏函数