@vitarx/plugin-vite 1.0.1 → 1.0.2-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/dist/hmr-client/index.d.ts +15 -5
- package/dist/hmr-client/index.js +47 -25
- package/dist/passes/hmr/inject-imports.d.ts +4 -1
- package/dist/passes/hmr/inject-imports.js +8 -1
- package/dist/passes/hmr/inject-state.d.ts +6 -1
- package/dist/passes/hmr/inject-state.js +58 -5
- package/dist/passes/hmr/inject.d.ts +1 -1
- package/dist/passes/hmr/inject.js +27 -2
- package/dist/passes/imports/collectImports.d.ts +4 -4
- package/dist/passes/imports/collectImports.js +20 -5
- package/dist/passes/imports/collectRefVariables.d.ts +2 -2
- package/dist/passes/imports/collectRefVariables.js +47 -3
- package/dist/passes/imports/injectImports.d.ts +1 -0
- package/dist/passes/imports/injectImports.js +28 -2
- package/dist/transform.js +11 -1
- package/dist/utils/ast-builders.d.ts +49 -41
- package/dist/utils/ast-builders.js +60 -41
- package/dist/utils/ast-guards.d.ts +2 -1
- package/dist/utils/ast-guards.js +4 -1
- package/dist/utils/collect-names.d.ts +13 -4
- package/dist/utils/collect-names.js +38 -4
- package/dist/utils/component-collect.d.ts +4 -2
- package/dist/utils/component-collect.js +181 -6
- package/dist/utils/generate.d.ts +3 -0
- package/dist/utils/generate.js +4 -0
- package/dist/utils/jsx-helpers.d.ts +14 -0
- package/dist/utils/jsx-helpers.js +21 -0
- package/dist/utils/pattern-helpers.d.ts +4 -4
- package/dist/utils/pattern-helpers.js +13 -4
- package/dist/utils/vif-helpers.d.ts +8 -0
- package/dist/utils/vif-helpers.js +36 -0
- package/package.json +1 -1
|
@@ -11,6 +11,11 @@ declare module 'vitarx' {
|
|
|
11
11
|
[HMR.state]?: Record<string, any>;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* HMR管理器
|
|
16
|
+
*
|
|
17
|
+
* 此管理器负责管理模块和组件的映射关系,并在模块更新时,更新对应的组件。
|
|
18
|
+
*/
|
|
14
19
|
export default class HMRManager {
|
|
15
20
|
#private;
|
|
16
21
|
/**
|
|
@@ -25,11 +30,11 @@ export default class HMRManager {
|
|
|
25
30
|
*/
|
|
26
31
|
bindId(component: Function, id: string): void;
|
|
27
32
|
/**
|
|
28
|
-
*
|
|
33
|
+
* 置换新组件
|
|
29
34
|
*
|
|
30
35
|
* 此方法提供给`createView`函数调用,保持每次创建组件实例都是最新的模块!
|
|
31
36
|
*
|
|
32
|
-
* @param component
|
|
37
|
+
* @param component - 组件构造函数
|
|
33
38
|
*/
|
|
34
39
|
resolveComponent(component: Component): Component;
|
|
35
40
|
/**
|
|
@@ -49,10 +54,15 @@ export default class HMRManager {
|
|
|
49
54
|
*/
|
|
50
55
|
register(view: ComponentView, component?: Component): void;
|
|
51
56
|
/**
|
|
52
|
-
*
|
|
57
|
+
* 获取组件的唯一id
|
|
53
58
|
*
|
|
54
|
-
* @param component
|
|
59
|
+
* @param component - 组件构造函数
|
|
55
60
|
*/
|
|
56
61
|
getId(component: Component): string;
|
|
57
|
-
|
|
62
|
+
/**
|
|
63
|
+
* 模块更新
|
|
64
|
+
*
|
|
65
|
+
* @param newModule - 新模块对象
|
|
66
|
+
*/
|
|
67
|
+
update(newModule: ModuleNamespace): void;
|
|
58
68
|
}
|
package/dist/hmr-client/index.js
CHANGED
|
@@ -4,9 +4,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
6
|
var _HMRManager_idMapToView, _HMRManager_idMapToComponent;
|
|
7
|
-
import { isReactive, isRef } from 'vitarx';
|
|
7
|
+
import { isComponent, isReactive, isRef } from 'vitarx';
|
|
8
8
|
import { HMR } from '../constants/index.js';
|
|
9
9
|
import { processUpdate } from './update.js';
|
|
10
|
+
/**
|
|
11
|
+
* HMR管理器
|
|
12
|
+
*
|
|
13
|
+
* 此管理器负责管理模块和组件的映射关系,并在模块更新时,更新对应的组件。
|
|
14
|
+
*/
|
|
10
15
|
class HMRManager {
|
|
11
16
|
constructor() {
|
|
12
17
|
/**
|
|
@@ -49,11 +54,11 @@ class HMRManager {
|
|
|
49
54
|
}
|
|
50
55
|
}
|
|
51
56
|
/**
|
|
52
|
-
*
|
|
57
|
+
* 置换新组件
|
|
53
58
|
*
|
|
54
59
|
* 此方法提供给`createView`函数调用,保持每次创建组件实例都是最新的模块!
|
|
55
60
|
*
|
|
56
|
-
* @param component
|
|
61
|
+
* @param component - 组件构造函数
|
|
57
62
|
*/
|
|
58
63
|
resolveComponent(component) {
|
|
59
64
|
const id = this.getId(component);
|
|
@@ -95,38 +100,55 @@ class HMRManager {
|
|
|
95
100
|
}
|
|
96
101
|
}
|
|
97
102
|
/**
|
|
98
|
-
*
|
|
103
|
+
* 获取组件的唯一id
|
|
99
104
|
*
|
|
100
|
-
* @param component
|
|
105
|
+
* @param component - 组件构造函数
|
|
101
106
|
*/
|
|
102
107
|
getId(component) {
|
|
103
108
|
return Reflect.get(component, HMR.id);
|
|
104
109
|
}
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
/**
|
|
111
|
+
* 模块更新
|
|
112
|
+
*
|
|
113
|
+
* @param newModule - 新模块对象
|
|
114
|
+
*/
|
|
115
|
+
update(newModule) {
|
|
116
|
+
if (!newModule)
|
|
107
117
|
return;
|
|
108
118
|
try {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
const components = [];
|
|
120
|
+
const updatedView = new Set();
|
|
121
|
+
// 先遍历模块更新id->component映射
|
|
122
|
+
for (const modKey in newModule) {
|
|
123
|
+
// 新组件
|
|
124
|
+
const newComponent = newModule[modKey];
|
|
125
|
+
// 如果不是组件则跳过
|
|
126
|
+
if (!isComponent(newComponent))
|
|
116
127
|
continue;
|
|
128
|
+
// 更新模块
|
|
129
|
+
const id = this.getId(newComponent);
|
|
130
|
+
if (id) {
|
|
131
|
+
__classPrivateFieldGet(this, _HMRManager_idMapToComponent, "f").set(id, newComponent);
|
|
132
|
+
components.push(newComponent);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
for (const component of components) {
|
|
136
|
+
const id = this.getId(component);
|
|
117
137
|
// 模块活跃的虚拟节点集合
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
if (!nodes)
|
|
138
|
+
const views = __classPrivateFieldGet(this, _HMRManager_idMapToView, "f").get(id);
|
|
139
|
+
if (!views)
|
|
121
140
|
continue;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
if (
|
|
129
|
-
|
|
141
|
+
// 遍历关联的所有视图,使其更新
|
|
142
|
+
for (const view of views) {
|
|
143
|
+
// 跳过已更新过的视图,避免同一次更新中同一个视图被更新多次
|
|
144
|
+
if (updatedView.has(view))
|
|
145
|
+
continue;
|
|
146
|
+
// 仅在视图是活跃状态,且已挂载时才更新
|
|
147
|
+
if (view.isActive && view.isMounted) {
|
|
148
|
+
// 处理视图更新
|
|
149
|
+
processUpdate(view, this.resolveComponent(view.component));
|
|
150
|
+
// 标记视图已更新
|
|
151
|
+
updatedView.add(view);
|
|
130
152
|
}
|
|
131
153
|
}
|
|
132
154
|
}
|
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* @module passes/hmr/inject-imports
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* getComponentView 的内部别名,使用特殊命名避免与用户代码冲突
|
|
9
|
+
* 格式:__$VITARX_XXX$__
|
|
10
|
+
*/
|
|
8
11
|
export declare const GET_COMPONENT_VIEW_ALIAS = "__$VITARX_GET_COMPONENT_VIEW$__";
|
|
9
12
|
/**
|
|
10
13
|
* 注入 HMR 客户端导入
|
|
@@ -5,14 +5,19 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
7
|
import { HMR } from '../../constants/index.js';
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* getComponentView 的内部别名,使用特殊命名避免与用户代码冲突
|
|
10
|
+
* 格式:__$VITARX_XXX$__
|
|
11
|
+
*/
|
|
9
12
|
export const GET_COMPONENT_VIEW_ALIAS = '__$VITARX_GET_COMPONENT_VIEW$__';
|
|
10
13
|
/**
|
|
11
14
|
* 注入 HMR 客户端导入
|
|
12
15
|
* @param program - AST Program 节点
|
|
13
16
|
*/
|
|
14
17
|
export function injectHMRImport(program) {
|
|
18
|
+
// 创建 import 语句: import __$VITARX_HMR$__ from '@vitarx/vite-plugin/hmr-client'
|
|
15
19
|
const importDecl = t.importDeclaration([t.importDefaultSpecifier(t.identifier(HMR.manager))], t.stringLiteral('@vitarx/vite-plugin/hmr-client'));
|
|
20
|
+
// 将导入语句插入到程序体最前面
|
|
16
21
|
program.body.unshift(importDecl);
|
|
17
22
|
}
|
|
18
23
|
/**
|
|
@@ -21,6 +26,8 @@ export function injectHMRImport(program) {
|
|
|
21
26
|
* @param program - AST Program 节点
|
|
22
27
|
*/
|
|
23
28
|
export function injectGetComponentViewImport(program) {
|
|
29
|
+
// 创建 import 语句: import { getComponentView as __$VITARX_GET_COMPONENT_VIEW$__ } from 'vitarx'
|
|
24
30
|
const importDecl = t.importDeclaration([t.importSpecifier(t.identifier(GET_COMPONENT_VIEW_ALIAS), t.identifier('getComponentView'))], t.stringLiteral('vitarx'));
|
|
31
|
+
// 将导入语句插入到程序体最前面
|
|
25
32
|
program.body.unshift(importDecl);
|
|
26
33
|
}
|
|
@@ -6,24 +6,29 @@
|
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
7
|
/**
|
|
8
8
|
* 创建组件函数体内的 HMR 注册代码
|
|
9
|
+
* 生成的代码包括:获取视图节点、注册到 HMR 管理器、保存状态
|
|
9
10
|
* @param variableNames - 需要追踪的变量名列表
|
|
10
11
|
* @returns HMR 注册语句数组
|
|
11
12
|
*/
|
|
12
13
|
export declare function createHMRRegistrationStatements(variableNames: string[]): t.Statement[];
|
|
13
14
|
/**
|
|
14
15
|
* 创建包装组件的 HMR 注册代码
|
|
15
|
-
*
|
|
16
|
+
* 包装组件(如 defineComponent、builder)只注册节点,不保存状态
|
|
17
|
+
* 传入组件函数自身作为第二个参数,便于更新时重新执行包装函数
|
|
16
18
|
* @param componentName - 组件函数名称
|
|
17
19
|
* @returns HMR 注册语句数组
|
|
18
20
|
*/
|
|
19
21
|
export declare function createWrappedComponentHMRStatements(componentName: string): t.Statement[];
|
|
20
22
|
/**
|
|
21
23
|
* 从函数体中收集局部变量名
|
|
24
|
+
* 递归遍历函数体中的所有变量声明,用于状态恢复
|
|
22
25
|
* @param functionBody - 函数体语句块
|
|
23
26
|
* @returns 变量名数组
|
|
24
27
|
*/
|
|
25
28
|
export declare function collectLocalVariableNames(functionBody: t.BlockStatement): string[];
|
|
26
29
|
/**
|
|
27
30
|
* 为函数体内的变量声明注入状态恢复代码
|
|
31
|
+
* 遍历函数体中的所有变量声明语句
|
|
32
|
+
* @param functionBody - 函数体语句块
|
|
28
33
|
*/
|
|
29
34
|
export declare function injectStatePreservation(functionBody: t.BlockStatement): void;
|
|
@@ -9,16 +9,26 @@ import { collectPatternBindings } from '../../utils/index.js';
|
|
|
9
9
|
import { GET_COMPONENT_VIEW_ALIAS } from './inject-imports.js';
|
|
10
10
|
/**
|
|
11
11
|
* 创建组件函数体内的 HMR 注册代码
|
|
12
|
+
* 生成的代码包括:获取视图节点、注册到 HMR 管理器、保存状态
|
|
12
13
|
* @param variableNames - 需要追踪的变量名列表
|
|
13
14
|
* @returns HMR 注册语句数组
|
|
14
15
|
*/
|
|
15
16
|
export function createHMRRegistrationStatements(variableNames) {
|
|
16
17
|
const statements = [];
|
|
18
|
+
// 语句1: const __$VITARX_HMR_VIEW_NODE$__ = __$VITARX_GET_COMPONENT_VIEW$__(true)
|
|
19
|
+
// 获取当前组件的视图节点
|
|
17
20
|
statements.push(t.variableDeclaration('const', [
|
|
18
21
|
t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), [t.booleanLiteral(true)]))
|
|
19
22
|
]));
|
|
23
|
+
// 语句2: __$VITARX_HMR$__.instance.register(__$VITARX_HMR_VIEW_NODE$__)
|
|
24
|
+
// 将视图节点注册到 HMR 管理器
|
|
20
25
|
statements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('register')), [t.identifier(HMR.view)])));
|
|
26
|
+
// 构建状态对象的 getter 属性,延迟求值避免不必要的计算
|
|
21
27
|
const stateProperties = variableNames.map(name => t.objectMethod('get', t.identifier(name), [], t.blockStatement([t.returnStatement(t.identifier(name))])));
|
|
28
|
+
// 语句3: __$VITARX_HMR_VIEW_NODE$__ && Promise.resolve().then(() => {
|
|
29
|
+
// __$VITARX_HMR_VIEW_NODE$__.__$vitarx_hmr_state$__ = { get 变量名() { return 变量名 } }
|
|
30
|
+
// })
|
|
31
|
+
// 异步保存状态到视图节点,确保 DOM 更新完成后再保存
|
|
22
32
|
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
33
|
t.arrowFunctionExpression([], t.blockStatement([
|
|
24
34
|
t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(HMR.view), t.identifier(HMR.state)), t.objectExpression(stateProperties)))
|
|
@@ -28,20 +38,25 @@ export function createHMRRegistrationStatements(variableNames) {
|
|
|
28
38
|
}
|
|
29
39
|
/**
|
|
30
40
|
* 创建包装组件的 HMR 注册代码
|
|
31
|
-
*
|
|
41
|
+
* 包装组件(如 defineComponent、builder)只注册节点,不保存状态
|
|
42
|
+
* 传入组件函数自身作为第二个参数,便于更新时重新执行包装函数
|
|
32
43
|
* @param componentName - 组件函数名称
|
|
33
44
|
* @returns HMR 注册语句数组
|
|
34
45
|
*/
|
|
35
46
|
export function createWrappedComponentHMRStatements(componentName) {
|
|
36
47
|
const statements = [];
|
|
48
|
+
// 语句1: const __$VITARX_HMR_VIEW_NODE$__ = __$VITARX_GET_COMPONENT_VIEW$__(true)
|
|
37
49
|
statements.push(t.variableDeclaration('const', [
|
|
38
50
|
t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), [t.booleanLiteral(true)]))
|
|
39
51
|
]));
|
|
52
|
+
// 语句2: __$VITARX_HMR$__.instance.register(__$VITARX_HMR_VIEW_NODE$__, Component)
|
|
53
|
+
// 第二个参数传入组件函数,支持热更新时重新执行
|
|
40
54
|
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
55
|
return statements;
|
|
42
56
|
}
|
|
43
57
|
/**
|
|
44
58
|
* 从函数体中收集局部变量名
|
|
59
|
+
* 递归遍历函数体中的所有变量声明,用于状态恢复
|
|
45
60
|
* @param functionBody - 函数体语句块
|
|
46
61
|
* @returns 变量名数组
|
|
47
62
|
*/
|
|
@@ -50,20 +65,35 @@ export function collectLocalVariableNames(functionBody) {
|
|
|
50
65
|
collectFromBlock(functionBody, variableNames);
|
|
51
66
|
return Array.from(variableNames);
|
|
52
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* 从语句块中收集变量名
|
|
70
|
+
* @param block - 语句块节点
|
|
71
|
+
* @param variableNames - 存储变量名的集合
|
|
72
|
+
*/
|
|
53
73
|
function collectFromBlock(block, variableNames) {
|
|
74
|
+
// 遍历语句块中的每个语句
|
|
54
75
|
for (const stmt of block.body) {
|
|
55
76
|
collectFromStatement(stmt, variableNames);
|
|
56
77
|
}
|
|
57
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* 从单个语句中收集变量名
|
|
81
|
+
* 支持多种语句类型:变量声明、条件语句、循环语句、switch、try-catch 等
|
|
82
|
+
* @param stmt - 语句节点
|
|
83
|
+
* @param variableNames - 存储变量名的集合
|
|
84
|
+
*/
|
|
58
85
|
function collectFromStatement(stmt, variableNames) {
|
|
86
|
+
// 处理变量声明语句:const/let/var
|
|
59
87
|
if (stmt.type === 'VariableDeclaration') {
|
|
60
88
|
for (const decl of stmt.declarations) {
|
|
89
|
+
// 跳过 void 模式(如 void 0)
|
|
61
90
|
if (decl.id.type !== 'VoidPattern') {
|
|
62
91
|
collectPatternBindings(decl.id, variableNames);
|
|
63
92
|
}
|
|
64
93
|
}
|
|
65
94
|
}
|
|
66
95
|
else if (stmt.type === 'IfStatement') {
|
|
96
|
+
// 递归处理 if 语句的两个分支
|
|
67
97
|
if (stmt.consequent.type === 'BlockStatement') {
|
|
68
98
|
collectFromBlock(stmt.consequent, variableNames);
|
|
69
99
|
}
|
|
@@ -80,6 +110,7 @@ function collectFromStatement(stmt, variableNames) {
|
|
|
80
110
|
}
|
|
81
111
|
}
|
|
82
112
|
else if (stmt.type === 'ForStatement' || stmt.type === 'WhileStatement' || stmt.type === 'DoWhileStatement') {
|
|
113
|
+
// 处理 for/while/do-while 循环
|
|
83
114
|
if (stmt.body.type === 'BlockStatement') {
|
|
84
115
|
collectFromBlock(stmt.body, variableNames);
|
|
85
116
|
}
|
|
@@ -88,6 +119,7 @@ function collectFromStatement(stmt, variableNames) {
|
|
|
88
119
|
}
|
|
89
120
|
}
|
|
90
121
|
else if (stmt.type === 'ForInStatement' || stmt.type === 'ForOfStatement') {
|
|
122
|
+
// 处理 for...in/for...of 循环
|
|
91
123
|
if (stmt.body.type === 'BlockStatement') {
|
|
92
124
|
collectFromBlock(stmt.body, variableNames);
|
|
93
125
|
}
|
|
@@ -96,9 +128,11 @@ function collectFromStatement(stmt, variableNames) {
|
|
|
96
128
|
}
|
|
97
129
|
}
|
|
98
130
|
else if (stmt.type === 'BlockStatement') {
|
|
131
|
+
// 处理嵌套语句块
|
|
99
132
|
collectFromBlock(stmt, variableNames);
|
|
100
133
|
}
|
|
101
134
|
else if (stmt.type === 'SwitchStatement') {
|
|
135
|
+
// 处理 switch 语句,遍历所有 case
|
|
102
136
|
for (const c of stmt.cases) {
|
|
103
137
|
for (const s of c.consequent) {
|
|
104
138
|
collectFromStatement(s, variableNames);
|
|
@@ -106,6 +140,7 @@ function collectFromStatement(stmt, variableNames) {
|
|
|
106
140
|
}
|
|
107
141
|
}
|
|
108
142
|
else if (stmt.type === 'TryStatement') {
|
|
143
|
+
// 处理 try-catch-finally
|
|
109
144
|
collectFromBlock(stmt.block, variableNames);
|
|
110
145
|
if (stmt.handler) {
|
|
111
146
|
collectFromBlock(stmt.handler.body, variableNames);
|
|
@@ -117,35 +152,53 @@ function collectFromStatement(stmt, variableNames) {
|
|
|
117
152
|
}
|
|
118
153
|
/**
|
|
119
154
|
* 判断表达式是否为函数类型
|
|
155
|
+
* 函数类型不需要状态恢复(函数定义本身不会改变)
|
|
156
|
+
* @param expr - 表达式节点
|
|
157
|
+
* @returns 是否为函数类型
|
|
120
158
|
*/
|
|
121
159
|
function isFunctionExpression(expr) {
|
|
122
160
|
if (!expr)
|
|
123
161
|
return false;
|
|
124
|
-
return (expr.type === 'ArrowFunctionExpression' ||
|
|
125
|
-
expr.type === 'FunctionExpression' ||
|
|
126
|
-
expr.type === 'ClassExpression'
|
|
162
|
+
return (expr.type === 'ArrowFunctionExpression' || // 箭头函数
|
|
163
|
+
expr.type === 'FunctionExpression' || // 函数表达式
|
|
164
|
+
expr.type === 'ClassExpression' // 类表达式
|
|
165
|
+
);
|
|
127
166
|
}
|
|
128
167
|
/**
|
|
129
168
|
* 创建状态恢复表达式
|
|
130
169
|
* 格式:__$VITARX_HMR$__.instance.memo(__$VITARX_HMR_VIEW_NODE$__, '变量名') ?? 原始初始值
|
|
170
|
+
* 如果 memo 方法返回保存的状态则使用,否则使用原始初始值
|
|
171
|
+
* @param variableName - 变量名
|
|
172
|
+
* @param originalInit - 原始初始化表达式
|
|
173
|
+
* @returns 状态恢复表达式
|
|
131
174
|
*/
|
|
132
175
|
function createMemoExpression(variableName, originalInit) {
|
|
133
|
-
return t.logicalExpression('??',
|
|
176
|
+
return t.logicalExpression('??', // 使用空值合并运算符
|
|
177
|
+
t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('memo')), [t.identifier(HMR.view), t.stringLiteral(variableName)]), originalInit // 原始初始值作为 fallback
|
|
178
|
+
);
|
|
134
179
|
}
|
|
135
180
|
/**
|
|
136
181
|
* 为单个变量声明注入状态恢复代码
|
|
182
|
+
* 将 const x = value 转换为 const x = memo(view, 'x') ?? value
|
|
183
|
+
* @param decl - 变量声明节点
|
|
137
184
|
*/
|
|
138
185
|
function injectStatePreservationForDeclaration(decl) {
|
|
186
|
+
// 只处理标识符形式的变量名(跳过解构赋值)
|
|
139
187
|
if (decl.id.type !== 'Identifier')
|
|
140
188
|
return;
|
|
189
|
+
// 跳过没有初始值的声明
|
|
141
190
|
if (!decl.init)
|
|
142
191
|
return;
|
|
192
|
+
// 跳过函数类型的初始值(不需要状态恢复)
|
|
143
193
|
if (isFunctionExpression(decl.init))
|
|
144
194
|
return;
|
|
195
|
+
// 替换初始值为状态恢复表达式
|
|
145
196
|
decl.init = createMemoExpression(decl.id.name, decl.init);
|
|
146
197
|
}
|
|
147
198
|
/**
|
|
148
199
|
* 为函数体内的变量声明注入状态恢复代码
|
|
200
|
+
* 遍历函数体中的所有变量声明语句
|
|
201
|
+
* @param functionBody - 函数体语句块
|
|
149
202
|
*/
|
|
150
203
|
export function injectStatePreservation(functionBody) {
|
|
151
204
|
for (const stmt of functionBody.body) {
|
|
@@ -10,6 +10,6 @@ import type { ComponentInfo } from '../../utils/index.js';
|
|
|
10
10
|
* 主入口函数,为所有组件注入完整的 HMR 支持
|
|
11
11
|
* @param program - AST Program 节点
|
|
12
12
|
* @param components - 组件信息列表
|
|
13
|
-
* @param filename -
|
|
13
|
+
* @param filename - 文件名(用于生成组件唯一 ID)
|
|
14
14
|
*/
|
|
15
15
|
export declare function injectHMRSupport(program: t.Program, components: ComponentInfo[], filename: string): void;
|
|
@@ -11,34 +11,47 @@ import { createHMRRegistrationStatements, createWrappedComponentHMRStatements, c
|
|
|
11
11
|
* 为组件函数注入 HMR 注册代码
|
|
12
12
|
* @param func - 函数声明/表达式/箭头函数
|
|
13
13
|
* @param variableNames - 需要追踪的变量名列表
|
|
14
|
-
* @param isWrapped -
|
|
14
|
+
* @param isWrapped - 是否为包装组件(defineComponent/builder)
|
|
15
15
|
* @param componentName - 组件名称(包装组件需要)
|
|
16
16
|
*/
|
|
17
17
|
function injectHMRIntoFunction(func, variableNames, isWrapped = false, componentName) {
|
|
18
|
+
// 如果是箭头函数且函数体不是块语句(单行箭头函数),转换为块语句
|
|
18
19
|
if (func.type === 'ArrowFunctionExpression' && func.body.type !== 'BlockStatement') {
|
|
19
20
|
const returnStmt = t.returnStatement(func.body);
|
|
20
21
|
func.body = t.blockStatement([returnStmt]);
|
|
21
22
|
}
|
|
23
|
+
// 确保函数体是块语句
|
|
22
24
|
if (!func.body || func.body.type !== 'BlockStatement')
|
|
23
25
|
return;
|
|
26
|
+
// 非包装组件需要注入状态恢复代码
|
|
24
27
|
if (!isWrapped) {
|
|
25
28
|
injectStatePreservation(func.body);
|
|
26
29
|
}
|
|
30
|
+
// 根据组件类型创建对应的 HMR 注册语句
|
|
27
31
|
const statements = isWrapped
|
|
28
32
|
? createWrappedComponentHMRStatements(componentName)
|
|
29
33
|
: createHMRRegistrationStatements(variableNames);
|
|
34
|
+
// 将注册语句插入到函数体开头
|
|
30
35
|
func.body.body.unshift(...statements);
|
|
31
36
|
}
|
|
32
37
|
/**
|
|
33
38
|
* 创建 bindId 语句
|
|
39
|
+
* 将组件名称与唯一 ID 绑定,用于热更新时识别组件
|
|
40
|
+
* @param componentName - 组件名称
|
|
41
|
+
* @param componentId - 组件唯一 ID
|
|
42
|
+
* @returns bindId 调用语句
|
|
34
43
|
*/
|
|
35
44
|
function createBindIdStatement(componentName, componentId) {
|
|
45
|
+
// 生成:__$VITARX_HMR$__.instance.bindId(Component, 'hash')
|
|
36
46
|
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)]));
|
|
37
47
|
}
|
|
38
48
|
/**
|
|
39
49
|
* 创建 import.meta.hot.accept 语句
|
|
50
|
+
* 监听模块热更新事件,触发组件更新
|
|
51
|
+
* @returns hot.accept 调用语句
|
|
40
52
|
*/
|
|
41
53
|
function createHotAcceptStatement() {
|
|
54
|
+
// 生成:import.meta.hot.accept(mod => { __$VITARX_HMR$__.instance.update(mod) })
|
|
42
55
|
return t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.identifier('import'), t.identifier('meta')), t.identifier('hot')), t.identifier('accept')), [
|
|
43
56
|
t.arrowFunctionExpression([t.identifier('mod')], t.blockStatement([
|
|
44
57
|
t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('update')), [t.identifier('mod')]))
|
|
@@ -47,14 +60,20 @@ function createHotAcceptStatement() {
|
|
|
47
60
|
}
|
|
48
61
|
/**
|
|
49
62
|
* 生成组件唯一 ID(文件路径 + 组件名称)
|
|
63
|
+
* 使用 FNV-1a 哈希算法生成短哈希值
|
|
64
|
+
* @param filename - 文件路径
|
|
65
|
+
* @param componentName - 组件名称
|
|
66
|
+
* @returns 组件唯一 ID(十六进制字符串)
|
|
50
67
|
*/
|
|
51
68
|
function generateComponentId(filename, componentName) {
|
|
52
69
|
const combined = `${filename}:${componentName}`;
|
|
70
|
+
// FNV-1a 哈希算法初始值
|
|
53
71
|
let hash = 2166136261;
|
|
54
72
|
for (let i = 0; i < combined.length; i++) {
|
|
55
73
|
hash ^= combined.charCodeAt(i);
|
|
56
74
|
hash = Math.imul(hash, 16777619);
|
|
57
75
|
}
|
|
76
|
+
// 转换为无符号整数并转为十六进制
|
|
58
77
|
return (hash >>> 0).toString(16);
|
|
59
78
|
}
|
|
60
79
|
/**
|
|
@@ -62,19 +81,25 @@ function generateComponentId(filename, componentName) {
|
|
|
62
81
|
* 主入口函数,为所有组件注入完整的 HMR 支持
|
|
63
82
|
* @param program - AST Program 节点
|
|
64
83
|
* @param components - 组件信息列表
|
|
65
|
-
* @param filename -
|
|
84
|
+
* @param filename - 文件名(用于生成组件唯一 ID)
|
|
66
85
|
*/
|
|
67
86
|
export function injectHMRSupport(program, components, filename) {
|
|
87
|
+
// 如果没有组件,直接返回
|
|
68
88
|
if (components.length === 0)
|
|
69
89
|
return;
|
|
90
|
+
// 步骤1: 注入 HMR 相关的 import 语句
|
|
70
91
|
injectHMRImport(program);
|
|
71
92
|
injectGetComponentViewImport(program);
|
|
93
|
+
// 步骤2: 为每个组件函数注入 HMR 注册代码和状态恢复
|
|
72
94
|
for (const { node, isWrapped, name } of components) {
|
|
95
|
+
// 收集函数体内的局部变量名(用于状态保存)
|
|
73
96
|
const variableNames = node.body?.type === 'BlockStatement' ? collectLocalVariableNames(node.body) : [];
|
|
74
97
|
injectHMRIntoFunction(node, variableNames, isWrapped, name);
|
|
75
98
|
}
|
|
99
|
+
// 步骤3: 为每个组件创建 bindId 语句(用于热更新时识别)
|
|
76
100
|
for (const { name } of components) {
|
|
77
101
|
program.body.push(createBindIdStatement(name, generateComponentId(filename, name)));
|
|
78
102
|
}
|
|
103
|
+
// 步骤4: 创建 import.meta.hot.accept 语句(监听热更新事件)
|
|
79
104
|
program.body.push(createHotAcceptStatement());
|
|
80
105
|
}
|
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* 只收集普通的 import 语句,跳过 import type 语句
|
|
8
|
+
* 收集现有 vitarx 导入信息
|
|
9
|
+
* 只收集普通的 import 语句,跳过 import type/typeof 语句
|
|
10
10
|
* @param program - AST Program 节点
|
|
11
|
-
* @returns
|
|
11
|
+
* @returns 映射表:导入名 -> 本地别名
|
|
12
12
|
*/
|
|
13
13
|
export declare function collectExistingImports(program: t.Program): Map<string, string>;
|
|
14
14
|
/**
|
|
15
15
|
* 收集本地变量绑定
|
|
16
16
|
* 包括导入绑定、变量声明、函数声明、类声明
|
|
17
|
-
* 只收集运行时存在的绑定,跳过 import type
|
|
17
|
+
* 只收集运行时存在的绑定,跳过 import type/typeof
|
|
18
18
|
* @param program - AST Program 节点
|
|
19
19
|
* @returns 本地变量名集合
|
|
20
20
|
*/
|
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
import { VITARX_MODULE } from '../../constants/index.js';
|
|
2
2
|
import { collectPatternBindings } from '../../utils/index.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* 只收集普通的 import 语句,跳过 import type 语句
|
|
4
|
+
* 收集现有 vitarx 导入信息
|
|
5
|
+
* 只收集普通的 import 语句,跳过 import type/typeof 语句
|
|
6
6
|
* @param program - AST Program 节点
|
|
7
|
-
* @returns
|
|
7
|
+
* @returns 映射表:导入名 -> 本地别名
|
|
8
8
|
*/
|
|
9
9
|
export function collectExistingImports(program) {
|
|
10
|
+
// 存储导入名到本地别名的映射
|
|
10
11
|
const vitarxImports = new Map();
|
|
12
|
+
// 遍历程序体中的所有节点
|
|
11
13
|
for (const node of program.body) {
|
|
14
|
+
// 跳过非导入声明节点
|
|
12
15
|
if (node.type !== 'ImportDeclaration')
|
|
13
16
|
continue;
|
|
17
|
+
// 跳过类型导入(不影响运行时)
|
|
14
18
|
if (node.importKind === 'type' || node.importKind === 'typeof')
|
|
15
19
|
continue;
|
|
20
|
+
// 只处理 vitarx 模块的导入
|
|
16
21
|
const source = node.source.value;
|
|
17
22
|
if (source !== VITARX_MODULE)
|
|
18
23
|
continue;
|
|
24
|
+
// 遍历导入说明符
|
|
19
25
|
for (const specifier of node.specifiers) {
|
|
20
26
|
if (specifier.type === 'ImportSpecifier') {
|
|
27
|
+
// 获取导入的原始名称(支持字符串字面量和标识符)
|
|
21
28
|
const importedName = specifier.imported.type === 'Identifier'
|
|
22
29
|
? specifier.imported.name
|
|
23
30
|
: specifier.imported.value;
|
|
31
|
+
// 记录导入名到本地别名的映射
|
|
24
32
|
vitarxImports.set(importedName, specifier.local.name);
|
|
25
33
|
}
|
|
26
34
|
}
|
|
@@ -30,31 +38,38 @@ export function collectExistingImports(program) {
|
|
|
30
38
|
/**
|
|
31
39
|
* 收集本地变量绑定
|
|
32
40
|
* 包括导入绑定、变量声明、函数声明、类声明
|
|
33
|
-
* 只收集运行时存在的绑定,跳过 import type
|
|
41
|
+
* 只收集运行时存在的绑定,跳过 import type/typeof
|
|
34
42
|
* @param program - AST Program 节点
|
|
35
43
|
* @returns 本地变量名集合
|
|
36
44
|
*/
|
|
37
45
|
export function collectLocalBindings(program) {
|
|
38
46
|
const bindings = new Set();
|
|
47
|
+
// 遍历程序体中的所有节点
|
|
39
48
|
for (const node of program.body) {
|
|
49
|
+
// 处理导入声明
|
|
40
50
|
if (node.type === 'ImportDeclaration') {
|
|
41
|
-
//
|
|
51
|
+
// 跳过类型导入(不影响运行时绑定)
|
|
42
52
|
if (node.importKind === 'type' || node.importKind === 'typeof')
|
|
43
53
|
continue;
|
|
54
|
+
// 收集所有导入的本地名称
|
|
44
55
|
for (const specifier of node.specifiers) {
|
|
45
56
|
bindings.add(specifier.local.name);
|
|
46
57
|
}
|
|
47
58
|
}
|
|
59
|
+
// 处理变量声明(支持解构赋值)
|
|
48
60
|
if (node.type === 'VariableDeclaration') {
|
|
49
61
|
for (const decl of node.declarations) {
|
|
62
|
+
// 跳过 void 模式(如 void 0)
|
|
50
63
|
if (decl.id.type !== 'VoidPattern') {
|
|
51
64
|
collectPatternBindings(decl.id, bindings);
|
|
52
65
|
}
|
|
53
66
|
}
|
|
54
67
|
}
|
|
68
|
+
// 处理函数声明
|
|
55
69
|
if (node.type === 'FunctionDeclaration' && node.id) {
|
|
56
70
|
bindings.add(node.id.name);
|
|
57
71
|
}
|
|
72
|
+
// 处理类声明
|
|
58
73
|
if (node.type === 'ClassDeclaration' && node.id) {
|
|
59
74
|
bindings.add(node.id.name);
|
|
60
75
|
}
|
|
@@ -14,7 +14,7 @@ import type { RefApiAliases } from '../../context.js';
|
|
|
14
14
|
export declare function collectRefApiAliases(program: t.Program): RefApiAliases;
|
|
15
15
|
/**
|
|
16
16
|
* 收集通过 ref API 定义的变量
|
|
17
|
-
*
|
|
17
|
+
* 包括直接赋值(const x = ref(0))和 toRefs 解构(const { a, b } = toRefs(obj))
|
|
18
18
|
* @param program - AST Program 节点
|
|
19
19
|
* @param refApiAliases - ref API 别名映射
|
|
20
20
|
* @returns ref 变量名集合
|
|
@@ -22,7 +22,7 @@ export declare function collectRefApiAliases(program: t.Program): RefApiAliases;
|
|
|
22
22
|
export declare function collectRefVariables(program: t.Program, refApiAliases: RefApiAliases): Set<string>;
|
|
23
23
|
/**
|
|
24
24
|
* 收集确定不是 ref 的变量
|
|
25
|
-
*
|
|
25
|
+
* 包括函数声明、箭头函数和函数表达式变量(这些不可能是 ref)
|
|
26
26
|
* @param program - AST Program 节点
|
|
27
27
|
* @returns 非 ref 变量名集合
|
|
28
28
|
*/
|