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,92 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _FileRouter_nodeTree, _FileRouter_fileMap, _FileRouter_generateResult;
|
|
13
|
+
import { existsSync, mkdirSync, readdirSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import nodePath from 'node:path';
|
|
9
15
|
import { resolveConfig } from './config/index.js';
|
|
10
|
-
import {
|
|
11
|
-
import { removeDefinePage } from './macros/index.js';
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
16
|
+
import { generateRoutes } from './generator/index.js';
|
|
17
|
+
import { isEqualPageOptions, mergePageOptions, parseDefinePage, removeDefinePage } from './macros/index.js';
|
|
18
|
+
import { checkDefaultExport, isPageFile, isPageFileInDirs } from './parser/index.js';
|
|
19
|
+
import { extractFileInfo, parsePageFile } from './parser/parsePage.js';
|
|
20
|
+
import { computeRouteFullPath } from './parser/routePath.js';
|
|
21
|
+
import { applyPathStrategy, info, normalizePathSeparator, readFileContent, resolvePathVariable, validateOptions, warn } from './utils/index.js';
|
|
22
|
+
export { resolvePageConfigs } from './config/resolve.js';
|
|
23
|
+
export * from './generator/index.js';
|
|
24
|
+
export { mergePageOptions } from './macros/definePage.js';
|
|
25
|
+
export { findRoute } from './utils/findRoute.js';
|
|
14
26
|
export * from './utils/logger.js';
|
|
15
|
-
export * from './types.js';
|
|
16
27
|
/**
|
|
17
28
|
* 文件路由管理器
|
|
18
|
-
*
|
|
19
|
-
* 封装文件路由的核心流程,提供统一的 API 供不同构建工具使用。
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```typescript
|
|
23
|
-
* // 基本使用
|
|
24
|
-
* const router = new FileRouter({
|
|
25
|
-
* pagesDir: 'src/pages',
|
|
26
|
-
* extensions: ['.tsx', '.ts']
|
|
27
|
-
* })
|
|
28
|
-
*
|
|
29
|
-
* router.setRoot('/project/root')
|
|
30
|
-
* router.scan()
|
|
31
|
-
*
|
|
32
|
-
* // 生成路由代码
|
|
33
|
-
* const routes = await router.generateRoutes()
|
|
34
|
-
*
|
|
35
|
-
* // 生成类型定义
|
|
36
|
-
* const dts = router.generateDts()
|
|
37
|
-
* ```
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```typescript
|
|
41
|
-
* // 在 Vite 插件中使用
|
|
42
|
-
* const router = new FileRouter(options)
|
|
43
|
-
*
|
|
44
|
-
* return {
|
|
45
|
-
* configResolved(config) {
|
|
46
|
-
* router.setRoot(config.root)
|
|
47
|
-
* router.scan()
|
|
48
|
-
* },
|
|
49
|
-
* async load(id) {
|
|
50
|
-
* if (id === VIRTUAL_ID) {
|
|
51
|
-
* return (await router.generateRoutes()).code
|
|
52
|
-
* }
|
|
53
|
-
* }
|
|
54
|
-
* }
|
|
55
|
-
* ```
|
|
56
29
|
*/
|
|
57
30
|
export class FileRouter {
|
|
58
31
|
/**
|
|
59
32
|
* 创建文件路由管理器
|
|
60
33
|
*
|
|
61
34
|
* @param options - 配置选项
|
|
35
|
+
* @param [init = true] - 是否初始化加载
|
|
62
36
|
*/
|
|
63
|
-
constructor(options = {}) {
|
|
37
|
+
constructor(options = {}, init = true) {
|
|
38
|
+
/**
|
|
39
|
+
* 配置项
|
|
40
|
+
*/
|
|
64
41
|
Object.defineProperty(this, "config", {
|
|
65
42
|
enumerable: true,
|
|
66
43
|
configurable: true,
|
|
67
44
|
writable: true,
|
|
68
45
|
value: void 0
|
|
69
46
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
47
|
+
/**
|
|
48
|
+
* 扫描的节点树
|
|
49
|
+
*
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
_FileRouter_nodeTree.set(this, void 0);
|
|
53
|
+
/**
|
|
54
|
+
* 文件映射表
|
|
55
|
+
* @private
|
|
56
|
+
*/
|
|
57
|
+
_FileRouter_fileMap.set(this, new Map()
|
|
58
|
+
/**
|
|
59
|
+
* 生成结果
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
);
|
|
63
|
+
/**
|
|
64
|
+
* 生成结果
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
_FileRouter_generateResult.set(this, null
|
|
68
|
+
/**
|
|
69
|
+
* 创建文件路由管理器
|
|
70
|
+
*
|
|
71
|
+
* @param options - 配置选项
|
|
72
|
+
* @param [init = true] - 是否初始化加载
|
|
73
|
+
*/
|
|
74
|
+
);
|
|
88
75
|
validateOptions(options);
|
|
89
76
|
this.config = resolveConfig(options);
|
|
77
|
+
__classPrivateFieldSet(this, _FileRouter_nodeTree, init ? this.scanPages() : [], "f");
|
|
90
78
|
}
|
|
91
79
|
/**
|
|
92
80
|
* 获取项目根目录
|
|
@@ -94,116 +82,564 @@ export class FileRouter {
|
|
|
94
82
|
get root() {
|
|
95
83
|
return this.config.root;
|
|
96
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* 获取节点树
|
|
87
|
+
*/
|
|
88
|
+
get nodeTree() {
|
|
89
|
+
return __classPrivateFieldGet(this, _FileRouter_nodeTree, "f");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 获取文件映射表
|
|
93
|
+
*
|
|
94
|
+
* 键为文件或目录路径,值为对应的节点对象
|
|
95
|
+
*/
|
|
96
|
+
get fileMap() {
|
|
97
|
+
return __classPrivateFieldGet(this, _FileRouter_fileMap, "f");
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 加载/重新加载文件路由管理器
|
|
101
|
+
*
|
|
102
|
+
* @returns {FileRouter} 文件路由管理器实例
|
|
103
|
+
*/
|
|
104
|
+
reload() {
|
|
105
|
+
this.clearGenerateResult();
|
|
106
|
+
__classPrivateFieldGet(this, _FileRouter_fileMap, "f").clear();
|
|
107
|
+
__classPrivateFieldSet(this, _FileRouter_nodeTree, this.scanPages(), "f");
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 构建路由数组
|
|
112
|
+
*
|
|
113
|
+
* @returns 扫描到的页面文件列表
|
|
114
|
+
*/
|
|
115
|
+
scanPages() {
|
|
116
|
+
const pages = [];
|
|
117
|
+
for (const page of this.config.pages) {
|
|
118
|
+
if (!existsSync(page.dir)) {
|
|
119
|
+
warn(`Directory ${page.dir} does not exist, please check your configuration.`);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (page.group && page.prefix) {
|
|
123
|
+
const route = {
|
|
124
|
+
isGroup: true,
|
|
125
|
+
filePath: page.dir,
|
|
126
|
+
path: this.applyPathStrategy(page.prefix)
|
|
127
|
+
};
|
|
128
|
+
route.children = this.scanPageDir({ ...page, prefix: '' }, route);
|
|
129
|
+
// 如果有子路由则添加到页面列表中
|
|
130
|
+
if (route.children.size > 0) {
|
|
131
|
+
pages.push(route);
|
|
132
|
+
this.fileMap.set(route.filePath, route);
|
|
133
|
+
}
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const children = this.scanPageDir(page);
|
|
137
|
+
pages.push(...children.values());
|
|
138
|
+
}
|
|
139
|
+
return pages;
|
|
140
|
+
}
|
|
97
141
|
/**
|
|
98
142
|
* 扫描页面目录
|
|
99
143
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
144
|
+
* @param page - 页面配置
|
|
145
|
+
* @param parent - 父路由
|
|
146
|
+
* @protected
|
|
102
147
|
*/
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
148
|
+
scanPageDir(page, parent) {
|
|
149
|
+
const entries = readdirSync(page.dir, { withFileTypes: true });
|
|
150
|
+
// 直接子路由映射,键为文件名,不包含@视图命名,用于合并命名视图到同一个路由对象
|
|
151
|
+
const pageMapping = new Map();
|
|
152
|
+
const children = new Set();
|
|
153
|
+
for (const dirent of entries) {
|
|
154
|
+
const filePath = normalizePathSeparator(nodePath.resolve(dirent.parentPath, dirent.name));
|
|
155
|
+
let route = null;
|
|
156
|
+
if (dirent.isDirectory()) {
|
|
157
|
+
// 处理嵌套子目录
|
|
158
|
+
route = this.processDir(filePath, dirent.name, page, parent);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// 处理文件
|
|
162
|
+
route = this.processFile(filePath, page, pageMapping, parent);
|
|
163
|
+
}
|
|
164
|
+
if (route) {
|
|
165
|
+
children.add(route);
|
|
166
|
+
this.fileMap.set(filePath, route);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// 返回子路由集合
|
|
170
|
+
return children;
|
|
111
171
|
}
|
|
112
172
|
/**
|
|
113
|
-
*
|
|
173
|
+
* 处理目录
|
|
114
174
|
*
|
|
115
|
-
*
|
|
175
|
+
* @param filePath - 目录路径
|
|
176
|
+
* @param fileName - 目录名
|
|
177
|
+
* @param page - 页面配置
|
|
178
|
+
* @param parent - 父节点
|
|
179
|
+
* @private
|
|
116
180
|
*/
|
|
117
|
-
|
|
118
|
-
this.
|
|
181
|
+
processDir(filePath, fileName, page, parent) {
|
|
182
|
+
const { routePath, options } = this.parseGroupResult(fileName, filePath);
|
|
183
|
+
const pathPrefix = parent ? '' : page.prefix;
|
|
184
|
+
const route = {
|
|
185
|
+
isGroup: true,
|
|
186
|
+
parent,
|
|
187
|
+
filePath,
|
|
188
|
+
path: this.applyPathStrategy(pathPrefix + routePath)
|
|
189
|
+
};
|
|
190
|
+
if (options)
|
|
191
|
+
route.options = options;
|
|
192
|
+
route.children = this.scanPageDir({ ...page, dir: filePath, prefix: '' }, route);
|
|
193
|
+
return route.children.size > 0 ? route : null;
|
|
119
194
|
}
|
|
120
195
|
/**
|
|
121
|
-
*
|
|
196
|
+
* 解析分组目录的自定义路径和选项
|
|
122
197
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
198
|
+
* 通过 groupParser 解析目录名,支持返回字符串路径或包含路径与选项的对象。
|
|
199
|
+
*
|
|
200
|
+
* @param fileName - 目录名
|
|
201
|
+
* @param filePath - 目录完整路径
|
|
202
|
+
* @returns 解析后的路径和选项
|
|
203
|
+
* @private
|
|
125
204
|
*/
|
|
126
|
-
|
|
127
|
-
|
|
205
|
+
parseGroupResult(fileName, filePath) {
|
|
206
|
+
if (!this.config.groupParser) {
|
|
207
|
+
return { routePath: fileName };
|
|
208
|
+
}
|
|
209
|
+
const result = this.config.groupParser(fileName, filePath);
|
|
210
|
+
if (typeof result === 'string') {
|
|
211
|
+
return { routePath: result };
|
|
212
|
+
}
|
|
213
|
+
return { routePath: result.path, options: result.options };
|
|
128
214
|
}
|
|
129
215
|
/**
|
|
130
|
-
*
|
|
216
|
+
* 处理文件
|
|
217
|
+
*
|
|
218
|
+
* 根据文件类型分发到对应的处理器。
|
|
131
219
|
*
|
|
132
|
-
* @
|
|
220
|
+
* @param filePath - 文件路径
|
|
221
|
+
* @param page - 页面配置
|
|
222
|
+
* @param pageMapping - 同路径路由映射
|
|
223
|
+
* @param parent - 父节点
|
|
224
|
+
* @private
|
|
133
225
|
*/
|
|
134
|
-
|
|
135
|
-
|
|
226
|
+
processFile(filePath, page, pageMapping, parent) {
|
|
227
|
+
const { fileInfo, fileType } = this.resolveFile(filePath, page);
|
|
228
|
+
switch (fileType) {
|
|
229
|
+
case 'ignore':
|
|
230
|
+
return null;
|
|
231
|
+
case 'config':
|
|
232
|
+
this.processConfigFile(filePath, parent);
|
|
233
|
+
return null;
|
|
234
|
+
case 'layout':
|
|
235
|
+
this.processLayoutFile(filePath, fileInfo, parent);
|
|
236
|
+
return null;
|
|
237
|
+
case 'page':
|
|
238
|
+
return this.processPageFile(filePath, fileInfo, page, pageMapping, parent);
|
|
239
|
+
}
|
|
136
240
|
}
|
|
137
241
|
/**
|
|
138
|
-
*
|
|
242
|
+
* 解析文件信息与类型
|
|
139
243
|
*
|
|
140
|
-
*
|
|
244
|
+
* 统一入口,避免多处重复调用 extractFileInfo + getPageType。
|
|
245
|
+
*
|
|
246
|
+
* @param filePath - 文件路径
|
|
247
|
+
* @param page - 页面配置
|
|
248
|
+
* @returns 文件信息与类型
|
|
141
249
|
*/
|
|
142
|
-
|
|
143
|
-
|
|
250
|
+
resolveFile(filePath, page) {
|
|
251
|
+
const fileInfo = extractFileInfo(filePath);
|
|
252
|
+
const fileType = this.getPageType(filePath, fileInfo.rawName, page);
|
|
253
|
+
return { fileInfo, fileType };
|
|
144
254
|
}
|
|
145
255
|
/**
|
|
146
|
-
*
|
|
256
|
+
* 处理分组配置文件
|
|
147
257
|
*
|
|
148
|
-
*
|
|
258
|
+
* 解析 definePage 宏并合并到父路由选项中。
|
|
259
|
+
*
|
|
260
|
+
* @param filePath - 文件路径
|
|
261
|
+
* @param parent - 父节点
|
|
149
262
|
*/
|
|
150
|
-
|
|
151
|
-
if (!
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
263
|
+
processConfigFile(filePath, parent) {
|
|
264
|
+
if (!parent)
|
|
265
|
+
return;
|
|
266
|
+
const content = this.readFile(filePath);
|
|
267
|
+
const pageOptions = parseDefinePage(content, filePath);
|
|
268
|
+
if (pageOptions) {
|
|
269
|
+
parent.options = mergePageOptions(parent.options, pageOptions);
|
|
270
|
+
parent.dirConfigFile = filePath;
|
|
271
|
+
this.fileMap.set(filePath, parent);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* 处理分组布局文件
|
|
276
|
+
*
|
|
277
|
+
* 将布局组件注册到父路由的 components 中。
|
|
278
|
+
*
|
|
279
|
+
* @param filePath - 文件路径
|
|
280
|
+
* @param fileInfo - 文件信息
|
|
281
|
+
* @param parent - 父节点
|
|
282
|
+
*/
|
|
283
|
+
processLayoutFile(filePath, fileInfo, parent) {
|
|
284
|
+
if (!parent)
|
|
285
|
+
return;
|
|
286
|
+
const content = this.readFile(filePath);
|
|
287
|
+
if (checkDefaultExport(content, filePath)) {
|
|
288
|
+
parent.components ?? (parent.components = {});
|
|
289
|
+
parent.components[fileInfo.viewName ?? 'default'] = filePath;
|
|
290
|
+
}
|
|
291
|
+
this.fileMap.set(filePath, parent);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* 处理页面文件
|
|
295
|
+
*
|
|
296
|
+
* 解析路由路径、视图命名和页面选项,创建或合并路由节点。
|
|
297
|
+
*
|
|
298
|
+
* @param filePath - 文件路径
|
|
299
|
+
* @param fileInfo - 文件信息
|
|
300
|
+
* @param page - 页面配置
|
|
301
|
+
* @param pageMapping - 同路径路由映射
|
|
302
|
+
* @param parent - 父节点
|
|
303
|
+
* @param [precomputedParsed] - 预计算的解析结果,避免重复调用 parsePageFile
|
|
304
|
+
* @returns 新创建的路由节点,或 null(合并到已有路由时)
|
|
305
|
+
*/
|
|
306
|
+
processPageFile(filePath, fileInfo, page, pageMapping, parent, precomputedParsed) {
|
|
307
|
+
const parsed = precomputedParsed ?? parsePageFile(filePath, this.config.pageParser, fileInfo);
|
|
308
|
+
const viewName = parsed.viewName ?? 'default';
|
|
309
|
+
const content = this.readFile(filePath);
|
|
310
|
+
const sameRoute = pageMapping.get(parsed.path);
|
|
311
|
+
if (sameRoute) {
|
|
312
|
+
if (!checkDefaultExport(content, filePath))
|
|
313
|
+
return null;
|
|
314
|
+
sameRoute.components[viewName] = filePath;
|
|
315
|
+
this.fileMap.set(filePath, sameRoute);
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
const definePageOptions = parseDefinePage(content, filePath);
|
|
319
|
+
const pageOptions = mergePageOptions(parsed.options, definePageOptions);
|
|
320
|
+
if (!pageOptions.redirect && !checkDefaultExport(content, filePath))
|
|
321
|
+
return null;
|
|
322
|
+
const finalPath = this.applyPathStrategy((parent ? '' : page.prefix) + (parsed.path === 'index' ? '' : parsed.path));
|
|
323
|
+
const route = {
|
|
324
|
+
isGroup: false,
|
|
325
|
+
parent,
|
|
326
|
+
filePath,
|
|
327
|
+
path: finalPath,
|
|
328
|
+
components: {
|
|
329
|
+
[viewName]: filePath
|
|
330
|
+
}
|
|
164
331
|
};
|
|
332
|
+
if (Object.keys(pageOptions).length)
|
|
333
|
+
route.options = pageOptions;
|
|
334
|
+
pageMapping.set(parsed.path, route);
|
|
335
|
+
return route;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* 在已有路由树中查找同路径路由
|
|
339
|
+
*
|
|
340
|
+
* 用于 addPage 场景:新增文件时需要检查是否已存在同路径的路由节点,
|
|
341
|
+
* 以便将命名视图合并到已有路由而非创建重复路由。
|
|
342
|
+
*
|
|
343
|
+
* @param pathKey - 标准化后的路由路径
|
|
344
|
+
* @param prefix - 路径前缀
|
|
345
|
+
* @param parent - 父节点
|
|
346
|
+
* @returns 同路径的路由节点,未找到返回 null
|
|
347
|
+
*/
|
|
348
|
+
findSameRoute(pathKey, prefix, parent) {
|
|
349
|
+
const newRoutePath = this.applyPathStrategy(prefix + pathKey);
|
|
350
|
+
const pages = parent ? parent.children : this.nodeTree;
|
|
351
|
+
for (const route of pages) {
|
|
352
|
+
if (route.path === newRoutePath)
|
|
353
|
+
return route;
|
|
354
|
+
}
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* 应用路径策略
|
|
359
|
+
*
|
|
360
|
+
* @param path - 路径
|
|
361
|
+
* @protected
|
|
362
|
+
*/
|
|
363
|
+
applyPathStrategy(path) {
|
|
364
|
+
return resolvePathVariable(applyPathStrategy(path, this.config.pathStrategy));
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* 读取文件内容
|
|
368
|
+
*
|
|
369
|
+
* @param file - 文件绝对路径
|
|
370
|
+
*/
|
|
371
|
+
readFile(file) {
|
|
372
|
+
return readFileContent(file, this.config.transform);
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* 获取文件类型
|
|
376
|
+
*
|
|
377
|
+
* @param file - 文件绝对路径
|
|
378
|
+
* @param rawName - 文件名(不含扩展名和 @视图命名)
|
|
379
|
+
* @param pages - 页面配置
|
|
380
|
+
* @returns 文件类型
|
|
381
|
+
*/
|
|
382
|
+
getPageType(file, rawName, pages) {
|
|
383
|
+
if (rawName === this.config.layoutFileName) {
|
|
384
|
+
return 'layout';
|
|
385
|
+
}
|
|
386
|
+
if (rawName === this.config.configFileName && (file.endsWith('.ts') || file.endsWith('.js'))) {
|
|
387
|
+
return 'config';
|
|
388
|
+
}
|
|
389
|
+
if (this.isPageFile(file, pages)) {
|
|
390
|
+
return 'page';
|
|
391
|
+
}
|
|
392
|
+
return 'ignore';
|
|
165
393
|
}
|
|
166
394
|
/**
|
|
167
|
-
*
|
|
395
|
+
* 检查文件是否为页面文件
|
|
396
|
+
*
|
|
397
|
+
* @param file - 文件绝对路径
|
|
398
|
+
* @param filter - 过滤配置,默认为 `config.pages`
|
|
399
|
+
* @returns {boolean} - 是否为页面文件
|
|
400
|
+
*/
|
|
401
|
+
isPageFile(file, filter) {
|
|
402
|
+
if (filter) {
|
|
403
|
+
if (Array.isArray(filter)) {
|
|
404
|
+
return !!isPageFileInDirs(file, filter);
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
return isPageFile(file, filter);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return !!isPageFileInDirs(file, this.config.pages);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* 获取文件的完整路由路径
|
|
414
|
+
*
|
|
415
|
+
* 判断文件是否为页面文件,如果是则计算其最终生成的路由 fullPath。
|
|
416
|
+
* 非页面文件(布局文件、配置文件等)返回 null。
|
|
168
417
|
*
|
|
169
|
-
* @
|
|
418
|
+
* @param filePath - 文件绝对路径
|
|
419
|
+
* @returns 完整路由路径,非页面文件返回 null
|
|
170
420
|
*/
|
|
171
|
-
|
|
172
|
-
|
|
421
|
+
getRouteFullPath(filePath) {
|
|
422
|
+
if (!this.isPageFile(filePath))
|
|
423
|
+
return null;
|
|
424
|
+
const fileInfo = extractFileInfo(filePath);
|
|
425
|
+
const fileType = this.getPageType(filePath, fileInfo.rawName);
|
|
426
|
+
if (fileType !== 'page')
|
|
427
|
+
return null;
|
|
428
|
+
return computeRouteFullPath(filePath, fileInfo, {
|
|
429
|
+
fileMap: this.fileMap,
|
|
430
|
+
pages: this.config.pages,
|
|
431
|
+
pageParser: this.config.pageParser,
|
|
432
|
+
pathStrategy: this.config.pathStrategy
|
|
433
|
+
});
|
|
173
434
|
}
|
|
174
435
|
/**
|
|
175
436
|
* 写入类型定义文件
|
|
437
|
+
*/
|
|
438
|
+
writeDts(content) {
|
|
439
|
+
const dtsPath = this.config.dts;
|
|
440
|
+
if (!dtsPath)
|
|
441
|
+
return void 0;
|
|
442
|
+
const absolutePath = nodePath.isAbsolute(dtsPath)
|
|
443
|
+
? dtsPath
|
|
444
|
+
: nodePath.resolve(this.root, dtsPath);
|
|
445
|
+
const dtsDir = nodePath.dirname(absolutePath);
|
|
446
|
+
if (!existsSync(dtsDir)) {
|
|
447
|
+
mkdirSync(dtsDir, { recursive: true });
|
|
448
|
+
}
|
|
449
|
+
writeFileSync(absolutePath, content, 'utf-8');
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* 生成路由
|
|
176
453
|
*
|
|
177
|
-
* @
|
|
178
|
-
* @returns 写入结果信息
|
|
454
|
+
* @returns {GenerateResult} 生成结果,包含routes、dts、code
|
|
179
455
|
*/
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
456
|
+
generate() {
|
|
457
|
+
if (!__classPrivateFieldGet(this, _FileRouter_generateResult, "f")) {
|
|
458
|
+
__classPrivateFieldSet(this, _FileRouter_generateResult, generateRoutes(this.nodeTree, {
|
|
459
|
+
imports: this.config.injectImports,
|
|
460
|
+
extendRoute: this.config.extendRoute,
|
|
461
|
+
beforeWriteRoutes: this.config.beforeWriteRoutes,
|
|
462
|
+
importMode: this.config.importMode,
|
|
463
|
+
dts: !!this.config.dts
|
|
464
|
+
}), "f");
|
|
465
|
+
this.writeDts(__classPrivateFieldGet(this, _FileRouter_generateResult, "f").dts);
|
|
185
466
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
467
|
+
return __classPrivateFieldGet(this, _FileRouter_generateResult, "f");
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* 清空生成结果
|
|
471
|
+
*/
|
|
472
|
+
clearGenerateResult() {
|
|
473
|
+
__classPrivateFieldSet(this, _FileRouter_generateResult, null, "f");
|
|
192
474
|
}
|
|
193
475
|
/**
|
|
194
476
|
* 移除 definePage 宏
|
|
195
477
|
*
|
|
196
|
-
*
|
|
197
|
-
* definePage
|
|
478
|
+
* 移除页面文件中的 definePage 宏调用。
|
|
479
|
+
* definePage 在客户端无法运行,必须移除。
|
|
198
480
|
*
|
|
199
481
|
* @param code - 源代码
|
|
200
482
|
* @param filePath - 文件路径
|
|
201
483
|
* @returns 转换后的代码,无需转换返回 null
|
|
202
484
|
*/
|
|
203
485
|
removeDefinePage(code, filePath) {
|
|
204
|
-
if (!this.isPageFile(filePath)) {
|
|
205
|
-
return null;
|
|
206
|
-
}
|
|
207
486
|
return removeDefinePage(code, filePath);
|
|
208
487
|
}
|
|
488
|
+
/**
|
|
489
|
+
* 添加页面文件
|
|
490
|
+
*
|
|
491
|
+
* 根据文件类型直接调用对应处理器,避免重复类型判断和文件解析。
|
|
492
|
+
*
|
|
493
|
+
* @param filePath - 文件路径
|
|
494
|
+
* @returns 是否创建了新的路由节点
|
|
495
|
+
*/
|
|
496
|
+
addPage(filePath) {
|
|
497
|
+
const page = isPageFileInDirs(filePath, this.config.pages);
|
|
498
|
+
if (!page)
|
|
499
|
+
return false;
|
|
500
|
+
const dirPath = nodePath.dirname(filePath);
|
|
501
|
+
const parent = this.fileMap.get(dirPath);
|
|
502
|
+
const prefix = parent ? '' : page.prefix;
|
|
503
|
+
const { fileInfo, fileType } = this.resolveFile(filePath, page);
|
|
504
|
+
const pageConfig = {
|
|
505
|
+
dir: dirPath,
|
|
506
|
+
include: page.include,
|
|
507
|
+
exclude: page.exclude,
|
|
508
|
+
prefix
|
|
509
|
+
};
|
|
510
|
+
switch (fileType) {
|
|
511
|
+
case 'ignore':
|
|
512
|
+
return false;
|
|
513
|
+
case 'config':
|
|
514
|
+
this.processConfigFile(filePath, parent);
|
|
515
|
+
return false;
|
|
516
|
+
case 'layout':
|
|
517
|
+
this.processLayoutFile(filePath, fileInfo, parent);
|
|
518
|
+
return false;
|
|
519
|
+
case 'page': {
|
|
520
|
+
const parsed = parsePageFile(filePath, this.config.pageParser, fileInfo);
|
|
521
|
+
const pageMapping = new Map();
|
|
522
|
+
const sameRoute = this.findSameRoute(parsed.path, prefix, parent);
|
|
523
|
+
if (sameRoute) {
|
|
524
|
+
pageMapping.set(parsed.path, sameRoute);
|
|
525
|
+
}
|
|
526
|
+
const route = this.processPageFile(filePath, fileInfo, pageConfig, pageMapping, parent, parsed);
|
|
527
|
+
if (route) {
|
|
528
|
+
this.fileMap.set(filePath, route);
|
|
529
|
+
return true;
|
|
530
|
+
}
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* 移除指定的文件或目录
|
|
537
|
+
*
|
|
538
|
+
* 通常用于在开发模式下,文件内容改变时,移除旧的路由映射,重新生成
|
|
539
|
+
*
|
|
540
|
+
* 移除成功后续手动调用 `clearGenerateResult` 方法确保下一次获取新的生成结果!
|
|
541
|
+
*
|
|
542
|
+
* @param filePath - 文件/目录路径
|
|
543
|
+
* @returns {boolean} - 存在则移除并返回 true,不存在则返回 false
|
|
544
|
+
*/
|
|
545
|
+
removePage(filePath) {
|
|
546
|
+
const route = this.fileMap.get(filePath);
|
|
547
|
+
if (route) {
|
|
548
|
+
// 移除组件映射,其中包含页面文件自身以及命名文件
|
|
549
|
+
if (route.components) {
|
|
550
|
+
Object.values(route.components).forEach(file => {
|
|
551
|
+
this.fileMap.delete(file);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
// 移除配置文件映射
|
|
555
|
+
if (route.dirConfigFile) {
|
|
556
|
+
this.fileMap.delete(route.dirConfigFile);
|
|
557
|
+
}
|
|
558
|
+
// 递归移除子文件
|
|
559
|
+
if (route.children) {
|
|
560
|
+
// 分组路由需要移除自身的映射
|
|
561
|
+
this.fileMap.delete(filePath);
|
|
562
|
+
route.children.forEach(child => {
|
|
563
|
+
this.removePage(child.filePath);
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
// 从父级移除
|
|
567
|
+
route.parent?.children?.delete(route);
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* 更新文件
|
|
574
|
+
*
|
|
575
|
+
* 如果文件未被扫描,则会自动添加。
|
|
576
|
+
*
|
|
577
|
+
* @param filePath - 文件路径
|
|
578
|
+
* @returns {boolean} - 是否更新了文件
|
|
579
|
+
*/
|
|
580
|
+
updatePage(filePath) {
|
|
581
|
+
const route = this.fileMap.get(filePath);
|
|
582
|
+
if (route) {
|
|
583
|
+
const content = this.readFile(filePath);
|
|
584
|
+
// 解析页面选项
|
|
585
|
+
const newOptions = parseDefinePage(content, filePath);
|
|
586
|
+
// 忽略不具备默认导出,且无重定向配置的文件
|
|
587
|
+
if (!newOptions?.redirect && !checkDefaultExport(content, filePath)) {
|
|
588
|
+
// 移除文件
|
|
589
|
+
this.removePage(filePath);
|
|
590
|
+
}
|
|
591
|
+
if (isEqualPageOptions(route.options, newOptions)) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
if (newOptions) {
|
|
595
|
+
route.options = newOptions;
|
|
596
|
+
}
|
|
597
|
+
else {
|
|
598
|
+
delete route.options;
|
|
599
|
+
}
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
return this.addPage(filePath);
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* 处理文件变化
|
|
606
|
+
*
|
|
607
|
+
* @param eventName - 文件变化事件名
|
|
608
|
+
* @param path - 文件路径
|
|
609
|
+
* @returns {boolean} - 是否影响了路由
|
|
610
|
+
* @example
|
|
611
|
+
* ```typescript
|
|
612
|
+
* router.handleChange('change', '/path/to/file.ts')
|
|
613
|
+
* // 在vite中使用
|
|
614
|
+
* server.watcher.on('all', (event,path) => {
|
|
615
|
+
* const mod = server.moduleGraph.getModuleById(RESOLVED_ROUTES_ID)
|
|
616
|
+
* if (mod) {
|
|
617
|
+
* const result = router.handleChange(event, path)
|
|
618
|
+
* // 如果 handleChange 返回 true,则表示路由受到影响,需要更新模块
|
|
619
|
+
* if(result) server.moduleGraph.invalidateModule(mod)
|
|
620
|
+
* }
|
|
621
|
+
* })
|
|
622
|
+
* ```
|
|
623
|
+
*/
|
|
624
|
+
handleChange(eventName, path) {
|
|
625
|
+
let result;
|
|
626
|
+
switch (eventName) {
|
|
627
|
+
case 'unlinkDir':
|
|
628
|
+
case 'unlink':
|
|
629
|
+
result = this.removePage(path);
|
|
630
|
+
break;
|
|
631
|
+
case 'change':
|
|
632
|
+
result = this.updatePage(path);
|
|
633
|
+
break;
|
|
634
|
+
default:
|
|
635
|
+
result = false;
|
|
636
|
+
}
|
|
637
|
+
if (result) {
|
|
638
|
+
this.clearGenerateResult();
|
|
639
|
+
const relativePath = nodePath.relative(this.root, path);
|
|
640
|
+
info(`✨ Route updated: ${relativePath}`);
|
|
641
|
+
}
|
|
642
|
+
return result;
|
|
643
|
+
}
|
|
209
644
|
}
|
|
645
|
+
_FileRouter_nodeTree = new WeakMap(), _FileRouter_fileMap = new WeakMap(), _FileRouter_generateResult = new WeakMap();
|