@vitarx/plugin-vite 0.0.1-beta.6 → 0.0.1-beta.8
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/context.d.ts +8 -15
- package/dist/context.js +3 -14
- package/dist/hmr-client/index.d.ts +2 -1
- package/dist/hmr-client/index.js +4 -3
- package/dist/hmr-client/utils.js +1 -1
- package/dist/index.js +5 -4
- package/dist/passes/directives/processDirectives.js +1 -1
- 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/jsx/processChildren.js +1 -1
- package/dist/passes/jsx/processJSXElement.js +1 -1
- package/dist/passes/jsx/processJSXFragment.js +4 -4
- package/dist/passes/props/index.js +1 -8
- package/dist/transform.d.ts +12 -11
- package/dist/transform.js +19 -20
- package/dist/types.d.ts +11 -0
- package/dist/utils/ast-builders.d.ts +2 -1
- package/dist/utils/ast-builders.js +4 -5
- package/dist/utils/branch-factory.js +1 -1
- 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 +66 -133
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/jsx-helpers.d.ts +3 -2
- package/dist/utils/jsx-helpers.js +3 -2
- package/package.json +11 -3
package/dist/context.d.ts
CHANGED
|
@@ -3,16 +3,9 @@
|
|
|
3
3
|
* 定义编译转换过程中的上下文数据结构
|
|
4
4
|
* @module context
|
|
5
5
|
*/
|
|
6
|
-
import type { File } from '@babel/types';
|
|
7
|
-
import type { CompileOptions } from './transform.js';
|
|
6
|
+
import type { CallExpression, File } from '@babel/types';
|
|
8
7
|
import type { CompilerWarning } from './error.js';
|
|
9
|
-
import {
|
|
10
|
-
/**
|
|
11
|
-
* UI 相关的 API 名称列表
|
|
12
|
-
* 这些 API 用于创建视图,在 HMR 时需要识别
|
|
13
|
-
*/
|
|
14
|
-
export declare const UI_API_NAMES_CONST: readonly ["createView", "branch", "dynamic", "access", "withDirectives"];
|
|
15
|
-
export type UIApiName = (typeof UI_API_NAMES)[number];
|
|
8
|
+
import type { CompileOptions } from './types.js';
|
|
16
9
|
/**
|
|
17
10
|
* 导入信息
|
|
18
11
|
* 追踪需要注入的运行时 API
|
|
@@ -87,6 +80,12 @@ export interface TransformContext {
|
|
|
87
80
|
refVariables: Set<string>;
|
|
88
81
|
/** UI API 别名集合(用于 HMR 代码分离识别) */
|
|
89
82
|
uiApiAliases: Set<string>;
|
|
83
|
+
/** builder 函数的本地别名(用于识别纯构建组件) */
|
|
84
|
+
builderAlias: string | null;
|
|
85
|
+
/** 已处理的 AST 节点追踪集合 */
|
|
86
|
+
processedNodes: WeakSet<any>;
|
|
87
|
+
/** 已添加 PURE 注释的节点追踪集合 */
|
|
88
|
+
pureCommentedNodes: WeakSet<CallExpression>;
|
|
90
89
|
/** 编译警告列表 */
|
|
91
90
|
warnings: CompilerWarning[];
|
|
92
91
|
}
|
|
@@ -105,12 +104,6 @@ export declare function createContext(code: string, id: string, options: Compile
|
|
|
105
104
|
* @param name - 导入名称
|
|
106
105
|
*/
|
|
107
106
|
export declare function markImport(ctx: TransformContext, name: keyof ImportInfo): void;
|
|
108
|
-
/**
|
|
109
|
-
* 记录 UI API 别名
|
|
110
|
-
* @param ctx - 转换上下文
|
|
111
|
-
* @param alias - API 别名
|
|
112
|
-
*/
|
|
113
|
-
export declare function markUiApiAlias(ctx: TransformContext, alias: string): void;
|
|
114
107
|
/**
|
|
115
108
|
* 添加编译警告
|
|
116
109
|
* @param ctx - 转换上下文
|
package/dist/context.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { UI_API_NAMES } from './constants/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* UI 相关的 API 名称列表
|
|
4
|
-
* 这些 API 用于创建视图,在 HMR 时需要识别
|
|
5
|
-
*/
|
|
6
|
-
export const UI_API_NAMES_CONST = UI_API_NAMES;
|
|
7
1
|
/**
|
|
8
2
|
* 创建转换上下文
|
|
9
3
|
* @param code - 源代码
|
|
@@ -50,6 +44,9 @@ export function createContext(code, id, options, ast) {
|
|
|
50
44
|
},
|
|
51
45
|
refVariables: new Set(),
|
|
52
46
|
uiApiAliases: new Set(),
|
|
47
|
+
builderAlias: null,
|
|
48
|
+
processedNodes: new WeakSet(),
|
|
49
|
+
pureCommentedNodes: new WeakSet(),
|
|
53
50
|
warnings: []
|
|
54
51
|
};
|
|
55
52
|
}
|
|
@@ -61,14 +58,6 @@ export function createContext(code, id, options, ast) {
|
|
|
61
58
|
export function markImport(ctx, name) {
|
|
62
59
|
ctx.imports[name] = true;
|
|
63
60
|
}
|
|
64
|
-
/**
|
|
65
|
-
* 记录 UI API 别名
|
|
66
|
-
* @param ctx - 转换上下文
|
|
67
|
-
* @param alias - API 别名
|
|
68
|
-
*/
|
|
69
|
-
export function markUiApiAlias(ctx, alias) {
|
|
70
|
-
ctx.uiApiAliases.add(alias);
|
|
71
|
-
}
|
|
72
61
|
/**
|
|
73
62
|
* 添加编译警告
|
|
74
63
|
* @param ctx - 转换上下文
|
package/dist/hmr-client/index.js
CHANGED
|
@@ -80,11 +80,12 @@ class HMRManager {
|
|
|
80
80
|
* 注册节点
|
|
81
81
|
*
|
|
82
82
|
* @param view - 组件视图节点
|
|
83
|
+
* @param component - 组件构造函数
|
|
83
84
|
*/
|
|
84
|
-
register(view) {
|
|
85
|
+
register(view, component) {
|
|
85
86
|
if (!view)
|
|
86
87
|
return;
|
|
87
|
-
|
|
88
|
+
component ?? (component = view.component);
|
|
88
89
|
const id = this.getId(component);
|
|
89
90
|
if (__classPrivateFieldGet(this, _HMRManager_idMapToView, "f").has(id)) {
|
|
90
91
|
__classPrivateFieldGet(this, _HMRManager_idMapToView, "f").get(id).add(view);
|
|
@@ -124,7 +125,7 @@ class HMRManager {
|
|
|
124
125
|
if (id)
|
|
125
126
|
__classPrivateFieldGet(this, _HMRManager_idMapToComponent, "f").set(id, newComponent);
|
|
126
127
|
// 更新节点视图
|
|
127
|
-
if (node.active && node.isMounted) {
|
|
128
|
+
if ((node.isActive || node.active) && node.isMounted) {
|
|
128
129
|
processUpdate(node, newComponent);
|
|
129
130
|
}
|
|
130
131
|
}
|
package/dist/hmr-client/utils.js
CHANGED
|
@@ -10,7 +10,7 @@ import { UI_API_NAMES } from '../constants/index.js';
|
|
|
10
10
|
* UI 相关的运行时 API 名称
|
|
11
11
|
* 这些 API 调用代表 UI 描述代码
|
|
12
12
|
*/
|
|
13
|
-
const UI_APIS = new Set([...UI_API_NAMES, 'jsxDEV']);
|
|
13
|
+
const UI_APIS = new Set([...UI_API_NAMES, 'jsxDEV', 'h']);
|
|
14
14
|
/**
|
|
15
15
|
* 判断标识符名称是否为 UI API
|
|
16
16
|
* 支持原始名称和带后缀的名称(如 jsxDEV$1)
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { version } from 'vite';
|
|
4
|
-
import { transform } from './transform.js';
|
|
4
|
+
import { shouldTransform, transform } from './transform.js';
|
|
5
5
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
const IS_V8 = version.startsWith('8');
|
|
7
7
|
let viteTransform;
|
|
@@ -32,7 +32,6 @@ export default function vitarx(options) {
|
|
|
32
32
|
let viteConfig;
|
|
33
33
|
return {
|
|
34
34
|
name: 'vite-plugin-vitarx',
|
|
35
|
-
enforce: 'pre',
|
|
36
35
|
config(config, env) {
|
|
37
36
|
isDEV = env.command === 'serve' && !env.isPreview;
|
|
38
37
|
const configSSR = !!config.build?.ssr;
|
|
@@ -59,15 +58,17 @@ export default function vitarx(options) {
|
|
|
59
58
|
ssr: isSSR,
|
|
60
59
|
hmr: isDEV && !isSSR,
|
|
61
60
|
runtimeModule: 'vitarx',
|
|
62
|
-
sourceMap:
|
|
61
|
+
sourceMap: true,
|
|
63
62
|
transformClassNameToClass: options?.transformClassNameToClass ?? false
|
|
64
63
|
};
|
|
65
64
|
},
|
|
66
65
|
async transform(code, id) {
|
|
66
|
+
if (!shouldTransform(id))
|
|
67
|
+
return null;
|
|
67
68
|
const result = await transform(code, id, compileOptions);
|
|
68
69
|
if (!result)
|
|
69
70
|
return null;
|
|
70
|
-
return
|
|
71
|
+
return viteTransform(result.code, id, undefined, result.map || undefined, viteConfig);
|
|
71
72
|
}
|
|
72
73
|
};
|
|
73
74
|
}
|
|
@@ -27,7 +27,7 @@ export function processDirectives(viewCall, directives, ctx) {
|
|
|
27
27
|
}
|
|
28
28
|
markImport(ctx, 'withDirectives');
|
|
29
29
|
const withDirectivesAlias = getAlias(ctx.vitarxAliases, 'withDirectives');
|
|
30
|
-
return addPureComment(createWithDirectivesCall(viewCall, directiveArray, withDirectivesAlias));
|
|
30
|
+
return addPureComment(createWithDirectivesCall(viewCall, directiveArray, withDirectivesAlias), ctx);
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
33
|
* 构建指令值对象
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR 导入注入模块
|
|
3
|
+
* 负责注入 HMR 所需的导入语句
|
|
4
|
+
* @module passes/hmr/inject-imports
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
/** getComponentView 的内部别名,避免与用户代码冲突 */
|
|
8
|
+
export declare const GET_COMPONENT_VIEW_ALIAS = "__$VITARX_GET_COMPONENT_VIEW$__";
|
|
9
|
+
/**
|
|
10
|
+
* 注入 HMR 客户端导入
|
|
11
|
+
* @param program - AST Program 节点
|
|
12
|
+
*/
|
|
13
|
+
export declare function injectHMRImport(program: t.Program): void;
|
|
14
|
+
/**
|
|
15
|
+
* 注入 getComponentView 导入
|
|
16
|
+
* 使用唯一的别名避免与用户代码冲突
|
|
17
|
+
* @param program - AST Program 节点
|
|
18
|
+
*/
|
|
19
|
+
export declare function injectGetComponentViewImport(program: t.Program): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR 导入注入模块
|
|
3
|
+
* 负责注入 HMR 所需的导入语句
|
|
4
|
+
* @module passes/hmr/inject-imports
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
import { HMR } from '../../constants/index.js';
|
|
8
|
+
/** getComponentView 的内部别名,避免与用户代码冲突 */
|
|
9
|
+
export const GET_COMPONENT_VIEW_ALIAS = '__$VITARX_GET_COMPONENT_VIEW$__';
|
|
10
|
+
/**
|
|
11
|
+
* 注入 HMR 客户端导入
|
|
12
|
+
* @param program - AST Program 节点
|
|
13
|
+
*/
|
|
14
|
+
export function injectHMRImport(program) {
|
|
15
|
+
const importDecl = t.importDeclaration([t.importDefaultSpecifier(t.identifier(HMR.manager))], t.stringLiteral('@vitarx/vite-plugin/hmr-client'));
|
|
16
|
+
program.body.unshift(importDecl);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 注入 getComponentView 导入
|
|
20
|
+
* 使用唯一的别名避免与用户代码冲突
|
|
21
|
+
* @param program - AST Program 节点
|
|
22
|
+
*/
|
|
23
|
+
export function injectGetComponentViewImport(program) {
|
|
24
|
+
const importDecl = t.importDeclaration([t.importSpecifier(t.identifier(GET_COMPONENT_VIEW_ALIAS), t.identifier('getComponentView'))], t.stringLiteral('vitarx'));
|
|
25
|
+
program.body.unshift(importDecl);
|
|
26
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR 状态恢复模块
|
|
3
|
+
* 负责为组件函数注入状态保存和恢复代码
|
|
4
|
+
* @module passes/hmr/inject-state
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
/**
|
|
8
|
+
* 创建组件函数体内的 HMR 注册代码
|
|
9
|
+
* @param variableNames - 需要追踪的变量名列表
|
|
10
|
+
* @returns HMR 注册语句数组
|
|
11
|
+
*/
|
|
12
|
+
export declare function createHMRRegistrationStatements(variableNames: string[]): t.Statement[];
|
|
13
|
+
/**
|
|
14
|
+
* 创建包装组件的 HMR 注册代码
|
|
15
|
+
* 只注册节点,不保存状态,传入组件函数自身作为第二个参数
|
|
16
|
+
* @param componentName - 组件函数名称
|
|
17
|
+
* @returns HMR 注册语句数组
|
|
18
|
+
*/
|
|
19
|
+
export declare function createWrappedComponentHMRStatements(componentName: string): t.Statement[];
|
|
20
|
+
/**
|
|
21
|
+
* 从函数体中收集局部变量名
|
|
22
|
+
* @param functionBody - 函数体语句块
|
|
23
|
+
* @returns 变量名数组
|
|
24
|
+
*/
|
|
25
|
+
export declare function collectLocalVariableNames(functionBody: t.BlockStatement): string[];
|
|
26
|
+
/**
|
|
27
|
+
* 为函数体内的变量声明注入状态恢复代码
|
|
28
|
+
*/
|
|
29
|
+
export declare function injectStatePreservation(functionBody: t.BlockStatement): void;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR 状态恢复模块
|
|
3
|
+
* 负责为组件函数注入状态保存和恢复代码
|
|
4
|
+
* @module passes/hmr/inject-state
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
import { HMR } from '../../constants/index.js';
|
|
8
|
+
import { collectPatternBindings } from '../../utils/index.js';
|
|
9
|
+
import { GET_COMPONENT_VIEW_ALIAS } from './inject-imports.js';
|
|
10
|
+
/**
|
|
11
|
+
* 创建组件函数体内的 HMR 注册代码
|
|
12
|
+
* @param variableNames - 需要追踪的变量名列表
|
|
13
|
+
* @returns HMR 注册语句数组
|
|
14
|
+
*/
|
|
15
|
+
export function createHMRRegistrationStatements(variableNames) {
|
|
16
|
+
const statements = [];
|
|
17
|
+
statements.push(t.variableDeclaration('const', [
|
|
18
|
+
t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), []))
|
|
19
|
+
]));
|
|
20
|
+
statements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('register')), [t.identifier(HMR.view)])));
|
|
21
|
+
const stateProperties = variableNames.map(name => t.objectMethod('get', t.identifier(name), [], t.blockStatement([t.returnStatement(t.identifier(name))])));
|
|
22
|
+
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')), [
|
|
23
|
+
t.arrowFunctionExpression([], t.blockStatement([
|
|
24
|
+
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(HMR.view), t.identifier(HMR.state)), t.objectExpression(stateProperties)))
|
|
25
|
+
]))
|
|
26
|
+
]))));
|
|
27
|
+
return statements;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 创建包装组件的 HMR 注册代码
|
|
31
|
+
* 只注册节点,不保存状态,传入组件函数自身作为第二个参数
|
|
32
|
+
* @param componentName - 组件函数名称
|
|
33
|
+
* @returns HMR 注册语句数组
|
|
34
|
+
*/
|
|
35
|
+
export function createWrappedComponentHMRStatements(componentName) {
|
|
36
|
+
const statements = [];
|
|
37
|
+
statements.push(t.variableDeclaration('const', [
|
|
38
|
+
t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), []))
|
|
39
|
+
]));
|
|
40
|
+
statements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('register')), [t.identifier(HMR.view), t.identifier(componentName)])));
|
|
41
|
+
return statements;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 从函数体中收集局部变量名
|
|
45
|
+
* @param functionBody - 函数体语句块
|
|
46
|
+
* @returns 变量名数组
|
|
47
|
+
*/
|
|
48
|
+
export function collectLocalVariableNames(functionBody) {
|
|
49
|
+
const variableNames = new Set();
|
|
50
|
+
for (const stmt of functionBody.body) {
|
|
51
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
52
|
+
for (const decl of stmt.declarations) {
|
|
53
|
+
if (decl.id.type !== 'VoidPattern') {
|
|
54
|
+
collectPatternBindings(decl.id, variableNames);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return Array.from(variableNames);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 判断表达式是否为函数类型
|
|
63
|
+
*/
|
|
64
|
+
function isFunctionExpression(expr) {
|
|
65
|
+
if (!expr)
|
|
66
|
+
return false;
|
|
67
|
+
return (expr.type === 'ArrowFunctionExpression' ||
|
|
68
|
+
expr.type === 'FunctionExpression' ||
|
|
69
|
+
expr.type === 'ClassExpression');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 创建状态恢复表达式
|
|
73
|
+
* 格式:__$VITARX_HMR$__.instance.memo(__$VITARX_HMR_VIEW_NODE$__, '变量名') ?? 原始初始值
|
|
74
|
+
*/
|
|
75
|
+
function createMemoExpression(variableName, originalInit) {
|
|
76
|
+
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);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 为单个变量声明注入状态恢复代码
|
|
80
|
+
*/
|
|
81
|
+
function injectStatePreservationForDeclaration(decl) {
|
|
82
|
+
if (decl.id.type !== 'Identifier')
|
|
83
|
+
return;
|
|
84
|
+
if (!decl.init)
|
|
85
|
+
return;
|
|
86
|
+
if (isFunctionExpression(decl.init))
|
|
87
|
+
return;
|
|
88
|
+
decl.init = createMemoExpression(decl.id.name, decl.init);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 为函数体内的变量声明注入状态恢复代码
|
|
92
|
+
*/
|
|
93
|
+
export function injectStatePreservation(functionBody) {
|
|
94
|
+
for (const stmt of functionBody.body) {
|
|
95
|
+
if (stmt.type === 'VariableDeclaration') {
|
|
96
|
+
for (const decl of stmt.declarations) {
|
|
97
|
+
injectStatePreservationForDeclaration(decl);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -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
|
}
|
|
@@ -22,7 +22,7 @@ function handleLogicalExpression(expr, ctx, loc) {
|
|
|
22
22
|
markImport(ctx, 'dynamic');
|
|
23
23
|
const dynamicAlias = getAlias(ctx.vitarxAliases, 'dynamic');
|
|
24
24
|
const locInfo = loc ? getDevLocInfo(ctx, { loc }) : null;
|
|
25
|
-
return addPureComment(createDynamicCall(expr, dynamicAlias, locInfo));
|
|
25
|
+
return addPureComment(createDynamicCall(expr, dynamicAlias, locInfo), ctx);
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* 处理子节点数组
|
|
@@ -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;
|
|
@@ -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
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
runtimeModule: string;
|
|
10
|
-
sourceMap: boolean | 'inline' | 'both';
|
|
11
|
-
transformClassNameToClass: boolean;
|
|
1
|
+
/**
|
|
2
|
+
* 主转换模块
|
|
3
|
+
* 负责解析 JSX/TSX 代码并转换为 createView 调用
|
|
4
|
+
* @module transform
|
|
5
|
+
*/
|
|
6
|
+
import { type GeneratorResult } from '@babel/generator';
|
|
7
|
+
import type { CompileOptions } from './types.js';
|
|
8
|
+
export interface TransformResult extends GeneratorResult {
|
|
12
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* 检查文件是否需要转换
|
|
12
|
+
*/
|
|
13
|
+
export declare function shouldTransform(id: string): boolean;
|
|
13
14
|
/**
|
|
14
15
|
* 转换 JSX/TSX 代码
|
|
15
16
|
*/
|