@vitarx/plugin-vite 1.0.4 → 1.0.5

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.
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import * as t from '@babel/types';
7
7
  import { TransformContext } from '../../context.js';
8
- import type { DirectiveInfo } from '../../passes/props/types.js';
8
+ import type { DirectiveInfo } from '../props/types.js';
9
9
  /**
10
10
  * 处理指令
11
11
  * 将指令转换为 withDirectives 调用
@@ -52,16 +52,21 @@ ctx // 转换上下文,包含别名、导入等信息
52
52
  const properties = []; // 定义对象属性数组,可以是属性或方法
53
53
  // 添加 value 属性
54
54
  if (isIdentifier(value)) {
55
- // 如果值是一个标识符
56
- markImport(ctx, 'unref'); // 标记需要导入unref函数
57
- const unrefAlias = getAlias(ctx.vitarxAliases, 'unref'); // 获取unref函数的别名
58
- properties.push(
59
- // 添加一个getter方法作为属性
60
- t.objectMethod('get', // 方法类型为getter
61
- t.identifier('value'), // 属性名为value
62
- [], // 无参数
63
- t.blockStatement([t.returnStatement(t.callExpression(t.identifier(unrefAlias), [value]))]) // 返回unref(value)
64
- ));
55
+ // 三态决策:已知 ref → .value,已知非 ref → 原样,未知 → unref()
56
+ if (ctx.refVariables.has(value.name)) {
57
+ // 已知 ref:直接使用 .value
58
+ properties.push(t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(t.memberExpression(value, t.identifier('value')))])));
59
+ }
60
+ else if (ctx.nonRefVariables.has(value.name)) {
61
+ // 已知非 ref(函数等):原样返回,无需 unref
62
+ properties.push(t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(value)])));
63
+ }
64
+ else {
65
+ // 未知标识符:使用 unref() 解包(非 ref 原样返回)
66
+ markImport(ctx, 'unref');
67
+ const unrefAlias = getAlias(ctx.vitarxAliases, 'unref');
68
+ properties.push(t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(t.callExpression(t.identifier(unrefAlias), [value]))])));
69
+ }
65
70
  }
66
71
  else {
67
72
  // 如果值不是标识符
@@ -28,7 +28,7 @@ export declare function createWrappedComponentHMRStatements(componentName: strin
28
28
  export declare function collectLocalVariableNames(functionBody: t.BlockStatement): string[];
29
29
  /**
30
30
  * 为函数体内的变量声明注入状态恢复代码
31
- * 遍历函数体中的所有变量声明语句
31
+ * 递归遍历所有变量声明(包括 if/for/while/switch/try 嵌套块)
32
32
  * @param functionBody - 函数体语句块
33
33
  */
34
34
  export declare function injectStatePreservation(functionBody: t.BlockStatement): void;
@@ -62,94 +62,79 @@ export function createWrappedComponentHMRStatements(componentName) {
62
62
  */
63
63
  export function collectLocalVariableNames(functionBody) {
64
64
  const variableNames = new Set();
65
- collectFromBlock(functionBody, variableNames);
65
+ forEachVariableDeclarator(functionBody, decl => {
66
+ if (decl.id.type !== 'VoidPattern') {
67
+ collectPatternBindings(decl.id, variableNames);
68
+ }
69
+ });
66
70
  return Array.from(variableNames);
67
71
  }
68
72
  /**
69
- * 从语句块中收集变量名
73
+ * 遍历语句块中的所有变量声明,对每个 VariableDeclarator 执行回调
74
+ * 递归处理嵌套的控制流语句(if/for/while/switch/try 等)
70
75
  * @param block - 语句块节点
71
- * @param variableNames - 存储变量名的集合
76
+ * @param visitor - 变量声明访问者回调
72
77
  */
73
- function collectFromBlock(block, variableNames) {
74
- // 遍历语句块中的每个语句
78
+ function forEachVariableDeclarator(block, visitor) {
75
79
  for (const stmt of block.body) {
76
- collectFromStatement(stmt, variableNames);
80
+ traverseStatementForDeclarators(stmt, visitor);
77
81
  }
78
82
  }
79
83
  /**
80
- * 从单个语句中收集变量名
84
+ * 从单个语句中递归查找变量声明并执行回调
81
85
  * 支持多种语句类型:变量声明、条件语句、循环语句、switch、try-catch 等
82
86
  * @param stmt - 语句节点
83
- * @param variableNames - 存储变量名的集合
87
+ * @param visitor - 变量声明访问者回调
84
88
  */
85
- function collectFromStatement(stmt, variableNames) {
86
- // 处理变量声明语句:const/let/var
89
+ function traverseStatementForDeclarators(stmt, visitor) {
87
90
  if (stmt.type === 'VariableDeclaration') {
88
91
  for (const decl of stmt.declarations) {
89
- // 跳过 void 模式(如 void 0)
90
- if (decl.id.type !== 'VoidPattern') {
91
- collectPatternBindings(decl.id, variableNames);
92
- }
92
+ visitor(decl);
93
93
  }
94
94
  }
95
95
  else if (stmt.type === 'IfStatement') {
96
- // 递归处理 if 语句的两个分支
97
- if (stmt.consequent.type === 'BlockStatement') {
98
- collectFromBlock(stmt.consequent, variableNames);
99
- }
100
- else {
101
- collectFromStatement(stmt.consequent, variableNames);
102
- }
96
+ traverseBodyForDeclarators(stmt.consequent, visitor);
103
97
  if (stmt.alternate) {
104
- if (stmt.alternate.type === 'BlockStatement') {
105
- collectFromBlock(stmt.alternate, variableNames);
106
- }
107
- else {
108
- collectFromStatement(stmt.alternate, variableNames);
109
- }
98
+ traverseBodyForDeclarators(stmt.alternate, visitor);
110
99
  }
111
100
  }
112
101
  else if (stmt.type === 'ForStatement' || stmt.type === 'WhileStatement' || stmt.type === 'DoWhileStatement') {
113
- // 处理 for/while/do-while 循环
114
- if (stmt.body.type === 'BlockStatement') {
115
- collectFromBlock(stmt.body, variableNames);
116
- }
117
- else {
118
- collectFromStatement(stmt.body, variableNames);
119
- }
102
+ traverseBodyForDeclarators(stmt.body, visitor);
120
103
  }
121
104
  else if (stmt.type === 'ForInStatement' || stmt.type === 'ForOfStatement') {
122
- // 处理 for...in/for...of 循环
123
- if (stmt.body.type === 'BlockStatement') {
124
- collectFromBlock(stmt.body, variableNames);
125
- }
126
- else {
127
- collectFromStatement(stmt.body, variableNames);
128
- }
105
+ traverseBodyForDeclarators(stmt.body, visitor);
129
106
  }
130
107
  else if (stmt.type === 'BlockStatement') {
131
- // 处理嵌套语句块
132
- collectFromBlock(stmt, variableNames);
108
+ forEachVariableDeclarator(stmt, visitor);
133
109
  }
134
110
  else if (stmt.type === 'SwitchStatement') {
135
- // 处理 switch 语句,遍历所有 case
136
111
  for (const c of stmt.cases) {
137
112
  for (const s of c.consequent) {
138
- collectFromStatement(s, variableNames);
113
+ traverseStatementForDeclarators(s, visitor);
139
114
  }
140
115
  }
141
116
  }
142
117
  else if (stmt.type === 'TryStatement') {
143
- // 处理 try-catch-finally
144
- collectFromBlock(stmt.block, variableNames);
118
+ forEachVariableDeclarator(stmt.block, visitor);
145
119
  if (stmt.handler) {
146
- collectFromBlock(stmt.handler.body, variableNames);
120
+ forEachVariableDeclarator(stmt.handler.body, visitor);
147
121
  }
148
122
  if (stmt.finalizer) {
149
- collectFromBlock(stmt.finalizer, variableNames);
123
+ forEachVariableDeclarator(stmt.finalizer, visitor);
150
124
  }
151
125
  }
152
126
  }
127
+ /**
128
+ * 遍历语句体(可能是 BlockStatement 或单条语句)中的变量声明
129
+ */
130
+ function traverseBodyForDeclarators(body, visitor) {
131
+ if (body.type === 'BlockStatement') {
132
+ forEachVariableDeclarator(body, visitor);
133
+ }
134
+ else {
135
+ traverseStatementForDeclarators(body, visitor);
136
+ }
137
+ }
153
138
  /**
154
139
  * 判断表达式是否为函数类型
155
140
  * 函数类型不需要状态恢复(函数定义本身不会改变)
@@ -197,15 +182,11 @@ function injectStatePreservationForDeclaration(decl) {
197
182
  }
198
183
  /**
199
184
  * 为函数体内的变量声明注入状态恢复代码
200
- * 遍历函数体中的所有变量声明语句
185
+ * 递归遍历所有变量声明(包括 if/for/while/switch/try 嵌套块)
201
186
  * @param functionBody - 函数体语句块
202
187
  */
203
188
  export function injectStatePreservation(functionBody) {
204
- for (const stmt of functionBody.body) {
205
- if (stmt.type === 'VariableDeclaration') {
206
- for (const decl of stmt.declarations) {
207
- injectStatePreservationForDeclaration(decl);
208
- }
209
- }
210
- }
189
+ forEachVariableDeclarator(functionBody, decl => {
190
+ injectStatePreservationForDeclaration(decl);
191
+ });
211
192
  }
@@ -23,6 +23,7 @@ export declare function collectRefVariables(program: t.Program, refApiAliases: R
23
23
  /**
24
24
  * 收集确定不是 ref 的变量
25
25
  * 包括函数声明、箭头函数和函数表达式变量(这些不可能是 ref)
26
+ * 静态分析能明确变量类型时,跳过 unref 包装以提升性能
26
27
  * @param program - AST Program 节点
27
28
  * @returns 非 ref 变量名集合
28
29
  */
@@ -1,5 +1,5 @@
1
1
  import { REF_APIS, RESPONSIVE_MODULES } from '../../constants/index.js';
2
- import { collectPatternBindings, collectObjectPatternBindings } from '../../utils/index.js';
2
+ import { collectObjectPatternBindings, collectPatternBindings } from '../../utils/index.js';
3
3
  /**
4
4
  * 收集 ref API 的别名
5
5
  * 识别从 vitarx 或 @vitarx/responsive 导入的 ref/toRef/toRefs/shallowRef/computed
@@ -76,6 +76,15 @@ function collectRefVariablesFromNode(node, refVariables, refApiLocalNames, toRef
76
76
  if (decl.id.type === 'VoidPattern')
77
77
  continue;
78
78
  const init = decl.init;
79
+ // 如果 init 是箭头函数或函数表达式,递归处理其函数体内的 ref 声明
80
+ // 确保组件函数(如 const App = () => { const count = ref(0) })内的 ref 被正确收集
81
+ if ((init.type === 'ArrowFunctionExpression' || init.type === 'FunctionExpression') &&
82
+ init.body.type === 'BlockStatement') {
83
+ for (const stmt of init.body.body) {
84
+ collectRefVariablesFromNode(stmt, refVariables, refApiLocalNames, toRefsLocalNames);
85
+ }
86
+ continue;
87
+ }
79
88
  // 检查是否为函数调用表达式
80
89
  if (init.type !== 'CallExpression' || init.callee.type !== 'Identifier') {
81
90
  continue;
@@ -102,24 +111,32 @@ function collectRefVariablesFromNode(node, refVariables, refApiLocalNames, toRef
102
111
  collectRefVariablesFromNode(node.declaration, refVariables, refApiLocalNames, toRefsLocalNames);
103
112
  }
104
113
  else if (node.type === 'ExportDefaultDeclaration') {
105
- // 处理默认导出
114
+ // 处理默认导出(支持函数声明、箭头函数、函数表达式)
106
115
  const decl = node.declaration;
107
116
  if (decl.type === 'FunctionDeclaration' && decl.body) {
108
117
  for (const stmt of decl.body.body) {
109
118
  collectRefVariablesFromNode(stmt, refVariables, refApiLocalNames, toRefsLocalNames);
110
119
  }
111
120
  }
121
+ else if ((decl.type === 'ArrowFunctionExpression' || decl.type === 'FunctionExpression') &&
122
+ decl.body.type === 'BlockStatement') {
123
+ for (const stmt of decl.body.body) {
124
+ collectRefVariablesFromNode(stmt, refVariables, refApiLocalNames, toRefsLocalNames);
125
+ }
126
+ }
112
127
  }
128
+ // 控制流语句(if/for/while/switch/try)中的变量声明不递归收集
129
+ // 函数控制流中通常不会声明 ref 变量,仅收集模块顶层和函数体顶层
113
130
  }
114
131
  /**
115
132
  * 收集确定不是 ref 的变量
116
133
  * 包括函数声明、箭头函数和函数表达式变量(这些不可能是 ref)
134
+ * 静态分析能明确变量类型时,跳过 unref 包装以提升性能
117
135
  * @param program - AST Program 节点
118
136
  * @returns 非 ref 变量名集合
119
137
  */
120
138
  export function collectNonRefVariables(program) {
121
139
  const nonRefVariables = new Set();
122
- // 遍历程序体中的所有节点
123
140
  for (const node of program.body) {
124
141
  collectNonRefFromNode(node, nonRefVariables);
125
142
  }
@@ -127,6 +144,7 @@ export function collectNonRefVariables(program) {
127
144
  }
128
145
  /**
129
146
  * 从节点中递归收集非 ref 变量
147
+ * 仅处理模块顶层和函数体顶层的声明,不递归控制流语句
130
148
  * @param node - AST 节点
131
149
  * @param nonRefVariables - 存储非 ref 变量名的集合
132
150
  */
@@ -134,7 +152,6 @@ function collectNonRefFromNode(node, nonRefVariables) {
134
152
  // 函数声明一定不是 ref
135
153
  if (node.type === 'FunctionDeclaration' && node.id) {
136
154
  nonRefVariables.add(node.id.name);
137
- // 递归处理函数体内的变量
138
155
  if (node.body) {
139
156
  for (const stmt of node.body.body) {
140
157
  collectNonRefFromNode(stmt, nonRefVariables);
@@ -142,33 +159,49 @@ function collectNonRefFromNode(node, nonRefVariables) {
142
159
  }
143
160
  }
144
161
  else if (node.type === 'VariableDeclaration') {
145
- // 变量声明:只有箭头函数或函数表达式才是非 ref
162
+ // 变量声明:箭头函数或函数表达式赋值一定不是 ref
146
163
  for (const decl of node.declarations) {
147
164
  if (!decl.init)
148
165
  continue;
149
- // 只处理简单标识符(不处理解构)
150
- if (decl.id.type !== 'Identifier')
151
- continue;
152
166
  const init = decl.init;
153
- if (init.type === 'ArrowFunctionExpression' ||
154
- init.type === 'FunctionExpression') {
155
- nonRefVariables.add(decl.id.name);
167
+ if (init.type === 'ArrowFunctionExpression' || init.type === 'FunctionExpression') {
168
+ if (decl.id.type === 'Identifier') {
169
+ nonRefVariables.add(decl.id.name);
170
+ }
171
+ // 递归处理函数体内的变量声明
172
+ if (init.body.type === 'BlockStatement') {
173
+ for (const stmt of init.body.body) {
174
+ collectNonRefFromNode(stmt, nonRefVariables);
175
+ }
176
+ }
156
177
  }
157
178
  }
158
179
  }
159
- else if (node.type === 'ExportNamedDeclaration') {
160
- // 处理命名导出的声明
161
- if (node.declaration) {
162
- collectNonRefFromNode(node.declaration, nonRefVariables);
163
- }
180
+ else if (node.type === 'ExportNamedDeclaration' && node.declaration) {
181
+ collectNonRefFromNode(node.declaration, nonRefVariables);
164
182
  }
165
183
  else if (node.type === 'ExportDefaultDeclaration') {
166
- // 处理默认导出
167
184
  const decl = node.declaration;
168
- if (decl.type === 'FunctionDeclaration' && decl.id) {
169
- nonRefVariables.add(decl.id.name);
185
+ if (decl.type === 'FunctionDeclaration') {
186
+ if (decl.id) {
187
+ nonRefVariables.add(decl.id.name);
188
+ }
189
+ // 递归处理函数体,收集内部非 ref 变量(与 collectRefVariablesFromNode 一致)
190
+ if (decl.body) {
191
+ for (const stmt of decl.body.body) {
192
+ collectNonRefFromNode(stmt, nonRefVariables);
193
+ }
194
+ }
195
+ }
196
+ else if ((decl.type === 'ArrowFunctionExpression' || decl.type === 'FunctionExpression') &&
197
+ decl.body.type === 'BlockStatement') {
198
+ // 处理未被转换的箭头函数/函数表达式默认导出
199
+ for (const stmt of decl.body.body) {
200
+ collectNonRefFromNode(stmt, nonRefVariables);
201
+ }
170
202
  }
171
203
  }
204
+ // 控制流语句(if/for/while/switch/try)中的变量声明不递归收集
172
205
  }
173
206
  /**
174
207
  * 构建 ref API 的本地名称集合
@@ -1,7 +1,24 @@
1
1
  import * as t from '@babel/types';
2
+ import { isJSXElement } from '@babel/types';
2
3
  import { markImport } from '../../context.js';
3
- import { addPureComment, createCreateViewCall, filterWhitespaceChildren, getAlias, getDevLocInfo, validateNoDirectMatchChild } from '../../utils/index.js';
4
+ import { createError } from '../../error.js';
5
+ import { addPureComment, createCreateViewCall, filterWhitespaceChildren, getAlias, getDevLocInfo, getJSXElementName } from '../../utils/index.js';
4
6
  import { processChildren } from './processChildren.js';
7
+ /**
8
+ * 校验子节点中不能直接包含 Match 组件
9
+ * Match 必须在 Switch 内使用,非 Switch 元素的子节点中不允许出现 Match
10
+ * @param children - 子节点数组
11
+ */
12
+ function validateNoDirectMatchChild(children) {
13
+ for (const child of children) {
14
+ if (isJSXElement(child)) {
15
+ const childName = getJSXElementName(child);
16
+ if (childName === 'Match') {
17
+ throw createError('E012', child);
18
+ }
19
+ }
20
+ }
21
+ }
5
22
  /**
6
23
  * 处理 JSX Fragment
7
24
  * @param path - JSX Fragment 路径
@@ -32,6 +32,9 @@ export declare function getAttributeValue(value: t.JSXAttribute['value']): t.Exp
32
32
  *
33
33
  * 特殊处理:
34
34
  * - children 属性不使用 unref,因为 ref 作为 children 是合法的可变渲染源
35
+ * - ref 属性不使用 unref/.value,因为 ref 绑定需要 ref 对象本身
36
+ * 运行时 resolveProps() 通过 isRef() 判定 ref 属性值,若此处解包,
37
+ * popProperty 取到的是解包后的值(如 null),isRef 判定失败导致绑定失效
35
38
  * - 其他标识符属性使用 unref 解包
36
39
  *
37
40
  * @param key - 属性名
@@ -87,6 +87,9 @@ export function getAttributeValue(value) {
87
87
  *
88
88
  * 特殊处理:
89
89
  * - children 属性不使用 unref,因为 ref 作为 children 是合法的可变渲染源
90
+ * - ref 属性不使用 unref/.value,因为 ref 绑定需要 ref 对象本身
91
+ * 运行时 resolveProps() 通过 isRef() 判定 ref 属性值,若此处解包,
92
+ * popProperty 取到的是解包后的值(如 null),isRef 判定失败导致绑定失效
90
93
  * - 其他标识符属性使用 unref 解包
91
94
  *
92
95
  * @param key - 属性名
@@ -107,15 +110,22 @@ ctx // 转换上下文,包含转换过程中的相关信息
107
110
  }
108
111
  // 如果是标识符(变量名)
109
112
  if (t.isIdentifier(value)) {
110
- // 特殊处理 children 属性或不在 refVariables 中的变量
111
- if (key === 'children' || ctx.nonRefVariables.has(value.name)) {
113
+ // 特殊处理 children 和 ref 属性:
114
+ // - children: ref 作为 children 是合法的可变渲染源,需保持引用本身
115
+ // - ref: ref 绑定需要 ref 对象本身,运行时通过 isRef() 判定后赋值 .value
116
+ // 若此处 unref/.value 解包,popProperty 取到解包值(如 null),isRef 失败
117
+ if (key === 'children' || key === 'ref') {
118
+ return t.objectProperty(keyNode, value);
119
+ }
120
+ // 已知非 ref 变量(函数声明、箭头函数等):静态分析确定非 ref,直接赋值无需 unref
121
+ if (ctx.nonRefVariables.has(value.name)) {
112
122
  return t.objectProperty(keyNode, value);
113
123
  }
114
124
  // 如果是 ref 变量,创建 getter 方法返回 ref 的 value
115
125
  if (ctx.refVariables.has(value.name)) {
116
126
  return createGetter(keyNode, t.returnStatement(t.memberExpression(value, t.identifier('value'))));
117
127
  }
118
- // 其他标识符属性创建 unref getter
128
+ // 未知标识符:创建 unref getter(非 ref 原样返回,安全)
119
129
  return createUnrefGetter(keyNode, value, ctx);
120
130
  }
121
131
  // 如果是成员表达式(如 obj.property)
package/dist/transform.js CHANGED
@@ -64,12 +64,13 @@ function setupAliases(ctx, program) {
64
64
  }
65
65
  /**
66
66
  * 收集 ref 相关信息
67
+ * 注意:nonRefVariables 在 collectComponentFunctions 之后单独收集,
68
+ * 因为匿名默认导出需要先被转换为命名函数才能被识别
67
69
  */
68
70
  function collectRefInfo(ctx, program) {
69
71
  const refApiAliases = collectRefApiAliases(program);
70
72
  ctx.refApiAliases = refApiAliases;
71
73
  ctx.refVariables = collectRefVariables(program, refApiAliases);
72
- ctx.nonRefVariables = collectNonRefVariables(program);
73
74
  }
74
75
  /**
75
76
  * 转换 AST
@@ -146,6 +147,9 @@ export async function transform(code, id, options) {
146
147
  setupAliases(ctx, ast.program);
147
148
  collectRefInfo(ctx, ast.program);
148
149
  const components = collectComponentFunctions(ast.program, ctx.builderAlias);
150
+ // 在组件函数收集之后收集非 ref 变量
151
+ // 此时匿名默认导出已被转换为命名函数,可被正确识别为非 ref
152
+ ctx.nonRefVariables = collectNonRefVariables(ast.program);
149
153
  transformAST(ast, ctx);
150
154
  injectImports(ast.program, ctx);
151
155
  if (options.hmr && components.length > 0 && !isEntryFile(id)) {
package/dist/types.d.ts CHANGED
@@ -53,7 +53,7 @@ declare global {
53
53
  *
54
54
  * ```tsx
55
55
  * const show = ref(true)
56
- * // v-if指令不支持 ref 自动解包,必须使用.value
56
+ * // v-if 指令支持 ref 自动解包,ref.value 是可选的
57
57
  * <div v-if={show.value}></div>
58
58
  * ```
59
59
  */
@@ -16,9 +16,7 @@ import { addPureComment, createArrowFunction, createBranchCall, createUnrefCall,
16
16
  export function createBranch(config, ctx) {
17
17
  const { conditions, branches, useRef = true } = config;
18
18
  markImport(ctx, 'branch');
19
- if (useRef && conditions.some(c => isIdentifier(c) && ctx.refVariables.has(c.name))) {
20
- markImport(ctx, 'unref');
21
- }
19
+ // unref 导入标记由 buildNestedCondition 内部统一管理
22
20
  const conditionExpr = buildNestedCondition(conditions, ctx, useRef);
23
21
  const branchAlias = getAlias(ctx.vitarxAliases, 'branch');
24
22
  return addPureComment(createBranchCall(createArrowFunction(conditionExpr), branches, branchAlias), ctx);
@@ -63,8 +61,28 @@ export function buildNestedCondition(conditions, ctx, useRef = true) {
63
61
  result = t.numericLiteral(i);
64
62
  continue;
65
63
  }
66
- // 构建条件表达式
67
- const conditionExpr = useRef && isIdentifier(condition) ? createUnrefCall(condition, unrefAlias) : condition;
64
+ // 构建条件表达式:对标识符进行三态解包
65
+ // 已知 ref → .value(高效),已知非 ref → 原样(静态分析确定),未知 → unref()(安全)
66
+ let conditionExpr;
67
+ if (useRef && isIdentifier(condition)) {
68
+ if (ctx.refVariables.has(condition.name)) {
69
+ // 已知 ref:直接使用 .value
70
+ conditionExpr = t.memberExpression(condition, t.identifier('value'));
71
+ }
72
+ else if (ctx.nonRefVariables.has(condition.name)) {
73
+ // 已知非 ref(函数等):原样使用,无需解包
74
+ conditionExpr = condition;
75
+ }
76
+ else {
77
+ // 未知标识符:使用 unref() 解包(非 ref 原样返回)
78
+ markImport(ctx, 'unref');
79
+ conditionExpr = createUnrefCall(condition, unrefAlias);
80
+ }
81
+ }
82
+ else {
83
+ // 复杂表达式:原样不解包,用户手动写 .value
84
+ conditionExpr = condition;
85
+ }
68
86
  // 对复杂条件表达式添加括号
69
87
  const wrappedCondition = wrapExpression(conditionExpr);
70
88
  if (result === null) {
@@ -89,6 +107,8 @@ export function buildNestedCondition(conditions, ctx, useRef = true) {
89
107
  export function createBinaryBranch(condition, consequent, alternate, ctx) {
90
108
  const conditions = [condition, t.booleanLiteral(true)];
91
109
  const branches = [createArrowFunction(consequent), createArrowFunction(alternate)];
92
- const useRef = isIdentifier(condition);
110
+ // 三元表达式不解包 ref,用户需手动写 .value
111
+ // 这样 TypeScript 类型推导能正确检查 Ref<T> 参与运算的类型错误
112
+ const useRef = false;
93
113
  return createBranch({ conditions, branches, useRef }, ctx);
94
114
  }
@@ -14,6 +14,13 @@ export interface ComponentInfo {
14
14
  /** 是否为包装组件(传递给 builder 函数) */
15
15
  isWrapped?: boolean;
16
16
  }
17
+ /**
18
+ * 检查名称是否为有效的组件名称
19
+ * 必须以大写字母开头(符合 React/Vue 组件命名规范)
20
+ * @param name - 待检查的名称
21
+ * @returns 是否为有效组件名称
22
+ */
23
+ export declare function isValidComponentName(name: string): boolean;
17
24
  /**
18
25
  * 收集模块中的组件函数
19
26
  * 遍历 AST 程序节点,提取所有导出的组件函数