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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +42 -17
  2. package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.d.ts +1 -1
  3. package/dist/components/RouterView.js +5 -4
  4. package/dist/core/common/constant.d.ts +5 -6
  5. package/dist/core/common/constant.js +5 -6
  6. package/dist/core/common/utils.js +2 -1
  7. package/dist/core/router/checkOptions.d.ts +11 -0
  8. package/dist/core/router/checkOptions.js +119 -0
  9. package/dist/core/router/manager.js +27 -23
  10. package/dist/core/router/router.d.ts +154 -1
  11. package/dist/core/router/router.js +303 -230
  12. package/dist/core/router/web.d.ts +13 -0
  13. package/dist/core/router/web.js +35 -4
  14. package/dist/core/shared/link.d.ts +7 -0
  15. package/dist/core/shared/link.js +11 -8
  16. package/dist/core/shared/route.js +1 -2
  17. package/dist/core/shared/router.d.ts +3 -3
  18. package/dist/core/shared/router.js +7 -4
  19. package/dist/core/types/options.d.ts +2 -0
  20. package/dist/file-router/config/index.d.ts +2 -1
  21. package/dist/file-router/config/index.js +2 -1
  22. package/dist/file-router/config/resolve.d.ts +43 -0
  23. package/dist/file-router/config/resolve.js +69 -0
  24. package/dist/file-router/{utils/validateOptions.d.ts → config/validate.d.ts} +11 -10
  25. package/dist/file-router/config/validate.js +280 -0
  26. package/dist/file-router/constants.d.ts +12 -2
  27. package/dist/file-router/constants.js +13 -3
  28. package/dist/file-router/generator/generateRoutes.d.ts +44 -13
  29. package/dist/file-router/generator/generateRoutes.js +159 -80
  30. package/dist/file-router/generator/generateTypes.d.ts +3 -29
  31. package/dist/file-router/generator/generateTypes.js +36 -41
  32. package/dist/file-router/global.d.ts +1 -1
  33. package/dist/file-router/index.d.ts +224 -90
  34. package/dist/file-router/index.js +571 -135
  35. package/dist/file-router/macros/astValueExtractor.d.ts +1 -1
  36. package/dist/file-router/macros/astValueExtractor.js +27 -7
  37. package/dist/file-router/macros/definePage.d.ts +20 -3
  38. package/dist/file-router/macros/definePage.js +120 -40
  39. package/dist/file-router/parser/exportChecker.d.ts +4 -23
  40. package/dist/file-router/parser/exportChecker.js +38 -79
  41. package/dist/file-router/parser/filterUtils.d.ts +25 -0
  42. package/dist/file-router/parser/filterUtils.js +43 -0
  43. package/dist/file-router/parser/index.d.ts +2 -1
  44. package/dist/file-router/parser/index.js +2 -1
  45. package/dist/file-router/parser/parsePage.d.ts +56 -9
  46. package/dist/file-router/parser/parsePage.js +194 -172
  47. package/dist/file-router/parser/routePath.d.ts +22 -0
  48. package/dist/file-router/parser/routePath.js +74 -0
  49. package/dist/file-router/types/hooks.d.ts +52 -0
  50. package/dist/file-router/types/index.d.ts +3 -0
  51. package/dist/file-router/types/index.js +1 -0
  52. package/dist/file-router/types/options.d.ts +279 -0
  53. package/dist/file-router/types/options.js +1 -0
  54. package/dist/file-router/types/route.d.ts +114 -0
  55. package/dist/file-router/types/route.js +1 -0
  56. package/dist/file-router/utils/fileReader.d.ts +11 -0
  57. package/dist/file-router/utils/fileReader.js +22 -0
  58. package/dist/file-router/utils/findRoute.d.ts +8 -0
  59. package/dist/file-router/utils/findRoute.js +22 -0
  60. package/dist/file-router/utils/index.d.ts +4 -2
  61. package/dist/file-router/utils/index.js +4 -2
  62. package/dist/file-router/utils/logger.d.ts +6 -6
  63. package/dist/file-router/utils/logger.js +44 -4
  64. package/dist/file-router/utils/pathStrategy.d.ts +28 -0
  65. package/dist/file-router/utils/{namingStrategy.js → pathStrategy.js} +18 -28
  66. package/dist/file-router/utils/pathUtils.d.ts +31 -0
  67. package/dist/file-router/utils/pathUtils.js +53 -1
  68. package/dist/plugin-vite/constant.d.ts +9 -0
  69. package/dist/plugin-vite/constant.js +9 -0
  70. package/dist/plugin-vite/index.d.ts +4 -24
  71. package/dist/plugin-vite/index.js +4 -94
  72. package/dist/plugin-vite/plugin.d.ts +86 -0
  73. package/dist/plugin-vite/plugin.js +181 -0
  74. package/dist/plugin-vite/watcher.d.ts +15 -0
  75. package/dist/plugin-vite/watcher.js +65 -0
  76. package/package.json +9 -7
  77. package/dist/file-router/config/configUtils.d.ts +0 -54
  78. package/dist/file-router/config/configUtils.js +0 -88
  79. package/dist/file-router/scanner/filterUtils.d.ts +0 -35
  80. package/dist/file-router/scanner/filterUtils.js +0 -188
  81. package/dist/file-router/scanner/index.d.ts +0 -8
  82. package/dist/file-router/scanner/index.js +0 -8
  83. package/dist/file-router/scanner/routeTreeBuilder.d.ts +0 -21
  84. package/dist/file-router/scanner/routeTreeBuilder.js +0 -312
  85. package/dist/file-router/scanner/scanPages.d.ts +0 -48
  86. package/dist/file-router/scanner/scanPages.js +0 -174
  87. package/dist/file-router/types.d.ts +0 -344
  88. package/dist/file-router/utils/namingStrategy.d.ts +0 -57
  89. package/dist/file-router/utils/validateOptions.js +0 -233
  90. /package/dist/{plugin-vite/auto-routes → auto-routes}/handleHotUpdate.js +0 -0
  91. /package/dist/{plugin-vite/auto-routes → auto-routes}/index.d.ts +0 -0
  92. /package/dist/{plugin-vite/auto-routes → auto-routes}/index.js +0 -0
  93. /package/dist/file-router/{types.js → types/hooks.js} +0 -0
@@ -5,7 +5,7 @@
5
5
  * 用于解析 definePage 宏的配置参数。
6
6
  */
7
7
  import type * as BabelTypes from '@babel/types';
8
- import type { PageOptions } from '../types.js';
8
+ import type { PageOptions } from '../types/index.js';
9
9
  /**
10
10
  * 从对象表达式提取页面配置
11
11
  *
@@ -47,7 +47,11 @@ function extractStringRecord(node) {
47
47
  for (const prop of node.properties) {
48
48
  if (prop.type !== 'ObjectProperty')
49
49
  continue;
50
- const key = prop.key.type === 'Identifier' ? prop.key.name : null;
50
+ const key = prop.key.type === 'Identifier'
51
+ ? prop.key.name
52
+ : prop.key.type === 'StringLiteral'
53
+ ? prop.key.value
54
+ : null;
51
55
  if (!key)
52
56
  continue;
53
57
  if (prop.value.type === 'StringLiteral') {
@@ -69,7 +73,11 @@ function extractMetaValue(node) {
69
73
  for (const prop of node.properties) {
70
74
  if (prop.type !== 'ObjectProperty')
71
75
  continue;
72
- const key = prop.key.type === 'Identifier' ? prop.key.name : null;
76
+ const key = prop.key.type === 'Identifier'
77
+ ? prop.key.name
78
+ : prop.key.type === 'StringLiteral'
79
+ ? prop.key.value
80
+ : null;
73
81
  if (!key)
74
82
  continue;
75
83
  meta[key] = extractAstLiteralValue(prop.value);
@@ -91,7 +99,11 @@ function extractPatternValue(node) {
91
99
  for (const prop of node.properties) {
92
100
  if (prop.type !== 'ObjectProperty')
93
101
  continue;
94
- const key = prop.key.type === 'Identifier' ? prop.key.name : null;
102
+ const key = prop.key.type === 'Identifier'
103
+ ? prop.key.name
104
+ : prop.key.type === 'StringLiteral'
105
+ ? prop.key.value
106
+ : null;
95
107
  if (!key)
96
108
  continue;
97
109
  const value = prop.value;
@@ -116,8 +128,8 @@ function extractRegExp(node) {
116
128
  if (node.callee.type === 'Identifier' && node.callee.name === 'RegExp') {
117
129
  const args = node.arguments;
118
130
  if (args.length >= 1) {
119
- const firstArg = args[0];
120
- const secondArg = args[1];
131
+ const firstArg = args.at(0);
132
+ const secondArg = args.at(1);
121
133
  let patternStr = null;
122
134
  let flags = '';
123
135
  if (firstArg.type === 'StringLiteral') {
@@ -158,7 +170,11 @@ function extractRedirectValue(node) {
158
170
  for (const prop of node.properties) {
159
171
  if (prop.type !== 'ObjectProperty')
160
172
  continue;
161
- const key = prop.key.type === 'Identifier' ? prop.key.name : null;
173
+ const key = prop.key.type === 'Identifier'
174
+ ? prop.key.name
175
+ : prop.key.type === 'StringLiteral'
176
+ ? prop.key.value
177
+ : null;
162
178
  if (!key)
163
179
  continue;
164
180
  if (key === 'index' && prop.value.type === 'StringLiteral') {
@@ -209,7 +225,11 @@ export function extractPageOptions(node) {
209
225
  for (const prop of node.properties) {
210
226
  if (prop.type !== 'ObjectProperty')
211
227
  continue;
212
- const key = prop.key.type === 'Identifier' ? prop.key.name : null;
228
+ const key = prop.key.type === 'Identifier'
229
+ ? prop.key.name
230
+ : prop.key.type === 'StringLiteral'
231
+ ? prop.key.value
232
+ : null;
213
233
  if (!key)
214
234
  continue;
215
235
  switch (key) {
@@ -6,17 +6,26 @@
6
6
  * definePage 作为全局宏使用,无需导入,因此只需移除调用语句。
7
7
  */
8
8
  import type { GeneratorResult } from '@babel/generator';
9
- import type { PageOptions } from '../types.js';
9
+ import type { PageOptions } from '../types/index.js';
10
+ type Falsy = false | null | undefined | 0 | '';
11
+ /**
12
+ * 合并多个页面配置
13
+ *
14
+ * @param optionsList - 配置列表
15
+ * @returns 合并后的配置
16
+ */
17
+ export declare function mergePageOptions(...optionsList: (PageOptions | Falsy)[]): PageOptions;
10
18
  /**
11
19
  * 解析 definePage 宏配置
12
20
  *
13
21
  * definePage 作为全局宏使用,无需导入。
14
22
  * 直接查找代码中的 definePage 调用并提取配置。
15
23
  *
16
- * @param filePath - 页面文件路径
24
+ * @param content - 源代码
25
+ * @param filePath - 文件路径
17
26
  * @returns 解析出的页面配置,解析失败返回 null
18
27
  */
19
- export declare function parseDefinePage(filePath: string): PageOptions | null;
28
+ export declare function parseDefinePage(content: string, filePath: string): PageOptions | null;
20
29
  /**
21
30
  * 移除 definePage 宏调用(仅在构建模式下)
22
31
  *
@@ -28,3 +37,11 @@ export declare function parseDefinePage(filePath: string): PageOptions | null;
28
37
  * @returns 转换后的代码,无需转换返回 null
29
38
  */
30
39
  export declare function removeDefinePage(code: string, filename: string): GeneratorResult | null;
40
+ /**
41
+ * 判断两个页面配置是否相等
42
+ *
43
+ * @param a
44
+ * @param b
45
+ */
46
+ export declare function isEqualPageOptions(a?: PageOptions | null, b?: PageOptions | null): boolean;
47
+ export {};
@@ -5,8 +5,7 @@
5
5
  *
6
6
  * definePage 作为全局宏使用,无需导入,因此只需移除调用语句。
7
7
  */
8
- import fs from 'node:fs';
9
- import { babelGenerate, babelTraverse, parseCode, warn } from '../utils/index.js';
8
+ import { babelGenerate, babelTraverse, error, parseCode, warn } from '../utils/index.js';
10
9
  import { extractPageOptions } from './astValueExtractor.js';
11
10
  /**
12
11
  * 合并多个页面配置
@@ -14,23 +13,19 @@ import { extractPageOptions } from './astValueExtractor.js';
14
13
  * @param optionsList - 配置列表
15
14
  * @returns 合并后的配置
16
15
  */
17
- function mergePageOptions(optionsList) {
16
+ export function mergePageOptions(...optionsList) {
18
17
  const merged = {};
19
- // 遍历所有配置,按优先级合并
20
18
  for (const options of optionsList) {
21
- // 名称直接覆盖
19
+ if (!options)
20
+ continue;
22
21
  if (options.name)
23
22
  merged.name = options.name;
24
- // meta 合并
25
23
  if (options.meta)
26
24
  merged.meta = { ...merged.meta, ...options.meta };
27
- // pattern 合并
28
25
  if (options.pattern)
29
26
  merged.pattern = { ...merged.pattern, ...options.pattern };
30
- // 重定向直接覆盖
31
27
  if (options.redirect)
32
28
  merged.redirect = options.redirect;
33
- // 别名合并(支持字符串和数组形式)
34
29
  if (options.alias) {
35
30
  if (!merged.alias) {
36
31
  merged.alias = options.alias;
@@ -50,55 +45,41 @@ function mergePageOptions(optionsList) {
50
45
  * definePage 作为全局宏使用,无需导入。
51
46
  * 直接查找代码中的 definePage 调用并提取配置。
52
47
  *
53
- * @param filePath - 页面文件路径
48
+ * @param content - 源代码
49
+ * @param filePath - 文件路径
54
50
  * @returns 解析出的页面配置,解析失败返回 null
55
51
  */
56
- export function parseDefinePage(filePath) {
57
- // 检查文件是否存在
58
- if (!fs.existsSync(filePath)) {
59
- return null;
60
- }
61
- const content = fs.readFileSync(filePath, 'utf-8');
62
- // 快速检查是否包含 definePage 调用
52
+ export function parseDefinePage(content, filePath) {
63
53
  if (!content.includes('definePage')) {
64
54
  return null;
65
55
  }
66
56
  try {
67
- // 解析代码为 AST
68
57
  const ast = parseCode(content);
69
- const pageOptionsList = [];
70
- // 遍历 AST 查找 definePage 调用
58
+ const routeOptionsList = [];
71
59
  babelTraverse(ast, {
72
60
  CallExpression(nodePath) {
73
61
  const { node } = nodePath;
74
- // 检查是否是 definePage 调用
75
62
  if (node.callee.type !== 'Identifier' || node.callee.name !== 'definePage') {
76
63
  return;
77
64
  }
78
- // 检查参数是否为对象表达式
79
65
  const arg = node.arguments[0];
80
66
  if (!arg || arg.type !== 'ObjectExpression') {
81
67
  return;
82
68
  }
83
- // 提取页面配置
84
69
  const options = extractPageOptions(arg);
85
- pageOptionsList.push(options);
70
+ routeOptionsList.push(options);
86
71
  }
87
72
  });
88
- // 没有找到 definePage 调用
89
- if (pageOptionsList.length === 0) {
73
+ if (routeOptionsList.length === 0) {
90
74
  return null;
91
75
  }
92
- // 处理多个 definePage 调用的情况
93
- if (pageOptionsList.length > 1) {
94
- warn('检测到多个 definePage 调用,将合并所有配置', '建议每个文件只调用一次 definePage');
76
+ if (routeOptionsList.length > 1) {
77
+ warn('检测到多个 definePage 调用,将合并所有配置,建议每个文件只调用一次 definePage', `in ${filePath}`);
95
78
  }
96
- // 合并所有配置
97
- return mergePageOptions(pageOptionsList);
79
+ return mergePageOptions(...routeOptionsList);
98
80
  }
99
81
  catch (e) {
100
- // 解析失败,返回 null
101
- warn('解析 definePage 失败', `${filePath}: ${e?.toString()}`);
82
+ error(`解析 definePage 失败 in ${e}`, e);
102
83
  return null;
103
84
  }
104
85
  }
@@ -113,29 +94,22 @@ export function parseDefinePage(filePath) {
113
94
  * @returns 转换后的代码,无需转换返回 null
114
95
  */
115
96
  export function removeDefinePage(code, filename) {
116
- // 快速检查是否包含 definePage 调用
117
97
  if (!code.includes('definePage')) {
118
98
  return null;
119
99
  }
120
- // 解析代码为 AST
121
100
  const ast = parseCode(code);
122
101
  let hasDefinePage = false;
123
- // 遍历 AST 查找并移除 definePage 调用
124
102
  babelTraverse(ast, {
125
103
  CallExpression(nodePath) {
126
104
  const { node } = nodePath;
127
- // 检查是否是 definePage 调用
128
105
  if (node.callee.type === 'Identifier' && node.callee.name === 'definePage') {
129
106
  hasDefinePage = true;
130
- // 移除节点
131
107
  nodePath.remove();
132
108
  }
133
109
  }
134
110
  });
135
- // 没有找到 definePage 调用
136
111
  if (!hasDefinePage)
137
112
  return null;
138
- // 生成转换后的代码
139
113
  return babelGenerate(ast, {
140
114
  retainLines: false,
141
115
  compact: false,
@@ -144,3 +118,109 @@ export function removeDefinePage(code, filename) {
144
118
  sourceFileName: filename
145
119
  });
146
120
  }
121
+ /** 普通 Record<string, string> 比较 */
122
+ function recordEqual(a, b) {
123
+ if (a === b)
124
+ return true;
125
+ if (!a || !b)
126
+ return false;
127
+ const keys = Object.keys(a);
128
+ if (keys.length !== Object.keys(b).length)
129
+ return false;
130
+ for (let i = 0; i < keys.length; i++) {
131
+ const k = keys[i];
132
+ if (a[k] !== b[k])
133
+ return false;
134
+ }
135
+ return true;
136
+ }
137
+ /** RegExp 结构比较 */
138
+ function patternEqual(a, b) {
139
+ if (a === b)
140
+ return true;
141
+ if (!a || !b)
142
+ return false;
143
+ const keys = Object.keys(a);
144
+ if (keys.length !== Object.keys(b).length)
145
+ return false;
146
+ for (let i = 0; i < keys.length; i++) {
147
+ const k = keys[i];
148
+ const ra = a[k];
149
+ const rb = b[k];
150
+ if (!rb)
151
+ return false;
152
+ // 关键:比较正则内容 + flags
153
+ if (ra.source !== rb.source || ra.flags !== rb.flags) {
154
+ return false;
155
+ }
156
+ }
157
+ return true;
158
+ }
159
+ /** redirect: string | Record */
160
+ function redirectEqual(a, b) {
161
+ if (a === b)
162
+ return true;
163
+ if (!a || !b)
164
+ return false;
165
+ const ta = typeof a;
166
+ const tb = typeof b;
167
+ // 类型不同,直接不等
168
+ if (ta !== tb)
169
+ return false;
170
+ // string
171
+ if (ta === 'string') {
172
+ return a === b;
173
+ }
174
+ // RedirectConfig
175
+ const ra = a;
176
+ const rb = b;
177
+ if (ra.index !== rb.index)
178
+ return false;
179
+ if (!recordEqual(ra.query, rb.query))
180
+ return false;
181
+ return recordEqual(ra.params, rb.params);
182
+ }
183
+ /** alias: string | string[] */
184
+ function aliasEqual(a, b) {
185
+ if (a === b)
186
+ return true;
187
+ if (!a || !b)
188
+ return false;
189
+ const arrA = Array.isArray(a) ? a : [a];
190
+ const arrB = Array.isArray(b) ? b : [b];
191
+ const len = arrA.length;
192
+ if (len !== arrB.length)
193
+ return false;
194
+ // ⚠️ 默认:顺序敏感(更安全,避免隐藏 bug)
195
+ for (let i = 0; i < len; i++) {
196
+ if (arrA[i] !== arrB[i])
197
+ return false;
198
+ }
199
+ return true;
200
+ }
201
+ /**
202
+ * 判断两个页面配置是否相等
203
+ *
204
+ * @param a
205
+ * @param b
206
+ */
207
+ export function isEqualPageOptions(a, b) {
208
+ if (a === b)
209
+ return true;
210
+ if (!a || !b)
211
+ return false;
212
+ // name
213
+ if (a.name !== b.name)
214
+ return false;
215
+ // meta
216
+ if (!recordEqual(a.meta, b.meta))
217
+ return false;
218
+ // pattern (RegExp)
219
+ if (!patternEqual(a.pattern, b.pattern))
220
+ return false;
221
+ // redirect
222
+ if (!redirectEqual(a.redirect, b.redirect))
223
+ return false;
224
+ // alias
225
+ return aliasEqual(a.alias, b.alias);
226
+ }
@@ -1,27 +1,8 @@
1
- /**
2
- * 默认导出检测结果
3
- */
4
- export interface DefaultExportCheckResult {
5
- /** 是否有默认导出 */
6
- hasDefaultExport: boolean;
7
- /** 默认导出是否为函数或类 */
8
- isFunctionOrClass: boolean;
9
- /** 导出名称(如果有) */
10
- exportName: string | null;
11
- /** 警告信息 */
12
- warning: string | null;
13
- }
14
1
  /**
15
2
  * 检测文件是否具有有效的默认导出函数组件
16
3
  *
17
- * @param filePath - 文件路径
18
- * @returns 检测结果
19
- */
20
- export declare function checkDefaultExport(filePath: string): DefaultExportCheckResult;
21
- /**
22
- * 检查文件扩展名是否需要导出检测
23
- *
24
- * @param ext - 文件扩展名
25
- * @returns 是否需要检测
4
+ * @param content - 文件内容
5
+ * @param file - 文件路径
6
+ * @returns {boolean} 检测结果
26
7
  */
27
- export declare function shouldCheckExport(ext: string): boolean;
8
+ export declare function checkDefaultExport(content: string, file: string): boolean;
@@ -1,7 +1,4 @@
1
- import fs from 'node:fs';
2
- import { parseCode, babelTraverse } from '../utils/babelUtils.js';
3
- /** 需要进行默认导出检测的文件扩展名 */
4
- const CHECK_EXPORT_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'];
1
+ import { babelTraverse, debug, parseCode, warn } from '../utils/index.js';
5
2
  /**
6
3
  * 收集变量声明
7
4
  *
@@ -23,9 +20,6 @@ function createVariableDeclarationVisitor(variableDeclarations) {
23
20
  else if (node.init.type === 'FunctionExpression') {
24
21
  variableDeclarations.set(name, 'function');
25
22
  }
26
- else if (node.init.type === 'ClassExpression') {
27
- variableDeclarations.set(name, 'class');
28
- }
29
23
  else {
30
24
  variableDeclarations.set(name, 'unknown');
31
25
  }
@@ -37,12 +31,6 @@ function createVariableDeclarationVisitor(variableDeclarations) {
37
31
  if (node.id) {
38
32
  variableDeclarations.set(node.id.name, 'function');
39
33
  }
40
- },
41
- ClassDeclaration(nodePath) {
42
- const { node } = nodePath;
43
- if (node.id) {
44
- variableDeclarations.set(node.id.name, 'class');
45
- }
46
34
  }
47
35
  };
48
36
  }
@@ -62,16 +50,15 @@ function createExportDeclarationVisitor(result, variableDeclarations) {
62
50
  switch (declaration.type) {
63
51
  case 'FunctionDeclaration':
64
52
  case 'FunctionExpression':
53
+ case 'ArrowFunctionExpression':
65
54
  result.isFunction = true;
66
- result.exportName = declaration.id?.name || null;
67
55
  break;
68
- case 'ArrowFunctionExpression':
56
+ case 'CallExpression':
69
57
  result.isFunction = true;
70
58
  break;
71
59
  case 'Identifier':
72
- result.exportName = declaration.name;
73
60
  const varType = variableDeclarations.get(declaration.name);
74
- if (varType === 'function' || varType === 'arrow' || varType === 'class') {
61
+ if (varType === 'function' || varType === 'arrow') {
75
62
  result.isFunction = true;
76
63
  }
77
64
  break;
@@ -110,11 +97,6 @@ function processNamedExportDeclarationNode(declaration, variableDeclarations) {
110
97
  variableDeclarations.set(declaration.id.name, 'function');
111
98
  }
112
99
  }
113
- else if (declaration.type === 'ClassDeclaration') {
114
- if (declaration.id) {
115
- variableDeclarations.set(declaration.id.name, 'class');
116
- }
117
- }
118
100
  else if (declaration.type === 'VariableDeclaration') {
119
101
  for (const decl of declaration.declarations) {
120
102
  if (decl.id.type === 'Identifier') {
@@ -126,9 +108,6 @@ function processNamedExportDeclarationNode(declaration, variableDeclarations) {
126
108
  else if (decl.init.type === 'FunctionExpression') {
127
109
  variableDeclarations.set(name, 'function');
128
110
  }
129
- else if (decl.init.type === 'ClassExpression') {
130
- variableDeclarations.set(name, 'class');
131
- }
132
111
  }
133
112
  }
134
113
  }
@@ -149,9 +128,8 @@ function processExportSpecifiers(specifiers, result, variableDeclarations) {
149
128
  : specifier.exported.value;
150
129
  if (exportedName === 'default') {
151
130
  result.hasDefaultExport = true;
152
- result.exportName = specifier.local.name;
153
131
  const varType = variableDeclarations.get(specifier.local.name);
154
- if (varType === 'function' || varType === 'arrow' || varType === 'class') {
132
+ if (varType === 'function' || varType === 'arrow') {
155
133
  result.isFunction = true;
156
134
  }
157
135
  }
@@ -161,68 +139,49 @@ function processExportSpecifiers(specifiers, result, variableDeclarations) {
161
139
  /**
162
140
  * 检测文件是否具有有效的默认导出函数组件
163
141
  *
164
- * @param filePath - 文件路径
165
- * @returns 检测结果
142
+ * @param content - 文件内容
143
+ * @param file - 文件路径
144
+ * @returns {boolean} 检测结果
166
145
  */
167
- export function checkDefaultExport(filePath) {
168
- if (!fs.existsSync(filePath)) {
169
- return {
170
- hasDefaultExport: false,
171
- isFunctionOrClass: false,
172
- exportName: null,
173
- warning: `文件不存在: ${filePath}`
174
- };
175
- }
176
- const content = fs.readFileSync(filePath, 'utf-8');
177
- if (!content.includes('export')) {
178
- return {
179
- hasDefaultExport: false,
180
- isFunctionOrClass: false,
181
- exportName: null,
182
- warning: `未检测到默认导出 (default export),该文件将被跳过。请确保导出一个函数组件。`
183
- };
146
+ export function checkDefaultExport(content, file) {
147
+ // 快速检测,避免对文件进行复杂的解析
148
+ if (!content.includes('export default')) {
149
+ debug(`⚠️ 未检测到默认导出 (default export),该文件可能被跳过。请确保导出一个函数组件。`, `in ${file}`);
150
+ return false;
184
151
  }
152
+ // 解析文件内容为 AST 进行精确检查
153
+ const ast = parseCode(content);
154
+ const variableDeclarations = new Map();
155
+ const result = {
156
+ hasDefaultExport: false,
157
+ isFunction: false
158
+ };
185
159
  try {
186
- const ast = parseCode(content);
187
- const variableDeclarations = new Map();
188
- const result = {
189
- hasDefaultExport: false,
190
- isFunction: false,
191
- exportName: null
192
- };
193
160
  babelTraverse(ast, {
194
161
  ...createVariableDeclarationVisitor(variableDeclarations),
195
- ...createExportDeclarationVisitor(result, variableDeclarations)
162
+ ...createExportDeclarationVisitor(result, variableDeclarations),
163
+ CallExpression(nodePath) {
164
+ const { node } = nodePath;
165
+ if (node.callee.type === 'Identifier' && node.callee.name === 'console') {
166
+ nodePath.remove();
167
+ }
168
+ }
196
169
  });
197
- let warning = null;
170
+ let debugInfo = null;
198
171
  if (!result.hasDefaultExport) {
199
- warning = `未检测到默认导出 (default export),该文件将被跳过。请确保导出一个函数组件。`;
172
+ debugInfo = `⚠️ 未检测到默认导出 (default export),该文件可能被跳过。请确保导出一个函数组件。`;
200
173
  }
201
174
  else if (!result.isFunction) {
202
- warning = `默认导出不是函数或类,该文件将被跳过。请确保导出一个有效的 React 组件。`;
175
+ debugInfo = `⚠️ 默认导出的不是函数,该文件可能被跳过。请确保导出一个有效的函数组件。`;
203
176
  }
204
- return {
205
- hasDefaultExport: result.hasDefaultExport,
206
- isFunctionOrClass: result.isFunction,
207
- exportName: result.exportName,
208
- warning
209
- };
177
+ if (debugInfo) {
178
+ debug(debugInfo, `in ${file}`);
179
+ return false;
180
+ }
181
+ return true;
210
182
  }
211
- catch (error) {
212
- return {
213
- hasDefaultExport: false,
214
- isFunctionOrClass: false,
215
- exportName: null,
216
- warning: `解析文件失败: ${error instanceof Error ? error.message : String(error)}`
217
- };
183
+ catch (e) {
184
+ warn(`⚠️ 无法解析文件 ${file}`, `${e instanceof Error ? e.message : String(e)}`);
185
+ return false;
218
186
  }
219
187
  }
220
- /**
221
- * 检查文件扩展名是否需要导出检测
222
- *
223
- * @param ext - 文件扩展名
224
- * @returns 是否需要检测
225
- */
226
- export function shouldCheckExport(ext) {
227
- return CHECK_EXPORT_EXTENSIONS.includes(ext);
228
- }
@@ -0,0 +1,25 @@
1
+ export interface FilterOptions {
2
+ dir: string;
3
+ include: readonly string[];
4
+ exclude: readonly string[];
5
+ }
6
+ /**
7
+ * 检查文件是否为页面文件(单目录版本)
8
+ *
9
+ * 支持 glob 模式匹配包含和排除规则。
10
+ *
11
+ * @param file - 文件路径
12
+ * @param options - 页面目录配置
13
+ * @returns 如果是有效的页面文件返回 true
14
+ */
15
+ export declare function isPageFile(file: string, options: FilterOptions): boolean;
16
+ /**
17
+ * 检查文件是否为页面文件(多目录版本)
18
+ *
19
+ * 遍历所有页面目录配置,检查文件是否属于任一目录。
20
+ *
21
+ * @param file - 文件路径
22
+ * @param pages - 页面目录配置列表
23
+ * @returns 如果是有效的页面文件返回 true
24
+ */
25
+ export declare function isPageFileInDirs(file: string, pages: readonly FilterOptions[]): FilterOptions | false;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @fileoverview 文件过滤工具模块
3
+ *
4
+ * 提供基于 glob 模式的文件包含/排除过滤功能。
5
+ * 从 scanPages.ts 中提取,消除重复代码。
6
+ */
7
+ import micromatch from 'micromatch';
8
+ import path from 'node:path';
9
+ import { normalizePathSeparator } from '../utils/index.js';
10
+ /**
11
+ * 检查文件是否为页面文件(单目录版本)
12
+ *
13
+ * 支持 glob 模式匹配包含和排除规则。
14
+ *
15
+ * @param file - 文件路径
16
+ * @param options - 页面目录配置
17
+ * @returns 如果是有效的页面文件返回 true
18
+ */
19
+ export function isPageFile(file, options) {
20
+ const { dir, include, exclude } = options;
21
+ if (!file.startsWith(dir))
22
+ return false;
23
+ const normalizedPath = normalizePathSeparator(path.relative(dir, file));
24
+ if (include.length === 0)
25
+ return true;
26
+ return micromatch.isMatch(normalizedPath, include, { dot: true, noext: true, ignore: exclude });
27
+ }
28
+ /**
29
+ * 检查文件是否为页面文件(多目录版本)
30
+ *
31
+ * 遍历所有页面目录配置,检查文件是否属于任一目录。
32
+ *
33
+ * @param file - 文件路径
34
+ * @param pages - 页面目录配置列表
35
+ * @returns 如果是有效的页面文件返回 true
36
+ */
37
+ export function isPageFileInDirs(file, pages) {
38
+ for (const page of pages) {
39
+ if (isPageFile(file, page))
40
+ return page;
41
+ }
42
+ return false;
43
+ }
@@ -3,4 +3,5 @@
3
3
  *
4
4
  * 导出所有解析相关功能,提供统一的访问入口。
5
5
  */
6
- export * from './parsePage.js';
6
+ export * from './exportChecker.js';
7
+ export * from './filterUtils.js';
@@ -3,4 +3,5 @@
3
3
  *
4
4
  * 导出所有解析相关功能,提供统一的访问入口。
5
5
  */
6
- export * from './parsePage.js';
6
+ export * from './exportChecker.js';
7
+ export * from './filterUtils.js';