@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.
- package/dist/passes/directives/processDirectives.d.ts +1 -1
- package/dist/passes/directives/processDirectives.js +15 -10
- package/dist/passes/hmr/inject-state.d.ts +1 -1
- package/dist/passes/hmr/inject-state.js +38 -57
- package/dist/passes/imports/collectRefVariables.d.ts +1 -0
- package/dist/passes/imports/collectRefVariables.js +52 -19
- package/dist/passes/jsx/processJSXFragment.js +18 -1
- package/dist/passes/props/attribute.d.ts +3 -0
- package/dist/passes/props/attribute.js +13 -3
- package/dist/transform.js +5 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils/branch-factory.js +26 -6
- package/dist/utils/component-collect.d.ts +7 -0
- package/dist/utils/component-collect.js +12 -236
- package/dist/utils/default-export.d.ts +38 -0
- package/dist/utils/default-export.js +119 -0
- package/dist/utils/function-extract.d.ts +45 -0
- package/dist/utils/function-extract.js +98 -0
- package/dist/utils/generate.d.ts +7 -0
- package/dist/utils/generate.js +20 -0
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +2 -2
- package/dist/utils/jsx-helpers.d.ts +1 -4
- package/dist/utils/jsx-helpers.js +2 -15
- package/dist/utils/vif-helpers.js +6 -3
- package/package.json +2 -2
|
@@ -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 '
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
|
76
|
+
* @param visitor - 变量声明访问者回调
|
|
72
77
|
*/
|
|
73
|
-
function
|
|
74
|
-
// 遍历语句块中的每个语句
|
|
78
|
+
function forEachVariableDeclarator(block, visitor) {
|
|
75
79
|
for (const stmt of block.body) {
|
|
76
|
-
|
|
80
|
+
traverseStatementForDeclarators(stmt, visitor);
|
|
77
81
|
}
|
|
78
82
|
}
|
|
79
83
|
/**
|
|
80
|
-
*
|
|
84
|
+
* 从单个语句中递归查找变量声明并执行回调
|
|
81
85
|
* 支持多种语句类型:变量声明、条件语句、循环语句、switch、try-catch 等
|
|
82
86
|
* @param stmt - 语句节点
|
|
83
|
-
* @param
|
|
87
|
+
* @param visitor - 变量声明访问者回调
|
|
84
88
|
*/
|
|
85
|
-
function
|
|
86
|
-
// 处理变量声明语句:const/let/var
|
|
89
|
+
function traverseStatementForDeclarators(stmt, visitor) {
|
|
87
90
|
if (stmt.type === 'VariableDeclaration') {
|
|
88
91
|
for (const decl of stmt.declarations) {
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
+
traverseStatementForDeclarators(s, visitor);
|
|
139
114
|
}
|
|
140
115
|
}
|
|
141
116
|
}
|
|
142
117
|
else if (stmt.type === 'TryStatement') {
|
|
143
|
-
|
|
144
|
-
collectFromBlock(stmt.block, variableNames);
|
|
118
|
+
forEachVariableDeclarator(stmt.block, visitor);
|
|
145
119
|
if (stmt.handler) {
|
|
146
|
-
|
|
120
|
+
forEachVariableDeclarator(stmt.handler.body, visitor);
|
|
147
121
|
}
|
|
148
122
|
if (stmt.finalizer) {
|
|
149
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
injectStatePreservationForDeclaration(decl);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
189
|
+
forEachVariableDeclarator(functionBody, decl => {
|
|
190
|
+
injectStatePreservationForDeclaration(decl);
|
|
191
|
+
});
|
|
211
192
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { REF_APIS, RESPONSIVE_MODULES } from '../../constants/index.js';
|
|
2
|
-
import {
|
|
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
|
-
//
|
|
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
|
-
|
|
155
|
-
|
|
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'
|
|
169
|
-
|
|
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 {
|
|
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
|
|
111
|
-
|
|
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
|
-
//
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 程序节点,提取所有导出的组件函数
|