@vitarx/plugin-vite 0.0.1-alpha.0
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/LICENSE +27 -0
- package/README.md +298 -0
- package/dist/components.js +1 -0
- package/dist/constants/index.js +59 -0
- package/dist/context.js +78 -0
- package/dist/error.js +156 -0
- package/dist/hmr-client/index.js +143 -0
- package/dist/hmr-client/update.js +53 -0
- package/dist/hmr-client/utils.js +125 -0
- package/dist/index.js +65 -0
- package/dist/passes/components/ifBlock.js +42 -0
- package/dist/passes/components/index.js +24 -0
- package/dist/passes/components/switch.js +102 -0
- package/dist/passes/directives/index.js +6 -0
- package/dist/passes/directives/processDirectives.js +46 -0
- package/dist/passes/directives/vIf.js +64 -0
- package/dist/passes/hmr/index.js +5 -0
- package/dist/passes/hmr/inject.js +189 -0
- package/dist/passes/imports/collectImports.js +56 -0
- package/dist/passes/imports/collectRefVariables.js +95 -0
- package/dist/passes/imports/index.js +7 -0
- package/dist/passes/imports/injectImports.js +96 -0
- package/dist/passes/index.js +16 -0
- package/dist/passes/jsx/index.js +7 -0
- package/dist/passes/jsx/processChildren.js +114 -0
- package/dist/passes/jsx/processJSXElement.js +173 -0
- package/dist/passes/jsx/processJSXFragment.js +37 -0
- package/dist/passes/props/attribute.js +165 -0
- package/dist/passes/props/index.js +94 -0
- package/dist/passes/props/types.js +1 -0
- package/dist/passes/props/vmodel.js +115 -0
- package/dist/transform.js +144 -0
- package/dist/utils/ast-builders.js +142 -0
- package/dist/utils/ast-guards.js +16 -0
- package/dist/utils/branch-factory.js +107 -0
- package/dist/utils/component-collect.js +233 -0
- package/dist/utils/generate.js +11 -0
- package/dist/utils/index.js +16 -0
- package/dist/utils/jsx-helpers.js +168 -0
- package/dist/utils/pattern-helpers.js +47 -0
- package/dist/utils/vif-helpers.js +127 -0
- package/package.json +63 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR 代码注入模块
|
|
3
|
+
* 在 HMR 模式下为组件函数注入热更新支持代码
|
|
4
|
+
* @module passes/hmr
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
import { HMR } from '../../constants/index.js';
|
|
8
|
+
import { collectPatternBindings } from '../../utils/index.js';
|
|
9
|
+
/** getComponentView 的内部别名,避免与用户代码冲突 */
|
|
10
|
+
const GET_COMPONENT_VIEW_ALIAS = '__$VITARX_GET_COMPONENT_VIEW$__';
|
|
11
|
+
/**
|
|
12
|
+
* 注入 HMR 客户端导入
|
|
13
|
+
* @param program - AST Program 节点
|
|
14
|
+
*/
|
|
15
|
+
function injectHMRImport(program) {
|
|
16
|
+
const importDecl = t.importDeclaration([t.importDefaultSpecifier(t.identifier(HMR.manager))], t.stringLiteral('@vitarx/vite-plugin/hmr-client'));
|
|
17
|
+
program.body.unshift(importDecl);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 注入 getComponentView 导入
|
|
21
|
+
* 使用唯一的别名避免与用户代码冲突
|
|
22
|
+
* @param program - AST Program 节点
|
|
23
|
+
*/
|
|
24
|
+
function injectGetComponentViewImport(program) {
|
|
25
|
+
// 创建独立的 import 语句,使用不会冲突的别名
|
|
26
|
+
const importDecl = t.importDeclaration([t.importSpecifier(t.identifier(GET_COMPONENT_VIEW_ALIAS), t.identifier('getComponentView'))], t.stringLiteral('vitarx'));
|
|
27
|
+
program.body.unshift(importDecl);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 创建组件函数体内的 HMR 注册代码
|
|
31
|
+
* @param variableNames - 需要追踪的变量名列表
|
|
32
|
+
* @returns HMR 注册语句数组
|
|
33
|
+
*/
|
|
34
|
+
function createHMRRegistrationStatements(variableNames) {
|
|
35
|
+
const statements = [];
|
|
36
|
+
// 声明并获取当前视图实例
|
|
37
|
+
statements.push(t.variableDeclaration('const', [
|
|
38
|
+
t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), []))
|
|
39
|
+
]));
|
|
40
|
+
// 注册到 HMR 管理器
|
|
41
|
+
statements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('register')), [t.identifier(HMR.view)])));
|
|
42
|
+
// 创建状态追踪对象
|
|
43
|
+
const stateProperties = variableNames.map(name => t.objectMethod('get', t.identifier(name), [], t.blockStatement([t.returnStatement(t.identifier(name))])));
|
|
44
|
+
// 异步设置状态
|
|
45
|
+
statements.push(t.expressionStatement(t.logicalExpression('&&', t.identifier(HMR.view), t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.identifier('Promise'), t.identifier('resolve')), []), t.identifier('then')), [
|
|
46
|
+
t.arrowFunctionExpression([], t.blockStatement([
|
|
47
|
+
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(HMR.view), t.identifier(HMR.state)), t.objectExpression(stateProperties)))
|
|
48
|
+
]))
|
|
49
|
+
]))));
|
|
50
|
+
return statements;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 从函数体中收集局部变量名
|
|
54
|
+
* @param functionBody - 函数体语句块
|
|
55
|
+
* @returns 变量名数组
|
|
56
|
+
*/
|
|
57
|
+
function collectLocalVariableNames(functionBody) {
|
|
58
|
+
const variableNames = new Set();
|
|
59
|
+
for (const stmt of functionBody.body) {
|
|
60
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
61
|
+
for (const decl of stmt.declarations) {
|
|
62
|
+
if (decl.id.type !== 'VoidPattern') {
|
|
63
|
+
collectPatternBindings(decl.id, variableNames);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return Array.from(variableNames);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 判断表达式是否为函数类型
|
|
72
|
+
* 包括:箭头函数、函数表达式、类表达式
|
|
73
|
+
*/
|
|
74
|
+
function isFunctionExpression(expr) {
|
|
75
|
+
if (!expr)
|
|
76
|
+
return false;
|
|
77
|
+
return (expr.type === 'ArrowFunctionExpression' ||
|
|
78
|
+
expr.type === 'FunctionExpression' ||
|
|
79
|
+
expr.type === 'ClassExpression');
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 创建状态恢复表达式
|
|
83
|
+
* 格式:__$VITARX_HMR$__.instance.memo(__$VITARX_HMR_VIEW_NODE$__, '变量名') ?? 原始初始值
|
|
84
|
+
*/
|
|
85
|
+
function createMemoExpression(variableName, originalInit) {
|
|
86
|
+
return t.logicalExpression('??', t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('memo')), [t.identifier(HMR.view), t.stringLiteral(variableName)]), originalInit);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 为单个变量声明注入状态恢复代码
|
|
90
|
+
* 仅处理标识符类型的变量声明,且初始值不是函数
|
|
91
|
+
*/
|
|
92
|
+
function injectStatePreservationForDeclaration(decl) {
|
|
93
|
+
// 只处理标识符类型的变量声明
|
|
94
|
+
if (decl.id.type !== 'Identifier')
|
|
95
|
+
return;
|
|
96
|
+
// 没有初始值,不需要处理
|
|
97
|
+
if (!decl.init)
|
|
98
|
+
return;
|
|
99
|
+
// 初始值是函数,不需要状态恢复
|
|
100
|
+
if (isFunctionExpression(decl.init))
|
|
101
|
+
return;
|
|
102
|
+
// 注入状态恢复代码
|
|
103
|
+
decl.init = createMemoExpression(decl.id.name, decl.init);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 为函数体内的变量声明注入状态恢复代码
|
|
107
|
+
*/
|
|
108
|
+
function injectStatePreservation(functionBody) {
|
|
109
|
+
for (const stmt of functionBody.body) {
|
|
110
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
111
|
+
for (const decl of stmt.declarations) {
|
|
112
|
+
injectStatePreservationForDeclaration(decl);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 为组件函数注入 HMR 注册代码
|
|
119
|
+
* @param func - 函数声明/表达式/箭头函数
|
|
120
|
+
* @param variableNames - 需要追踪的变量名列表
|
|
121
|
+
*/
|
|
122
|
+
function injectHMRIntoFunction(func, variableNames) {
|
|
123
|
+
// 处理箭头函数没有函数体的情况(表达式体)
|
|
124
|
+
if (func.type === 'ArrowFunctionExpression' && func.body.type !== 'BlockStatement') {
|
|
125
|
+
// 将表达式体转换为块语句
|
|
126
|
+
const returnStmt = t.returnStatement(func.body);
|
|
127
|
+
func.body = t.blockStatement([returnStmt]);
|
|
128
|
+
}
|
|
129
|
+
if (!func.body || func.body.type !== 'BlockStatement')
|
|
130
|
+
return;
|
|
131
|
+
// 注入状态恢复代码
|
|
132
|
+
injectStatePreservation(func.body);
|
|
133
|
+
// 注入 HMR 注册代码
|
|
134
|
+
const statements = createHMRRegistrationStatements(variableNames);
|
|
135
|
+
func.body.body.unshift(...statements);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 创建 bindId 语句
|
|
139
|
+
*/
|
|
140
|
+
function createBindIdStatement(componentName, componentId) {
|
|
141
|
+
return t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('bindId')), [t.identifier(componentName), t.stringLiteral(componentId)]));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 创建 import.meta.hot.accept 语句
|
|
145
|
+
*/
|
|
146
|
+
function createHotAcceptStatement() {
|
|
147
|
+
return t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.identifier('import'), t.identifier('meta')), t.identifier('hot')), t.identifier('accept')), [
|
|
148
|
+
t.arrowFunctionExpression([t.identifier('mod')], t.blockStatement([
|
|
149
|
+
t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('update')), [t.identifier('mod')]))
|
|
150
|
+
]))
|
|
151
|
+
]));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* 生成组件唯一 ID(文件路径 + 组件名称)
|
|
155
|
+
*/
|
|
156
|
+
function generateComponentId(filename, componentName) {
|
|
157
|
+
const combined = `${filename}:${componentName}`;
|
|
158
|
+
let hash = 0;
|
|
159
|
+
for (let i = 0; i < combined.length; i++) {
|
|
160
|
+
hash = ((hash << 5) - hash + combined.charCodeAt(i)) | 0;
|
|
161
|
+
}
|
|
162
|
+
return Math.abs(hash).toString(16);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* 注入 HMR 支持
|
|
166
|
+
* 主入口函数,为所有组件注入完整的 HMR 支持
|
|
167
|
+
* @param program - AST Program 节点
|
|
168
|
+
* @param components - 组件信息列表
|
|
169
|
+
* @param filename - 文件名
|
|
170
|
+
*/
|
|
171
|
+
export function injectHMRSupport(program, components, filename) {
|
|
172
|
+
if (components.length === 0)
|
|
173
|
+
return;
|
|
174
|
+
// 注入必要的导入
|
|
175
|
+
injectHMRImport(program);
|
|
176
|
+
injectGetComponentViewImport(program);
|
|
177
|
+
// 为每个组件函数注入 HMR 代码
|
|
178
|
+
for (const { node } of components) {
|
|
179
|
+
// 收集局部变量名(如果有函数体)
|
|
180
|
+
const variableNames = node.body?.type === 'BlockStatement' ? collectLocalVariableNames(node.body) : [];
|
|
181
|
+
injectHMRIntoFunction(node, variableNames);
|
|
182
|
+
}
|
|
183
|
+
// 为每个组件绑定 ID
|
|
184
|
+
for (const { name } of components) {
|
|
185
|
+
program.body.push(createBindIdStatement(name, generateComponentId(filename, name)));
|
|
186
|
+
}
|
|
187
|
+
// 注入 import.meta.hot.accept
|
|
188
|
+
program.body.push(createHotAcceptStatement());
|
|
189
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { VITARX_MODULE } from '../../constants/index.js';
|
|
2
|
+
import { collectPatternBindings } from '../../utils/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* 收集现有导入信息
|
|
5
|
+
* @param program - AST Program 节点
|
|
6
|
+
* @returns 本地变量名集合和 vitarx 导入映射
|
|
7
|
+
*/
|
|
8
|
+
export function collectExistingImports(program) {
|
|
9
|
+
const localNames = new Set();
|
|
10
|
+
const vitarxImports = new Map();
|
|
11
|
+
for (const node of program.body) {
|
|
12
|
+
if (node.type !== 'ImportDeclaration')
|
|
13
|
+
continue;
|
|
14
|
+
const source = node.source.value;
|
|
15
|
+
for (const specifier of node.specifiers) {
|
|
16
|
+
localNames.add(specifier.local.name);
|
|
17
|
+
if (source === VITARX_MODULE && specifier.type === 'ImportSpecifier') {
|
|
18
|
+
const importedName = specifier.imported.type === 'Identifier'
|
|
19
|
+
? specifier.imported.name
|
|
20
|
+
: specifier.imported.value;
|
|
21
|
+
vitarxImports.set(importedName, specifier.local.name);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { localNames, vitarxImports };
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 收集本地变量绑定
|
|
29
|
+
* 包括导入绑定、变量声明、函数声明、类声明
|
|
30
|
+
* @param program - AST Program 节点
|
|
31
|
+
* @returns 本地变量名集合
|
|
32
|
+
*/
|
|
33
|
+
export function collectLocalBindings(program) {
|
|
34
|
+
const bindings = new Set();
|
|
35
|
+
for (const node of program.body) {
|
|
36
|
+
if (node.type === 'ImportDeclaration') {
|
|
37
|
+
for (const specifier of node.specifiers) {
|
|
38
|
+
bindings.add(specifier.local.name);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (node.type === 'VariableDeclaration') {
|
|
42
|
+
for (const decl of node.declarations) {
|
|
43
|
+
if (decl.id.type !== 'VoidPattern') {
|
|
44
|
+
collectPatternBindings(decl.id, bindings);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (node.type === 'FunctionDeclaration' && node.id) {
|
|
49
|
+
bindings.add(node.id.name);
|
|
50
|
+
}
|
|
51
|
+
if (node.type === 'ClassDeclaration' && node.id) {
|
|
52
|
+
bindings.add(node.id.name);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return bindings;
|
|
56
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { REF_APIS, RESPONSIVE_MODULES } from '../../constants/index.js';
|
|
2
|
+
import { collectPatternBindings, collectObjectPatternBindings } from '../../utils/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* 收集 ref API 的别名
|
|
5
|
+
* 识别从 vitarx 或 @vitarx/responsive 导入的 ref/toRef/toRefs/shallowRef/computed
|
|
6
|
+
* @param program - AST Program 节点
|
|
7
|
+
* @returns ref API 别名映射
|
|
8
|
+
*/
|
|
9
|
+
export function collectRefApiAliases(program) {
|
|
10
|
+
const aliases = {
|
|
11
|
+
ref: null,
|
|
12
|
+
toRef: null,
|
|
13
|
+
toRefs: null,
|
|
14
|
+
shallowRef: null,
|
|
15
|
+
computed: null
|
|
16
|
+
};
|
|
17
|
+
for (const node of program.body) {
|
|
18
|
+
if (node.type !== 'ImportDeclaration')
|
|
19
|
+
continue;
|
|
20
|
+
const source = node.source.value;
|
|
21
|
+
if (!RESPONSIVE_MODULES.includes(source))
|
|
22
|
+
continue;
|
|
23
|
+
for (const specifier of node.specifiers) {
|
|
24
|
+
if (specifier.type !== 'ImportSpecifier')
|
|
25
|
+
continue;
|
|
26
|
+
const importedName = specifier.imported.type === 'Identifier'
|
|
27
|
+
? specifier.imported.name
|
|
28
|
+
: specifier.imported.value;
|
|
29
|
+
if (Object.values(REF_APIS).includes(importedName)) {
|
|
30
|
+
aliases[importedName] = specifier.local.name;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return aliases;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 收集通过 ref API 定义的变量
|
|
38
|
+
* 包括直接赋值和 toRefs 解构
|
|
39
|
+
* @param program - AST Program 节点
|
|
40
|
+
* @param refApiAliases - ref API 别名映射
|
|
41
|
+
* @returns ref 变量名集合
|
|
42
|
+
*/
|
|
43
|
+
export function collectRefVariables(program, refApiAliases) {
|
|
44
|
+
const refVariables = new Set();
|
|
45
|
+
const { refApiLocalNames, toRefsLocalNames } = buildApiNameSets(refApiAliases);
|
|
46
|
+
for (const node of program.body) {
|
|
47
|
+
if (node.type !== 'VariableDeclaration')
|
|
48
|
+
continue;
|
|
49
|
+
for (const decl of node.declarations) {
|
|
50
|
+
if (!decl.init)
|
|
51
|
+
continue;
|
|
52
|
+
if (decl.id.type === 'VoidPattern')
|
|
53
|
+
continue;
|
|
54
|
+
const init = decl.init;
|
|
55
|
+
if (init.type !== 'CallExpression' || init.callee.type !== 'Identifier') {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const calleeName = init.callee.name;
|
|
59
|
+
if (toRefsLocalNames.has(calleeName) && decl.id.type === 'ObjectPattern') {
|
|
60
|
+
collectObjectPatternBindings(decl.id, refVariables);
|
|
61
|
+
}
|
|
62
|
+
else if (refApiLocalNames.has(calleeName)) {
|
|
63
|
+
collectPatternBindings(decl.id, refVariables);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return refVariables;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 构建 API 名称集合
|
|
71
|
+
*/
|
|
72
|
+
function buildApiNameSets(refApiAliases) {
|
|
73
|
+
const refApiLocalNames = new Set();
|
|
74
|
+
const toRefsLocalNames = new Set();
|
|
75
|
+
for (const key of Object.keys(refApiAliases)) {
|
|
76
|
+
const alias = refApiAliases[key];
|
|
77
|
+
if (alias) {
|
|
78
|
+
if (key === 'toRefs') {
|
|
79
|
+
toRefsLocalNames.add(alias);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
refApiLocalNames.add(alias);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
if (key === 'toRefs') {
|
|
87
|
+
toRefsLocalNames.add(key);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
refApiLocalNames.add(key);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return { refApiLocalNames, toRefsLocalNames };
|
|
95
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 导入处理模块
|
|
3
|
+
* @module passes/imports
|
|
4
|
+
*/
|
|
5
|
+
export { collectExistingImports, collectLocalBindings } from './collectImports.js';
|
|
6
|
+
export { collectRefApiAliases, collectRefVariables } from './collectRefVariables.js';
|
|
7
|
+
export { injectImports } from './injectImports.js';
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 导入注入模块
|
|
3
|
+
* 负责动态注入 vitarx 导入语句
|
|
4
|
+
* @module passes/imports/injectImports
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
import { VITARX_MODULE } from '../../constants/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* API 导入配置
|
|
10
|
+
* 定义需要注入的 API 及其与 ImportInfo 的映射关系
|
|
11
|
+
*/
|
|
12
|
+
const API_IMPORT_CONFIG = [
|
|
13
|
+
{ name: 'createView', importKey: 'createView' },
|
|
14
|
+
{ name: 'Fragment', importKey: 'Fragment' },
|
|
15
|
+
{ name: 'branch', importKey: 'branch' },
|
|
16
|
+
{ name: 'dynamic', importKey: 'dynamic' },
|
|
17
|
+
{ name: 'access', importKey: 'access' },
|
|
18
|
+
{ name: 'withDirectives', importKey: 'withDirectives' },
|
|
19
|
+
{ name: 'unref', importKey: 'unref' },
|
|
20
|
+
{ name: 'isRef', importKey: 'isRef' }
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* 注入 vitarx 导入
|
|
24
|
+
* 根据已使用的 API 动态生成导入语句
|
|
25
|
+
* @param program - AST Program 节点
|
|
26
|
+
* @param ctx - 转换上下文
|
|
27
|
+
*/
|
|
28
|
+
export function injectImports(program, ctx) {
|
|
29
|
+
if (!needsInject(ctx))
|
|
30
|
+
return;
|
|
31
|
+
const existingVitarxImport = findExistingVitarxImport(program);
|
|
32
|
+
if (existingVitarxImport) {
|
|
33
|
+
appendImportSpecifiers(existingVitarxImport, ctx);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const specifiers = buildImportSpecifiers(ctx);
|
|
37
|
+
if (specifiers.length === 0)
|
|
38
|
+
return;
|
|
39
|
+
const importDecl = t.importDeclaration(specifiers, t.stringLiteral(VITARX_MODULE));
|
|
40
|
+
program.body.unshift(importDecl);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 查找已存在的 vitarx 导入语句
|
|
45
|
+
*/
|
|
46
|
+
function findExistingVitarxImport(program) {
|
|
47
|
+
for (const node of program.body) {
|
|
48
|
+
if (node.type === 'ImportDeclaration' && node.source.value === VITARX_MODULE) {
|
|
49
|
+
return node;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 向已存在的导入语句追加说明符
|
|
56
|
+
*/
|
|
57
|
+
function appendImportSpecifiers(importDecl, ctx) {
|
|
58
|
+
const existingLocals = new Set();
|
|
59
|
+
for (const spec of importDecl.specifiers) {
|
|
60
|
+
if (spec.type === 'ImportSpecifier') {
|
|
61
|
+
existingLocals.add(spec.local.name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const { name, importKey } of API_IMPORT_CONFIG) {
|
|
65
|
+
if (!ctx.imports[importKey])
|
|
66
|
+
continue;
|
|
67
|
+
const alias = ctx.vitarxAliases[name];
|
|
68
|
+
const localName = alias || name;
|
|
69
|
+
if (existingLocals.has(localName))
|
|
70
|
+
continue;
|
|
71
|
+
const imported = t.identifier(name);
|
|
72
|
+
const local = t.identifier(localName);
|
|
73
|
+
importDecl.specifiers.push(t.importSpecifier(local, imported));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 检查是否需要注入导入
|
|
78
|
+
*/
|
|
79
|
+
function needsInject(ctx) {
|
|
80
|
+
return Object.values(ctx.imports).some(Boolean);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 构建导入说明符列表
|
|
84
|
+
*/
|
|
85
|
+
function buildImportSpecifiers(ctx) {
|
|
86
|
+
const specifiers = [];
|
|
87
|
+
for (const { name, importKey } of API_IMPORT_CONFIG) {
|
|
88
|
+
if (ctx.imports[importKey]) {
|
|
89
|
+
const alias = ctx.vitarxAliases[name];
|
|
90
|
+
const imported = t.identifier(name);
|
|
91
|
+
const local = alias ? t.identifier(alias) : imported;
|
|
92
|
+
specifiers.push(t.importSpecifier(local, imported));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return specifiers;
|
|
96
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 编译转换处理模块
|
|
3
|
+
* @module passes
|
|
4
|
+
*/
|
|
5
|
+
// 导入处理
|
|
6
|
+
export { collectExistingImports, collectLocalBindings, collectRefApiAliases, collectRefVariables, injectImports } from './imports/index.js';
|
|
7
|
+
// 编译宏组件
|
|
8
|
+
export { processPureCompileComponent, processSwitch, processIfBlock } from './components/index.js';
|
|
9
|
+
// 指令处理
|
|
10
|
+
export { processVIfChain, processDirectives } from './directives/index.js';
|
|
11
|
+
// JSX 处理
|
|
12
|
+
export { processChildren, processJSXElement, transformJSXElement, processJSXFragment } from './jsx/index.js';
|
|
13
|
+
// Props 处理
|
|
14
|
+
export { processProps } from './props/index.js';
|
|
15
|
+
// HMR 注入
|
|
16
|
+
export { injectHMRSupport } from './hmr/index.js';
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 子元素处理模块
|
|
3
|
+
* 处理 JSX 元素的子节点
|
|
4
|
+
* @module passes/jsx/processChildren
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
import { isBooleanLiteral, isConditionalExpression, isIdentifier, isJSXElement, isJSXExpressionContainer, isJSXFragment, isJSXText, isLogicalExpression, isMemberExpression, isNumericLiteral, isStringLiteral } from '@babel/types';
|
|
8
|
+
import { markImport } from '../../context.js';
|
|
9
|
+
import { addPureComment, createAccessCall, createBinaryBranch, createDynamicCall, getAlias } from '../../utils/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* 处理子节点数组
|
|
12
|
+
* @param children - 子节点数组
|
|
13
|
+
* @param ctx - 转换上下文
|
|
14
|
+
* @returns 处理后的表达式数组
|
|
15
|
+
*/
|
|
16
|
+
export function processChildren(children, ctx) {
|
|
17
|
+
const result = [];
|
|
18
|
+
for (const child of children) {
|
|
19
|
+
const processed = processChildNode(child, ctx);
|
|
20
|
+
if (processed !== null) {
|
|
21
|
+
result.push(processed);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 处理单个子节点
|
|
28
|
+
*/
|
|
29
|
+
function processChildNode(node, ctx) {
|
|
30
|
+
// JSX 文本
|
|
31
|
+
if (isJSXText(node)) {
|
|
32
|
+
const trimmed = node.value.trim();
|
|
33
|
+
if (!trimmed)
|
|
34
|
+
return null;
|
|
35
|
+
return t.stringLiteral(trimmed);
|
|
36
|
+
}
|
|
37
|
+
// JSX 表达式容器
|
|
38
|
+
if (isJSXExpressionContainer(node)) {
|
|
39
|
+
if (node.expression.type === 'JSXEmptyExpression')
|
|
40
|
+
return null;
|
|
41
|
+
return processChildExpression(node.expression, ctx);
|
|
42
|
+
}
|
|
43
|
+
// JSX 展开子元素
|
|
44
|
+
if (node.type === 'JSXSpreadChild') {
|
|
45
|
+
return processChildExpression(node.expression, ctx);
|
|
46
|
+
}
|
|
47
|
+
// JSX 元素或片段
|
|
48
|
+
if (isJSXElement(node) || isJSXFragment(node)) {
|
|
49
|
+
return node;
|
|
50
|
+
}
|
|
51
|
+
// 字面量
|
|
52
|
+
if (isStringLiteral(node) || isNumericLiteral(node) || isBooleanLiteral(node)) {
|
|
53
|
+
return node;
|
|
54
|
+
}
|
|
55
|
+
// 标识符
|
|
56
|
+
if (isIdentifier(node)) {
|
|
57
|
+
return node;
|
|
58
|
+
}
|
|
59
|
+
// 成员表达式
|
|
60
|
+
if (isMemberExpression(node)) {
|
|
61
|
+
markImport(ctx, 'access');
|
|
62
|
+
const accessAlias = getAlias(ctx.vitarxAliases, 'access');
|
|
63
|
+
return createAccessCall(node.object, node.property, accessAlias);
|
|
64
|
+
}
|
|
65
|
+
// 条件表达式
|
|
66
|
+
if (isConditionalExpression(node)) {
|
|
67
|
+
return processConditionalExpression(node, ctx);
|
|
68
|
+
}
|
|
69
|
+
// 逻辑表达式
|
|
70
|
+
if (isLogicalExpression(node)) {
|
|
71
|
+
markImport(ctx, 'dynamic');
|
|
72
|
+
const dynamicAlias = getAlias(ctx.vitarxAliases, 'dynamic');
|
|
73
|
+
return addPureComment(createDynamicCall(node, dynamicAlias));
|
|
74
|
+
}
|
|
75
|
+
// 调用表达式
|
|
76
|
+
if (node.type === 'CallExpression') {
|
|
77
|
+
return node;
|
|
78
|
+
}
|
|
79
|
+
return node;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 处理子表达式
|
|
83
|
+
*/
|
|
84
|
+
function processChildExpression(expr, ctx) {
|
|
85
|
+
if (isIdentifier(expr)) {
|
|
86
|
+
return expr;
|
|
87
|
+
}
|
|
88
|
+
if (isMemberExpression(expr)) {
|
|
89
|
+
markImport(ctx, 'access');
|
|
90
|
+
const accessAlias = getAlias(ctx.vitarxAliases, 'access');
|
|
91
|
+
return createAccessCall(expr.object, expr.property, accessAlias);
|
|
92
|
+
}
|
|
93
|
+
if (isConditionalExpression(expr)) {
|
|
94
|
+
return processConditionalExpression(expr, ctx);
|
|
95
|
+
}
|
|
96
|
+
if (isLogicalExpression(expr)) {
|
|
97
|
+
markImport(ctx, 'dynamic');
|
|
98
|
+
const dynamicAlias = getAlias(ctx.vitarxAliases, 'dynamic');
|
|
99
|
+
return addPureComment(createDynamicCall(expr, dynamicAlias));
|
|
100
|
+
}
|
|
101
|
+
return expr;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 处理条件表达式
|
|
105
|
+
* 转换为 branch 调用
|
|
106
|
+
*/
|
|
107
|
+
function processConditionalExpression(node, ctx) {
|
|
108
|
+
const { test, consequent, alternate } = node;
|
|
109
|
+
// 处理分支
|
|
110
|
+
const processedConsequent = processChildNode(consequent, ctx) || t.nullLiteral();
|
|
111
|
+
const processedAlternate = processChildNode(alternate, ctx) || t.nullLiteral();
|
|
112
|
+
// 使用公共的 createBinaryBranch
|
|
113
|
+
return createBinaryBranch(test, processedConsequent, processedAlternate, ctx);
|
|
114
|
+
}
|