vitarx-router 4.0.2 → 4.0.3

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.
@@ -6,10 +6,22 @@
6
6
  */
7
7
  import type * as BabelTypes from '@babel/types';
8
8
  import type { PageOptions } from '../types/index.js';
9
+ /**
10
+ * 不支持节点的警告信息
11
+ */
12
+ export interface UnsupportedNodeWarning {
13
+ /** 不支持的 AST 节点类型 */
14
+ nodeType: string;
15
+ /** 属性访问路径,如 "meta.nav.items[0].label" */
16
+ path: string;
17
+ /** 修复建议 */
18
+ suggestion: string;
19
+ }
9
20
  /**
10
21
  * 从对象表达式提取页面配置
11
22
  *
12
23
  * @param node - 对象表达式节点
13
- * @returns 页面配置
24
+ * @param warnings - 警告收集器(可选,不传则不收集警告)
25
+ * @returns 页面配置和警告列表
14
26
  */
15
- export declare function extractPageOptions(node: BabelTypes.ObjectExpression): PageOptions;
27
+ export declare function extractPageOptions(node: BabelTypes.ObjectExpression, warnings?: UnsupportedNodeWarning[]): PageOptions;
@@ -1,12 +1,46 @@
1
+ /**
2
+ * 不支持节点类型到修复建议的映射
3
+ */
4
+ const UNSUPPORTED_NODE_SUGGESTIONS = {
5
+ Identifier: '不支持变量引用,请使用字面量值。例如将 { name: myVar } 改为 { name: "value" }',
6
+ MemberExpression: '不支持属性访问表达式,请使用字面量值。例如将 { api: config.url } 改为 { api: "https://example.com" }',
7
+ CallExpression: '不支持函数调用,请使用字面量值。例如将 { id: genId() } 改为 { id: "static-id" }',
8
+ ArrowFunctionExpression: '不支持箭头函数,请使用字面量值。例如将 { handler: () => {} } 改为 { handler: "handlerName" }',
9
+ FunctionExpression: '不支持函数表达式,请使用字面量值。例如将 { handler: function() {} } 改为 { handler: "handlerName" }',
10
+ NewExpression: '不支持 new 表达式(除 new RegExp 外),请使用字面量值',
11
+ SpreadElement: '不支持展开运算符,请显式列出所有属性',
12
+ ConditionalExpression: '不支持三元表达式,请使用字面量值。例如将 { val: cond ? "a" : "b" } 改为 { val: "a" }',
13
+ BinaryExpression: '不支持二元运算表达式,请使用字面量值。例如将 { val: 1 + 2 } 改为 { val: 3 }',
14
+ LogicalExpression: '不支持逻辑运算表达式,请使用字面量值。例如将 { val: a || "default" } 改为 { val: "default" }',
15
+ SequenceExpression: '不支持逗号表达式,请使用字面量值',
16
+ AssignmentExpression: '不支持赋值表达式,请使用字面量值',
17
+ UpdateExpression: '不支持更新表达式,请使用字面量值。例如将 { val: i++ } 改为 { val: 1 }',
18
+ TaggedTemplateExpression: '不支持标签模板字符串,请使用普通字符串字面量',
19
+ ClassExpression: '不支持类表达式,请使用字面量值',
20
+ ObjectMethod: '不支持对象方法定义,请使用字面量值。例如将 { fn() {} } 改为 { fn: "methodName" }'
21
+ };
22
+ /**
23
+ * 获取不支持节点的修复建议
24
+ *
25
+ * @param nodeType - AST 节点类型
26
+ * @returns 修复建议
27
+ */
28
+ function getSuggestion(nodeType) {
29
+ return (UNSUPPORTED_NODE_SUGGESTIONS[nodeType] ??
30
+ `不支持 ${nodeType} 类型的表达式,definePage 配置仅支持静态字面量值(字符串、数字、布尔、null、对象、数组、正则、一元表达式、简单模板字符串)`);
31
+ }
1
32
  /**
2
33
  * 提取字面量值
3
34
  *
4
35
  * 从 AST 节点提取 JavaScript 字面量值。
36
+ * 遇到不支持的节点类型时,将警告信息收集到 warnings 数组中。
5
37
  *
6
38
  * @param node - AST 节点
39
+ * @param currentPath - 当前属性访问路径
40
+ * @param warnings - 警告收集器
7
41
  * @returns 提取的值
8
42
  */
9
- function extractAstLiteralValue(node) {
43
+ function extractAstLiteralValue(node, currentPath, warnings) {
10
44
  switch (node.type) {
11
45
  case 'StringLiteral':
12
46
  return node.value;
@@ -16,11 +50,57 @@ function extractAstLiteralValue(node) {
16
50
  return node.value;
17
51
  case 'NullLiteral':
18
52
  return null;
53
+ case 'RegExpLiteral':
54
+ return new RegExp(node.pattern, node.flags || '');
55
+ case 'TemplateLiteral': {
56
+ // 仅支持无表达式的简单模板字符串,如 `hello`
57
+ if (node.quasis.length === 1 && node.expressions.length === 0) {
58
+ return node.quasis[0].value.cooked;
59
+ }
60
+ warnings.push({
61
+ nodeType: node.type,
62
+ path: currentPath,
63
+ suggestion: '不支持带插值表达式的模板字符串,请使用纯字符串字面量。例如将 `Hello ${name}` 改为 "Hello World"'
64
+ });
65
+ return undefined;
66
+ }
67
+ case 'UnaryExpression': {
68
+ const arg = extractAstLiteralValue(node.argument, currentPath, warnings);
69
+ if (arg === undefined || arg === null)
70
+ return undefined;
71
+ switch (node.operator) {
72
+ case '-':
73
+ return -arg;
74
+ case '+':
75
+ return +arg;
76
+ case '!':
77
+ return !arg;
78
+ case '~':
79
+ return ~arg;
80
+ case 'void':
81
+ return undefined;
82
+ default:
83
+ warnings.push({
84
+ nodeType: node.type,
85
+ path: currentPath,
86
+ suggestion: `不支持 "${node.operator}" 一元运算符,仅支持 -, +, !, ~, void`
87
+ });
88
+ return undefined;
89
+ }
90
+ }
19
91
  case 'ArrayExpression':
20
- return node.elements.map(elem => (elem ? extractAstLiteralValue(elem) : null));
92
+ return node.elements.map((elem, index) => elem ? extractAstLiteralValue(elem, `${currentPath}[${index}]`, warnings) : null);
21
93
  case 'ObjectExpression': {
22
94
  const obj = {};
23
95
  for (const prop of node.properties) {
96
+ if (prop.type === 'ObjectMethod') {
97
+ warnings.push({
98
+ nodeType: 'ObjectMethod',
99
+ path: currentPath,
100
+ suggestion: getSuggestion('ObjectMethod')
101
+ });
102
+ continue;
103
+ }
24
104
  if (prop.type !== 'ObjectProperty')
25
105
  continue;
26
106
  const key = prop.key.type === 'Identifier'
@@ -29,13 +109,19 @@ function extractAstLiteralValue(node) {
29
109
  ? prop.key.value
30
110
  : null;
31
111
  if (key) {
32
- obj[key] = extractAstLiteralValue(prop.value);
112
+ obj[key] = extractAstLiteralValue(prop.value, `${currentPath}.${key}`, warnings);
33
113
  }
34
114
  }
35
115
  return obj;
36
116
  }
37
- default:
117
+ default: {
118
+ warnings.push({
119
+ nodeType: node.type,
120
+ path: currentPath,
121
+ suggestion: getSuggestion(node.type)
122
+ });
38
123
  return undefined;
124
+ }
39
125
  }
40
126
  }
41
127
  /**
@@ -68,9 +154,11 @@ function extractStringRecord(node) {
68
154
  * 提取 meta 值
69
155
  *
70
156
  * @param node - AST 节点
157
+ * @param currentPath - 当前属性访问路径
158
+ * @param warnings - 警告收集器
71
159
  * @returns meta 对象
72
160
  */
73
- function extractMetaValue(node) {
161
+ function extractMetaValue(node, currentPath, warnings) {
74
162
  if (node.type !== 'ObjectExpression')
75
163
  return undefined;
76
164
  const meta = {};
@@ -84,7 +172,7 @@ function extractMetaValue(node) {
84
172
  : null;
85
173
  if (!key)
86
174
  continue;
87
- meta[key] = extractAstLiteralValue(prop.value);
175
+ meta[key] = extractAstLiteralValue(prop.value, `${currentPath}.${key}`, warnings);
88
176
  }
89
177
  return meta;
90
178
  }
@@ -222,10 +310,12 @@ function extractAliasValue(node) {
222
310
  * 从对象表达式提取页面配置
223
311
  *
224
312
  * @param node - 对象表达式节点
225
- * @returns 页面配置
313
+ * @param warnings - 警告收集器(可选,不传则不收集警告)
314
+ * @returns 页面配置和警告列表
226
315
  */
227
- export function extractPageOptions(node) {
316
+ export function extractPageOptions(node, warnings) {
228
317
  const options = {};
318
+ const warnList = warnings ?? [];
229
319
  for (const prop of node.properties) {
230
320
  if (prop.type !== 'ObjectProperty')
231
321
  continue;
@@ -243,7 +333,7 @@ export function extractPageOptions(node) {
243
333
  }
244
334
  break;
245
335
  case 'meta':
246
- options.meta = extractMetaValue(prop.value);
336
+ options.meta = extractMetaValue(prop.value, 'meta', warnList);
247
337
  break;
248
338
  case 'pattern':
249
339
  options.pattern = extractPatternValue(prop.value);
@@ -56,6 +56,7 @@ export function parseDefinePage(content, filePath) {
56
56
  try {
57
57
  const ast = parseCode(content);
58
58
  const routeOptionsList = [];
59
+ const warnings = [];
59
60
  babelTraverse(ast, {
60
61
  CallExpression(nodePath) {
61
62
  const { node } = nodePath;
@@ -66,7 +67,7 @@ export function parseDefinePage(content, filePath) {
66
67
  if (!arg || arg.type !== 'ObjectExpression') {
67
68
  return;
68
69
  }
69
- const options = extractPageOptions(arg);
70
+ const options = extractPageOptions(arg, warnings);
70
71
  routeOptionsList.push(options);
71
72
  }
72
73
  });
@@ -76,6 +77,10 @@ export function parseDefinePage(content, filePath) {
76
77
  if (routeOptionsList.length > 1) {
77
78
  warn('检测到多个 definePage 调用,将合并所有配置,建议每个文件只调用一次 definePage', `in ${filePath}`);
78
79
  }
80
+ // 输出不支持节点的警告
81
+ for (const w of warnings) {
82
+ warn(`definePage 配置解析警告: 路径 "${w.path}" 处遇到不支持的 ${w.nodeType} 节点,该属性值将被忽略。${w.suggestion}`, `in ${filePath}`);
83
+ }
79
84
  return mergePageOptions(...routeOptionsList);
80
85
  }
81
86
  catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitarx-router",
3
- "version": "4.0.2",
3
+ "version": "4.0.3",
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",