@vitarx/plugin-vite 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants/index.d.ts +0 -9
- package/dist/constants/index.js +0 -9
- package/dist/hmr-client/index.d.ts +27 -1
- package/dist/hmr-client/index.js +108 -12
- package/dist/hmr-client/update.d.ts +11 -2
- package/dist/hmr-client/update.js +25 -59
- package/dist/hmr-client/weak-refs.d.ts +37 -0
- package/dist/hmr-client/weak-refs.js +41 -0
- package/dist/passes/directives/processDirectives.d.ts +1 -1
- package/dist/passes/directives/processDirectives.js +15 -10
- package/dist/passes/hmr/index.d.ts +1 -1
- package/dist/passes/hmr/index.js +1 -1
- package/dist/passes/hmr/inject-state.d.ts +16 -10
- package/dist/passes/hmr/inject-state.js +141 -135
- package/dist/passes/hmr/inject.d.ts +10 -0
- package/dist/passes/hmr/inject.js +47 -10
- package/dist/passes/imports/collectRefVariables.d.ts +1 -0
- package/dist/passes/imports/collectRefVariables.js +52 -19
- package/dist/passes/index.d.ts +1 -1
- package/dist/passes/index.js +1 -1
- package/dist/passes/jsx/processJSXFragment.js +18 -1
- package/dist/passes/props/attribute.d.ts +3 -0
- package/dist/passes/props/attribute.js +13 -3
- package/dist/transform.js +16 -5
- package/dist/types.d.ts +1 -1
- package/dist/utils/branch-factory.js +26 -6
- package/dist/utils/component-collect.d.ts +7 -0
- package/dist/utils/component-collect.js +12 -236
- package/dist/utils/default-export.d.ts +38 -0
- package/dist/utils/default-export.js +119 -0
- package/dist/utils/forward-export.d.ts +28 -0
- package/dist/utils/forward-export.js +76 -0
- package/dist/utils/function-extract.d.ts +45 -0
- package/dist/utils/function-extract.js +98 -0
- package/dist/utils/generate.d.ts +7 -0
- package/dist/utils/generate.js +20 -0
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.js +4 -2
- package/dist/utils/jsx-helpers.d.ts +1 -4
- package/dist/utils/jsx-helpers.js +2 -15
- package/dist/utils/vif-helpers.js +6 -3
- package/package.json +5 -4
- package/dist/hmr-client/utils.d.ts +0 -23
- package/dist/hmr-client/utils.js +0 -137
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 导入转发默认导出检测
|
|
3
|
+
*
|
|
4
|
+
* 识别「纯转发组件默认导出」的模块——典型场景是文件路由的 _layout 布局
|
|
5
|
+
* 转发文件(`export default AdminLayout` / `export { AdminLayout as default }`,
|
|
6
|
+
* AdminLayout 为 import 绑定)。此类模块本身不含组件函数定义,组件收集
|
|
7
|
+
* 结果为空,此前不会注入任何 HMR 代码,编辑该文件时 Vite 找不到自接受
|
|
8
|
+
* 边界而回落整页刷新。
|
|
9
|
+
*
|
|
10
|
+
* 注意只匹配组件形态(大写开头)的导入绑定:转发普通值(工具函数、常量)
|
|
11
|
+
* 的模块不具备组件热更新能力,保持整页刷新的默认行为。
|
|
12
|
+
* @module utils/forward-export
|
|
13
|
+
*/
|
|
14
|
+
import * as t from '@babel/types';
|
|
15
|
+
/**
|
|
16
|
+
* 检测模块是否存在「导入转发形式的组件默认导出」
|
|
17
|
+
*
|
|
18
|
+
* 覆盖两种转发形态(转发目标均为本地 import 绑定且大写开头):
|
|
19
|
+
* 1. `export default AdminLayout`(含 `as T` 断言包装)
|
|
20
|
+
* 2. `export { AdminLayout as default }`
|
|
21
|
+
*
|
|
22
|
+
* 带 source 的 re-export(`export { X as default } from './x'`)没有本地
|
|
23
|
+
* 绑定,不在导入绑定集合中,天然被排除。
|
|
24
|
+
*
|
|
25
|
+
* @param program - AST Program 节点
|
|
26
|
+
* @returns 是否存在转发形式的组件默认导出
|
|
27
|
+
*/
|
|
28
|
+
export declare function hasForwardedComponentDefaultExport(program: t.Program): boolean;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { isValidComponentName } from './component-collect.js';
|
|
2
|
+
/**
|
|
3
|
+
* 收集模块内所有本地 import 绑定名称
|
|
4
|
+
* @param program - AST Program 节点
|
|
5
|
+
* @returns 导入绑定名称集合
|
|
6
|
+
*/
|
|
7
|
+
function collectImportedBindingNames(program) {
|
|
8
|
+
const importedBindings = new Set();
|
|
9
|
+
for (const node of program.body) {
|
|
10
|
+
if (node.type !== 'ImportDeclaration')
|
|
11
|
+
continue;
|
|
12
|
+
for (const spec of node.specifiers) {
|
|
13
|
+
importedBindings.add(spec.local.name);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return importedBindings;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 解包 TS 类型包装表达式
|
|
20
|
+
*
|
|
21
|
+
* `export default X as T` 在 babel TS 插件下解析为 TSAsExpression
|
|
22
|
+
* (还有 satisfies / 非空断言变体),解包到真实表达式后再判定。
|
|
23
|
+
* @param node - 默认导出声明表达式
|
|
24
|
+
* @returns 解包后的表达式
|
|
25
|
+
*/
|
|
26
|
+
function unwrapTsWrappers(node) {
|
|
27
|
+
while (node.type === 'TSAsExpression' ||
|
|
28
|
+
node.type === 'TSSatisfiesExpression' ||
|
|
29
|
+
node.type === 'TSNonNullExpression') {
|
|
30
|
+
node = node.expression;
|
|
31
|
+
}
|
|
32
|
+
return node;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 检测模块是否存在「导入转发形式的组件默认导出」
|
|
36
|
+
*
|
|
37
|
+
* 覆盖两种转发形态(转发目标均为本地 import 绑定且大写开头):
|
|
38
|
+
* 1. `export default AdminLayout`(含 `as T` 断言包装)
|
|
39
|
+
* 2. `export { AdminLayout as default }`
|
|
40
|
+
*
|
|
41
|
+
* 带 source 的 re-export(`export { X as default } from './x'`)没有本地
|
|
42
|
+
* 绑定,不在导入绑定集合中,天然被排除。
|
|
43
|
+
*
|
|
44
|
+
* @param program - AST Program 节点
|
|
45
|
+
* @returns 是否存在转发形式的组件默认导出
|
|
46
|
+
*/
|
|
47
|
+
export function hasForwardedComponentDefaultExport(program) {
|
|
48
|
+
const importedBindings = collectImportedBindingNames(program);
|
|
49
|
+
if (importedBindings.size === 0)
|
|
50
|
+
return false;
|
|
51
|
+
for (const node of program.body) {
|
|
52
|
+
// 形态1:export default X
|
|
53
|
+
if (node.type === 'ExportDefaultDeclaration') {
|
|
54
|
+
const decl = unwrapTsWrappers(node.declaration);
|
|
55
|
+
if (decl.type === 'Identifier') {
|
|
56
|
+
const name = decl.name;
|
|
57
|
+
if (importedBindings.has(name) && isValidComponentName(name))
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// 形态2:export { X as default }
|
|
62
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
63
|
+
for (const spec of node.specifiers) {
|
|
64
|
+
if (spec.type === 'ExportSpecifier' &&
|
|
65
|
+
spec.exported.type === 'Identifier' &&
|
|
66
|
+
spec.exported.name === 'default' &&
|
|
67
|
+
spec.local.type === 'Identifier' &&
|
|
68
|
+
importedBindings.has(spec.local.name) &&
|
|
69
|
+
isValidComponentName(spec.local.name)) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 函数提取模块
|
|
3
|
+
* 从调用表达式中提取匿名函数,转换为命名函数声明
|
|
4
|
+
* @module utils/function-extract
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
/**
|
|
8
|
+
* 将函数表达式或箭头函数转换为命名函数声明
|
|
9
|
+
* 用于将匿名函数转换为可追踪的命名函数
|
|
10
|
+
* @param func - 函数表达式或箭头函数节点
|
|
11
|
+
* @param name - 生成的函数名称
|
|
12
|
+
* @returns 命名函数声明节点
|
|
13
|
+
*/
|
|
14
|
+
export declare function convertToNamedFunctionDeclaration(func: t.FunctionExpression | t.ArrowFunctionExpression, name: string): t.FunctionDeclaration;
|
|
15
|
+
/**
|
|
16
|
+
* 从调用表达式中提取函数参数
|
|
17
|
+
* 将 export default builder(()=><div/>) 转换为:
|
|
18
|
+
* const _defaultExport = ()=><div/>
|
|
19
|
+
* export default builder(_defaultExport)
|
|
20
|
+
* @param callExpr - 调用表达式
|
|
21
|
+
* @param exportedNames - 已存在的名称集合
|
|
22
|
+
* @param builderAlias - builder 的本地别名
|
|
23
|
+
* @returns 提取结果,包含名称、函数声明节点和是否是 builder 包装
|
|
24
|
+
*/
|
|
25
|
+
export declare function extractFunctionFromCallExpression(callExpr: t.CallExpression, exportedNames: Set<string>, builderAlias: string | null): {
|
|
26
|
+
name: string;
|
|
27
|
+
funcNode: t.FunctionDeclaration;
|
|
28
|
+
isBuilderWrapped: boolean;
|
|
29
|
+
} | null;
|
|
30
|
+
/**
|
|
31
|
+
* 从变量声明的调用表达式中提取函数
|
|
32
|
+
* 将 const App = builder(() => <div/>) 转换为:
|
|
33
|
+
* function App$1() { return <div/> }
|
|
34
|
+
* const App = builder(App$1)
|
|
35
|
+
* @param callExpr - 调用表达式
|
|
36
|
+
* @param variableName - 变量名(作为内部函数名的基础)
|
|
37
|
+
* @param allBindingNames - 所有绑定名称集合(用于避免命名冲突)
|
|
38
|
+
* @param builderAlias - builder 的本地别名
|
|
39
|
+
* @returns 提取结果,包含名称、函数声明节点和是否是 builder 包装
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractFunctionFromVariableInit(callExpr: t.CallExpression, variableName: string, allBindingNames: Set<string>, builderAlias: string | null): {
|
|
42
|
+
name: string;
|
|
43
|
+
funcNode: t.FunctionDeclaration;
|
|
44
|
+
isBuilderWrapped: boolean;
|
|
45
|
+
} | null;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 函数提取模块
|
|
3
|
+
* 从调用表达式中提取匿名函数,转换为命名函数声明
|
|
4
|
+
* @module utils/function-extract
|
|
5
|
+
*/
|
|
6
|
+
import * as t from '@babel/types';
|
|
7
|
+
import { isBuilderCall } from './collect-names.js';
|
|
8
|
+
import { generateUniqueAlias, generateUniqueDefaultName } from './generate.js';
|
|
9
|
+
/**
|
|
10
|
+
* 将函数表达式或箭头函数转换为命名函数声明
|
|
11
|
+
* 用于将匿名函数转换为可追踪的命名函数
|
|
12
|
+
* @param func - 函数表达式或箭头函数节点
|
|
13
|
+
* @param name - 生成的函数名称
|
|
14
|
+
* @returns 命名函数声明节点
|
|
15
|
+
*/
|
|
16
|
+
export function convertToNamedFunctionDeclaration(func, name) {
|
|
17
|
+
let body;
|
|
18
|
+
// 处理箭头函数的隐式返回:将表达式体包装为显式 return 语句
|
|
19
|
+
if (func.type === 'ArrowFunctionExpression' && func.body.type !== 'BlockStatement') {
|
|
20
|
+
body = t.blockStatement([t.returnStatement(func.body)]);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
body = func.body;
|
|
24
|
+
}
|
|
25
|
+
// 创建函数声明节点
|
|
26
|
+
const funcDecl = t.functionDeclaration(t.identifier(name), // 函数名称
|
|
27
|
+
func.params, // 参数列表
|
|
28
|
+
body, // 函数体
|
|
29
|
+
func.type === 'FunctionExpression' ? func.generator : false, // 是否为生成器
|
|
30
|
+
func.async // 是否为异步函数
|
|
31
|
+
);
|
|
32
|
+
// 保留原始位置信息用于源码映射
|
|
33
|
+
funcDecl.loc = func.loc;
|
|
34
|
+
return funcDecl;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 从调用表达式中提取函数参数
|
|
38
|
+
* 将 export default builder(()=><div/>) 转换为:
|
|
39
|
+
* const _defaultExport = ()=><div/>
|
|
40
|
+
* export default builder(_defaultExport)
|
|
41
|
+
* @param callExpr - 调用表达式
|
|
42
|
+
* @param exportedNames - 已存在的名称集合
|
|
43
|
+
* @param builderAlias - builder 的本地别名
|
|
44
|
+
* @returns 提取结果,包含名称、函数声明节点和是否是 builder 包装
|
|
45
|
+
*/
|
|
46
|
+
export function extractFunctionFromCallExpression(callExpr, exportedNames, builderAlias) {
|
|
47
|
+
// 判断是否为 builder 函数调用
|
|
48
|
+
const isBuilder = isBuilderCall(callExpr, builderAlias);
|
|
49
|
+
// 遍历调用表达式的所有参数
|
|
50
|
+
for (let i = 0; i < callExpr.arguments.length; i++) {
|
|
51
|
+
const arg = callExpr.arguments[i];
|
|
52
|
+
// 找到作为参数传入的匿名函数
|
|
53
|
+
if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
|
|
54
|
+
// 生成唯一名称
|
|
55
|
+
const name = generateUniqueDefaultName(exportedNames);
|
|
56
|
+
// 转换为命名函数声明
|
|
57
|
+
const funcNode = convertToNamedFunctionDeclaration(arg, name);
|
|
58
|
+
// 将原参数替换为标识符引用
|
|
59
|
+
callExpr.arguments[i] = t.identifier(name);
|
|
60
|
+
return { name, funcNode, isBuilderWrapped: isBuilder };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 未找到可提取的函数参数
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 从变量声明的调用表达式中提取函数
|
|
68
|
+
* 将 const App = builder(() => <div/>) 转换为:
|
|
69
|
+
* function App$1() { return <div/> }
|
|
70
|
+
* const App = builder(App$1)
|
|
71
|
+
* @param callExpr - 调用表达式
|
|
72
|
+
* @param variableName - 变量名(作为内部函数名的基础)
|
|
73
|
+
* @param allBindingNames - 所有绑定名称集合(用于避免命名冲突)
|
|
74
|
+
* @param builderAlias - builder 的本地别名
|
|
75
|
+
* @returns 提取结果,包含名称、函数声明节点和是否是 builder 包装
|
|
76
|
+
*/
|
|
77
|
+
export function extractFunctionFromVariableInit(callExpr, variableName, allBindingNames, builderAlias) {
|
|
78
|
+
// 判断是否为 builder 函数调用
|
|
79
|
+
const isBuilder = isBuilderCall(callExpr, builderAlias);
|
|
80
|
+
// 遍历调用表达式的所有参数
|
|
81
|
+
for (let i = 0; i < callExpr.arguments.length; i++) {
|
|
82
|
+
const arg = callExpr.arguments[i];
|
|
83
|
+
// 找到作为参数传入的匿名函数
|
|
84
|
+
if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
|
|
85
|
+
// 以变量名为基础生成唯一别名,避免与已有名称冲突
|
|
86
|
+
const name = generateUniqueAlias(variableName, allBindingNames);
|
|
87
|
+
// 将新名称加入集合,防止后续生成重复名称
|
|
88
|
+
allBindingNames.add(name);
|
|
89
|
+
// 转换为命名函数声明
|
|
90
|
+
const funcNode = convertToNamedFunctionDeclaration(arg, name);
|
|
91
|
+
// 将原参数替换为标识符引用
|
|
92
|
+
callExpr.arguments[i] = t.identifier(name);
|
|
93
|
+
return { name, funcNode, isBuilderWrapped: isBuilder };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// 未找到可提取的函数参数
|
|
97
|
+
return null;
|
|
98
|
+
}
|
package/dist/utils/generate.d.ts
CHANGED
|
@@ -6,3 +6,10 @@
|
|
|
6
6
|
* @returns 唯一的别名
|
|
7
7
|
*/
|
|
8
8
|
export declare function generateUniqueAlias(baseName: string, existingNames: Set<string>): string;
|
|
9
|
+
/**
|
|
10
|
+
* 生成唯一的默认导出组件名称
|
|
11
|
+
* 优先使用基础名称,若冲突则生成唯一别名
|
|
12
|
+
* @param existingNames - 已存在的名称集合
|
|
13
|
+
* @returns 唯一的默认导出名称
|
|
14
|
+
*/
|
|
15
|
+
export declare function generateUniqueDefaultName(existingNames: Set<string>): string;
|
package/dist/utils/generate.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 名称生成工具模块
|
|
3
|
+
* 提供唯一别名和默认导出名称的生成功能
|
|
4
|
+
* @module utils/generate
|
|
5
|
+
*/
|
|
6
|
+
import { DEFAULT_EXPORT_BASE_NAME } from '../constants/index.js';
|
|
1
7
|
/**
|
|
2
8
|
* 生成唯一的别名
|
|
3
9
|
* 从 $1 开始递增,直到找到不冲突的名称
|
|
@@ -13,3 +19,17 @@ export function generateUniqueAlias(baseName, existingNames) {
|
|
|
13
19
|
}
|
|
14
20
|
return `${baseName}$${index}`;
|
|
15
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* 生成唯一的默认导出组件名称
|
|
24
|
+
* 优先使用基础名称,若冲突则生成唯一别名
|
|
25
|
+
* @param existingNames - 已存在的名称集合
|
|
26
|
+
* @returns 唯一的默认导出名称
|
|
27
|
+
*/
|
|
28
|
+
export function generateUniqueDefaultName(existingNames) {
|
|
29
|
+
// 如果默认名称未被占用,直接返回
|
|
30
|
+
if (!existingNames.has(DEFAULT_EXPORT_BASE_NAME)) {
|
|
31
|
+
return DEFAULT_EXPORT_BASE_NAME;
|
|
32
|
+
}
|
|
33
|
+
// 否则生成唯一别名
|
|
34
|
+
return generateUniqueAlias(DEFAULT_EXPORT_BASE_NAME, existingNames);
|
|
35
|
+
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { isWhitespaceJSXText, isJSXComment } from './ast-guards.js';
|
|
2
|
-
export { getJSXElementName, resolveJSXElementType, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren
|
|
2
|
+
export { getJSXElementName, resolveJSXElementType, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren } from './jsx-helpers.js';
|
|
3
3
|
export { createUnrefCall, createAccessorCall, createExprCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
|
|
4
4
|
export { collectPatternBindings, collectObjectPatternBindings } from './pattern-helpers.js';
|
|
5
5
|
export { createBranch, createBinaryBranch, buildNestedCondition, type BranchConfig } from './branch-factory.js';
|
|
6
6
|
export { validateVIfChain, collectVIfChainInfo, collectFragmentVIfChains, type VIfChainInfo } from './vif-helpers.js';
|
|
7
7
|
export { collectComponentFunctions, type ComponentInfo } from './component-collect.js';
|
|
8
|
-
export {
|
|
8
|
+
export { hasForwardedComponentDefaultExport } from './forward-export.js';
|
|
9
|
+
export { generateUniqueAlias, generateUniqueDefaultName } from './generate.js';
|
package/dist/utils/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 类型守卫
|
|
2
2
|
export { isWhitespaceJSXText, isJSXComment } from './ast-guards.js';
|
|
3
3
|
// JSX 辅助函数
|
|
4
|
-
export { getJSXElementName, resolveJSXElementType, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren
|
|
4
|
+
export { getJSXElementName, resolveJSXElementType, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren } from './jsx-helpers.js';
|
|
5
5
|
// AST 构建函数
|
|
6
6
|
export { createUnrefCall, createAccessorCall, createExprCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
|
|
7
7
|
// 模式处理辅助函数
|
|
@@ -12,5 +12,7 @@ export { createBranch, createBinaryBranch, buildNestedCondition } from './branch
|
|
|
12
12
|
export { validateVIfChain, collectVIfChainInfo, collectFragmentVIfChains } from './vif-helpers.js';
|
|
13
13
|
// 组件收集
|
|
14
14
|
export { collectComponentFunctions } from './component-collect.js';
|
|
15
|
+
// 导入转发默认导出检测
|
|
16
|
+
export { hasForwardedComponentDefaultExport } from './forward-export.js';
|
|
15
17
|
// 生成器
|
|
16
|
-
export { generateUniqueAlias } from './generate.js';
|
|
18
|
+
export { generateUniqueAlias, generateUniqueDefaultName } from './generate.js';
|
|
@@ -101,8 +101,5 @@ export declare function hasEffectiveChildren(node: JSXElement): boolean;
|
|
|
101
101
|
*/
|
|
102
102
|
export declare function filterEffectiveChildren(node: JSXElement): t.Node[];
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
105
|
-
* Match 必须在 Switch 内使用,非 Switch 元素的子节点中不允许出现 Match
|
|
106
|
-
* @param children - 子节点数组
|
|
104
|
+
* 过滤掉空白文本子节点
|
|
107
105
|
*/
|
|
108
|
-
export declare function validateNoDirectMatchChild(children: t.Node[]): void;
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
* @module utils/jsx-helpers
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
|
-
import {
|
|
7
|
+
import { isJSXText } from '@babel/types';
|
|
8
8
|
import { DIRECTIVE_PREFIX, PURE_COMPILE_COMPONENTS, V_IF_CHAIN_DIRECTIVES } from '../constants/index.js';
|
|
9
|
-
import { createError } from '../error.js';
|
|
10
9
|
import { isWhitespaceJSXText } from './ast-guards.js';
|
|
11
10
|
/**
|
|
12
11
|
* 获取 JSX 元素的名称
|
|
@@ -253,17 +252,5 @@ export function filterEffectiveChildren(node) {
|
|
|
253
252
|
return node.children.filter(isEffectiveChild);
|
|
254
253
|
}
|
|
255
254
|
/**
|
|
256
|
-
*
|
|
257
|
-
* Match 必须在 Switch 内使用,非 Switch 元素的子节点中不允许出现 Match
|
|
258
|
-
* @param children - 子节点数组
|
|
255
|
+
* 过滤掉空白文本子节点
|
|
259
256
|
*/
|
|
260
|
-
export function validateNoDirectMatchChild(children) {
|
|
261
|
-
for (const child of children) {
|
|
262
|
-
if (isJSXElement(child)) {
|
|
263
|
-
const childName = getJSXElementName(child);
|
|
264
|
-
if (childName === 'Match') {
|
|
265
|
-
throw createError('E012', child);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
}
|
|
@@ -125,7 +125,9 @@ export function collectFragmentVIfChains(children) {
|
|
|
125
125
|
*/
|
|
126
126
|
function collectSingleChainFromFragment(children, startIndex) {
|
|
127
127
|
const nodes = [children[startIndex]];
|
|
128
|
-
|
|
128
|
+
// v-if 指令值已由 validateVIfChain 保证存在,fallback 为 true(无表达式时)
|
|
129
|
+
const vIfValue = getDirectiveValue(nodes[0], 'v-if') ?? t.booleanLiteral(true);
|
|
130
|
+
const conditions = [vIfValue];
|
|
129
131
|
let j = startIndex + 1;
|
|
130
132
|
// 继续收集后续的 v-else-if 和 v-else
|
|
131
133
|
while (j < children.length) {
|
|
@@ -147,9 +149,10 @@ function collectSingleChainFromFragment(children, startIndex) {
|
|
|
147
149
|
if (!isVIfChain(nextChild))
|
|
148
150
|
break;
|
|
149
151
|
if (isVElseIf(nextChild)) {
|
|
150
|
-
// 收集 v-else-if
|
|
152
|
+
// 收集 v-else-if,值已由 validateVIfChain 保证存在
|
|
151
153
|
nodes.push(nextChild);
|
|
152
|
-
|
|
154
|
+
const vElseIfValue = getDirectiveValue(nextChild, 'v-else-if') ?? t.booleanLiteral(true);
|
|
155
|
+
conditions.push(vElseIfValue);
|
|
153
156
|
j++;
|
|
154
157
|
}
|
|
155
158
|
else if (isVElse(nextChild)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitarx/plugin-vite",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "The official plugin for Vitarx support in Vite.",
|
|
5
5
|
"author": "ZhuChongLin <8210856@qq.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,7 +47,6 @@
|
|
|
47
47
|
"acorn-walk": "^8.3.5"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"typescript": "^6.0.3",
|
|
51
50
|
"@commitlint/cli": "^20.4.2",
|
|
52
51
|
"@commitlint/config-conventional": "^20.4.2",
|
|
53
52
|
"@commitlint/types": "^20.4.0",
|
|
@@ -55,13 +54,15 @@
|
|
|
55
54
|
"@types/babel__generator": "^7.27.0",
|
|
56
55
|
"@types/babel__traverse": "^7.28.0",
|
|
57
56
|
"@types/node": "^25.3.2",
|
|
57
|
+
"happy-dom": "^20.14.5",
|
|
58
58
|
"husky": "^9.1.7",
|
|
59
59
|
"node": "^25.7.0",
|
|
60
60
|
"prettier": "^3.8.1",
|
|
61
61
|
"rimraf": "^6.1.3",
|
|
62
|
-
"
|
|
62
|
+
"typescript": "^6.0.3",
|
|
63
|
+
"vitarx": "^4.0.6",
|
|
63
64
|
"vite": "^8.0.5",
|
|
64
|
-
"
|
|
65
|
+
"vitest": "^4.0.18"
|
|
65
66
|
},
|
|
66
67
|
"scripts": {
|
|
67
68
|
"build": "vitest run && rimraf dist && tsc -p tsconfig.build.json",
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 代码变更类型
|
|
3
|
-
*/
|
|
4
|
-
export interface ChangeCode {
|
|
5
|
-
/** UI 描述代码是否变化(需要重新构建视图) */
|
|
6
|
-
build: boolean;
|
|
7
|
-
/** 非 UI 代码是否变化(需要完全重新挂载) */
|
|
8
|
-
logic: boolean;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* 判断两个函数组件的代码差异
|
|
12
|
-
*
|
|
13
|
-
* 分析策略:
|
|
14
|
-
* 1. 解析函数代码,提取所有 createView/branch/expr 等 UI API 调用
|
|
15
|
-
* 2. 将这些调用识别为 UI 描述代码
|
|
16
|
-
* 3. 其余代码识别为非 UI 代码(逻辑代码)
|
|
17
|
-
* 4. 分别比较 UI 代码和非 UI 代码是否变化
|
|
18
|
-
*
|
|
19
|
-
* @param newCode 新函数代码
|
|
20
|
-
* @param oldCode 旧函数代码
|
|
21
|
-
* @returns {ChangeCode} 变更检测结果
|
|
22
|
-
*/
|
|
23
|
-
export declare function diffComponentChange(newCode: string, oldCode: string): ChangeCode;
|
package/dist/hmr-client/utils.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HMR 代码变更检测工具
|
|
3
|
-
* 用于判断组件代码中 UI 描述部分和非 UI 部分的变更
|
|
4
|
-
* @module hmr-client/utils
|
|
5
|
-
*/
|
|
6
|
-
import { parse } from 'acorn';
|
|
7
|
-
import { simple as walkSimple } from 'acorn-walk';
|
|
8
|
-
import { UI_API_NAMES } from '../constants/index.js';
|
|
9
|
-
/**
|
|
10
|
-
* UI 相关的运行时 API 名称
|
|
11
|
-
* 这些 API 调用代表 UI 描述代码
|
|
12
|
-
*/
|
|
13
|
-
const UI_APIS = new Set([...UI_API_NAMES, 'jsxDEV', 'h']);
|
|
14
|
-
/**
|
|
15
|
-
* 判断标识符名称是否为 UI API
|
|
16
|
-
* 支持原始名称和带后缀的名称(如 jsxDEV$1、branch$2、expr$1)
|
|
17
|
-
*/
|
|
18
|
-
function isUIApi(name) {
|
|
19
|
-
if (UI_APIS.has(name))
|
|
20
|
-
return true;
|
|
21
|
-
for (const api of UI_APIS) {
|
|
22
|
-
if (name.startsWith(api + '$'))
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* 从函数代码中分离 UI 代码和非 UI 代码
|
|
29
|
-
* @param functionCode 完整函数代码
|
|
30
|
-
* @returns 包含逻辑代码和渲染代码的对象
|
|
31
|
-
*/
|
|
32
|
-
function separateLogicAndRender(functionCode) {
|
|
33
|
-
const uiNodes = [];
|
|
34
|
-
// 包装代码以便解析
|
|
35
|
-
const wrappedCode = `(${functionCode})`;
|
|
36
|
-
try {
|
|
37
|
-
const ast = parse(wrappedCode, {
|
|
38
|
-
ecmaVersion: 'latest',
|
|
39
|
-
sourceType: 'module',
|
|
40
|
-
allowReturnOutsideFunction: true,
|
|
41
|
-
allowAwaitOutsideFunction: true,
|
|
42
|
-
allowImportExportEverywhere: true,
|
|
43
|
-
allowHashBang: true,
|
|
44
|
-
allowReserved: true
|
|
45
|
-
});
|
|
46
|
-
// 遍历 AST 收集所有 UI API 调用节点
|
|
47
|
-
walkSimple(ast, {
|
|
48
|
-
CallExpression(node) {
|
|
49
|
-
const callee = node.callee;
|
|
50
|
-
// 直接调用:createView(...)
|
|
51
|
-
if (callee.type === 'Identifier' && isUIApi(callee.name)) {
|
|
52
|
-
uiNodes.push({ start: node.start, end: node.end });
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
// 成员调用:exports.createView(...)
|
|
56
|
-
if (callee.type === 'MemberExpression' &&
|
|
57
|
-
callee.property.type === 'Identifier' &&
|
|
58
|
-
isUIApi(callee.property.name)) {
|
|
59
|
-
uiNodes.push({ start: node.start, end: node.end });
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// 解析失败时,返回原始代码作为逻辑代码
|
|
66
|
-
return {
|
|
67
|
-
logicCode: functionCode.trim(),
|
|
68
|
-
renderCode: ''
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
// 按位置排序,从后向前替换以保持索引正确
|
|
72
|
-
uiNodes.sort((a, b) => a.start - b.start);
|
|
73
|
-
const filteredNodes = [];
|
|
74
|
-
for (const node of uiNodes) {
|
|
75
|
-
const isContained = filteredNodes.some(parent => node.start >= parent.start && node.end <= parent.end);
|
|
76
|
-
if (!isContained) {
|
|
77
|
-
filteredNodes.push(node);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
filteredNodes.sort((a, b) => b.start - a.start);
|
|
81
|
-
// 提取 UI 代码
|
|
82
|
-
const renderCode = filteredNodes
|
|
83
|
-
.map(n => wrappedCode.slice(n.start, n.end))
|
|
84
|
-
.reverse()
|
|
85
|
-
.join('\n');
|
|
86
|
-
// 移除 UI 代码后的逻辑代码
|
|
87
|
-
let logicCode = wrappedCode;
|
|
88
|
-
for (const node of filteredNodes) {
|
|
89
|
-
logicCode = logicCode.slice(0, node.start) + logicCode.slice(node.end);
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
logicCode: logicCode.trim(),
|
|
93
|
-
renderCode
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* 规范化代码字符串
|
|
98
|
-
* 移除多余空白、注释等,用于比较
|
|
99
|
-
*/
|
|
100
|
-
function normalizeCode(code) {
|
|
101
|
-
// 移除多行注释
|
|
102
|
-
let result = code.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
103
|
-
// 移除单行注释
|
|
104
|
-
result = result.replace(/\/\/.*$/gm, '');
|
|
105
|
-
// 移除多余空白
|
|
106
|
-
result = result.replace(/\s+/g, ' ');
|
|
107
|
-
// 移除首尾空白
|
|
108
|
-
return result.trim();
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* 判断两个函数组件的代码差异
|
|
112
|
-
*
|
|
113
|
-
* 分析策略:
|
|
114
|
-
* 1. 解析函数代码,提取所有 createView/branch/expr 等 UI API 调用
|
|
115
|
-
* 2. 将这些调用识别为 UI 描述代码
|
|
116
|
-
* 3. 其余代码识别为非 UI 代码(逻辑代码)
|
|
117
|
-
* 4. 分别比较 UI 代码和非 UI 代码是否变化
|
|
118
|
-
*
|
|
119
|
-
* @param newCode 新函数代码
|
|
120
|
-
* @param oldCode 旧函数代码
|
|
121
|
-
* @returns {ChangeCode} 变更检测结果
|
|
122
|
-
*/
|
|
123
|
-
export function diffComponentChange(newCode, oldCode) {
|
|
124
|
-
const { renderCode: newRenderCode, logicCode: newLogicCode } = separateLogicAndRender(newCode);
|
|
125
|
-
const { renderCode: oldRenderCode, logicCode: oldLogicCode } = separateLogicAndRender(oldCode);
|
|
126
|
-
// 规范化后比较,避免格式差异导致误判
|
|
127
|
-
const normalizedNewLogic = normalizeCode(newLogicCode);
|
|
128
|
-
const normalizedOldLogic = normalizeCode(oldLogicCode);
|
|
129
|
-
const normalizedNewRender = normalizeCode(newRenderCode);
|
|
130
|
-
const normalizedOldRender = normalizeCode(oldRenderCode);
|
|
131
|
-
return {
|
|
132
|
-
// UI 代码变化:需要重新构建视图
|
|
133
|
-
build: normalizedNewRender !== normalizedOldRender,
|
|
134
|
-
// 非 UI 代码变化:需要完全重新挂载组件
|
|
135
|
-
logic: normalizedNewLogic !== normalizedOldLogic
|
|
136
|
-
};
|
|
137
|
-
}
|