vitarx-router 4.0.0-beta.3 → 4.0.0-beta.4
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/dist/core/shared/route.js +1 -2
- package/dist/file-router/config/resolve.d.ts +2 -1
- package/dist/file-router/config/resolve.js +3 -2
- package/dist/file-router/config/validate.d.ts +1 -0
- package/dist/file-router/config/validate.js +15 -0
- package/dist/file-router/generator/generateRoutes.js +1 -1
- package/dist/file-router/index.d.ts +3 -4
- package/dist/file-router/index.js +20 -26
- package/dist/file-router/parser/parsePage.d.ts +28 -16
- package/dist/file-router/parser/parsePage.js +207 -8
- package/dist/file-router/types/options.d.ts +30 -0
- package/dist/plugin-vite/index.js +0 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CodeTransformHook, ExtendRouteHook, FileRouterOptions, ImportMode, PageDirOptions, PathStrategy } from '../types/index.js';
|
|
1
|
+
import type { CodeTransformHook, ExtendRouteHook, FileRouterOptions, ImportMode, PageDirOptions, PathParser, PathStrategy } from '../types/index.js';
|
|
2
2
|
export type PageDirConfig = Required<PageDirOptions>;
|
|
3
3
|
/**
|
|
4
4
|
* 规范化后的配置
|
|
@@ -14,6 +14,7 @@ export interface ResolvedConfig {
|
|
|
14
14
|
configFileName: string;
|
|
15
15
|
transform?: CodeTransformHook;
|
|
16
16
|
extendRoute?: ExtendRouteHook;
|
|
17
|
+
pathParser?: PathParser;
|
|
17
18
|
}
|
|
18
19
|
/**
|
|
19
20
|
* 规范化文件路由配置
|
|
@@ -56,7 +56,7 @@ function resolvePageConfigs(pages, root) {
|
|
|
56
56
|
* @returns - 规范化后的配置对象
|
|
57
57
|
*/
|
|
58
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;
|
|
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, pathParser } = options;
|
|
60
60
|
const resolvedPages = resolvePageConfigs(pages, root);
|
|
61
61
|
return {
|
|
62
62
|
dts: typeof dts === 'string' ? dts : dts ? DEFAULT_DTS_FILE : false,
|
|
@@ -68,6 +68,7 @@ export function resolveConfig(options) {
|
|
|
68
68
|
layoutFileName,
|
|
69
69
|
configFileName,
|
|
70
70
|
transform,
|
|
71
|
-
extendRoute
|
|
71
|
+
extendRoute,
|
|
72
|
+
pathParser
|
|
72
73
|
};
|
|
73
74
|
}
|
|
@@ -198,6 +198,19 @@ function validateExtendRoute(opts) {
|
|
|
198
198
|
throw new Error('options.extendRoute 必须是函数');
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* 验证 pathParser 配置
|
|
203
|
+
*
|
|
204
|
+
* @param opts - 配置选项
|
|
205
|
+
* @throws {Error} 当配置无效时抛出错误
|
|
206
|
+
*/
|
|
207
|
+
function validatePathParser(opts) {
|
|
208
|
+
if (opts.pathParser === undefined)
|
|
209
|
+
return;
|
|
210
|
+
if (typeof opts.pathParser !== 'function') {
|
|
211
|
+
throw new Error('options.pathParser 必须是函数');
|
|
212
|
+
}
|
|
213
|
+
}
|
|
201
214
|
/**
|
|
202
215
|
* 验证插件配置选项
|
|
203
216
|
*
|
|
@@ -212,6 +225,7 @@ function validateExtendRoute(opts) {
|
|
|
212
225
|
* 8. configFileName 配置
|
|
213
226
|
* 9. transform 配置
|
|
214
227
|
* 10. extendRoute 配置
|
|
228
|
+
* 11. pathParser 配置
|
|
215
229
|
*
|
|
216
230
|
* @param opts - 用户提供的配置选项
|
|
217
231
|
* @throws {Error} 当配置无效时抛出错误
|
|
@@ -227,4 +241,5 @@ export function validateOptions(opts) {
|
|
|
227
241
|
validateConfigFileName(opts);
|
|
228
242
|
validateTransform(opts);
|
|
229
243
|
validateExtendRoute(opts);
|
|
244
|
+
validatePathParser(opts);
|
|
230
245
|
}
|
|
@@ -126,7 +126,7 @@ function formatComponent(component, importMode, importLines) {
|
|
|
126
126
|
else {
|
|
127
127
|
expr = `lazy(() => import(${importPath}))`;
|
|
128
128
|
}
|
|
129
|
-
return `${name}: ${expr}`;
|
|
129
|
+
return `${JSON.stringify(name)}: ${expr}`;
|
|
130
130
|
});
|
|
131
131
|
return `{ ${entries.join(', ')} }`;
|
|
132
132
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { GeneratorResult } from '@babel/generator';
|
|
8
8
|
import { ResolvedConfig } from './config/index.js';
|
|
9
9
|
import { type GenerateResult } from './generator/index.js';
|
|
10
|
+
import { type FilterOptions } from './parser/index.js';
|
|
10
11
|
import type { FileRouterOptions, ParsedNode } from './types/index.js';
|
|
11
12
|
export type * from './types/index.js';
|
|
12
13
|
export * from './utils/logger.js';
|
|
@@ -66,7 +67,6 @@ export declare class FileRouter {
|
|
|
66
67
|
/**
|
|
67
68
|
* 处理文件
|
|
68
69
|
* @param filePath - 文件路径
|
|
69
|
-
* @param fileName - 文件名
|
|
70
70
|
* @param page - 页面配置
|
|
71
71
|
* @param pageMapping - 子路由
|
|
72
72
|
* @param parent - 父节点
|
|
@@ -91,7 +91,6 @@ export declare class FileRouter {
|
|
|
91
91
|
*
|
|
92
92
|
* @param file - 文件绝对路径
|
|
93
93
|
* @param name - 文件名
|
|
94
|
-
* @param ext - 文件扩展名
|
|
95
94
|
* @param pages - 页面配置,默认为 `config.pages`
|
|
96
95
|
* @returns {string} - 文件类型,可选值有 `layout`、`config`、`page`、`ignore`
|
|
97
96
|
*/
|
|
@@ -100,10 +99,10 @@ export declare class FileRouter {
|
|
|
100
99
|
* 检查文件是否为页面文件
|
|
101
100
|
*
|
|
102
101
|
* @param file - 文件绝对路径
|
|
103
|
-
* @param
|
|
102
|
+
* @param filter - 过滤配置,默认为 `config.pages`
|
|
104
103
|
* @returns {boolean} - 是否为页面文件
|
|
105
104
|
*/
|
|
106
|
-
|
|
105
|
+
isPageFile(file: string, filter?: FilterOptions | readonly FilterOptions[]): boolean;
|
|
107
106
|
/**
|
|
108
107
|
* 写入类型定义文件
|
|
109
108
|
*/
|
|
@@ -137,7 +137,7 @@ export class FileRouter {
|
|
|
137
137
|
}
|
|
138
138
|
else {
|
|
139
139
|
// 处理文件
|
|
140
|
-
route = this.processFile(filePath,
|
|
140
|
+
route = this.processFile(filePath, page, pageMapping, parent);
|
|
141
141
|
}
|
|
142
142
|
if (route) {
|
|
143
143
|
children.add(route);
|
|
@@ -169,18 +169,15 @@ export class FileRouter {
|
|
|
169
169
|
/**
|
|
170
170
|
* 处理文件
|
|
171
171
|
* @param filePath - 文件路径
|
|
172
|
-
* @param fileName - 文件名
|
|
173
172
|
* @param page - 页面配置
|
|
174
173
|
* @param pageMapping - 子路由
|
|
175
174
|
* @param parent - 父节点
|
|
176
175
|
* @private
|
|
177
176
|
*/
|
|
178
|
-
processFile(filePath,
|
|
179
|
-
//
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
const baseName = nodePath.basename(fileName, ext);
|
|
183
|
-
const fileType = this.getPageType(filePath, baseName, ext, page);
|
|
177
|
+
processFile(filePath, page, pageMapping, parent) {
|
|
178
|
+
// 分离出路由 path 和视图命名
|
|
179
|
+
const { routePath, viewName = 'default' } = parseRoutePath(filePath, this.config.pathParser);
|
|
180
|
+
const fileType = this.getPageType(filePath, routePath, page);
|
|
184
181
|
if (fileType === 'ignore')
|
|
185
182
|
return null;
|
|
186
183
|
// 处理分组配置文件
|
|
@@ -201,8 +198,6 @@ export class FileRouter {
|
|
|
201
198
|
}
|
|
202
199
|
return null;
|
|
203
200
|
}
|
|
204
|
-
// 分离出路由 path 和视图命名
|
|
205
|
-
const { routePath, viewName } = parseRoutePath(baseName);
|
|
206
201
|
// 处理分组布局文件
|
|
207
202
|
if (fileType === 'layout') {
|
|
208
203
|
if (!parent)
|
|
@@ -273,15 +268,14 @@ export class FileRouter {
|
|
|
273
268
|
*
|
|
274
269
|
* @param file - 文件绝对路径
|
|
275
270
|
* @param name - 文件名
|
|
276
|
-
* @param ext - 文件扩展名
|
|
277
271
|
* @param pages - 页面配置,默认为 `config.pages`
|
|
278
272
|
* @returns {string} - 文件类型,可选值有 `layout`、`config`、`page`、`ignore`
|
|
279
273
|
*/
|
|
280
|
-
getPageType(file, name,
|
|
281
|
-
if (name === this.config.layoutFileName
|
|
274
|
+
getPageType(file, name, pages) {
|
|
275
|
+
if (name === this.config.layoutFileName) {
|
|
282
276
|
return 'layout';
|
|
283
277
|
}
|
|
284
|
-
if (name === this.config.configFileName && (
|
|
278
|
+
if (name === this.config.configFileName && (file.endsWith('.ts') || file.endsWith('.js'))) {
|
|
285
279
|
return 'config';
|
|
286
280
|
}
|
|
287
281
|
if (this.isPageFile(file, pages)) {
|
|
@@ -293,16 +287,16 @@ export class FileRouter {
|
|
|
293
287
|
* 检查文件是否为页面文件
|
|
294
288
|
*
|
|
295
289
|
* @param file - 文件绝对路径
|
|
296
|
-
* @param
|
|
290
|
+
* @param filter - 过滤配置,默认为 `config.pages`
|
|
297
291
|
* @returns {boolean} - 是否为页面文件
|
|
298
292
|
*/
|
|
299
|
-
isPageFile(file,
|
|
300
|
-
if (
|
|
301
|
-
if (Array.isArray(
|
|
302
|
-
return !!isPageFileInDirs(file,
|
|
293
|
+
isPageFile(file, filter) {
|
|
294
|
+
if (filter) {
|
|
295
|
+
if (Array.isArray(filter)) {
|
|
296
|
+
return !!isPageFileInDirs(file, filter);
|
|
303
297
|
}
|
|
304
298
|
else {
|
|
305
|
-
return isPageFile(file,
|
|
299
|
+
return isPageFile(file, filter);
|
|
306
300
|
}
|
|
307
301
|
}
|
|
308
302
|
return !!isPageFileInDirs(file, this.config.pages);
|
|
@@ -376,16 +370,16 @@ export class FileRouter {
|
|
|
376
370
|
const page = isPageFileInDirs(filePath, this.config.pages);
|
|
377
371
|
if (!page)
|
|
378
372
|
return false;
|
|
373
|
+
// 分离出路由 path 和视图命名
|
|
374
|
+
const { routePath, viewName } = parseRoutePath(filePath, this.config.pathParser);
|
|
379
375
|
const dirPath = nodePath.dirname(filePath);
|
|
380
|
-
const filename = nodePath.basename(filePath);
|
|
381
376
|
const parent = this.fileMap.get(dirPath);
|
|
382
377
|
const pageMapping = new Map();
|
|
383
378
|
const prefix = parent ? '' : page.prefix;
|
|
384
379
|
// 如果是命名文件,则先查找是否存在同名路由,存在则添加到同名路由的 children 中
|
|
385
|
-
if (
|
|
386
|
-
const baseName = filename.split('@')[0];
|
|
380
|
+
if (viewName) {
|
|
387
381
|
const pages = parent ? parent.children : this.nodeTree;
|
|
388
|
-
const newRoutePath = this.applyPathStrategy(prefix +
|
|
382
|
+
const newRoutePath = this.applyPathStrategy(prefix + routePath);
|
|
389
383
|
let sameRoute = null;
|
|
390
384
|
for (const route of pages) {
|
|
391
385
|
if (route.path === newRoutePath) {
|
|
@@ -394,10 +388,10 @@ export class FileRouter {
|
|
|
394
388
|
}
|
|
395
389
|
}
|
|
396
390
|
if (sameRoute) {
|
|
397
|
-
pageMapping.set(
|
|
391
|
+
pageMapping.set(routePath, sameRoute);
|
|
398
392
|
}
|
|
399
393
|
}
|
|
400
|
-
const route = this.processFile(filePath,
|
|
394
|
+
const route = this.processFile(filePath, {
|
|
401
395
|
dir: dirPath,
|
|
402
396
|
include: page.include,
|
|
403
397
|
exclude: page.exclude,
|
|
@@ -1,23 +1,35 @@
|
|
|
1
|
+
import type { PathParser, PathParseResult } from '../types/index.js';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* 路径解析错误类
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* - 路由路径转换
|
|
6
|
-
*
|
|
7
|
-
* 与构建工具无关,可在任何 Node.js 环境中使用。
|
|
5
|
+
* 提供详细的错误上下文信息,包括文件路径、错误类型和原始值。
|
|
8
6
|
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
export declare class PathParseError 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
|
+
}
|
|
13
25
|
/**
|
|
14
|
-
*
|
|
26
|
+
* 解析路由路径和视图名称
|
|
15
27
|
*
|
|
16
|
-
*
|
|
28
|
+
* 从文件名中提取路由路径和命名视图名称。
|
|
17
29
|
*
|
|
18
|
-
* @param
|
|
19
|
-
* @param
|
|
20
|
-
* @returns
|
|
30
|
+
* @param filePath - 文件路径
|
|
31
|
+
* @param [parser] - 路径解析器
|
|
32
|
+
* @returns 路由路径和视图名称
|
|
33
|
+
* @throws {PathParseError} 当路径解析失败时抛出
|
|
21
34
|
*/
|
|
22
|
-
export declare function parseRoutePath(
|
|
23
|
-
export {};
|
|
35
|
+
export declare function parseRoutePath(filePath: string, parser?: PathParser): Exclude<PathParseResult, string>;
|
|
@@ -6,16 +6,215 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 与构建工具无关,可在任何 Node.js 环境中使用。
|
|
8
8
|
*/
|
|
9
|
+
import path from 'node:path';
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* 路径解析错误类
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
+
* 提供详细的错误上下文信息,包括文件路径、错误类型和原始值。
|
|
14
|
+
*/
|
|
15
|
+
export class PathParseError 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, PathParseError);
|
|
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
|
+
* 解析路由路径和视图名称
|
|
66
|
+
*
|
|
67
|
+
* 从文件名中提取路由路径和命名视图名称。
|
|
68
|
+
*
|
|
69
|
+
* @param filePath - 文件路径
|
|
70
|
+
* @param [parser] - 路径解析器
|
|
71
|
+
* @returns 路由路径和视图名称
|
|
72
|
+
* @throws {PathParseError} 当路径解析失败时抛出
|
|
73
|
+
*/
|
|
74
|
+
export function parseRoutePath(filePath, parser) {
|
|
75
|
+
const { basename } = extractFileInfo(filePath);
|
|
76
|
+
if (!parser) {
|
|
77
|
+
return parseDefaultRoutePath(basename);
|
|
78
|
+
}
|
|
79
|
+
const result = parser(basename, filePath);
|
|
80
|
+
return parseCustomRouteResult(result, filePath);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 提取文件信息
|
|
84
|
+
*
|
|
85
|
+
* @param filePath - 文件路径
|
|
86
|
+
* @returns 文件基本信息
|
|
87
|
+
*/
|
|
88
|
+
function extractFileInfo(filePath) {
|
|
89
|
+
const ext = path.extname(filePath);
|
|
90
|
+
const basename = path.basename(filePath, ext);
|
|
91
|
+
return { basename, ext };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 解析默认路由路径(无自定义解析器)
|
|
95
|
+
*
|
|
96
|
+
* @param basename - 文件基本名称
|
|
97
|
+
* @returns 解析结果
|
|
98
|
+
*/
|
|
99
|
+
function parseDefaultRoutePath(basename) {
|
|
100
|
+
const [routePath, viewName] = basename.split('@', 2);
|
|
101
|
+
return {
|
|
102
|
+
routePath,
|
|
103
|
+
viewName: viewName || 'default'
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 解析自定义路由结果
|
|
108
|
+
*
|
|
109
|
+
* @param result - 解析器返回的结果
|
|
110
|
+
* @param filePath - 文件路径(用于错误上下文)
|
|
111
|
+
* @returns 解析结果
|
|
112
|
+
* @throws {PathParseError} 当结果无效时抛出
|
|
113
|
+
*/
|
|
114
|
+
function parseCustomRouteResult(result, filePath) {
|
|
115
|
+
if (typeof result === 'string') {
|
|
116
|
+
return parseStringResult(result, filePath);
|
|
117
|
+
}
|
|
118
|
+
if (result && typeof result === 'object' && !Array.isArray(result)) {
|
|
119
|
+
return parseObjectResult(result, filePath);
|
|
120
|
+
}
|
|
121
|
+
throw new PathParseError('pathParser returned invalid result type', {
|
|
122
|
+
filePath,
|
|
123
|
+
originalValue: result,
|
|
124
|
+
field: 'result'
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 解析字符串类型的结果
|
|
129
|
+
*
|
|
130
|
+
* @param result - 字符串结果
|
|
131
|
+
* @param filePath - 文件路径
|
|
132
|
+
* @returns 解析结果
|
|
133
|
+
* @throws {PathParseError} 当路径无效时抛出
|
|
134
|
+
*/
|
|
135
|
+
function parseStringResult(result, filePath) {
|
|
136
|
+
const routePath = normalizeRoutePath(result);
|
|
137
|
+
if (!routePath) {
|
|
138
|
+
throw new PathParseError('pathParser returned empty routePath', {
|
|
139
|
+
filePath,
|
|
140
|
+
originalValue: result,
|
|
141
|
+
field: 'routePath'
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return { routePath, viewName: 'default' };
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 解析对象类型的结果
|
|
148
|
+
*
|
|
149
|
+
* @param result - 对象结果
|
|
150
|
+
* @param filePath - 文件路径
|
|
151
|
+
* @returns 解析结果
|
|
152
|
+
* @throws {PathParseError} 当结果无效时抛出
|
|
153
|
+
*/
|
|
154
|
+
function parseObjectResult(result, filePath) {
|
|
155
|
+
const { routePath: rawRoutePath, viewName } = result;
|
|
156
|
+
validateRoutePathType(rawRoutePath, filePath);
|
|
157
|
+
const routePath = normalizeRoutePath(rawRoutePath);
|
|
158
|
+
if (!routePath) {
|
|
159
|
+
throw new PathParseError('pathParser returned empty routePath after normalization', {
|
|
160
|
+
filePath,
|
|
161
|
+
originalValue: rawRoutePath,
|
|
162
|
+
field: 'routePath'
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
validateViewName(viewName, filePath);
|
|
166
|
+
return {
|
|
167
|
+
routePath,
|
|
168
|
+
viewName: viewName || 'default'
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 验证路由路径类型
|
|
173
|
+
*
|
|
174
|
+
* @param routePath - 路由路径
|
|
175
|
+
* @param filePath - 文件路径(用于错误上下文)
|
|
176
|
+
* @throws {PathParseError} 当类型无效时抛出
|
|
177
|
+
*/
|
|
178
|
+
function validateRoutePathType(routePath, filePath) {
|
|
179
|
+
if (typeof routePath !== 'string') {
|
|
180
|
+
throw new PathParseError('pathParser returned non-string routePath', {
|
|
181
|
+
filePath,
|
|
182
|
+
originalValue: routePath,
|
|
183
|
+
field: 'routePath'
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (!routePath.trim()) {
|
|
187
|
+
throw new PathParseError('pathParser returned empty or whitespace-only routePath', {
|
|
188
|
+
filePath,
|
|
189
|
+
originalValue: routePath,
|
|
190
|
+
field: 'routePath'
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 验证视图名称
|
|
196
|
+
*
|
|
197
|
+
* @param viewName - 视图名称
|
|
198
|
+
* @param filePath - 文件路径(用于错误上下文)
|
|
199
|
+
* @throws {PathParseError} 当视图名称无效时抛出
|
|
200
|
+
*/
|
|
201
|
+
function validateViewName(viewName, filePath) {
|
|
202
|
+
if (viewName !== undefined && typeof viewName !== 'string') {
|
|
203
|
+
throw new PathParseError('pathParser returned non-string viewName', {
|
|
204
|
+
filePath,
|
|
205
|
+
originalValue: viewName,
|
|
206
|
+
field: 'viewName'
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* 标准化路由路径
|
|
212
|
+
*
|
|
213
|
+
* 去除路径首尾空白和开头的斜杠。
|
|
13
214
|
*
|
|
14
|
-
* @param
|
|
15
|
-
* @
|
|
16
|
-
* @returns 视图名称和去除视图名的基础名
|
|
215
|
+
* @param routePath - 原始路由路径
|
|
216
|
+
* @returns 标准化后的路由路径
|
|
17
217
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return { viewName: viewName || defaultName, routePath };
|
|
218
|
+
function normalizeRoutePath(routePath) {
|
|
219
|
+
return routePath.trim().replace(/^\/+/, '');
|
|
21
220
|
}
|
|
@@ -11,6 +11,23 @@ export type ImportMode = 'lazy' | 'sync';
|
|
|
11
11
|
*/
|
|
12
12
|
export type PathStrategy = 'kebab' | 'lowercase' | 'raw';
|
|
13
13
|
export type PageSource = string | PageDirOptions;
|
|
14
|
+
/**
|
|
15
|
+
* 路径解析结果
|
|
16
|
+
*/
|
|
17
|
+
export type PathParseResult = string | {
|
|
18
|
+
/** 解析后的路径 */
|
|
19
|
+
routePath: string;
|
|
20
|
+
/** 视图名称 */
|
|
21
|
+
viewName?: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 路径解析器
|
|
25
|
+
*
|
|
26
|
+
* @param basename - 文件名称(不包含扩展名)
|
|
27
|
+
* @param filePath - 完整的文件路径
|
|
28
|
+
* @returns {PathParseResult} 路径解析结果包含路径和视图名称
|
|
29
|
+
*/
|
|
30
|
+
export type PathParser = (basename: string, filePath: string) => PathParseResult;
|
|
14
31
|
/**
|
|
15
32
|
* 页面目录选项
|
|
16
33
|
*/
|
|
@@ -60,6 +77,11 @@ export interface FileRouterOptions {
|
|
|
60
77
|
/**
|
|
61
78
|
* 路径转换策略
|
|
62
79
|
*
|
|
80
|
+
* - 'kebab': 转换为 kebab-case
|
|
81
|
+
* - 'lowercase': 转换为 lowercase
|
|
82
|
+
* - 'raw': 不转换
|
|
83
|
+
*
|
|
84
|
+
* @values 'kebab' | 'lowercase' | 'raw'
|
|
63
85
|
* @default 'kebab'
|
|
64
86
|
*/
|
|
65
87
|
pathStrategy?: PathStrategy;
|
|
@@ -135,4 +157,12 @@ export interface FileRouterOptions {
|
|
|
135
157
|
* ```
|
|
136
158
|
*/
|
|
137
159
|
extendRoute?: ExtendRouteHook;
|
|
160
|
+
/**
|
|
161
|
+
* 路径解析器
|
|
162
|
+
*
|
|
163
|
+
* @param basename - 文件名称(不包含扩展名)或目录名称
|
|
164
|
+
* @param filePath - 完整的文件路径
|
|
165
|
+
* @returns {PathParseResult} 返回字符串path,或包含path和viewName的对象
|
|
166
|
+
*/
|
|
167
|
+
pathParser?: PathParser;
|
|
138
168
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitarx-router",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.4",
|
|
4
4
|
"description": "Official routing solution for Vitarx framework with declarative routing, navigation guards, dynamic routes, file-based routing with HMR, and full TypeScript support.",
|
|
5
5
|
"author": "ZhuChonglin <8210856@qq.com>",
|
|
6
6
|
"license": "MIT",
|