@vitarx/plugin-vite 0.0.1-beta.7 → 1.0.0-beta.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/README.md +64 -4
- package/dist/constants/index.d.ts +1 -11
- package/dist/constants/index.js +1 -11
- package/dist/context.d.ts +14 -10
- package/dist/context.js +7 -5
- package/dist/hmr-client/index.d.ts +2 -1
- package/dist/hmr-client/index.js +3 -2
- package/dist/hmr-client/utils.d.ts +1 -1
- package/dist/hmr-client/utils.js +7 -4
- package/dist/index.js +10 -4
- package/dist/passes/components/ifBlock.js +2 -4
- package/dist/passes/components/switch.js +2 -4
- package/dist/passes/directives/processDirectives.js +1 -1
- package/dist/passes/directives/vIf.js +2 -5
- package/dist/passes/hmr/inject-imports.d.ts +19 -0
- package/dist/passes/hmr/inject-imports.js +26 -0
- package/dist/passes/hmr/inject-state.d.ts +29 -0
- package/dist/passes/hmr/inject-state.js +101 -0
- package/dist/passes/hmr/inject.d.ts +1 -1
- package/dist/passes/hmr/inject.js +13 -123
- package/dist/passes/imports/injectImports.js +2 -2
- package/dist/passes/jsx/processChildren.js +30 -51
- package/dist/passes/jsx/processJSXElement.js +3 -6
- package/dist/passes/jsx/processJSXFragment.js +4 -4
- package/dist/passes/props/index.js +1 -8
- package/dist/transform.d.ts +5 -8
- package/dist/transform.js +19 -20
- package/dist/types.d.ts +11 -0
- package/dist/utils/ast-builders.d.ts +18 -18
- package/dist/utils/ast-builders.js +27 -41
- package/dist/utils/branch-factory.d.ts +1 -4
- package/dist/utils/branch-factory.js +4 -7
- package/dist/utils/collect-names.d.ts +37 -0
- package/dist/utils/collect-names.js +129 -0
- package/dist/utils/component-collect.d.ts +9 -4
- package/dist/utils/component-collect.js +64 -74
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +2 -2
- package/dist/utils/jsx-helpers.d.ts +3 -2
- package/dist/utils/jsx-helpers.js +3 -2
- package/package.json +11 -4
|
@@ -5,133 +5,28 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
7
|
import { HMR } from '../../constants/index.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
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
|
-
}
|
|
8
|
+
import { injectHMRImport, injectGetComponentViewImport } from './inject-imports.js';
|
|
9
|
+
import { createHMRRegistrationStatements, createWrappedComponentHMRStatements, collectLocalVariableNames, injectStatePreservation } from './inject-state.js';
|
|
117
10
|
/**
|
|
118
11
|
* 为组件函数注入 HMR 注册代码
|
|
119
12
|
* @param func - 函数声明/表达式/箭头函数
|
|
120
13
|
* @param variableNames - 需要追踪的变量名列表
|
|
14
|
+
* @param isWrapped - 是否为包装组件
|
|
15
|
+
* @param componentName - 组件名称(包装组件需要)
|
|
121
16
|
*/
|
|
122
|
-
function injectHMRIntoFunction(func, variableNames) {
|
|
123
|
-
// 处理箭头函数没有函数体的情况(表达式体)
|
|
17
|
+
function injectHMRIntoFunction(func, variableNames, isWrapped = false, componentName) {
|
|
124
18
|
if (func.type === 'ArrowFunctionExpression' && func.body.type !== 'BlockStatement') {
|
|
125
|
-
// 将表达式体转换为块语句
|
|
126
19
|
const returnStmt = t.returnStatement(func.body);
|
|
127
20
|
func.body = t.blockStatement([returnStmt]);
|
|
128
21
|
}
|
|
129
22
|
if (!func.body || func.body.type !== 'BlockStatement')
|
|
130
23
|
return;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const statements =
|
|
24
|
+
if (!isWrapped) {
|
|
25
|
+
injectStatePreservation(func.body);
|
|
26
|
+
}
|
|
27
|
+
const statements = isWrapped
|
|
28
|
+
? createWrappedComponentHMRStatements(componentName)
|
|
29
|
+
: createHMRRegistrationStatements(variableNames);
|
|
135
30
|
func.body.body.unshift(...statements);
|
|
136
31
|
}
|
|
137
32
|
/**
|
|
@@ -171,19 +66,14 @@ function generateComponentId(filename, componentName) {
|
|
|
171
66
|
export function injectHMRSupport(program, components, filename) {
|
|
172
67
|
if (components.length === 0)
|
|
173
68
|
return;
|
|
174
|
-
// 注入必要的导入
|
|
175
69
|
injectHMRImport(program);
|
|
176
70
|
injectGetComponentViewImport(program);
|
|
177
|
-
|
|
178
|
-
for (const { node } of components) {
|
|
179
|
-
// 收集局部变量名(如果有函数体)
|
|
71
|
+
for (const { node, isWrapped, name } of components) {
|
|
180
72
|
const variableNames = node.body?.type === 'BlockStatement' ? collectLocalVariableNames(node.body) : [];
|
|
181
|
-
injectHMRIntoFunction(node, variableNames);
|
|
73
|
+
injectHMRIntoFunction(node, variableNames, isWrapped, name);
|
|
182
74
|
}
|
|
183
|
-
// 为每个组件绑定 ID
|
|
184
75
|
for (const { name } of components) {
|
|
185
76
|
program.body.push(createBindIdStatement(name, generateComponentId(filename, name)));
|
|
186
77
|
}
|
|
187
|
-
// 注入 import.meta.hot.accept
|
|
188
78
|
program.body.push(createHotAcceptStatement());
|
|
189
79
|
}
|
|
@@ -13,8 +13,8 @@ const API_IMPORT_CONFIG = [
|
|
|
13
13
|
{ name: 'createView', importKey: 'createView' },
|
|
14
14
|
{ name: 'Fragment', importKey: 'Fragment' },
|
|
15
15
|
{ name: 'branch', importKey: 'branch' },
|
|
16
|
-
{ name: '
|
|
17
|
-
{ name: '
|
|
16
|
+
{ name: 'expr', importKey: 'expr' },
|
|
17
|
+
{ name: 'accessor', importKey: 'accessor' },
|
|
18
18
|
{ name: 'withDirectives', importKey: 'withDirectives' },
|
|
19
19
|
{ name: 'unref', importKey: 'unref' },
|
|
20
20
|
{ name: 'isRef', importKey: 'isRef' }
|
|
@@ -4,25 +4,28 @@
|
|
|
4
4
|
* @module passes/jsx/processChildren
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
|
-
import {
|
|
7
|
+
import { isBinaryExpression, isConditionalExpression, isIdentifier, isJSXElement, isJSXExpressionContainer, isJSXFragment, isJSXText, isLogicalExpression, isMemberExpression } from '@babel/types';
|
|
8
8
|
import { markImport } from '../../context.js';
|
|
9
|
-
import { addPureComment,
|
|
9
|
+
import { addPureComment, createAccessorCall, createBinaryBranch, createExprCall, getAlias } from '../../utils/index.js';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* 处理成员表达式,生成 accessor 调用
|
|
12
12
|
*/
|
|
13
13
|
function handleMemberExpression(expr, ctx) {
|
|
14
|
-
markImport(ctx, '
|
|
15
|
-
const
|
|
16
|
-
return
|
|
14
|
+
markImport(ctx, 'accessor');
|
|
15
|
+
const accessorAlias = getAlias(ctx.vitarxAliases, 'accessor');
|
|
16
|
+
return createAccessorCall(expr.object, expr.property, accessorAlias);
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* 使用 expr 包装表达式
|
|
20
|
+
* 用于逻辑表达式、二元表达式和调用表达式的统一包装
|
|
21
|
+
* 运行时根据返回值类型决定是否跟踪响应式:
|
|
22
|
+
* - 数组返回值:原样返回(仅渲染一次)
|
|
23
|
+
* - 其他返回值:创建 DynamicView 追踪
|
|
20
24
|
*/
|
|
21
|
-
function
|
|
22
|
-
markImport(ctx, '
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
return addPureComment(createDynamicCall(expr, dynamicAlias, locInfo));
|
|
25
|
+
function wrapWithExpr(expr, ctx) {
|
|
26
|
+
markImport(ctx, 'expr');
|
|
27
|
+
const exprAlias = getAlias(ctx.vitarxAliases, 'expr');
|
|
28
|
+
return addPureComment(createExprCall(expr, exprAlias), ctx);
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
28
31
|
* 处理子节点数组
|
|
@@ -42,59 +45,40 @@ export function processChildren(children, ctx) {
|
|
|
42
45
|
}
|
|
43
46
|
/**
|
|
44
47
|
* 处理单个子节点
|
|
48
|
+
* JSX 容器节点在此拆包后委托给 processChildExpression 处理
|
|
49
|
+
* 已转换的 API 调用(createView/branch 等 CallExpression)直接透传
|
|
45
50
|
*/
|
|
46
51
|
function processChildNode(node, ctx) {
|
|
47
|
-
// JSX 文本
|
|
48
52
|
if (isJSXText(node)) {
|
|
49
53
|
const trimmed = node.value.trim();
|
|
50
54
|
if (!trimmed)
|
|
51
55
|
return null;
|
|
52
56
|
return t.stringLiteral(trimmed);
|
|
53
57
|
}
|
|
54
|
-
// JSX 表达式容器
|
|
55
58
|
if (isJSXExpressionContainer(node)) {
|
|
56
59
|
if (node.expression.type === 'JSXEmptyExpression')
|
|
57
60
|
return null;
|
|
58
|
-
return processChildExpression(node.expression, ctx
|
|
61
|
+
return processChildExpression(node.expression, ctx);
|
|
59
62
|
}
|
|
60
|
-
// JSX 展开子元素
|
|
61
63
|
if (node.type === 'JSXSpreadChild') {
|
|
62
|
-
return processChildExpression(node.expression, ctx
|
|
64
|
+
return processChildExpression(node.expression, ctx);
|
|
63
65
|
}
|
|
64
|
-
// JSX 元素或片段
|
|
65
66
|
if (isJSXElement(node) || isJSXFragment(node)) {
|
|
66
67
|
return node;
|
|
67
68
|
}
|
|
68
|
-
//
|
|
69
|
-
if (isStringLiteral(node) || isNumericLiteral(node) || isBooleanLiteral(node)) {
|
|
70
|
-
return node;
|
|
71
|
-
}
|
|
72
|
-
// 标识符
|
|
73
|
-
if (isIdentifier(node)) {
|
|
74
|
-
return node;
|
|
75
|
-
}
|
|
76
|
-
// 成员表达式
|
|
77
|
-
if (isMemberExpression(node)) {
|
|
78
|
-
return handleMemberExpression(node, ctx);
|
|
79
|
-
}
|
|
80
|
-
// 条件表达式
|
|
81
|
-
if (isConditionalExpression(node)) {
|
|
82
|
-
return processConditionalExpression(node, ctx);
|
|
83
|
-
}
|
|
84
|
-
// 逻辑表达式
|
|
85
|
-
if (isLogicalExpression(node)) {
|
|
86
|
-
return handleLogicalExpression(node, ctx);
|
|
87
|
-
}
|
|
88
|
-
// 调用表达式
|
|
69
|
+
// 已转换的 API 调用(createView/branch 等)直接透传,不包装 expr
|
|
89
70
|
if (node.type === 'CallExpression') {
|
|
90
71
|
return node;
|
|
91
72
|
}
|
|
92
|
-
|
|
73
|
+
// 其他表达式类型委托给 processChildExpression
|
|
74
|
+
return processChildExpression(node, ctx);
|
|
93
75
|
}
|
|
94
76
|
/**
|
|
95
|
-
*
|
|
77
|
+
* 处理子表达式(来自 JSX 表达式容器或条件分支)
|
|
78
|
+
* 根据表达式类型生成对应的运行时 API 调用
|
|
79
|
+
* CallExpression 在此用 expr 包装(用户函数调用需要响应式追踪)
|
|
96
80
|
*/
|
|
97
|
-
function processChildExpression(expr, ctx
|
|
81
|
+
function processChildExpression(expr, ctx) {
|
|
98
82
|
if (isIdentifier(expr)) {
|
|
99
83
|
return expr;
|
|
100
84
|
}
|
|
@@ -104,22 +88,17 @@ function processChildExpression(expr, ctx, loc) {
|
|
|
104
88
|
if (isConditionalExpression(expr)) {
|
|
105
89
|
return processConditionalExpression(expr, ctx);
|
|
106
90
|
}
|
|
107
|
-
if (isLogicalExpression(expr)) {
|
|
108
|
-
return
|
|
91
|
+
if (isLogicalExpression(expr) || isBinaryExpression(expr) || expr.type === 'CallExpression') {
|
|
92
|
+
return wrapWithExpr(expr, ctx);
|
|
109
93
|
}
|
|
110
94
|
return expr;
|
|
111
95
|
}
|
|
112
96
|
/**
|
|
113
|
-
*
|
|
114
|
-
* 转换为 branch 调用
|
|
97
|
+
* 处理条件表达式,转换为 branch 调用
|
|
115
98
|
*/
|
|
116
99
|
function processConditionalExpression(node, ctx) {
|
|
117
100
|
const { test, consequent, alternate } = node;
|
|
118
|
-
// 处理分支
|
|
119
101
|
const processedConsequent = processChildNode(consequent, ctx) || t.nullLiteral();
|
|
120
102
|
const processedAlternate = processChildNode(alternate, ctx) || t.nullLiteral();
|
|
121
|
-
|
|
122
|
-
const locInfo = getDevLocInfo(ctx, node);
|
|
123
|
-
// 使用公共的 createBinaryBranch
|
|
124
|
-
return createBinaryBranch(test, processedConsequent, processedAlternate, ctx, locInfo);
|
|
103
|
+
return createBinaryBranch(test, processedConsequent, processedAlternate, ctx);
|
|
125
104
|
}
|
|
@@ -2,7 +2,7 @@ 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, getAlias, getDevLocInfo, getDirectiveValue, getJSXAttributeByName, getJSXElementName, hasEffectiveChildren,
|
|
5
|
+
import { addPureComment, createArrowFunction, createBranch, createCreateViewCall, filterEffectiveChildren, getAlias, getDevLocInfo, getDirectiveValue, getJSXAttributeByName, getJSXElementName, hasEffectiveChildren, isNativeElement, isVElse, isVIf, isVIfChain, removeVIfChainDirectives } from '../../utils/index.js';
|
|
6
6
|
import { processDirectives } from '../directives/index.js';
|
|
7
7
|
import { processProps } from '../props/index.js';
|
|
8
8
|
import { processChildren } from './processChildren.js';
|
|
@@ -87,7 +87,7 @@ export function transformJSXElement(node, ctx, handleVIf = false) {
|
|
|
87
87
|
viewCall = processDirectives(viewCall, directives, ctx);
|
|
88
88
|
}
|
|
89
89
|
else {
|
|
90
|
-
viewCall = addPureComment(viewCall);
|
|
90
|
+
viewCall = addPureComment(viewCall, ctx);
|
|
91
91
|
}
|
|
92
92
|
if (node.loc) {
|
|
93
93
|
viewCall.loc = node.loc;
|
|
@@ -146,10 +146,7 @@ function transformSingleVIf(node, ctx) {
|
|
|
146
146
|
const transformedNode = transformJSXElement(node, ctx, false);
|
|
147
147
|
if (!transformedNode)
|
|
148
148
|
return null;
|
|
149
|
-
|
|
150
|
-
const locInfo = getDevLocInfo(ctx, node);
|
|
151
|
-
// 生成 branch 调用
|
|
152
|
-
const branchCall = createBranch({ conditions: [condition], branches: [createArrowFunction(transformedNode)], locInfo }, ctx);
|
|
149
|
+
const branchCall = createBranch({ conditions: [condition], branches: [createArrowFunction(transformedNode)] }, ctx);
|
|
153
150
|
if (node.loc) {
|
|
154
151
|
branchCall.loc = node.loc;
|
|
155
152
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as t from '@babel/types';
|
|
2
|
-
import { addPureComment, createCreateViewCall, filterWhitespaceChildren, getAlias, getDevLocInfo,
|
|
2
|
+
import { addPureComment, createCreateViewCall, filterWhitespaceChildren, getAlias, getDevLocInfo, validateNoDirectMatchChild } from '../../utils/index.js';
|
|
3
3
|
import { processChildren } from './processChildren.js';
|
|
4
4
|
/**
|
|
5
5
|
* 处理 JSX Fragment
|
|
@@ -11,7 +11,7 @@ export function processJSXFragment(path, ctx) {
|
|
|
11
11
|
// 过滤空白子节点
|
|
12
12
|
const children = filterWhitespaceChildren(node.children);
|
|
13
13
|
// 校验 Match 组件
|
|
14
|
-
|
|
14
|
+
validateNoDirectMatchChild(children);
|
|
15
15
|
// 标记需要的导入
|
|
16
16
|
ctx.imports.Fragment = true;
|
|
17
17
|
ctx.imports.createView = true;
|
|
@@ -20,7 +20,7 @@ export function processJSXFragment(path, ctx) {
|
|
|
20
20
|
const locInfo = getDevLocInfo(ctx, node);
|
|
21
21
|
// 无子元素
|
|
22
22
|
if (children.length === 0) {
|
|
23
|
-
const viewCall = addPureComment(createCreateViewCall(t.identifier(fragmentAlias), null, locInfo, createViewAlias));
|
|
23
|
+
const viewCall = addPureComment(createCreateViewCall(t.identifier(fragmentAlias), null, locInfo, createViewAlias), ctx);
|
|
24
24
|
if (node.loc)
|
|
25
25
|
viewCall.loc = node.loc;
|
|
26
26
|
path.replaceWith(viewCall);
|
|
@@ -30,7 +30,7 @@ export function processJSXFragment(path, ctx) {
|
|
|
30
30
|
const processedChildren = processChildren(children, ctx);
|
|
31
31
|
const childrenValue = processedChildren.length === 1 ? processedChildren[0] : t.arrayExpression(processedChildren);
|
|
32
32
|
const props = t.objectExpression([t.objectProperty(t.identifier('children'), childrenValue)]);
|
|
33
|
-
const viewCall = addPureComment(createCreateViewCall(t.identifier(fragmentAlias), props, locInfo, createViewAlias));
|
|
33
|
+
const viewCall = addPureComment(createCreateViewCall(t.identifier(fragmentAlias), props, locInfo, createViewAlias), ctx);
|
|
34
34
|
if (node.loc)
|
|
35
35
|
viewCall.loc = node.loc;
|
|
36
36
|
path.replaceWith(viewCall);
|
|
@@ -5,16 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
7
|
import { createError } from '../../error.js';
|
|
8
|
-
import { isNativeElement } from '../../utils/index.js';
|
|
8
|
+
import { getJSXElementName, isNativeElement } from '../../utils/index.js';
|
|
9
9
|
import { createProperty, getAttributeValue, processAttribute, processSpreadAttribute } from './attribute.js';
|
|
10
10
|
import { createVModelProps, extractVModelState } from './vmodel.js';
|
|
11
|
-
/**
|
|
12
|
-
* 获取 JSX 元素的名称
|
|
13
|
-
*/
|
|
14
|
-
function getJSXElementName(node) {
|
|
15
|
-
const nameNode = node.openingElement.name;
|
|
16
|
-
return nameNode.type === 'JSXIdentifier' ? nameNode.name : null;
|
|
17
|
-
}
|
|
18
11
|
/**
|
|
19
12
|
* 校验 class 和 className 不能同时存在
|
|
20
13
|
*/
|
package/dist/transform.d.ts
CHANGED
|
@@ -4,16 +4,13 @@
|
|
|
4
4
|
* @module transform
|
|
5
5
|
*/
|
|
6
6
|
import { type GeneratorResult } from '@babel/generator';
|
|
7
|
+
import type { CompileOptions } from './types.js';
|
|
7
8
|
export interface TransformResult extends GeneratorResult {
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
runtimeModule: string;
|
|
14
|
-
sourceMap: boolean | 'inline' | 'both';
|
|
15
|
-
transformClassNameToClass: boolean;
|
|
16
|
-
}
|
|
10
|
+
/**
|
|
11
|
+
* 检查文件是否需要转换
|
|
12
|
+
*/
|
|
13
|
+
export declare function shouldTransform(id: string): boolean;
|
|
17
14
|
/**
|
|
18
15
|
* 转换 JSX/TSX 代码
|
|
19
16
|
*/
|
package/dist/transform.js
CHANGED
|
@@ -9,15 +9,6 @@ import traverse from '@babel/traverse';
|
|
|
9
9
|
import { createContext } from './context.js';
|
|
10
10
|
import { collectExistingImports, collectLocalBindings, collectRefApiAliases, collectRefVariables, injectHMRSupport, injectImports, processJSXElement, processJSXFragment, processPureCompileComponent, processVIfChain, transformJSXElement } from './passes/index.js';
|
|
11
11
|
import { collectComponentFunctions, generateUniqueAlias, getJSXElementName, isPureCompileComponent } from './utils/index.js';
|
|
12
|
-
/** 用于追踪已处理的节点 */
|
|
13
|
-
const processedNodes = new WeakSet();
|
|
14
|
-
/**
|
|
15
|
-
* 检查文件是否需要转换
|
|
16
|
-
*/
|
|
17
|
-
function shouldTransform(id) {
|
|
18
|
-
const ext = id.split('?')[0].split('.').pop()?.toLowerCase();
|
|
19
|
-
return ext === 'jsx' || ext === 'tsx';
|
|
20
|
-
}
|
|
21
12
|
/**
|
|
22
13
|
* 创建解析器选项
|
|
23
14
|
*/
|
|
@@ -49,8 +40,8 @@ function setupAliases(ctx, program) {
|
|
|
49
40
|
'createView',
|
|
50
41
|
'Fragment',
|
|
51
42
|
'branch',
|
|
52
|
-
'
|
|
53
|
-
'
|
|
43
|
+
'expr',
|
|
44
|
+
'accessor',
|
|
54
45
|
'withDirectives',
|
|
55
46
|
'unref',
|
|
56
47
|
'isRef'
|
|
@@ -66,6 +57,9 @@ function setupAliases(ctx, program) {
|
|
|
66
57
|
if (ctx.options.hmr) {
|
|
67
58
|
ctx.vitarxAliases.createView = generateUniqueAlias('jsxDEV', allNames);
|
|
68
59
|
}
|
|
60
|
+
if (vitarxImports.has('builder')) {
|
|
61
|
+
ctx.builderAlias = vitarxImports.get('builder');
|
|
62
|
+
}
|
|
69
63
|
}
|
|
70
64
|
/**
|
|
71
65
|
* 收集 ref 相关信息
|
|
@@ -83,21 +77,21 @@ function transformAST(ast, ctx) {
|
|
|
83
77
|
babelTraverse(ast, {
|
|
84
78
|
JSXElement: {
|
|
85
79
|
enter(path) {
|
|
86
|
-
if (processedNodes.has(path.node))
|
|
80
|
+
if (ctx.processedNodes.has(path.node))
|
|
87
81
|
return;
|
|
88
82
|
const name = getJSXElementName(path.node);
|
|
89
83
|
if (name && isPureCompileComponent(name)) {
|
|
90
|
-
processedNodes.add(path.node);
|
|
84
|
+
ctx.processedNodes.add(path.node);
|
|
91
85
|
processPureCompileComponent(path, ctx);
|
|
92
86
|
}
|
|
93
87
|
},
|
|
94
88
|
exit(path) {
|
|
95
|
-
if (processedNodes.has(path.node))
|
|
89
|
+
if (ctx.processedNodes.has(path.node))
|
|
96
90
|
return;
|
|
97
91
|
const name = getJSXElementName(path.node);
|
|
98
92
|
if (name && isPureCompileComponent(name))
|
|
99
93
|
return;
|
|
100
|
-
processedNodes.add(path.node);
|
|
94
|
+
ctx.processedNodes.add(path.node);
|
|
101
95
|
processJSXElement(path, ctx);
|
|
102
96
|
}
|
|
103
97
|
},
|
|
@@ -106,9 +100,9 @@ function transformAST(ast, ctx) {
|
|
|
106
100
|
processVIfChain(path, ctx, transformJSXElement);
|
|
107
101
|
},
|
|
108
102
|
exit(path) {
|
|
109
|
-
if (processedNodes.has(path.node))
|
|
103
|
+
if (ctx.processedNodes.has(path.node))
|
|
110
104
|
return;
|
|
111
|
-
processedNodes.add(path.node);
|
|
105
|
+
ctx.processedNodes.add(path.node);
|
|
112
106
|
processJSXFragment(path, ctx);
|
|
113
107
|
}
|
|
114
108
|
}
|
|
@@ -122,17 +116,22 @@ function generateCode(ast, code, id, sourceMap) {
|
|
|
122
116
|
const output = babelGenerate(ast, { sourceMaps: !!sourceMap, filename: id, sourceFileName: id }, code);
|
|
123
117
|
return { code: output.code, map: output.map };
|
|
124
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* 检查文件是否需要转换
|
|
121
|
+
*/
|
|
122
|
+
export function shouldTransform(id) {
|
|
123
|
+
const ext = id.split('?')[0].split('.').pop()?.toLowerCase();
|
|
124
|
+
return ext === 'jsx' || ext === 'tsx';
|
|
125
|
+
}
|
|
125
126
|
/**
|
|
126
127
|
* 转换 JSX/TSX 代码
|
|
127
128
|
*/
|
|
128
129
|
export async function transform(code, id, options) {
|
|
129
|
-
if (!shouldTransform(id))
|
|
130
|
-
return null;
|
|
131
130
|
const ast = parse(code, createParserOptions());
|
|
132
131
|
const ctx = createContext(code, id, options, ast);
|
|
133
132
|
setupAliases(ctx, ast.program);
|
|
134
133
|
collectRefInfo(ctx, ast.program);
|
|
135
|
-
const components = collectComponentFunctions(ast.program);
|
|
134
|
+
const components = collectComponentFunctions(ast.program, ctx.builderAlias);
|
|
136
135
|
transformAST(ast, ctx);
|
|
137
136
|
injectImports(ast.program, ctx);
|
|
138
137
|
if (options.hmr && components.length > 0) {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import type { RenderUnit, View } from 'vitarx';
|
|
2
|
+
/**
|
|
3
|
+
* 编译选项
|
|
4
|
+
*/
|
|
5
|
+
export interface CompileOptions {
|
|
6
|
+
hmr: boolean;
|
|
7
|
+
dev: boolean;
|
|
8
|
+
ssr: boolean;
|
|
9
|
+
runtimeModule: string;
|
|
10
|
+
sourceMap: boolean | 'inline' | 'both';
|
|
11
|
+
transformClassNameToClass: boolean;
|
|
12
|
+
}
|
|
2
13
|
export interface IfBlockProps {
|
|
3
14
|
/**
|
|
4
15
|
* 子元素列表
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* @module utils/ast-builders
|
|
4
4
|
*/
|
|
5
5
|
import * as t from '@babel/types';
|
|
6
|
-
import { type ArrowFunctionExpression, type CallExpression, type Expression, type ObjectExpression
|
|
6
|
+
import { type ArrowFunctionExpression, type CallExpression, type Expression, type ObjectExpression } from '@babel/types';
|
|
7
|
+
import type { SourceLocation } from 'acorn';
|
|
7
8
|
import type { TransformContext, VitarxImportAliases } from '../context.js';
|
|
8
9
|
/**
|
|
9
10
|
* 创建 unref 调用
|
|
@@ -13,30 +14,28 @@ import type { TransformContext, VitarxImportAliases } from '../context.js';
|
|
|
13
14
|
*/
|
|
14
15
|
export declare function createUnrefCall(argument: Expression, alias?: string): CallExpression;
|
|
15
16
|
/**
|
|
16
|
-
* 创建
|
|
17
|
+
* 创建 accessor 调用
|
|
17
18
|
* @param object - 对象表达式
|
|
18
19
|
* @param key - 键表达式
|
|
19
|
-
* @param alias -
|
|
20
|
+
* @param alias - accessor 别名
|
|
20
21
|
* @returns CallExpression
|
|
21
22
|
*/
|
|
22
|
-
export declare function
|
|
23
|
+
export declare function createAccessorCall(object: Expression, key: Expression, alias?: string): CallExpression;
|
|
23
24
|
/**
|
|
24
|
-
* 创建
|
|
25
|
+
* 创建 expr 调用
|
|
25
26
|
* @param argument - 参数表达式
|
|
26
|
-
* @param alias -
|
|
27
|
-
* @param locInfo - 位置信息对象
|
|
27
|
+
* @param alias - expr 别名
|
|
28
28
|
* @returns CallExpression
|
|
29
29
|
*/
|
|
30
|
-
export declare function
|
|
30
|
+
export declare function createExprCall(argument: Expression, alias?: string): CallExpression;
|
|
31
31
|
/**
|
|
32
32
|
* 创建 branch 调用
|
|
33
33
|
* @param condition - 条件函数
|
|
34
34
|
* @param branches - 分支函数数组
|
|
35
35
|
* @param alias - branch 别名
|
|
36
|
-
* @param locInfo - 位置信息对象
|
|
37
36
|
* @returns CallExpression
|
|
38
37
|
*/
|
|
39
|
-
export declare function createBranchCall(condition: ArrowFunctionExpression, branches: ArrowFunctionExpression[], alias?: string
|
|
38
|
+
export declare function createBranchCall(condition: ArrowFunctionExpression, branches: ArrowFunctionExpression[], alias?: string): CallExpression;
|
|
40
39
|
/**
|
|
41
40
|
* 创建 createView 调用
|
|
42
41
|
* @param type - 元素类型
|
|
@@ -60,19 +59,13 @@ export declare function createWithDirectivesCall(view: Expression, directives: A
|
|
|
60
59
|
* @returns ArrowFunctionExpression
|
|
61
60
|
*/
|
|
62
61
|
export declare function createArrowFunction(body: Expression): ArrowFunctionExpression;
|
|
63
|
-
/**
|
|
64
|
-
* 创建位置信息对象
|
|
65
|
-
* @param filename - 文件名
|
|
66
|
-
* @param loc - 源码位置
|
|
67
|
-
* @returns ObjectExpression
|
|
68
|
-
*/
|
|
69
|
-
export declare function createLocationObject(filename: string, loc: SourceLocation): ObjectExpression;
|
|
70
62
|
/**
|
|
71
63
|
* 为调用表达式添加 @__PURE__ 注释
|
|
72
64
|
* @param node - 调用表达式节点
|
|
65
|
+
* @param ctx - 转换上下文
|
|
73
66
|
* @returns 添加注释后的节点
|
|
74
67
|
*/
|
|
75
|
-
export declare function addPureComment<T extends CallExpression>(node: T): T;
|
|
68
|
+
export declare function addPureComment<T extends CallExpression>(node: T, ctx: TransformContext): T;
|
|
76
69
|
/**
|
|
77
70
|
* 获取 API 别名
|
|
78
71
|
* @param aliases - 别名映射
|
|
@@ -80,6 +73,13 @@ export declare function addPureComment<T extends CallExpression>(node: T): T;
|
|
|
80
73
|
* @returns 别名或原名
|
|
81
74
|
*/
|
|
82
75
|
export declare function getAlias(aliases: VitarxImportAliases, name: keyof VitarxImportAliases): string;
|
|
76
|
+
/**
|
|
77
|
+
* 创建位置信息对象
|
|
78
|
+
* @param filename - 文件名
|
|
79
|
+
* @param loc - 源码位置
|
|
80
|
+
* @returns ObjectExpression
|
|
81
|
+
*/
|
|
82
|
+
export declare function createLocationObject(filename: string, loc: SourceLocation): ObjectExpression;
|
|
83
83
|
/**
|
|
84
84
|
* 获取开发环境下的位置信息对象
|
|
85
85
|
* 仅在开发环境下返回位置信息,生产环境返回 null
|