vitarx-router 4.0.0-beta.13 → 4.0.0-beta.15
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/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/router/web.js +6 -1
- package/dist/file-router/config/resolve.d.ts +2 -1
- package/dist/file-router/config/resolve.js +2 -1
- package/dist/file-router/config/validate.js +12 -0
- package/dist/file-router/generator/generateRoutes.d.ts +8 -3
- package/dist/file-router/generator/generateRoutes.js +10 -4
- package/dist/file-router/index.d.ts +5 -4
- package/dist/file-router/index.js +12 -10
- package/dist/file-router/macros/astValueExtractor.js +2 -2
- package/dist/file-router/macros/definePage.d.ts +3 -1
- package/dist/file-router/macros/definePage.js +4 -2
- package/dist/file-router/parser/parsePage.d.ts +1 -1
- package/dist/file-router/parser/parsePage.js +40 -32
- package/dist/file-router/types/hooks.d.ts +6 -2
- package/dist/file-router/types/options.d.ts +15 -6
- package/dist/file-router/types/route.d.ts +16 -8
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ import { __ROUTER_VIEW_DEPTH_KEY__, useRouter } from '../core/index.js';
|
|
|
12
12
|
* @returns {View} 返回渲染的视图
|
|
13
13
|
*/
|
|
14
14
|
export function RouterView(props) {
|
|
15
|
-
const { children
|
|
15
|
+
const { children } = props;
|
|
16
16
|
// 获取路由实例
|
|
17
17
|
const router = useRouter();
|
|
18
18
|
// 获取父级 index
|
|
@@ -20,6 +20,7 @@ export function RouterView(props) {
|
|
|
20
20
|
const index = parentIndex + 1; // 计算当前视图的索引
|
|
21
21
|
provide(__ROUTER_VIEW_DEPTH_KEY__, index); // 向子组件提供当前索引
|
|
22
22
|
// 匹配的路由线路
|
|
23
|
+
const viewName = computed(() => props.name || 'default');
|
|
23
24
|
const matchedRoute = computed(() => {
|
|
24
25
|
return router.route.matched[index] ?? null;
|
|
25
26
|
});
|
|
@@ -28,8 +29,7 @@ export function RouterView(props) {
|
|
|
28
29
|
const currentRoute = matchedRoute.value;
|
|
29
30
|
if (!currentRoute)
|
|
30
31
|
return null;
|
|
31
|
-
|
|
32
|
-
let injectProps = currentRoute.props?.[name] ?? router.config.props ?? false;
|
|
32
|
+
let injectProps = currentRoute.props?.[viewName.value] ?? router.config.props ?? false;
|
|
33
33
|
if (injectProps === false)
|
|
34
34
|
return null; // 如果属性为 false,返回null
|
|
35
35
|
if (injectProps === true && currentRoute.pattern) {
|
|
@@ -52,7 +52,7 @@ export function RouterView(props) {
|
|
|
52
52
|
const route = matchedRoute.value; // 获取匹配的路由记录
|
|
53
53
|
if (!route)
|
|
54
54
|
return null;
|
|
55
|
-
return route.component?.[
|
|
55
|
+
return route.component?.[viewName.value] ?? null;
|
|
56
56
|
});
|
|
57
57
|
// 如果传入了 children 函数,则调用并返回其结果
|
|
58
58
|
if (isFunction(children)) {
|
|
@@ -61,6 +61,7 @@ export function RouterView(props) {
|
|
|
61
61
|
}
|
|
62
62
|
catch (e) {
|
|
63
63
|
logger.error('[RouterView] Error occurred while executing children function', e);
|
|
64
|
+
return createCommentView(`router-view:error`);
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
let lastView = null;
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* 导航状态
|
|
3
3
|
*
|
|
4
4
|
* 枚举值:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* 5. exception: 捕获到异常
|
|
5
|
+
* 1. success: 导航成功
|
|
6
|
+
* 2. aborted: 导航被阻止
|
|
7
|
+
* 4. cancelled: 导航被取消
|
|
8
|
+
* 8. duplicated: 重复导航
|
|
9
|
+
* 16. notfound: 路由未匹配
|
|
11
10
|
*/
|
|
12
11
|
export declare enum NavState {
|
|
13
12
|
/**
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* 导航状态
|
|
3
3
|
*
|
|
4
4
|
* 枚举值:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* 5. exception: 捕获到异常
|
|
5
|
+
* 1. success: 导航成功
|
|
6
|
+
* 2. aborted: 导航被阻止
|
|
7
|
+
* 4. cancelled: 导航被取消
|
|
8
|
+
* 8. duplicated: 重复导航
|
|
9
|
+
* 16. notfound: 路由未匹配
|
|
11
10
|
*/
|
|
12
11
|
export var NavState;
|
|
13
12
|
(function (NavState) {
|
package/dist/core/router/web.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isString, logger } from 'vitarx';
|
|
2
|
+
import { NavState } from '../common/constant.js';
|
|
2
3
|
import { parseHashContent } from '../common/utils.js';
|
|
3
4
|
import { normalizePath, parseQuery } from '../shared/utils.js';
|
|
4
5
|
import { Router } from './router.js';
|
|
@@ -49,7 +50,11 @@ export class WebRouter extends Router {
|
|
|
49
50
|
}
|
|
50
51
|
});
|
|
51
52
|
// 初始化路由
|
|
52
|
-
this.replace(this.urlToNavigateTarget()).then(
|
|
53
|
+
this.replace(this.urlToNavigateTarget()).then(res => {
|
|
54
|
+
if (__VITARX_DEV__ && res.state !== NavState.success && res.state !== NavState.cancelled) {
|
|
55
|
+
logger.error(`[Router] Initialization failed: ${res.message}`, res);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
53
58
|
// 初始化时监听 popstate 事件,处理历史记录返回时的路由恢复
|
|
54
59
|
window.addEventListener('popstate', this.onPopState);
|
|
55
60
|
if (this.config.mode === 'hash') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CodeTransformHook, ExtendRouteHook, FileRouterOptions, ImportMode, PageDirOptions, PageSource, PathParser, PathStrategy } from '../types/index.js';
|
|
1
|
+
import type { CodeTransformHook, ExtendRouteHook, FileRouterOptions, ImportMode, PageDirOptions, PageSource, PathParser, PathStrategy, RouteNode } 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
|
+
beforeWriteRoutes?: (routes: RouteNode[]) => void | RouteNode[];
|
|
17
18
|
pathParser?: PathParser;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
@@ -49,7 +49,7 @@ export function resolvePageConfigs(pages, root) {
|
|
|
49
49
|
* @returns - 规范化后的配置对象
|
|
50
50
|
*/
|
|
51
51
|
export function resolveConfig(options) {
|
|
52
|
-
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;
|
|
52
|
+
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, beforeWriteRoutes, pathParser } = options;
|
|
53
53
|
const resolvedPages = resolvePageConfigs(pages, root);
|
|
54
54
|
return {
|
|
55
55
|
dts: typeof dts === 'string' ? dts : dts ? DEFAULT_DTS_FILE : false,
|
|
@@ -62,6 +62,7 @@ export function resolveConfig(options) {
|
|
|
62
62
|
configFileName,
|
|
63
63
|
transform,
|
|
64
64
|
extendRoute,
|
|
65
|
+
beforeWriteRoutes,
|
|
65
66
|
pathParser
|
|
66
67
|
};
|
|
67
68
|
}
|
|
@@ -207,6 +207,17 @@ function validateExtendRoute(opts) {
|
|
|
207
207
|
throw new Error('options.extendRoute 必须是函数');
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* 验证 beforeWriteRoutes 配置
|
|
212
|
+
* @param opts
|
|
213
|
+
*/
|
|
214
|
+
function validateBeforeWriteRoutes(opts) {
|
|
215
|
+
if (opts.beforeWriteRoutes === undefined)
|
|
216
|
+
return;
|
|
217
|
+
if (typeof opts.beforeWriteRoutes !== 'function') {
|
|
218
|
+
throw new Error('options.beforeWriteRoutes 必须是函数');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
210
221
|
/**
|
|
211
222
|
* 验证 pathParser 配置
|
|
212
223
|
*
|
|
@@ -250,5 +261,6 @@ export function validateOptions(opts) {
|
|
|
250
261
|
validateConfigFileName(opts);
|
|
251
262
|
validateTransform(opts);
|
|
252
263
|
validateExtendRoute(opts);
|
|
264
|
+
validateBeforeWriteRoutes(opts);
|
|
253
265
|
validatePathParser(opts);
|
|
254
266
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 负责将解析后的页面信息转换为可执行的路由配置代码。
|
|
5
5
|
* 与构建工具无关,可在任何 Node.js 环境中使用。
|
|
6
6
|
*/
|
|
7
|
-
import type { ExtendRouteHook, ImportMode,
|
|
7
|
+
import type { BeforeWriteRoutesHook, ExtendRouteHook, ImportMode, RouteNode, ScanNode } from '../types/index.js';
|
|
8
8
|
/**
|
|
9
9
|
* 路由生成选项
|
|
10
10
|
*/
|
|
@@ -24,6 +24,11 @@ export interface GenerateRoutesOptions {
|
|
|
24
24
|
* 在生成每个路由配置时调用,允许开发者自定义扩展路由配置
|
|
25
25
|
*/
|
|
26
26
|
extendRoute?: ExtendRouteHook;
|
|
27
|
+
/**
|
|
28
|
+
* 路由写入前钩子
|
|
29
|
+
* 在生成路由代码前调用,允许开发者自定义路由配置
|
|
30
|
+
*/
|
|
31
|
+
beforeWriteRoutes?: BeforeWriteRoutesHook;
|
|
27
32
|
/**
|
|
28
33
|
* 自定义导入语句
|
|
29
34
|
* 允许向虚拟模块注入自定义的导入语句
|
|
@@ -49,7 +54,7 @@ export declare function pathToUniqueName(absolutePath: string): string;
|
|
|
49
54
|
* @param parent - 父路由
|
|
50
55
|
* @returns 路由配置列表
|
|
51
56
|
*/
|
|
52
|
-
export declare function buildRoutes(pages: Iterable<
|
|
57
|
+
export declare function buildRoutes(pages: Iterable<ScanNode>, extendRoute?: ExtendRouteHook, parent?: RouteNode): RouteNode[];
|
|
53
58
|
/**
|
|
54
59
|
* 生成路由代码字符串
|
|
55
60
|
*
|
|
@@ -69,4 +74,4 @@ export declare function generateRoutesCode(routes: RouteNode[], importMode?: Imp
|
|
|
69
74
|
* @param options - 路由生成选项
|
|
70
75
|
* @returns { GenerateResult } 包含routes、code、dts的路由配置结果
|
|
71
76
|
*/
|
|
72
|
-
export declare function generateRoutes(pages:
|
|
77
|
+
export declare function generateRoutes(pages: ScanNode[], options: GenerateRoutesOptions): GenerateResult;
|
|
@@ -59,6 +59,7 @@ function generateRouteName(fullPath) {
|
|
|
59
59
|
function buildRouteNode(page, extendRoute, parent) {
|
|
60
60
|
// 创建基础路由配置
|
|
61
61
|
const route = {
|
|
62
|
+
filePath: page.filePath,
|
|
62
63
|
path: page.path,
|
|
63
64
|
fullPath: parent ? normalizeRoutePath(parent.fullPath + '/' + page.path) : page.path
|
|
64
65
|
};
|
|
@@ -93,14 +94,14 @@ function buildRouteNode(page, extendRoute, parent) {
|
|
|
93
94
|
route.alias = Array.isArray(alias) ? Array.from(alias) : alias;
|
|
94
95
|
}
|
|
95
96
|
}
|
|
97
|
+
// 应用路由扩展钩子
|
|
98
|
+
if (extendRoute)
|
|
99
|
+
extendRoute(route);
|
|
96
100
|
// 处理子路由
|
|
97
101
|
if (page.children && page.children.size > 0) {
|
|
98
102
|
// 递归处理子路由
|
|
99
103
|
route.children = buildRoutes(page.children.values(), extendRoute, route);
|
|
100
104
|
}
|
|
101
|
-
// 应用路由扩展钩子
|
|
102
|
-
if (extendRoute)
|
|
103
|
-
extendRoute(route, page);
|
|
104
105
|
// 动态路由不存在name时生成一个name
|
|
105
106
|
if (!route.name && route.fullPath.includes('{')) {
|
|
106
107
|
route.name = generateRouteName(route.fullPath);
|
|
@@ -278,7 +279,12 @@ export function generateRoutesCode(routes, importMode = 'lazy', customImports, i
|
|
|
278
279
|
*/
|
|
279
280
|
export function generateRoutes(pages, options) {
|
|
280
281
|
// 构建解析后的路由配置
|
|
281
|
-
|
|
282
|
+
let routes = buildRoutes(pages, options.extendRoute);
|
|
283
|
+
if (options.beforeWriteRoutes) {
|
|
284
|
+
const result = options.beforeWriteRoutes(routes);
|
|
285
|
+
if (Array.isArray(result))
|
|
286
|
+
routes = result;
|
|
287
|
+
}
|
|
282
288
|
// 生成最终的路由代码
|
|
283
289
|
const code = generateRoutesCode(routes, options.importMode, options.imports);
|
|
284
290
|
const dts = options.dts ? generateDtsCode(routes) : '';
|
|
@@ -8,8 +8,9 @@ import type { GeneratorResult } from '@babel/generator';
|
|
|
8
8
|
import { ResolvedConfig } from './config/index.js';
|
|
9
9
|
import { type GenerateResult } from './generator/index.js';
|
|
10
10
|
import { type FilterOptions } from './parser/index.js';
|
|
11
|
-
import type { FileRouterOptions,
|
|
11
|
+
import type { FileRouterOptions, ScanNode } from './types/index.js';
|
|
12
12
|
export { resolvePageConfigs } from './config/resolve.js';
|
|
13
|
+
export { mergePageOptions } from './macros/definePage.js';
|
|
13
14
|
export * from './generator/index.js';
|
|
14
15
|
export type * from './types/index.js';
|
|
15
16
|
export * from './utils/logger.js';
|
|
@@ -42,13 +43,13 @@ export declare class FileRouter {
|
|
|
42
43
|
/**
|
|
43
44
|
* 获取节点树
|
|
44
45
|
*/
|
|
45
|
-
get nodeTree():
|
|
46
|
+
get nodeTree(): ScanNode[];
|
|
46
47
|
/**
|
|
47
48
|
* 获取文件映射表
|
|
48
49
|
*
|
|
49
50
|
* 键为文件或目录路径,值为对应的节点对象
|
|
50
51
|
*/
|
|
51
|
-
get fileMap(): Map<string,
|
|
52
|
+
get fileMap(): Map<string, ScanNode>;
|
|
52
53
|
/**
|
|
53
54
|
* 加载/重新加载文件路由管理器
|
|
54
55
|
*
|
|
@@ -60,7 +61,7 @@ export declare class FileRouter {
|
|
|
60
61
|
*
|
|
61
62
|
* @returns 扫描到的页面文件列表
|
|
62
63
|
*/
|
|
63
|
-
protected scanPages():
|
|
64
|
+
protected scanPages(): ScanNode[];
|
|
64
65
|
/**
|
|
65
66
|
* 扫描页面目录
|
|
66
67
|
*
|
|
@@ -19,6 +19,7 @@ import { checkDefaultExport, isPageFile, isPageFileInDirs } from './parser/index
|
|
|
19
19
|
import { parseRoutePath } from './parser/parsePage.js';
|
|
20
20
|
import { applyPathStrategy, info, normalizePathSeparator, readFileContent, resolvePathVariable, validateOptions, warn } from './utils/index.js';
|
|
21
21
|
export { resolvePageConfigs } from './config/resolve.js';
|
|
22
|
+
export { mergePageOptions } from './macros/definePage.js';
|
|
22
23
|
export * from './generator/index.js';
|
|
23
24
|
export * from './utils/logger.js';
|
|
24
25
|
/**
|
|
@@ -118,6 +119,7 @@ export class FileRouter {
|
|
|
118
119
|
}
|
|
119
120
|
if (page.group && page.prefix) {
|
|
120
121
|
const route = {
|
|
122
|
+
isGroup: true,
|
|
121
123
|
filePath: page.dir,
|
|
122
124
|
path: this.applyPathStrategy(page.prefix)
|
|
123
125
|
};
|
|
@@ -177,6 +179,7 @@ export class FileRouter {
|
|
|
177
179
|
processDir(filePath, fileName, page, parent) {
|
|
178
180
|
const pathPrefix = parent ? '' : page.prefix;
|
|
179
181
|
const route = {
|
|
182
|
+
isGroup: true,
|
|
180
183
|
parent,
|
|
181
184
|
filePath,
|
|
182
185
|
path: this.applyPathStrategy(pathPrefix + fileName)
|
|
@@ -194,7 +197,7 @@ export class FileRouter {
|
|
|
194
197
|
*/
|
|
195
198
|
processFile(filePath, page, pageMapping, parent) {
|
|
196
199
|
// 分离出路由 path 和视图命名
|
|
197
|
-
const { routePath, viewName = 'default' } = parseRoutePath(filePath, this.config.pathParser);
|
|
200
|
+
const { routePath, viewName = 'default', options } = parseRoutePath(filePath, this.config.pathParser);
|
|
198
201
|
const fileType = this.getPageType(filePath, routePath, page);
|
|
199
202
|
if (fileType === 'ignore')
|
|
200
203
|
return null;
|
|
@@ -205,12 +208,7 @@ export class FileRouter {
|
|
|
205
208
|
const content = this.readFile(filePath);
|
|
206
209
|
const pageOptions = parseDefinePage(content, filePath);
|
|
207
210
|
if (pageOptions) {
|
|
208
|
-
|
|
209
|
-
mergePageOptions([parent.options, pageOptions]);
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
parent.options = pageOptions;
|
|
213
|
-
}
|
|
211
|
+
parent.options = mergePageOptions(parent.options, pageOptions);
|
|
214
212
|
parent.dirConfigFile = filePath;
|
|
215
213
|
this.fileMap.set(filePath, parent);
|
|
216
214
|
}
|
|
@@ -242,14 +240,17 @@ export class FileRouter {
|
|
|
242
240
|
return null;
|
|
243
241
|
}
|
|
244
242
|
// 解析页面选项
|
|
245
|
-
const
|
|
243
|
+
const definePageOptions = parseDefinePage(content, filePath);
|
|
244
|
+
// 合并页面选项
|
|
245
|
+
const pageOptions = mergePageOptions(options, definePageOptions);
|
|
246
246
|
// 忽略不具备默认导出,且无重定向配置的文件
|
|
247
|
-
if (!pageOptions
|
|
247
|
+
if (!pageOptions.redirect && !checkDefaultExport(content, filePath))
|
|
248
248
|
return null;
|
|
249
249
|
// 最终 path
|
|
250
250
|
const finalPath = this.applyPathStrategy((parent ? '' : page.prefix) + (routePath === 'index' ? '' : routePath));
|
|
251
251
|
// 创建路由对象
|
|
252
252
|
const route = {
|
|
253
|
+
isGroup: false,
|
|
253
254
|
parent,
|
|
254
255
|
filePath,
|
|
255
256
|
path: finalPath,
|
|
@@ -258,7 +259,7 @@ export class FileRouter {
|
|
|
258
259
|
}
|
|
259
260
|
};
|
|
260
261
|
// 添加页面选项
|
|
261
|
-
if (pageOptions)
|
|
262
|
+
if (Object.keys(pageOptions).length)
|
|
262
263
|
route.options = pageOptions;
|
|
263
264
|
// 添加到子路由映射中
|
|
264
265
|
pageMapping.set(routePath, route);
|
|
@@ -345,6 +346,7 @@ export class FileRouter {
|
|
|
345
346
|
__classPrivateFieldSet(this, _FileRouter_generateResult, generateRoutes(this.nodeTree, {
|
|
346
347
|
imports: this.config.injectImports,
|
|
347
348
|
extendRoute: this.config.extendRoute,
|
|
349
|
+
beforeWriteRoutes: this.config.beforeWriteRoutes,
|
|
348
350
|
importMode: this.config.importMode,
|
|
349
351
|
dts: !!this.config.dts
|
|
350
352
|
}), "f");
|
|
@@ -128,8 +128,8 @@ function extractRegExp(node) {
|
|
|
128
128
|
if (node.callee.type === 'Identifier' && node.callee.name === 'RegExp') {
|
|
129
129
|
const args = node.arguments;
|
|
130
130
|
if (args.length >= 1) {
|
|
131
|
-
const firstArg = args
|
|
132
|
-
const secondArg = args
|
|
131
|
+
const firstArg = args.at(0);
|
|
132
|
+
const secondArg = args.at(1);
|
|
133
133
|
let patternStr = null;
|
|
134
134
|
let flags = '';
|
|
135
135
|
if (firstArg.type === 'StringLiteral') {
|
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { GeneratorResult } from '@babel/generator';
|
|
9
9
|
import type { PageOptions } from '../types/index.js';
|
|
10
|
+
type Falsy = false | null | undefined | 0 | '';
|
|
10
11
|
/**
|
|
11
12
|
* 合并多个页面配置
|
|
12
13
|
*
|
|
13
14
|
* @param optionsList - 配置列表
|
|
14
15
|
* @returns 合并后的配置
|
|
15
16
|
*/
|
|
16
|
-
export declare function mergePageOptions(optionsList: PageOptions[]): PageOptions;
|
|
17
|
+
export declare function mergePageOptions(...optionsList: (PageOptions | Falsy)[]): PageOptions;
|
|
17
18
|
/**
|
|
18
19
|
* 解析 definePage 宏配置
|
|
19
20
|
*
|
|
@@ -43,3 +44,4 @@ export declare function removeDefinePage(code: string, filename: string): Genera
|
|
|
43
44
|
* @param b
|
|
44
45
|
*/
|
|
45
46
|
export declare function isEqualPageOptions(a?: PageOptions | null, b?: PageOptions | null): boolean;
|
|
47
|
+
export {};
|
|
@@ -13,9 +13,11 @@ import { extractPageOptions } from './astValueExtractor.js';
|
|
|
13
13
|
* @param optionsList - 配置列表
|
|
14
14
|
* @returns 合并后的配置
|
|
15
15
|
*/
|
|
16
|
-
export function mergePageOptions(optionsList) {
|
|
16
|
+
export function mergePageOptions(...optionsList) {
|
|
17
17
|
const merged = {};
|
|
18
18
|
for (const options of optionsList) {
|
|
19
|
+
if (!options)
|
|
20
|
+
continue;
|
|
19
21
|
if (options.name)
|
|
20
22
|
merged.name = options.name;
|
|
21
23
|
if (options.meta)
|
|
@@ -74,7 +76,7 @@ export function parseDefinePage(content, filePath) {
|
|
|
74
76
|
if (routeOptionsList.length > 1) {
|
|
75
77
|
warn('检测到多个 definePage 调用,将合并所有配置,建议每个文件只调用一次 definePage', `in ${filePath}`);
|
|
76
78
|
}
|
|
77
|
-
return mergePageOptions(routeOptionsList);
|
|
79
|
+
return mergePageOptions(...routeOptionsList);
|
|
78
80
|
}
|
|
79
81
|
catch (e) {
|
|
80
82
|
error(`解析 definePage 失败 in ${e}`, e);
|
|
@@ -32,4 +32,4 @@ export declare class PathParseError extends TypeError {
|
|
|
32
32
|
* @returns 路由路径和视图名称
|
|
33
33
|
* @throws {PathParseError} 当路径解析失败时抛出
|
|
34
34
|
*/
|
|
35
|
-
export declare function parseRoutePath(filePath: string, parser?: PathParser):
|
|
35
|
+
export declare function parseRoutePath(filePath: string, parser?: PathParser): PathParseResult;
|
|
@@ -74,10 +74,10 @@ export class PathParseError extends TypeError {
|
|
|
74
74
|
export function parseRoutePath(filePath, parser) {
|
|
75
75
|
const { basename } = extractFileInfo(filePath);
|
|
76
76
|
if (!parser) {
|
|
77
|
-
return
|
|
77
|
+
return defaultPathParser(basename);
|
|
78
78
|
}
|
|
79
79
|
const result = parser(basename, filePath);
|
|
80
|
-
return
|
|
80
|
+
return parseCustomResult(result, filePath);
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
83
|
* 提取文件信息
|
|
@@ -96,24 +96,32 @@ function extractFileInfo(filePath) {
|
|
|
96
96
|
* @param basename - 文件基本名称
|
|
97
97
|
* @returns 解析结果
|
|
98
98
|
*/
|
|
99
|
-
function
|
|
100
|
-
const [
|
|
99
|
+
function defaultPathParser(basename) {
|
|
100
|
+
const [rawPath, viewName] = basename.split('@', 2);
|
|
101
|
+
const routePath = normalizeRoutePath(rawPath);
|
|
102
|
+
if (!routePath) {
|
|
103
|
+
throw new PathParseError('pathParser returned empty routePath', {
|
|
104
|
+
filePath: basename,
|
|
105
|
+
originalValue: rawPath,
|
|
106
|
+
field: 'routePath'
|
|
107
|
+
});
|
|
108
|
+
}
|
|
101
109
|
return {
|
|
102
110
|
routePath,
|
|
103
|
-
viewName
|
|
111
|
+
viewName
|
|
104
112
|
};
|
|
105
113
|
}
|
|
106
114
|
/**
|
|
107
|
-
*
|
|
115
|
+
* 解析自定义解析器结果
|
|
108
116
|
*
|
|
109
117
|
* @param result - 解析器返回的结果
|
|
110
118
|
* @param filePath - 文件路径(用于错误上下文)
|
|
111
119
|
* @returns 解析结果
|
|
112
120
|
* @throws {PathParseError} 当结果无效时抛出
|
|
113
121
|
*/
|
|
114
|
-
function
|
|
122
|
+
function parseCustomResult(result, filePath) {
|
|
115
123
|
if (typeof result === 'string') {
|
|
116
|
-
return
|
|
124
|
+
return defaultPathParser(result);
|
|
117
125
|
}
|
|
118
126
|
if (result && typeof result === 'object' && !Array.isArray(result)) {
|
|
119
127
|
return parseObjectResult(result, filePath);
|
|
@@ -124,25 +132,6 @@ function parseCustomRouteResult(result, filePath) {
|
|
|
124
132
|
field: 'result'
|
|
125
133
|
});
|
|
126
134
|
}
|
|
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
135
|
/**
|
|
147
136
|
* 解析对象类型的结果
|
|
148
137
|
*
|
|
@@ -152,7 +141,7 @@ function parseStringResult(result, filePath) {
|
|
|
152
141
|
* @throws {PathParseError} 当结果无效时抛出
|
|
153
142
|
*/
|
|
154
143
|
function parseObjectResult(result, filePath) {
|
|
155
|
-
const { routePath: rawRoutePath, viewName } = result;
|
|
144
|
+
const { routePath: rawRoutePath, viewName, options } = result;
|
|
156
145
|
validateRoutePathType(rawRoutePath, filePath);
|
|
157
146
|
const routePath = normalizeRoutePath(rawRoutePath);
|
|
158
147
|
if (!routePath) {
|
|
@@ -163,9 +152,10 @@ function parseObjectResult(result, filePath) {
|
|
|
163
152
|
});
|
|
164
153
|
}
|
|
165
154
|
validateViewName(viewName, filePath);
|
|
155
|
+
validateOptions(options, filePath);
|
|
166
156
|
return {
|
|
167
|
-
|
|
168
|
-
|
|
157
|
+
...result,
|
|
158
|
+
routePath
|
|
169
159
|
};
|
|
170
160
|
}
|
|
171
161
|
/**
|
|
@@ -207,14 +197,32 @@ function validateViewName(viewName, filePath) {
|
|
|
207
197
|
});
|
|
208
198
|
}
|
|
209
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* 验证选项
|
|
202
|
+
*
|
|
203
|
+
* @param options - 选项
|
|
204
|
+
* @param filePath - 文件路径
|
|
205
|
+
*/
|
|
206
|
+
function validateOptions(options, filePath) {
|
|
207
|
+
if (options !== undefined && Object.prototype.toString.call(options) !== '[object Object]') {
|
|
208
|
+
throw new PathParseError('pathParser returned invalid options type', {
|
|
209
|
+
filePath,
|
|
210
|
+
originalValue: options,
|
|
211
|
+
field: 'options'
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
210
215
|
/**
|
|
211
216
|
* 标准化路由路径
|
|
212
217
|
*
|
|
213
|
-
*
|
|
218
|
+
* 去除路径首尾空白和首尾的斜杠,将 . # 等不利于 URL 的字符替换为 -。
|
|
214
219
|
*
|
|
215
220
|
* @param routePath - 原始路由路径
|
|
216
221
|
* @returns 标准化后的路由路径
|
|
217
222
|
*/
|
|
218
223
|
function normalizeRoutePath(routePath) {
|
|
219
|
-
return routePath
|
|
224
|
+
return routePath
|
|
225
|
+
.trim()
|
|
226
|
+
.replace(/^\/+|\/+$/g, '')
|
|
227
|
+
.replace(/[.#]/g, '-');
|
|
220
228
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RouteNode } from './route.js';
|
|
2
2
|
/**
|
|
3
3
|
* 扩展路由的钩子
|
|
4
4
|
*
|
|
@@ -7,7 +7,11 @@ import type { ParsedNode, RouteNode } from './route.js';
|
|
|
7
7
|
* @param route - 生成的路由节点
|
|
8
8
|
* @param parsed - 解析的路由节点
|
|
9
9
|
*/
|
|
10
|
-
export type ExtendRouteHook = (route: RouteNode
|
|
10
|
+
export type ExtendRouteHook = (route: RouteNode) => void;
|
|
11
|
+
/**
|
|
12
|
+
* 写入路由文件前的钩子
|
|
13
|
+
*/
|
|
14
|
+
export type BeforeWriteRoutesHook = (routes: RouteNode[]) => void | RouteNode[];
|
|
11
15
|
/**
|
|
12
16
|
* 代码转换函数
|
|
13
17
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { CodeTransformHook, ExtendRouteHook } from './hooks.js';
|
|
1
|
+
import type { BeforeWriteRoutesHook, CodeTransformHook, ExtendRouteHook } from './hooks.js';
|
|
2
|
+
import type { PageOptions } from './route.js';
|
|
2
3
|
/**
|
|
3
4
|
* 自定义导入模式函数的上下文
|
|
4
5
|
*/
|
|
@@ -53,10 +54,14 @@ export type PageSource = string | PageDirOptions;
|
|
|
53
54
|
/**
|
|
54
55
|
* 路径解析结果
|
|
55
56
|
*/
|
|
56
|
-
export type PathParseResult =
|
|
57
|
-
/** 解析后的路径 */
|
|
57
|
+
export type PathParseResult = {
|
|
58
|
+
/** 解析后的路径 如:home.jsx -> 'home' */
|
|
58
59
|
routePath: string;
|
|
59
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* 页面相关可配置选项
|
|
62
|
+
*/
|
|
63
|
+
options?: PageOptions;
|
|
64
|
+
/** 视图名称 如:home.nav.jsx -> 'nav' */
|
|
60
65
|
viewName?: string;
|
|
61
66
|
};
|
|
62
67
|
/**
|
|
@@ -64,9 +69,9 @@ export type PathParseResult = string | {
|
|
|
64
69
|
*
|
|
65
70
|
* @param basename - 文件名称(不包含扩展名)
|
|
66
71
|
* @param filePath - 完整的文件路径
|
|
67
|
-
* @returns {PathParseResult}
|
|
72
|
+
* @returns {PathParseResult | string} 如果返回字符串则交由内置的pathParser继续处理,否则返回`PathParseResult`对象
|
|
68
73
|
*/
|
|
69
|
-
export type PathParser = (basename: string, filePath: string) => PathParseResult;
|
|
74
|
+
export type PathParser = (basename: string, filePath: string) => string | PathParseResult;
|
|
70
75
|
/**
|
|
71
76
|
* 页面目录选项
|
|
72
77
|
*/
|
|
@@ -212,6 +217,10 @@ export interface FileRouterOptions {
|
|
|
212
217
|
* ```
|
|
213
218
|
*/
|
|
214
219
|
extendRoute?: ExtendRouteHook;
|
|
220
|
+
/**
|
|
221
|
+
* 写入路由之前
|
|
222
|
+
*/
|
|
223
|
+
beforeWriteRoutes?: BeforeWriteRoutesHook;
|
|
215
224
|
/**
|
|
216
225
|
* 路径解析器
|
|
217
226
|
*
|
|
@@ -39,13 +39,17 @@ export interface PageOptions {
|
|
|
39
39
|
alias?: string | string[];
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* 扫描的节点 - 核心 IR 节点
|
|
43
43
|
*/
|
|
44
|
-
export interface
|
|
44
|
+
export interface ScanNode {
|
|
45
45
|
/**
|
|
46
46
|
* 目录配置文件
|
|
47
47
|
*/
|
|
48
48
|
dirConfigFile?: string;
|
|
49
|
+
/**
|
|
50
|
+
* 是否为分组
|
|
51
|
+
*/
|
|
52
|
+
readonly isGroup: boolean;
|
|
49
53
|
/**
|
|
50
54
|
* 文件绝对路径
|
|
51
55
|
*/
|
|
@@ -57,11 +61,11 @@ export interface ParsedNode {
|
|
|
57
61
|
/**
|
|
58
62
|
* 父节点
|
|
59
63
|
*/
|
|
60
|
-
parent?:
|
|
64
|
+
parent?: ScanNode;
|
|
61
65
|
/**
|
|
62
66
|
* 子节点映射
|
|
63
67
|
*/
|
|
64
|
-
children?: Set<
|
|
68
|
+
children?: Set<ScanNode>;
|
|
65
69
|
/**
|
|
66
70
|
* 组件映射(命名视图)
|
|
67
71
|
*/
|
|
@@ -75,18 +79,22 @@ export interface ParsedNode {
|
|
|
75
79
|
* 路由节点 - 解析后的路由节点
|
|
76
80
|
*/
|
|
77
81
|
export interface RouteNode extends PageOptions {
|
|
82
|
+
/**
|
|
83
|
+
* 文件绝对路径
|
|
84
|
+
*/
|
|
85
|
+
filePath: string;
|
|
78
86
|
/**
|
|
79
87
|
* 当前 path(不含父级)
|
|
80
88
|
*/
|
|
81
|
-
|
|
89
|
+
path: string;
|
|
82
90
|
/**
|
|
83
91
|
* 完整 path(含父级)
|
|
84
92
|
*/
|
|
85
|
-
|
|
93
|
+
fullPath: string;
|
|
86
94
|
/**
|
|
87
|
-
*
|
|
95
|
+
* 子节点
|
|
88
96
|
*/
|
|
89
|
-
children?:
|
|
97
|
+
children?: RouteNode[];
|
|
90
98
|
/**
|
|
91
99
|
* 组件映射(命名视图)
|
|
92
100
|
*
|
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.15",
|
|
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",
|