@vitarx/plugin-vite 1.0.0-beta.0 → 1.0.0-beta.2
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/constants/index.d.ts +1 -0
- package/dist/constants/index.js +1 -0
- package/dist/hmr-client/update.js +21 -4
- package/dist/hmr-client/utils.js +11 -3
- package/dist/index.js +9 -10
- package/dist/passes/components/ifBlock.d.ts +3 -0
- package/dist/passes/components/ifBlock.js +24 -8
- package/dist/passes/components/switch.js +49 -21
- package/dist/passes/directives/index.d.ts +1 -1
- package/dist/passes/directives/index.js +1 -1
- package/dist/passes/directives/processDirectives.d.ts +3 -1
- package/dist/passes/directives/processDirectives.js +36 -9
- package/dist/passes/directives/vIf.d.ts +18 -6
- package/dist/passes/directives/vIf.js +59 -44
- package/dist/passes/hmr/inject-state.js +64 -7
- package/dist/passes/hmr/inject.js +4 -3
- package/dist/passes/imports/collectImports.d.ts +1 -4
- package/dist/passes/imports/collectImports.js +4 -5
- package/dist/passes/imports/collectRefVariables.js +22 -3
- package/dist/passes/jsx/processChildren.js +30 -1
- package/dist/passes/jsx/processJSXElement.js +31 -27
- package/dist/passes/jsx/processJSXFragment.d.ts +1 -1
- package/dist/passes/jsx/processJSXFragment.js +3 -2
- package/dist/passes/props/attribute.d.ts +3 -1
- package/dist/passes/props/attribute.js +27 -5
- package/dist/transform.js +4 -1
- package/dist/utils/ast-builders.js +2 -11
- package/dist/utils/branch-factory.js +1 -1
- package/dist/utils/collect-names.js +29 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/jsx-helpers.d.ts +10 -0
- package/dist/utils/jsx-helpers.js +39 -11
- package/dist/utils/vif-helpers.d.ts +1 -3
- package/dist/utils/vif-helpers.js +2 -2
- package/package.json +1 -1
|
@@ -47,16 +47,73 @@ export function createWrappedComponentHMRStatements(componentName) {
|
|
|
47
47
|
*/
|
|
48
48
|
export function collectLocalVariableNames(functionBody) {
|
|
49
49
|
const variableNames = new Set();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
50
|
+
collectFromBlock(functionBody, variableNames);
|
|
51
|
+
return Array.from(variableNames);
|
|
52
|
+
}
|
|
53
|
+
function collectFromBlock(block, variableNames) {
|
|
54
|
+
for (const stmt of block.body) {
|
|
55
|
+
collectFromStatement(stmt, variableNames);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function collectFromStatement(stmt, variableNames) {
|
|
59
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
60
|
+
for (const decl of stmt.declarations) {
|
|
61
|
+
if (decl.id.type !== 'VoidPattern') {
|
|
62
|
+
collectPatternBindings(decl.id, variableNames);
|
|
56
63
|
}
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
|
-
|
|
66
|
+
else if (stmt.type === 'IfStatement') {
|
|
67
|
+
if (stmt.consequent.type === 'BlockStatement') {
|
|
68
|
+
collectFromBlock(stmt.consequent, variableNames);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
collectFromStatement(stmt.consequent, variableNames);
|
|
72
|
+
}
|
|
73
|
+
if (stmt.alternate) {
|
|
74
|
+
if (stmt.alternate.type === 'BlockStatement') {
|
|
75
|
+
collectFromBlock(stmt.alternate, variableNames);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
collectFromStatement(stmt.alternate, variableNames);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (stmt.type === 'ForStatement' || stmt.type === 'WhileStatement' || stmt.type === 'DoWhileStatement') {
|
|
83
|
+
if (stmt.body.type === 'BlockStatement') {
|
|
84
|
+
collectFromBlock(stmt.body, variableNames);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
collectFromStatement(stmt.body, variableNames);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (stmt.type === 'ForInStatement' || stmt.type === 'ForOfStatement') {
|
|
91
|
+
if (stmt.body.type === 'BlockStatement') {
|
|
92
|
+
collectFromBlock(stmt.body, variableNames);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
collectFromStatement(stmt.body, variableNames);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else if (stmt.type === 'BlockStatement') {
|
|
99
|
+
collectFromBlock(stmt, variableNames);
|
|
100
|
+
}
|
|
101
|
+
else if (stmt.type === 'SwitchStatement') {
|
|
102
|
+
for (const c of stmt.cases) {
|
|
103
|
+
for (const s of c.consequent) {
|
|
104
|
+
collectFromStatement(s, variableNames);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else if (stmt.type === 'TryStatement') {
|
|
109
|
+
collectFromBlock(stmt.block, variableNames);
|
|
110
|
+
if (stmt.handler) {
|
|
111
|
+
collectFromBlock(stmt.handler.body, variableNames);
|
|
112
|
+
}
|
|
113
|
+
if (stmt.finalizer) {
|
|
114
|
+
collectFromBlock(stmt.finalizer, variableNames);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
60
117
|
}
|
|
61
118
|
/**
|
|
62
119
|
* 判断表达式是否为函数类型
|
|
@@ -50,11 +50,12 @@ function createHotAcceptStatement() {
|
|
|
50
50
|
*/
|
|
51
51
|
function generateComponentId(filename, componentName) {
|
|
52
52
|
const combined = `${filename}:${componentName}`;
|
|
53
|
-
let hash =
|
|
53
|
+
let hash = 2166136261;
|
|
54
54
|
for (let i = 0; i < combined.length; i++) {
|
|
55
|
-
hash
|
|
55
|
+
hash ^= combined.charCodeAt(i);
|
|
56
|
+
hash = Math.imul(hash, 16777619);
|
|
56
57
|
}
|
|
57
|
-
return
|
|
58
|
+
return (hash >>> 0).toString(16);
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
60
61
|
* 注入 HMR 支持
|
|
@@ -10,10 +10,7 @@ import * as t from '@babel/types';
|
|
|
10
10
|
* @param program - AST Program 节点
|
|
11
11
|
* @returns 本地变量名集合和 vitarx 导入映射
|
|
12
12
|
*/
|
|
13
|
-
export declare function collectExistingImports(program: t.Program):
|
|
14
|
-
localNames: Set<string>;
|
|
15
|
-
vitarxImports: Map<string, string>;
|
|
16
|
-
};
|
|
13
|
+
export declare function collectExistingImports(program: t.Program): Map<string, string>;
|
|
17
14
|
/**
|
|
18
15
|
* 收集本地变量绑定
|
|
19
16
|
* 包括导入绑定、变量声明、函数声明、类声明
|
|
@@ -7,18 +7,17 @@ import { collectPatternBindings } from '../../utils/index.js';
|
|
|
7
7
|
* @returns 本地变量名集合和 vitarx 导入映射
|
|
8
8
|
*/
|
|
9
9
|
export function collectExistingImports(program) {
|
|
10
|
-
const localNames = new Set();
|
|
11
10
|
const vitarxImports = new Map();
|
|
12
11
|
for (const node of program.body) {
|
|
13
12
|
if (node.type !== 'ImportDeclaration')
|
|
14
13
|
continue;
|
|
15
|
-
// 跳过 import type 和 import typeof 语句
|
|
16
14
|
if (node.importKind === 'type' || node.importKind === 'typeof')
|
|
17
15
|
continue;
|
|
18
16
|
const source = node.source.value;
|
|
17
|
+
if (source !== VITARX_MODULE)
|
|
18
|
+
continue;
|
|
19
19
|
for (const specifier of node.specifiers) {
|
|
20
|
-
|
|
21
|
-
if (source === VITARX_MODULE && specifier.type === 'ImportSpecifier') {
|
|
20
|
+
if (specifier.type === 'ImportSpecifier') {
|
|
22
21
|
const importedName = specifier.imported.type === 'Identifier'
|
|
23
22
|
? specifier.imported.name
|
|
24
23
|
: specifier.imported.value;
|
|
@@ -26,7 +25,7 @@ export function collectExistingImports(program) {
|
|
|
26
25
|
}
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
|
-
return
|
|
28
|
+
return vitarxImports;
|
|
30
29
|
}
|
|
31
30
|
/**
|
|
32
31
|
* 收集本地变量绑定
|
|
@@ -44,8 +44,12 @@ export function collectRefVariables(program, refApiAliases) {
|
|
|
44
44
|
const refVariables = new Set();
|
|
45
45
|
const { refApiLocalNames, toRefsLocalNames } = buildApiNameSets(refApiAliases);
|
|
46
46
|
for (const node of program.body) {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
collectRefVariablesFromNode(node, refVariables, refApiLocalNames, toRefsLocalNames);
|
|
48
|
+
}
|
|
49
|
+
return refVariables;
|
|
50
|
+
}
|
|
51
|
+
function collectRefVariablesFromNode(node, refVariables, refApiLocalNames, toRefsLocalNames) {
|
|
52
|
+
if (node.type === 'VariableDeclaration') {
|
|
49
53
|
for (const decl of node.declarations) {
|
|
50
54
|
if (!decl.init)
|
|
51
55
|
continue;
|
|
@@ -64,7 +68,22 @@ export function collectRefVariables(program, refApiAliases) {
|
|
|
64
68
|
}
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
|
-
|
|
71
|
+
else if (node.type === 'FunctionDeclaration' && node.body) {
|
|
72
|
+
for (const stmt of node.body.body) {
|
|
73
|
+
collectRefVariablesFromNode(stmt, refVariables, refApiLocalNames, toRefsLocalNames);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (node.type === 'ExportNamedDeclaration' && node.declaration) {
|
|
77
|
+
collectRefVariablesFromNode(node.declaration, refVariables, refApiLocalNames, toRefsLocalNames);
|
|
78
|
+
}
|
|
79
|
+
else if (node.type === 'ExportDefaultDeclaration') {
|
|
80
|
+
const decl = node.declaration;
|
|
81
|
+
if (decl.type === 'FunctionDeclaration' && decl.body) {
|
|
82
|
+
for (const stmt of decl.body.body) {
|
|
83
|
+
collectRefVariablesFromNode(stmt, refVariables, refApiLocalNames, toRefsLocalNames);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
68
87
|
}
|
|
69
88
|
/**
|
|
70
89
|
* 构建 API 名称集合
|
|
@@ -34,13 +34,18 @@ function wrapWithExpr(expr, ctx) {
|
|
|
34
34
|
* @returns 处理后的表达式数组
|
|
35
35
|
*/
|
|
36
36
|
export function processChildren(children, ctx) {
|
|
37
|
+
// 初始化结果数组,用于存储处理后的表达式
|
|
37
38
|
const result = [];
|
|
39
|
+
// 遍历每个子节点
|
|
38
40
|
for (const child of children) {
|
|
41
|
+
// 处理单个子节点,将其转换为表达式
|
|
39
42
|
const processed = processChildNode(child, ctx);
|
|
43
|
+
// 如果处理结果不为null,则添加到结果数组中
|
|
40
44
|
if (processed !== null) {
|
|
41
45
|
result.push(processed);
|
|
42
46
|
}
|
|
43
47
|
}
|
|
48
|
+
// 返回处理后的表达式数组
|
|
44
49
|
return result;
|
|
45
50
|
}
|
|
46
51
|
/**
|
|
@@ -63,6 +68,7 @@ function processChildNode(node, ctx) {
|
|
|
63
68
|
if (node.type === 'JSXSpreadChild') {
|
|
64
69
|
return processChildExpression(node.expression, ctx);
|
|
65
70
|
}
|
|
71
|
+
// JSXElement/JSXFragment 由 Babel 遍历的 exit 阶段处理,此处直接透传
|
|
66
72
|
if (isJSXElement(node) || isJSXFragment(node)) {
|
|
67
73
|
return node;
|
|
68
74
|
}
|
|
@@ -78,27 +84,50 @@ function processChildNode(node, ctx) {
|
|
|
78
84
|
* 根据表达式类型生成对应的运行时 API 调用
|
|
79
85
|
* CallExpression 在此用 expr 包装(用户函数调用需要响应式追踪)
|
|
80
86
|
*/
|
|
87
|
+
/**
|
|
88
|
+
* 处理子表达式函数
|
|
89
|
+
* @param {t.Expression} expr - 要处理的表达式节点
|
|
90
|
+
* @param {TransformContext} ctx - 转换上下文,包含转换所需的环境信息
|
|
91
|
+
* @returns {t.Expression} 处理后的表达式节点
|
|
92
|
+
*/
|
|
81
93
|
function processChildExpression(expr, ctx) {
|
|
94
|
+
// 如果是简单标识符,直接返回
|
|
82
95
|
if (isIdentifier(expr)) {
|
|
83
96
|
return expr;
|
|
84
97
|
}
|
|
98
|
+
// 如果是成员表达式(如 obj.property),调用专门的处理函数
|
|
85
99
|
if (isMemberExpression(expr)) {
|
|
86
100
|
return handleMemberExpression(expr, ctx);
|
|
87
101
|
}
|
|
102
|
+
// 如果是条件表达式(如 ternary operator),递归处理条件表达式
|
|
88
103
|
if (isConditionalExpression(expr)) {
|
|
89
104
|
return processConditionalExpression(expr, ctx);
|
|
90
105
|
}
|
|
106
|
+
// 如果是逻辑表达式、二元表达式或函数调用表达式,用 expr 包装
|
|
107
|
+
// 这些表达式需要响应式追踪,所以需要特殊处理
|
|
91
108
|
if (isLogicalExpression(expr) || isBinaryExpression(expr) || expr.type === 'CallExpression') {
|
|
92
109
|
return wrapWithExpr(expr, ctx);
|
|
93
110
|
}
|
|
111
|
+
// 其他类型的表达式直接返回,无需特殊处理
|
|
94
112
|
return expr;
|
|
95
113
|
}
|
|
96
114
|
/**
|
|
97
115
|
* 处理条件表达式,转换为 branch 调用
|
|
116
|
+
* 该函数接收一个条件表达式节点和转换上下文,将其转换为二元分支调用
|
|
117
|
+
* @param node - 条件表达式节点,包含测试条件、 consequent 和 alternate 分支
|
|
118
|
+
* @param ctx - 转换上下文,包含转换过程中需要的辅助信息和工具函数
|
|
119
|
+
* @returns 返回一个调用表达式,表示转换后的分支逻辑
|
|
98
120
|
*/
|
|
99
|
-
function processConditionalExpression(node,
|
|
121
|
+
function processConditionalExpression(node, // 条件表达式节点,包含 test、consequent 和 alternate 属性
|
|
122
|
+
ctx // 转换上下文,提供转换过程中需要的辅助方法和状态
|
|
123
|
+
) {
|
|
124
|
+
// 返回一个调用表达式,表示分支逻辑
|
|
125
|
+
// 解构条件表达式节点的三个主要部分:测试条件、真分支和假分支
|
|
100
126
|
const { test, consequent, alternate } = node;
|
|
127
|
+
// 处理真分支,如果处理结果为空则使用 null 字面量作为默认值
|
|
101
128
|
const processedConsequent = processChildNode(consequent, ctx) || t.nullLiteral();
|
|
129
|
+
// 处理假分支,如果处理结果为空则使用 null 字面量作为默认值
|
|
102
130
|
const processedAlternate = processChildNode(alternate, ctx) || t.nullLiteral();
|
|
131
|
+
// 创建并返回一个二元分支调用,传入测试条件、处理后的真分支和假分支
|
|
103
132
|
return createBinaryBranch(test, processedConsequent, processedAlternate, ctx);
|
|
104
133
|
}
|
|
@@ -2,8 +2,8 @@ import * as t from '@babel/types';
|
|
|
2
2
|
import { isJSXElement } from '@babel/types';
|
|
3
3
|
import { addWarning, markImport } from '../../context.js';
|
|
4
4
|
import { createError, createWarning } from '../../error.js';
|
|
5
|
-
import { addPureComment, createArrowFunction, createBranch, createCreateViewCall, filterEffectiveChildren, getAlias, getDevLocInfo, getDirectiveValue, getJSXAttributeByName, getJSXElementName, hasEffectiveChildren,
|
|
6
|
-
import { processDirectives } from '../directives/index.js';
|
|
5
|
+
import { addPureComment, createArrowFunction, createBranch, createCreateViewCall, filterEffectiveChildren, getAlias, getDevLocInfo, getDirectiveValue, getJSXAttributeByName, getJSXElementName, hasEffectiveChildren, isVElse, isVIf, isVIfChain, removeVIfChainDirectives, resolveJSXElementType } from '../../utils/index.js';
|
|
6
|
+
import { processDirectives, transformChildrenVIfChains } from '../directives/index.js';
|
|
7
7
|
import { processProps } from '../props/index.js';
|
|
8
8
|
import { processChildren } from './processChildren.js';
|
|
9
9
|
/**
|
|
@@ -26,26 +26,14 @@ export function processJSXElement(path, ctx) {
|
|
|
26
26
|
* 校验 Match 组件必须在 Switch 内使用
|
|
27
27
|
*/
|
|
28
28
|
function validateMatchInSwitch(path) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (parent.node.type === 'JSXElement') {
|
|
35
|
-
const parentName = getJSXElementName(parent.node);
|
|
36
|
-
if (parentName === 'Switch') {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
// 检查是否在 Fragment 内且 Fragment 的父元素是 Switch
|
|
41
|
-
if (parent.node.type === 'JSXFragment' || parent.node.type === 'JSXElement') {
|
|
42
|
-
const grandParent = parent.parentPath;
|
|
43
|
-
if (grandParent?.node.type === 'JSXElement') {
|
|
44
|
-
const grandParentName = getJSXElementName(grandParent.node);
|
|
45
|
-
if (grandParentName === 'Switch') {
|
|
29
|
+
let current = path.parentPath;
|
|
30
|
+
while (current) {
|
|
31
|
+
if (current.node.type === 'JSXElement') {
|
|
32
|
+
const name = getJSXElementName(current.node);
|
|
33
|
+
if (name === 'Switch')
|
|
46
34
|
return;
|
|
47
|
-
}
|
|
48
35
|
}
|
|
36
|
+
current = current.parentPath;
|
|
49
37
|
}
|
|
50
38
|
throw createError('E012', path.node);
|
|
51
39
|
}
|
|
@@ -57,9 +45,6 @@ function validateMatchInSwitch(path) {
|
|
|
57
45
|
* @returns 转换后的表达式
|
|
58
46
|
*/
|
|
59
47
|
export function transformJSXElement(node, ctx, handleVIf = false) {
|
|
60
|
-
const name = getJSXElementName(node);
|
|
61
|
-
if (!name)
|
|
62
|
-
return null;
|
|
63
48
|
// 处理 v-if 链
|
|
64
49
|
if (handleVIf && isVIfChain(node)) {
|
|
65
50
|
if (isVIf(node)) {
|
|
@@ -69,8 +54,10 @@ export function transformJSXElement(node, ctx, handleVIf = false) {
|
|
|
69
54
|
throw createError(isVElse(node) ? 'E003' : 'E004', node);
|
|
70
55
|
}
|
|
71
56
|
}
|
|
72
|
-
//
|
|
73
|
-
const type =
|
|
57
|
+
// 解析 JSX 元素类型
|
|
58
|
+
const type = resolveJSXElementType(node);
|
|
59
|
+
if (!type)
|
|
60
|
+
return null;
|
|
74
61
|
// 检测是否有有效子元素
|
|
75
62
|
const hasChildren = hasEffectiveChildren(node);
|
|
76
63
|
// 处理属性(有子元素时跳过 children 属性)
|
|
@@ -123,8 +110,10 @@ function processElementChildren(node, props, ctx) {
|
|
|
123
110
|
}
|
|
124
111
|
}
|
|
125
112
|
}
|
|
113
|
+
// 处理子元素中的 v-if 链(确保嵌套链在 transformJSXElement 递归调用时被正确处理)
|
|
114
|
+
const chainProcessed = transformChildrenVIfChains(children, ctx, transformJSXElement);
|
|
126
115
|
// 处理子元素
|
|
127
|
-
const processedChildren = processChildren(
|
|
116
|
+
const processedChildren = processChildren(chainProcessed, ctx);
|
|
128
117
|
const childrenValue = processedChildren.length === 1 ? processedChildren[0] : t.arrayExpression(processedChildren);
|
|
129
118
|
// 添加 children 属性
|
|
130
119
|
if (props) {
|
|
@@ -135,20 +124,35 @@ function processElementChildren(node, props, ctx) {
|
|
|
135
124
|
}
|
|
136
125
|
/**
|
|
137
126
|
* 转换单个 v-if 元素
|
|
127
|
+
* 该函数处理带有 v-if 指令的 JSX 元素,将其转换为条件渲染的函数调用
|
|
128
|
+
* @param node - 要转换的 JSX 元素节点
|
|
129
|
+
* @param ctx - 转换上下文,包含转换所需的环境信息
|
|
130
|
+
* @returns 返回转换后的条件渲染函数调用表达式,如果转换失败则返回 null
|
|
138
131
|
*/
|
|
139
132
|
function transformSingleVIf(node, ctx) {
|
|
133
|
+
// 首先检查节点是否是有效的 v-if 元素
|
|
140
134
|
if (!isVIf(node))
|
|
141
135
|
return null;
|
|
136
|
+
// 获取 v-if 指令的值(条件表达式)
|
|
142
137
|
const condition = getDirectiveValue(node, 'v-if');
|
|
138
|
+
// 如果没有找到条件表达式,则返回 null
|
|
143
139
|
if (!condition)
|
|
144
140
|
return null;
|
|
141
|
+
// 移除节点上的 v-if 相关指令链
|
|
145
142
|
removeVIfChainDirectives(node);
|
|
143
|
+
// 转换 JSX 元素,不保留原始属性
|
|
146
144
|
const transformedNode = transformJSXElement(node, ctx, false);
|
|
145
|
+
// 如果转换失败,返回 null
|
|
147
146
|
if (!transformedNode)
|
|
148
147
|
return null;
|
|
149
|
-
|
|
148
|
+
// 创建条件分支的函数调用
|
|
149
|
+
const branchCall = createBranch(
|
|
150
|
+
// 包含条件数组和对应的分支函数
|
|
151
|
+
{ conditions: [condition], branches: [createArrowFunction(transformedNode)] }, ctx);
|
|
152
|
+
// 保留原始节点的位置信息
|
|
150
153
|
if (node.loc) {
|
|
151
154
|
branchCall.loc = node.loc;
|
|
152
155
|
}
|
|
156
|
+
// 返回转换后的分支函数调用
|
|
153
157
|
return branchCall;
|
|
154
158
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { NodePath } from '@babel/traverse';
|
|
7
7
|
import * as t from '@babel/types';
|
|
8
|
-
import { TransformContext } from '../../context.js';
|
|
8
|
+
import { type TransformContext } from '../../context.js';
|
|
9
9
|
/**
|
|
10
10
|
* 处理 JSX Fragment
|
|
11
11
|
* @param path - JSX Fragment 路径
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as t from '@babel/types';
|
|
2
|
+
import { markImport } from '../../context.js';
|
|
2
3
|
import { addPureComment, createCreateViewCall, filterWhitespaceChildren, getAlias, getDevLocInfo, validateNoDirectMatchChild } from '../../utils/index.js';
|
|
3
4
|
import { processChildren } from './processChildren.js';
|
|
4
5
|
/**
|
|
@@ -13,8 +14,8 @@ export function processJSXFragment(path, ctx) {
|
|
|
13
14
|
// 校验 Match 组件
|
|
14
15
|
validateNoDirectMatchChild(children);
|
|
15
16
|
// 标记需要的导入
|
|
16
|
-
ctx
|
|
17
|
-
ctx
|
|
17
|
+
markImport(ctx, 'Fragment');
|
|
18
|
+
markImport(ctx, 'createView');
|
|
18
19
|
const fragmentAlias = getAlias(ctx.vitarxAliases, 'Fragment');
|
|
19
20
|
const createViewAlias = getAlias(ctx.vitarxAliases, 'createView');
|
|
20
21
|
const locInfo = getDevLocInfo(ctx, node);
|
|
@@ -39,7 +39,9 @@ export declare function getAttributeValue(value: t.JSXAttribute['value']): t.Exp
|
|
|
39
39
|
* @param ctx - 转换上下文
|
|
40
40
|
* @returns 对象属性或方法节点
|
|
41
41
|
*/
|
|
42
|
-
export declare function createProperty(key: string,
|
|
42
|
+
export declare function createProperty(key: string, // 属性名称字符串
|
|
43
|
+
value: t.Expression, // 属性值的表达式,可以是各种类型的表达式
|
|
44
|
+
ctx: TransformContext): t.ObjectProperty | t.ObjectMethod;
|
|
43
45
|
/**
|
|
44
46
|
* 处理单个 JSX 属性
|
|
45
47
|
* 根据属性名类型分发到不同的处理逻辑
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* @module passes/props/attribute
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
|
-
import { isBooleanLiteral, isIdentifier, isJSXExpressionContainer, isNumericLiteral, isStringLiteral } from '@babel/types';
|
|
7
|
+
import { isBooleanLiteral, isIdentifier, isJSXExpressionContainer, isMemberExpression, isNumericLiteral, isStringLiteral } from '@babel/types';
|
|
8
8
|
import { markImport } from '../../context.js';
|
|
9
9
|
import { createError } from '../../error.js';
|
|
10
|
-
import { getAlias } from '../../utils/index.js';
|
|
10
|
+
import { createAccessorCall, getAlias } from '../../utils/index.js';
|
|
11
11
|
/**
|
|
12
12
|
* 处理展开属性 {...props}
|
|
13
13
|
* 将展开属性转换为 v-bind 特殊属性格式
|
|
@@ -95,22 +95,45 @@ export function getAttributeValue(value) {
|
|
|
95
95
|
* @param ctx - 转换上下文
|
|
96
96
|
* @returns 对象属性或方法节点
|
|
97
97
|
*/
|
|
98
|
-
export function createProperty(key,
|
|
98
|
+
export function createProperty(key, // 属性名称字符串
|
|
99
|
+
value, // 属性值的表达式,可以是各种类型的表达式
|
|
100
|
+
ctx // 转换上下文对象,包含转换所需的各种信息和工具函数
|
|
101
|
+
) {
|
|
102
|
+
// 返回类型,可以是对象属性或对象方法
|
|
103
|
+
// 创建属性名的字符串字面量节点
|
|
99
104
|
const keyNode = t.stringLiteral(key);
|
|
105
|
+
// 如果值是字符串、数字或布尔字面量,直接创建对象属性
|
|
100
106
|
if (isStringLiteral(value) || isNumericLiteral(value) || isBooleanLiteral(value)) {
|
|
101
107
|
return t.objectProperty(keyNode, value);
|
|
102
108
|
}
|
|
109
|
+
// 如果值是标识符
|
|
103
110
|
if (isIdentifier(value)) {
|
|
111
|
+
// 检查标识符是否是 ref 变量
|
|
104
112
|
if (ctx.refVariables.has(value.name)) {
|
|
113
|
+
// 如果是 ref 变量,创建一个 getter 方法来访问其 value 属性
|
|
105
114
|
return t.objectMethod('get', keyNode, [], t.blockStatement([t.returnStatement(t.memberExpression(value, t.identifier('value')))]));
|
|
106
115
|
}
|
|
116
|
+
// 特殊处理 children 属性,不使用 unref
|
|
107
117
|
if (key === 'children') {
|
|
108
118
|
return t.objectProperty(keyNode, value);
|
|
109
119
|
}
|
|
120
|
+
// 标记需要导入 unref 函数
|
|
110
121
|
markImport(ctx, 'unref');
|
|
122
|
+
// 获取 unref 函数的别名
|
|
111
123
|
const unrefAlias = getAlias(ctx.vitarxAliases, 'unref');
|
|
124
|
+
// 创建 getter 方法,使用 unref 解包属性值
|
|
112
125
|
return t.objectMethod('get', keyNode, [], t.blockStatement([t.returnStatement(t.callExpression(t.identifier(unrefAlias), [value]))]));
|
|
113
126
|
}
|
|
127
|
+
// 如果值是成员表达式,创建 accessor 调用
|
|
128
|
+
if (isMemberExpression(value)) {
|
|
129
|
+
// 标记需要导入 accessor 函数
|
|
130
|
+
markImport(ctx, 'accessor');
|
|
131
|
+
// 获取 accessor 函数的别名
|
|
132
|
+
const accessorAlias = getAlias(ctx.vitarxAliases, 'accessor');
|
|
133
|
+
// 创建属性节点,使用 accessor 处理成员表达式
|
|
134
|
+
return t.objectProperty(keyNode, createAccessorCall(value.object, value.property, accessorAlias));
|
|
135
|
+
}
|
|
136
|
+
// 默认情况下创建 getter 方法直接返回值
|
|
114
137
|
return t.objectMethod('get', keyNode, [], t.blockStatement([t.returnStatement(value)]));
|
|
115
138
|
}
|
|
116
139
|
/**
|
|
@@ -152,6 +175,5 @@ export function processAttribute(attr, existingPropNames, ctx) {
|
|
|
152
175
|
return { type: 'property', property };
|
|
153
176
|
}
|
|
154
177
|
// 未知属性类型,作为普通属性处理
|
|
155
|
-
|
|
156
|
-
return { type: 'property', property: t.objectProperty(t.identifier('unknown'), value) };
|
|
178
|
+
throw createError('E002', attr, 'Unsupported attribute name type');
|
|
157
179
|
}
|
package/dist/transform.js
CHANGED
|
@@ -33,7 +33,7 @@ function createParserOptions() {
|
|
|
33
33
|
* 设置 API 别名
|
|
34
34
|
*/
|
|
35
35
|
function setupAliases(ctx, program) {
|
|
36
|
-
const
|
|
36
|
+
const vitarxImports = collectExistingImports(program);
|
|
37
37
|
const localBindings = collectLocalBindings(program);
|
|
38
38
|
const allNames = new Set([...localBindings]);
|
|
39
39
|
const apiNames = [
|
|
@@ -84,6 +84,9 @@ function transformAST(ast, ctx) {
|
|
|
84
84
|
ctx.processedNodes.add(path.node);
|
|
85
85
|
processPureCompileComponent(path, ctx);
|
|
86
86
|
}
|
|
87
|
+
else {
|
|
88
|
+
processVIfChain(path, ctx, transformJSXElement);
|
|
89
|
+
}
|
|
87
90
|
},
|
|
88
91
|
exit(path) {
|
|
89
92
|
if (ctx.processedNodes.has(path.node))
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module utils/ast-builders
|
|
4
4
|
*/
|
|
5
5
|
import * as t from '@babel/types';
|
|
6
|
-
import { isIdentifier
|
|
6
|
+
import { isIdentifier } from '@babel/types';
|
|
7
7
|
import { PURE_COMMENT } from '../constants/index.js';
|
|
8
8
|
/**
|
|
9
9
|
* 创建 unref 调用
|
|
@@ -22,16 +22,7 @@ export function createUnrefCall(argument, alias) {
|
|
|
22
22
|
* @returns CallExpression
|
|
23
23
|
*/
|
|
24
24
|
export function createAccessorCall(object, key, alias) {
|
|
25
|
-
|
|
26
|
-
if (isIdentifier(key)) {
|
|
27
|
-
keyArg = t.stringLiteral(key.name);
|
|
28
|
-
}
|
|
29
|
-
else if (isStringLiteral(key)) {
|
|
30
|
-
keyArg = key;
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
keyArg = key;
|
|
34
|
-
}
|
|
25
|
+
const keyArg = isIdentifier(key) ? t.stringLiteral(key.name) : key;
|
|
35
26
|
return t.callExpression(t.identifier(alias || 'accessor'), [object, keyArg]);
|
|
36
27
|
}
|
|
37
28
|
/**
|
|
@@ -16,7 +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))) {
|
|
19
|
+
if (useRef && conditions.some(c => isIdentifier(c) && ctx.refVariables.has(c.name))) {
|
|
20
20
|
markImport(ctx, 'unref');
|
|
21
21
|
}
|
|
22
22
|
const conditionExpr = buildNestedCondition(conditions, ctx, useRef);
|
|
@@ -124,6 +124,35 @@ export function collectBuilderWrappedNames(program, builderAlias) {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
+
else if (node.type === 'ExportDefaultDeclaration' && node.declaration.type === 'CallExpression') {
|
|
128
|
+
const callExpr = node.declaration;
|
|
129
|
+
if (isBuilderCall(callExpr, builderAlias)) {
|
|
130
|
+
const arg = callExpr.arguments[0];
|
|
131
|
+
if (arg?.type === 'Identifier') {
|
|
132
|
+
wrappedNames.add(arg.name);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
else if (node.type === 'VariableDeclaration') {
|
|
137
|
+
for (const decl of node.declarations) {
|
|
138
|
+
if (decl.init?.type === 'CallExpression' && isBuilderCall(decl.init, builderAlias)) {
|
|
139
|
+
const arg = decl.init.arguments[0];
|
|
140
|
+
if (arg?.type === 'Identifier') {
|
|
141
|
+
wrappedNames.add(arg.name);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (node.type === 'ExportNamedDeclaration' && node.declaration?.type === 'VariableDeclaration') {
|
|
147
|
+
for (const decl of node.declaration.declarations) {
|
|
148
|
+
if (decl.init?.type === 'CallExpression' && isBuilderCall(decl.init, builderAlias)) {
|
|
149
|
+
const arg = decl.init.arguments[0];
|
|
150
|
+
if (arg?.type === 'Identifier') {
|
|
151
|
+
wrappedNames.add(arg.name);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
127
156
|
}
|
|
128
157
|
return wrappedNames;
|
|
129
158
|
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { isWhitespaceJSXText } from './ast-guards.js';
|
|
2
|
-
export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateNoDirectMatchChild } from './jsx-helpers.js';
|
|
2
|
+
export { getJSXElementName, resolveJSXElementType, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateNoDirectMatchChild } from './jsx-helpers.js';
|
|
3
3
|
export { createUnrefCall, createAccessorCall, createExprCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
|
|
4
4
|
export { collectPatternBindings, collectObjectPatternBindings } from './pattern-helpers.js';
|
|
5
5
|
export { createBranch, createBinaryBranch, buildNestedCondition, type BranchConfig } from './branch-factory.js';
|
package/dist/utils/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 类型守卫
|
|
2
2
|
export { isWhitespaceJSXText } from './ast-guards.js';
|
|
3
3
|
// JSX 辅助函数
|
|
4
|
-
export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateNoDirectMatchChild } from './jsx-helpers.js';
|
|
4
|
+
export { getJSXElementName, resolveJSXElementType, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateNoDirectMatchChild } from './jsx-helpers.js';
|
|
5
5
|
// AST 构建函数
|
|
6
6
|
export { createUnrefCall, createAccessorCall, createExprCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
|
|
7
7
|
// 模式处理辅助函数
|
|
@@ -8,6 +8,12 @@ import { type Expression, type JSXAttribute, type JSXElement } from '@babel/type
|
|
|
8
8
|
* 获取 JSX 元素的名称
|
|
9
9
|
*/
|
|
10
10
|
export declare function getJSXElementName(node: JSXElement): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* 将 JSX 元素名称节点转换为 createView 的类型表达式
|
|
13
|
+
* JSXIdentifier → Identifier(组件)或 StringLiteral(原生元素)
|
|
14
|
+
* JSXMemberExpression → MemberExpression(如 Obj.Key → Obj.Key)
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveJSXElementType(node: JSXElement): t.Expression | null;
|
|
11
17
|
/**
|
|
12
18
|
* 判断名称是否为纯编译组件
|
|
13
19
|
*/
|
|
@@ -26,10 +32,13 @@ export declare function isNativeElement(name: string): boolean;
|
|
|
26
32
|
export declare function getJSXAttributeByName(node: JSXElement, name: string): JSXAttribute | undefined;
|
|
27
33
|
/**
|
|
28
34
|
* 检查元素是否具有指定指令
|
|
35
|
+
* v-if/v-else-if/v-else 仅支持 JSXIdentifier 格式(v-if),不支持命名空间格式(v:if)
|
|
36
|
+
* 其他指令(如 v-show)支持 v-xxx:arg 命名空间格式
|
|
29
37
|
*/
|
|
30
38
|
export declare function hasDirective(node: JSXElement, directiveName: string): boolean;
|
|
31
39
|
/**
|
|
32
40
|
* 获取指令的值
|
|
41
|
+
* v-if/v-else-if/v-else 仅支持 JSXIdentifier 格式(v-if),不支持命名空间格式(v:if)
|
|
33
42
|
*/
|
|
34
43
|
export declare function getDirectiveValue(node: JSXElement, directiveName: string): Expression | null;
|
|
35
44
|
/**
|
|
@@ -54,6 +63,7 @@ export declare function isVElse(node: JSXElement): boolean;
|
|
|
54
63
|
export declare function removeVDirectives(node: JSXElement): void;
|
|
55
64
|
/**
|
|
56
65
|
* 移除元素上的 v-if 链指令(v-if、v-else-if、v-else)
|
|
66
|
+
* 仅处理 JSXIdentifier 格式,不支持命名空间格式(v:if)
|
|
57
67
|
*/
|
|
58
68
|
export declare function removeVIfChainDirectives(node: JSXElement): void;
|
|
59
69
|
/**
|