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.
- package/README.md +42 -17
- package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.d.ts +1 -1
- package/dist/components/RouterView.js +5 -4
- package/dist/core/common/constant.d.ts +5 -6
- package/dist/core/common/constant.js +5 -6
- package/dist/core/common/utils.js +2 -1
- package/dist/core/router/checkOptions.d.ts +11 -0
- package/dist/core/router/checkOptions.js +119 -0
- package/dist/core/router/manager.js +27 -23
- package/dist/core/router/router.d.ts +154 -1
- package/dist/core/router/router.js +303 -230
- package/dist/core/router/web.d.ts +13 -0
- package/dist/core/router/web.js +35 -4
- package/dist/core/shared/link.d.ts +7 -0
- package/dist/core/shared/link.js +11 -8
- package/dist/core/shared/route.js +1 -2
- package/dist/core/shared/router.d.ts +3 -3
- package/dist/core/shared/router.js +7 -4
- package/dist/core/types/options.d.ts +2 -0
- package/dist/file-router/config/index.d.ts +2 -1
- package/dist/file-router/config/index.js +2 -1
- package/dist/file-router/config/resolve.d.ts +43 -0
- package/dist/file-router/config/resolve.js +69 -0
- package/dist/file-router/{utils/validateOptions.d.ts → config/validate.d.ts} +11 -10
- package/dist/file-router/config/validate.js +280 -0
- package/dist/file-router/constants.d.ts +12 -2
- package/dist/file-router/constants.js +13 -3
- package/dist/file-router/generator/generateRoutes.d.ts +44 -13
- package/dist/file-router/generator/generateRoutes.js +159 -80
- package/dist/file-router/generator/generateTypes.d.ts +3 -29
- package/dist/file-router/generator/generateTypes.js +36 -41
- package/dist/file-router/global.d.ts +1 -1
- package/dist/file-router/index.d.ts +224 -90
- package/dist/file-router/index.js +571 -135
- package/dist/file-router/macros/astValueExtractor.d.ts +1 -1
- package/dist/file-router/macros/astValueExtractor.js +27 -7
- package/dist/file-router/macros/definePage.d.ts +20 -3
- package/dist/file-router/macros/definePage.js +120 -40
- package/dist/file-router/parser/exportChecker.d.ts +4 -23
- package/dist/file-router/parser/exportChecker.js +38 -79
- package/dist/file-router/parser/filterUtils.d.ts +25 -0
- package/dist/file-router/parser/filterUtils.js +43 -0
- package/dist/file-router/parser/index.d.ts +2 -1
- package/dist/file-router/parser/index.js +2 -1
- package/dist/file-router/parser/parsePage.d.ts +56 -9
- package/dist/file-router/parser/parsePage.js +194 -172
- package/dist/file-router/parser/routePath.d.ts +22 -0
- package/dist/file-router/parser/routePath.js +74 -0
- package/dist/file-router/types/hooks.d.ts +52 -0
- package/dist/file-router/types/index.d.ts +3 -0
- package/dist/file-router/types/index.js +1 -0
- package/dist/file-router/types/options.d.ts +279 -0
- package/dist/file-router/types/options.js +1 -0
- package/dist/file-router/types/route.d.ts +114 -0
- package/dist/file-router/types/route.js +1 -0
- package/dist/file-router/utils/fileReader.d.ts +11 -0
- package/dist/file-router/utils/fileReader.js +22 -0
- package/dist/file-router/utils/findRoute.d.ts +8 -0
- package/dist/file-router/utils/findRoute.js +22 -0
- package/dist/file-router/utils/index.d.ts +4 -2
- package/dist/file-router/utils/index.js +4 -2
- package/dist/file-router/utils/logger.d.ts +6 -6
- package/dist/file-router/utils/logger.js +44 -4
- package/dist/file-router/utils/pathStrategy.d.ts +28 -0
- package/dist/file-router/utils/{namingStrategy.js → pathStrategy.js} +18 -28
- package/dist/file-router/utils/pathUtils.d.ts +31 -0
- package/dist/file-router/utils/pathUtils.js +53 -1
- package/dist/plugin-vite/constant.d.ts +9 -0
- package/dist/plugin-vite/constant.js +9 -0
- package/dist/plugin-vite/index.d.ts +4 -24
- package/dist/plugin-vite/index.js +4 -94
- package/dist/plugin-vite/plugin.d.ts +86 -0
- package/dist/plugin-vite/plugin.js +181 -0
- package/dist/plugin-vite/watcher.d.ts +15 -0
- package/dist/plugin-vite/watcher.js +65 -0
- package/package.json +9 -7
- package/dist/file-router/config/configUtils.d.ts +0 -54
- package/dist/file-router/config/configUtils.js +0 -88
- package/dist/file-router/scanner/filterUtils.d.ts +0 -35
- package/dist/file-router/scanner/filterUtils.js +0 -188
- package/dist/file-router/scanner/index.d.ts +0 -8
- package/dist/file-router/scanner/index.js +0 -8
- package/dist/file-router/scanner/routeTreeBuilder.d.ts +0 -21
- package/dist/file-router/scanner/routeTreeBuilder.js +0 -312
- package/dist/file-router/scanner/scanPages.d.ts +0 -48
- package/dist/file-router/scanner/scanPages.js +0 -174
- package/dist/file-router/types.d.ts +0 -344
- package/dist/file-router/utils/namingStrategy.d.ts +0 -57
- package/dist/file-router/utils/validateOptions.js +0 -233
- /package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.js +0 -0
- /package/dist/{plugin-vite/auto-routes → auto-routes}/index.d.ts +0 -0
- /package/dist/{plugin-vite/auto-routes → auto-routes}/index.js +0 -0
- /package/dist/file-router/{types.js → types/hooks.js} +0 -0
|
@@ -1,12 +1,59 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PageParser, PageParseResult } from '../types/index.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* 路径解析错误类
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* @param pagesDir - 页面目录绝对路径
|
|
7
|
-
* @param parentPath - 父级路径(用于嵌套路由)
|
|
8
|
-
* @param namingStrategy - 命名策略,默认为 'kebab'
|
|
9
|
-
* @param pathPrefix - 路由路径前缀,默认为 ''
|
|
10
|
-
* @returns 解析后的页面信息,解析失败或无有效导出返回 null
|
|
5
|
+
* 提供详细的错误上下文信息,包括文件路径、错误类型和原始值。
|
|
11
6
|
*/
|
|
12
|
-
export declare
|
|
7
|
+
export declare class PageParseError extends TypeError {
|
|
8
|
+
/** 文件路径上下文 */
|
|
9
|
+
readonly filePath?: string;
|
|
10
|
+
/** 错误字段名称 */
|
|
11
|
+
readonly field?: string;
|
|
12
|
+
/** 原始值 */
|
|
13
|
+
readonly originalValue?: unknown;
|
|
14
|
+
constructor(message: string, options?: {
|
|
15
|
+
filePath?: string;
|
|
16
|
+
field?: string;
|
|
17
|
+
originalValue?: unknown;
|
|
18
|
+
cause?: Error;
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* 生成详细的错误信息
|
|
22
|
+
*/
|
|
23
|
+
toString(): string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 解析路由路径和视图名称
|
|
27
|
+
*
|
|
28
|
+
* 从文件名中提取路由路径和命名视图名称。
|
|
29
|
+
*
|
|
30
|
+
* @param filePath - 文件路径
|
|
31
|
+
* @param [parser] - 自定义解析器
|
|
32
|
+
* @param [precomputed] - 预计算的文件信息,避免重复解析文件路径
|
|
33
|
+
* @returns 路径解析结果
|
|
34
|
+
* @throws {PageParseError} 当路径解析失败时抛出
|
|
35
|
+
*/
|
|
36
|
+
export declare function parsePageFile(filePath: string, parser?: PageParser, precomputed?: FileInfo): PageParseResult;
|
|
37
|
+
/**
|
|
38
|
+
* 文件信息
|
|
39
|
+
*
|
|
40
|
+
* 从文件路径中提取的结构化信息,作为文件名解析的唯一来源。
|
|
41
|
+
*/
|
|
42
|
+
export interface FileInfo {
|
|
43
|
+
/** 文件名(不含扩展名),如 home@sidebar → home@sidebar */
|
|
44
|
+
basename: string;
|
|
45
|
+
/** 路由名(@之前的部分),如 home@sidebar → home */
|
|
46
|
+
rawName: string;
|
|
47
|
+
/** 视图名称(@之后的部分),如 home@sidebar → sidebar */
|
|
48
|
+
viewName: string | undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 提取文件信息
|
|
52
|
+
*
|
|
53
|
+
* 从文件路径中提取文件名、路由名和视图名称。
|
|
54
|
+
* 这是文件名解析的唯一入口,其他模块应复用此函数而非重复实现。
|
|
55
|
+
*
|
|
56
|
+
* @param filePath - 文件路径
|
|
57
|
+
* @returns 文件信息
|
|
58
|
+
*/
|
|
59
|
+
export declare function extractFileInfo(filePath: string): FileInfo;
|
|
@@ -3,209 +3,231 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 负责解析页面文件路径,提取路由信息,包括:
|
|
5
5
|
* - 路由路径转换
|
|
6
|
-
* - 动态参数识别
|
|
7
|
-
* - 路由名称生成
|
|
8
6
|
*
|
|
9
7
|
* 与构建工具无关,可在任何 Node.js 环境中使用。
|
|
10
8
|
*/
|
|
11
9
|
import path from 'node:path';
|
|
12
|
-
import { parseDefinePage } from '../macros/index.js';
|
|
13
|
-
import { warn } from '../utils/logger.js';
|
|
14
|
-
import { applyNamingStrategyToName, applyNamingStrategyToPath } from '../utils/namingStrategy.js';
|
|
15
|
-
import { normalizePathSeparator } from '../utils/pathUtils.js';
|
|
16
|
-
import { checkDefaultExport, shouldCheckExport } from './exportChecker.js';
|
|
17
|
-
/** 动态参数匹配正则,如 [id]、[slug]、[param?] */
|
|
18
|
-
const DYNAMIC_PARAM_REGEX = /^\[(.+?)(\?)?]$/;
|
|
19
10
|
/**
|
|
20
|
-
*
|
|
11
|
+
* 路径解析错误类
|
|
21
12
|
*
|
|
22
|
-
*
|
|
13
|
+
* 提供详细的错误上下文信息,包括文件路径、错误类型和原始值。
|
|
14
|
+
*/
|
|
15
|
+
export class PageParseError extends TypeError {
|
|
16
|
+
constructor(message, options) {
|
|
17
|
+
super(message, { cause: options?.cause });
|
|
18
|
+
/** 文件路径上下文 */
|
|
19
|
+
Object.defineProperty(this, "filePath", {
|
|
20
|
+
enumerable: true,
|
|
21
|
+
configurable: true,
|
|
22
|
+
writable: true,
|
|
23
|
+
value: void 0
|
|
24
|
+
});
|
|
25
|
+
/** 错误字段名称 */
|
|
26
|
+
Object.defineProperty(this, "field", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
});
|
|
32
|
+
/** 原始值 */
|
|
33
|
+
Object.defineProperty(this, "originalValue", {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
value: void 0
|
|
38
|
+
});
|
|
39
|
+
this.name = 'PathParseError';
|
|
40
|
+
this.filePath = options?.filePath;
|
|
41
|
+
this.field = options?.field;
|
|
42
|
+
this.originalValue = options?.originalValue;
|
|
43
|
+
if (Error.captureStackTrace) {
|
|
44
|
+
Error.captureStackTrace(this, PageParseError);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 生成详细的错误信息
|
|
49
|
+
*/
|
|
50
|
+
toString() {
|
|
51
|
+
let details = `${this.name}: ${this.message}`;
|
|
52
|
+
if (this.filePath) {
|
|
53
|
+
details += `\n File: ${this.filePath}`;
|
|
54
|
+
}
|
|
55
|
+
if (this.field) {
|
|
56
|
+
details += `\n Field: ${this.field}`;
|
|
57
|
+
}
|
|
58
|
+
if (this.originalValue !== undefined) {
|
|
59
|
+
details += `\n Value: ${String(this.originalValue)}`;
|
|
60
|
+
}
|
|
61
|
+
return details;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 解析路由路径和视图名称
|
|
23
66
|
*
|
|
24
|
-
*
|
|
67
|
+
* 从文件名中提取路由路径和命名视图名称。
|
|
68
|
+
*
|
|
69
|
+
* @param filePath - 文件路径
|
|
70
|
+
* @param [parser] - 自定义解析器
|
|
71
|
+
* @param [precomputed] - 预计算的文件信息,避免重复解析文件路径
|
|
72
|
+
* @returns 路径解析结果
|
|
73
|
+
* @throws {PageParseError} 当路径解析失败时抛出
|
|
74
|
+
*/
|
|
75
|
+
export function parsePageFile(filePath, parser, precomputed) {
|
|
76
|
+
const info = precomputed ?? extractFileInfo(filePath);
|
|
77
|
+
if (!parser) {
|
|
78
|
+
return defaultPageParser(info.rawName, info.viewName);
|
|
79
|
+
}
|
|
80
|
+
const result = parser(info.basename, filePath);
|
|
81
|
+
return parseCustomResult(result, filePath);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 提取文件信息
|
|
85
|
+
*
|
|
86
|
+
* 从文件路径中提取文件名、路由名和视图名称。
|
|
87
|
+
* 这是文件名解析的唯一入口,其他模块应复用此函数而非重复实现。
|
|
88
|
+
*
|
|
89
|
+
* @param filePath - 文件路径
|
|
90
|
+
* @returns 文件信息
|
|
91
|
+
*/
|
|
92
|
+
export function extractFileInfo(filePath) {
|
|
93
|
+
const ext = path.extname(filePath);
|
|
94
|
+
const basename = path.basename(filePath, ext);
|
|
95
|
+
const [rawName, viewName] = basename.split('@', 2);
|
|
96
|
+
return { basename, rawName, viewName };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* 解析默认路由路径(无自定义解析器)
|
|
100
|
+
*
|
|
101
|
+
* @param rawName - 路由名(@之前的部分)
|
|
102
|
+
* @param viewName - 视图名称(@之后的部分)
|
|
25
103
|
* @returns 解析结果
|
|
26
104
|
*/
|
|
27
|
-
function
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// 检查是否为可选参数(以 ? 结尾)
|
|
36
|
-
const isOptional = !!dynamicMatch[2];
|
|
37
|
-
params.push(paramName);
|
|
38
|
-
// 生成路由参数格式:{param} 或 {param?}
|
|
39
|
-
return { name: `{${paramName}${isOptional ? '?' : ''}}`, params, isDynamic };
|
|
105
|
+
function defaultPageParser(rawName, viewName) {
|
|
106
|
+
const routePath = normalizeRoutePath(rawName);
|
|
107
|
+
if (!routePath) {
|
|
108
|
+
throw new PageParseError('PageParser returned empty path', {
|
|
109
|
+
filePath: rawName,
|
|
110
|
+
originalValue: rawName,
|
|
111
|
+
field: 'path'
|
|
112
|
+
});
|
|
40
113
|
}
|
|
41
|
-
return {
|
|
114
|
+
return {
|
|
115
|
+
path: routePath,
|
|
116
|
+
viewName
|
|
117
|
+
};
|
|
42
118
|
}
|
|
43
119
|
/**
|
|
44
|
-
*
|
|
120
|
+
* 解析自定义解析器结果
|
|
45
121
|
*
|
|
46
|
-
* @param
|
|
47
|
-
* @param
|
|
48
|
-
* @
|
|
49
|
-
* @
|
|
122
|
+
* @param result - 解析器返回的结果
|
|
123
|
+
* @param filePath - 文件路径(用于错误上下文)
|
|
124
|
+
* @returns 解析结果
|
|
125
|
+
* @throws {PageParseError} 当结果无效时抛出
|
|
50
126
|
*/
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (pathPrefix) {
|
|
56
|
-
const prefixName = pathPrefix
|
|
57
|
-
.trim()
|
|
58
|
-
.replace(/^\/+/, '')
|
|
59
|
-
.replace(/[-\/]+$/, '')
|
|
60
|
-
.trim();
|
|
61
|
-
if (prefixName) {
|
|
62
|
-
segments.push(prefixName);
|
|
63
|
-
}
|
|
127
|
+
function parseCustomResult(result, filePath) {
|
|
128
|
+
if (typeof result === 'string') {
|
|
129
|
+
const [rawName, viewName] = result.split('@', 2);
|
|
130
|
+
return defaultPageParser(rawName, viewName);
|
|
64
131
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
segments.push(...normalizePathSeparator(dirPath).split('/'));
|
|
132
|
+
if (result && typeof result === 'object' && !Array.isArray(result)) {
|
|
133
|
+
return parseObjectResult(result, filePath);
|
|
68
134
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
else if (baseName !== 'index') {
|
|
75
|
-
// 处理动态参数
|
|
76
|
-
const dynamicMatch = baseName.match(DYNAMIC_PARAM_REGEX);
|
|
77
|
-
if (dynamicMatch) {
|
|
78
|
-
segments.push(dynamicMatch[1]);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
segments.push(baseName);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return segments.join('-');
|
|
135
|
+
throw new PageParseError('PageParser returned invalid result type', {
|
|
136
|
+
filePath,
|
|
137
|
+
originalValue: result
|
|
138
|
+
});
|
|
86
139
|
}
|
|
87
140
|
/**
|
|
88
|
-
*
|
|
141
|
+
* 解析对象类型的结果
|
|
89
142
|
*
|
|
90
|
-
* @param
|
|
91
|
-
* @param
|
|
92
|
-
* @
|
|
93
|
-
* @
|
|
94
|
-
* @returns 完整的路由路径
|
|
143
|
+
* @param result - 对象结果
|
|
144
|
+
* @param filePath - 文件路径
|
|
145
|
+
* @returns 解析结果
|
|
146
|
+
* @throws {PageParseError} 当结果无效时抛出
|
|
95
147
|
*/
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
148
|
+
function parseObjectResult(result, filePath) {
|
|
149
|
+
const { path: rawRoutePath, viewName, options } = result;
|
|
150
|
+
validateRoutePathType(rawRoutePath, filePath);
|
|
151
|
+
const routePath = normalizeRoutePath(rawRoutePath);
|
|
152
|
+
if (!routePath) {
|
|
153
|
+
throw new PageParseError('PageParser returned empty path after normalization', {
|
|
154
|
+
filePath,
|
|
155
|
+
originalValue: rawRoutePath,
|
|
156
|
+
field: 'path'
|
|
157
|
+
});
|
|
106
158
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
159
|
+
validateViewName(viewName, filePath);
|
|
160
|
+
validateOptions(options, filePath);
|
|
161
|
+
return {
|
|
162
|
+
...result,
|
|
163
|
+
path: routePath
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 验证路由路径类型
|
|
168
|
+
*
|
|
169
|
+
* @param routePath - 路由路径
|
|
170
|
+
* @param filePath - 文件路径(用于错误上下文)
|
|
171
|
+
* @throws {PageParseError} 当类型无效时抛出
|
|
172
|
+
*/
|
|
173
|
+
function validateRoutePathType(routePath, filePath) {
|
|
174
|
+
if (typeof routePath !== 'string') {
|
|
175
|
+
throw new PageParseError('PageParser returned non-string path', {
|
|
176
|
+
filePath,
|
|
177
|
+
originalValue: routePath,
|
|
178
|
+
field: 'path'
|
|
179
|
+
});
|
|
114
180
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
routePath = normalizedPrefix + routePath.slice(1);
|
|
124
|
-
}
|
|
181
|
+
if (!routePath.trim()) {
|
|
182
|
+
throw new PageParseError('PageParser returned empty or whitespace-only path', {
|
|
183
|
+
filePath,
|
|
184
|
+
originalValue: routePath,
|
|
185
|
+
field: 'path'
|
|
186
|
+
});
|
|
125
187
|
}
|
|
126
|
-
return routePath;
|
|
127
188
|
}
|
|
128
189
|
/**
|
|
129
|
-
*
|
|
190
|
+
* 验证视图名称
|
|
130
191
|
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* @
|
|
134
|
-
* @returns 视图名称和去除视图名的基础名
|
|
192
|
+
* @param viewName - 视图名称
|
|
193
|
+
* @param filePath - 文件路径(用于错误上下文)
|
|
194
|
+
* @throws {PageParseError} 当视图名称无效时抛出
|
|
135
195
|
*/
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
};
|
|
196
|
+
function validateViewName(viewName, filePath) {
|
|
197
|
+
if (viewName !== undefined && typeof viewName !== 'string') {
|
|
198
|
+
throw new PageParseError('PageParser returned non-string viewName', {
|
|
199
|
+
filePath,
|
|
200
|
+
originalValue: viewName,
|
|
201
|
+
field: 'viewName'
|
|
202
|
+
});
|
|
144
203
|
}
|
|
145
|
-
return { viewName: null, baseWithoutView: baseName };
|
|
146
204
|
}
|
|
147
205
|
/**
|
|
148
|
-
*
|
|
206
|
+
* 验证选项
|
|
149
207
|
*
|
|
150
|
-
* @param
|
|
151
|
-
* @param
|
|
152
|
-
* @param parentPath - 父级路径(用于嵌套路由)
|
|
153
|
-
* @param namingStrategy - 命名策略,默认为 'kebab'
|
|
154
|
-
* @param pathPrefix - 路由路径前缀,默认为 ''
|
|
155
|
-
* @returns 解析后的页面信息,解析失败或无有效导出返回 null
|
|
208
|
+
* @param options - 选项
|
|
209
|
+
* @param filePath - 文件路径
|
|
156
210
|
*/
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
// 检查文件导出
|
|
165
|
-
if (shouldCheckExport(ext)) {
|
|
166
|
-
const exportCheck = checkDefaultExport(filePath);
|
|
167
|
-
if (exportCheck.warning) {
|
|
168
|
-
const pageOptions = parseDefinePage(filePath);
|
|
169
|
-
// 检查是否有默认导出或 redirect 配置
|
|
170
|
-
if (!exportCheck.hasDefaultExport || !exportCheck.isFunctionOrClass) {
|
|
171
|
-
if (!pageOptions?.redirect) {
|
|
172
|
-
warn('页面文件缺少有效导出且未定义 redirect', `${filePath}\n ${exportCheck.warning}`);
|
|
173
|
-
return null;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
211
|
+
function validateOptions(options, filePath) {
|
|
212
|
+
if (options !== undefined && Object.prototype.toString.call(options) !== '[object Object]') {
|
|
213
|
+
throw new PageParseError('PageParser returned invalid options type', {
|
|
214
|
+
filePath,
|
|
215
|
+
originalValue: options,
|
|
216
|
+
field: 'options'
|
|
217
|
+
});
|
|
177
218
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const finalPath = applyNamingStrategyToPath(routePath, namingStrategy);
|
|
193
|
-
const finalName = applyNamingStrategyToName(name, namingStrategy);
|
|
194
|
-
// 返回解析结果
|
|
195
|
-
return {
|
|
196
|
-
path: finalPath,
|
|
197
|
-
filePath,
|
|
198
|
-
name: finalName,
|
|
199
|
-
params,
|
|
200
|
-
isIndex,
|
|
201
|
-
isDynamic,
|
|
202
|
-
children: [],
|
|
203
|
-
meta: pageOptions?.meta,
|
|
204
|
-
pattern: pageOptions?.pattern,
|
|
205
|
-
customName: pageOptions?.name,
|
|
206
|
-
parentPath,
|
|
207
|
-
redirect: pageOptions?.redirect,
|
|
208
|
-
alias: pageOptions?.alias,
|
|
209
|
-
viewName
|
|
210
|
-
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* 标准化路由路径
|
|
222
|
+
*
|
|
223
|
+
* 去除路径首尾空白和首尾的斜杠,将 . # 等不利于 URL 的字符替换为 -。
|
|
224
|
+
*
|
|
225
|
+
* @param routePath - 原始路由路径
|
|
226
|
+
* @returns 标准化后的路由路径
|
|
227
|
+
*/
|
|
228
|
+
function normalizeRoutePath(routePath) {
|
|
229
|
+
return routePath
|
|
230
|
+
.trim()
|
|
231
|
+
.replace(/^\/+|\/+$/g, '')
|
|
232
|
+
.replace(/[.#]/g, '-');
|
|
211
233
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type PageDirConfig } from '../config/resolve.js';
|
|
2
|
+
import type { PageParser, PathStrategy, ScanNode } from '../types/index.js';
|
|
3
|
+
import { type FileInfo } from './parsePage.js';
|
|
4
|
+
interface RouteFullPathContext {
|
|
5
|
+
fileMap: Map<string, ScanNode>;
|
|
6
|
+
pages: readonly PageDirConfig[];
|
|
7
|
+
pageParser?: PageParser;
|
|
8
|
+
pathStrategy: PathStrategy;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 计算文件的完整路由路径
|
|
12
|
+
*
|
|
13
|
+
* 判断文件是否为页面文件,如果是则计算其最终生成的路由 fullPath。
|
|
14
|
+
* 非页面文件(布局文件、配置文件等)返回 null。
|
|
15
|
+
*
|
|
16
|
+
* @param filePath - 文件绝对路径
|
|
17
|
+
* @param fileInfo - 文件信息
|
|
18
|
+
* @param context - 路由计算上下文
|
|
19
|
+
* @returns 完整路由路径,非页面文件返回 null
|
|
20
|
+
*/
|
|
21
|
+
export declare function computeRouteFullPath(filePath: string, fileInfo: FileInfo, context: RouteFullPathContext): string | null;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview 路由路径计算辅助函数
|
|
3
|
+
*
|
|
4
|
+
* 从文件路径计算最终生成的路由 fullPath,
|
|
5
|
+
* 支持已跟踪(在路由树中)和未跟踪(尚未扫描)两种场景。
|
|
6
|
+
*/
|
|
7
|
+
import nodePath from 'node:path';
|
|
8
|
+
import { applyPathStrategy } from '../utils/pathStrategy.js';
|
|
9
|
+
import { normalizeRoutePath, resolvePathVariable } from '../utils/pathUtils.js';
|
|
10
|
+
import { isPageFileInDirs } from './filterUtils.js';
|
|
11
|
+
import { parsePageFile } from './parsePage.js';
|
|
12
|
+
/**
|
|
13
|
+
* 应用路径策略(命名转换 + 动态参数转换)
|
|
14
|
+
*
|
|
15
|
+
* @param path - 路径
|
|
16
|
+
* @param strategy - 路径策略
|
|
17
|
+
* @returns 转换后的路径
|
|
18
|
+
*/
|
|
19
|
+
function applyFullPathStrategy(path, strategy) {
|
|
20
|
+
return resolvePathVariable(applyPathStrategy(path, strategy));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 从节点树计算完整路由路径
|
|
24
|
+
*
|
|
25
|
+
* 沿 parent 链向上拼接所有节点的 path,与生成阶段 fullPath 的计算逻辑一致。
|
|
26
|
+
*
|
|
27
|
+
* @param node - 路由节点
|
|
28
|
+
* @returns 完整路由路径
|
|
29
|
+
*/
|
|
30
|
+
function computeNodeFullPath(node) {
|
|
31
|
+
const segments = [];
|
|
32
|
+
let current = node;
|
|
33
|
+
while (current) {
|
|
34
|
+
segments.unshift(current.path);
|
|
35
|
+
current = current.parent;
|
|
36
|
+
}
|
|
37
|
+
return normalizeRoutePath(segments.join('/'));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 计算文件的完整路由路径
|
|
41
|
+
*
|
|
42
|
+
* 判断文件是否为页面文件,如果是则计算其最终生成的路由 fullPath。
|
|
43
|
+
* 非页面文件(布局文件、配置文件等)返回 null。
|
|
44
|
+
*
|
|
45
|
+
* @param filePath - 文件绝对路径
|
|
46
|
+
* @param fileInfo - 文件信息
|
|
47
|
+
* @param context - 路由计算上下文
|
|
48
|
+
* @returns 完整路由路径,非页面文件返回 null
|
|
49
|
+
*/
|
|
50
|
+
export function computeRouteFullPath(filePath, fileInfo, context) {
|
|
51
|
+
const { fileMap, pages, pageParser, pathStrategy } = context;
|
|
52
|
+
const node = fileMap.get(filePath);
|
|
53
|
+
if (node) {
|
|
54
|
+
return computeNodeFullPath(node);
|
|
55
|
+
}
|
|
56
|
+
const page = isPageFileInDirs(filePath, pages);
|
|
57
|
+
if (!page)
|
|
58
|
+
return null;
|
|
59
|
+
const parsed = parsePageFile(filePath, pageParser, fileInfo);
|
|
60
|
+
const pathSegment = parsed.path === 'index' ? '' : applyFullPathStrategy(parsed.path, pathStrategy);
|
|
61
|
+
const segments = pathSegment ? [pathSegment] : [];
|
|
62
|
+
let dirPath = nodePath.dirname(filePath);
|
|
63
|
+
while (dirPath.length > page.dir.length) {
|
|
64
|
+
const dirNode = fileMap.get(dirPath);
|
|
65
|
+
if (dirNode) {
|
|
66
|
+
const parentFullPath = computeNodeFullPath(dirNode);
|
|
67
|
+
return normalizeRoutePath(parentFullPath + '/' + segments.join('/'));
|
|
68
|
+
}
|
|
69
|
+
segments.unshift(applyFullPathStrategy(nodePath.basename(dirPath), pathStrategy));
|
|
70
|
+
dirPath = nodePath.dirname(dirPath);
|
|
71
|
+
}
|
|
72
|
+
const prefix = page.prefix ? applyFullPathStrategy(page.prefix, pathStrategy) : '';
|
|
73
|
+
return normalizeRoutePath(prefix + '/' + segments.join('/'));
|
|
74
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { RouteNode } from './route.js';
|
|
2
|
+
/**
|
|
3
|
+
* 扩展路由的钩子
|
|
4
|
+
*
|
|
5
|
+
* 支持对 route 的数据直接进行修改,
|
|
6
|
+
* 但此时分组路由的children还未赋值!
|
|
7
|
+
* 特殊需求可通过BeforeWriteRoutesHook钩子处理。
|
|
8
|
+
*
|
|
9
|
+
* @param route - 生成的路由节点
|
|
10
|
+
* @param parsed - 解析的路由节点
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```js
|
|
14
|
+
* {
|
|
15
|
+
* extendRoute(route) {
|
|
16
|
+
* if (route.path === '/home') {
|
|
17
|
+
* route.meta ??= { auth: true }
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export type ExtendRouteHook = (route: RouteNode) => void;
|
|
24
|
+
/**
|
|
25
|
+
* 写入路由文件前的钩子
|
|
26
|
+
*
|
|
27
|
+
* 支持对 routes 直接进行修改亦可以返回新的路由数组。
|
|
28
|
+
*
|
|
29
|
+
* @param routes - 路由数组
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```js
|
|
33
|
+
* {
|
|
34
|
+
* beforeWriteRoutes(routes) {
|
|
35
|
+
* routes.forEach(route => {
|
|
36
|
+
* if (route.path === '/') {
|
|
37
|
+
* route.path = '/home'
|
|
38
|
+
* }
|
|
39
|
+
* })
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export type BeforeWriteRoutesHook = (routes: RouteNode[]) => void | RouteNode[];
|
|
45
|
+
/**
|
|
46
|
+
* 代码转换函数
|
|
47
|
+
*
|
|
48
|
+
* @param content - 代码内容
|
|
49
|
+
* @param file - 文件路径
|
|
50
|
+
* @returns - 转换后的代码内容
|
|
51
|
+
*/
|
|
52
|
+
export type CodeTransformHook = (content: string, file: string) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|