@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/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
|
*/
|
|
@@ -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
|
}
|
|
@@ -119,20 +113,25 @@ function transformAST(ast, ctx) {
|
|
|
119
113
|
*/
|
|
120
114
|
function generateCode(ast, code, id, sourceMap) {
|
|
121
115
|
const babelGenerate = typeof generate === 'object' ? generate.default : generate;
|
|
122
|
-
const output = babelGenerate(ast, { sourceMaps: !!sourceMap, filename: id }, code);
|
|
123
|
-
return { code: output.code, map: output.map
|
|
116
|
+
const output = babelGenerate(ast, { sourceMaps: !!sourceMap, filename: id, sourceFileName: id }, code);
|
|
117
|
+
return { code: output.code, map: output.map };
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* 检查文件是否需要转换
|
|
121
|
+
*/
|
|
122
|
+
export function shouldTransform(id) {
|
|
123
|
+
const ext = id.split('?')[0].split('.').pop()?.toLowerCase();
|
|
124
|
+
return ext === 'jsx' || ext === 'tsx';
|
|
124
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
|
* 子元素列表
|
|
@@ -70,9 +70,10 @@ export declare function createLocationObject(filename: string, loc: SourceLocati
|
|
|
70
70
|
/**
|
|
71
71
|
* 为调用表达式添加 @__PURE__ 注释
|
|
72
72
|
* @param node - 调用表达式节点
|
|
73
|
+
* @param ctx - 转换上下文
|
|
73
74
|
* @returns 添加注释后的节点
|
|
74
75
|
*/
|
|
75
|
-
export declare function addPureComment<T extends CallExpression>(node: T): T;
|
|
76
|
+
export declare function addPureComment<T extends CallExpression>(node: T, ctx: TransformContext): T;
|
|
76
77
|
/**
|
|
77
78
|
* 获取 API 别名
|
|
78
79
|
* @param aliases - 别名映射
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
import * as t from '@babel/types';
|
|
6
6
|
import { isIdentifier, isStringLiteral } from '@babel/types';
|
|
7
7
|
import { PURE_COMMENT } from '../constants/index.js';
|
|
8
|
-
/** 用于追踪已添加 PURE 注释的节点 */
|
|
9
|
-
const pureCommentedNodes = new WeakSet();
|
|
10
8
|
/**
|
|
11
9
|
* 创建 unref 调用
|
|
12
10
|
* @param argument - 参数表达式
|
|
@@ -129,13 +127,14 @@ export function createLocationObject(filename, loc) {
|
|
|
129
127
|
/**
|
|
130
128
|
* 为调用表达式添加 @__PURE__ 注释
|
|
131
129
|
* @param node - 调用表达式节点
|
|
130
|
+
* @param ctx - 转换上下文
|
|
132
131
|
* @returns 添加注释后的节点
|
|
133
132
|
*/
|
|
134
|
-
export function addPureComment(node) {
|
|
135
|
-
if (pureCommentedNodes.has(node)) {
|
|
133
|
+
export function addPureComment(node, ctx) {
|
|
134
|
+
if (ctx.pureCommentedNodes.has(node)) {
|
|
136
135
|
return node;
|
|
137
136
|
}
|
|
138
|
-
pureCommentedNodes.add(node);
|
|
137
|
+
ctx.pureCommentedNodes.add(node);
|
|
139
138
|
t.addComment(node, 'leading', ` ${PURE_COMMENT} `, false);
|
|
140
139
|
return node;
|
|
141
140
|
}
|
|
@@ -22,7 +22,7 @@ export function createBranch(config, ctx) {
|
|
|
22
22
|
}
|
|
23
23
|
const conditionExpr = buildNestedCondition(conditions, ctx, useRef);
|
|
24
24
|
const branchAlias = getAlias(ctx.vitarxAliases, 'branch');
|
|
25
|
-
return addPureComment(createBranchCall(createArrowFunction(conditionExpr), branches, branchAlias, locInfo));
|
|
25
|
+
return addPureComment(createBranchCall(createArrowFunction(conditionExpr), branches, branchAlias, locInfo), ctx);
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* 判断表达式是否需要括号包裹
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 名称收集模块
|
|
3
|
+
* 负责从 AST 中收集导出名称、绑定名称和 builder 包装名称
|
|
4
|
+
* @module utils/collect-names
|
|
5
|
+
*/
|
|
6
|
+
import type * as t from '@babel/types';
|
|
7
|
+
/**
|
|
8
|
+
* 从命名导出中收集名称
|
|
9
|
+
*/
|
|
10
|
+
export declare function collectFromNamedExport(node: t.ExportNamedDeclaration, names: Set<string>): void;
|
|
11
|
+
/**
|
|
12
|
+
* 从默认导出中收集名称
|
|
13
|
+
*/
|
|
14
|
+
export declare function collectFromDefaultExport(node: t.ExportDefaultDeclaration, names: Set<string>): void;
|
|
15
|
+
/**
|
|
16
|
+
* 收集所有导出的标识符名称
|
|
17
|
+
*/
|
|
18
|
+
export declare function collectExportedNames(program: t.Program): Set<string>;
|
|
19
|
+
/**
|
|
20
|
+
* 收集模块中所有绑定的名称(用于避免命名冲突)
|
|
21
|
+
*/
|
|
22
|
+
export declare function collectAllBindingNames(program: t.Program): Set<string>;
|
|
23
|
+
/**
|
|
24
|
+
* 判断调用表达式是否是 builder 调用
|
|
25
|
+
* @param callExpr - 调用表达式
|
|
26
|
+
* @param builderAlias - builder 的本地别名
|
|
27
|
+
* @returns 是否是 builder 调用
|
|
28
|
+
*/
|
|
29
|
+
export declare function isBuilderCall(callExpr: t.CallExpression, builderAlias: string | null): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 收集被 builder 调用的组件名称
|
|
32
|
+
* 检测 builder(Comp) 或 builder(Comp, options) 形式的调用
|
|
33
|
+
* @param program - AST Program 节点
|
|
34
|
+
* @param builderAlias - builder 的本地别名
|
|
35
|
+
* @returns 被 builder 调用的组件名称集合
|
|
36
|
+
*/
|
|
37
|
+
export declare function collectBuilderWrappedNames(program: t.Program, builderAlias: string | null): Set<string>;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 从命名导出中收集名称
|
|
3
|
+
*/
|
|
4
|
+
export function collectFromNamedExport(node, names) {
|
|
5
|
+
if (node.declaration) {
|
|
6
|
+
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
7
|
+
names.add(node.declaration.id.name);
|
|
8
|
+
}
|
|
9
|
+
else if (node.declaration.type === 'VariableDeclaration') {
|
|
10
|
+
for (const decl of node.declaration.declarations) {
|
|
11
|
+
if (decl.id.type === 'Identifier') {
|
|
12
|
+
names.add(decl.id.name);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (node.specifiers) {
|
|
18
|
+
for (const spec of node.specifiers) {
|
|
19
|
+
if (spec.type === 'ExportSpecifier') {
|
|
20
|
+
names.add(spec.local.name);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 从默认导出中收集名称
|
|
27
|
+
*/
|
|
28
|
+
export function collectFromDefaultExport(node, names) {
|
|
29
|
+
const decl = node.declaration;
|
|
30
|
+
if (decl.type === 'FunctionDeclaration' && decl.id) {
|
|
31
|
+
names.add(decl.id.name);
|
|
32
|
+
}
|
|
33
|
+
else if (decl.type === 'Identifier') {
|
|
34
|
+
names.add(decl.name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 从变量声明中收集变量名
|
|
39
|
+
*/
|
|
40
|
+
function collectFromVariableDeclaration(node, names) {
|
|
41
|
+
for (const decl of node.declarations) {
|
|
42
|
+
if (decl.id.type === 'Identifier') {
|
|
43
|
+
names.add(decl.id.name);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 从导入声明中收集绑定的名称
|
|
49
|
+
*/
|
|
50
|
+
function collectFromImportDeclaration(node, names) {
|
|
51
|
+
for (const spec of node.specifiers) {
|
|
52
|
+
names.add(spec.local.name);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 收集所有导出的标识符名称
|
|
57
|
+
*/
|
|
58
|
+
export function collectExportedNames(program) {
|
|
59
|
+
const names = new Set();
|
|
60
|
+
for (const node of program.body) {
|
|
61
|
+
if (node.type === 'ExportNamedDeclaration') {
|
|
62
|
+
collectFromNamedExport(node, names);
|
|
63
|
+
}
|
|
64
|
+
else if (node.type === 'ExportDefaultDeclaration') {
|
|
65
|
+
collectFromDefaultExport(node, names);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return names;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 收集模块中所有绑定的名称(用于避免命名冲突)
|
|
72
|
+
*/
|
|
73
|
+
export function collectAllBindingNames(program) {
|
|
74
|
+
const names = new Set();
|
|
75
|
+
for (const node of program.body) {
|
|
76
|
+
if (node.type === 'FunctionDeclaration' && node.id) {
|
|
77
|
+
names.add(node.id.name);
|
|
78
|
+
}
|
|
79
|
+
else if (node.type === 'VariableDeclaration') {
|
|
80
|
+
collectFromVariableDeclaration(node, names);
|
|
81
|
+
}
|
|
82
|
+
else if (node.type === 'ImportDeclaration') {
|
|
83
|
+
collectFromImportDeclaration(node, names);
|
|
84
|
+
}
|
|
85
|
+
else if (node.type === 'ExportNamedDeclaration') {
|
|
86
|
+
collectFromNamedExport(node, names);
|
|
87
|
+
}
|
|
88
|
+
else if (node.type === 'ExportDefaultDeclaration') {
|
|
89
|
+
collectFromDefaultExport(node, names);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return names;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 判断调用表达式是否是 builder 调用
|
|
96
|
+
* @param callExpr - 调用表达式
|
|
97
|
+
* @param builderAlias - builder 的本地别名
|
|
98
|
+
* @returns 是否是 builder 调用
|
|
99
|
+
*/
|
|
100
|
+
export function isBuilderCall(callExpr, builderAlias) {
|
|
101
|
+
if (!builderAlias)
|
|
102
|
+
return false;
|
|
103
|
+
const callee = callExpr.callee;
|
|
104
|
+
return callee.type === 'Identifier' && callee.name === builderAlias;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 收集被 builder 调用的组件名称
|
|
108
|
+
* 检测 builder(Comp) 或 builder(Comp, options) 形式的调用
|
|
109
|
+
* @param program - AST Program 节点
|
|
110
|
+
* @param builderAlias - builder 的本地别名
|
|
111
|
+
* @returns 被 builder 调用的组件名称集合
|
|
112
|
+
*/
|
|
113
|
+
export function collectBuilderWrappedNames(program, builderAlias) {
|
|
114
|
+
const wrappedNames = new Set();
|
|
115
|
+
if (!builderAlias)
|
|
116
|
+
return wrappedNames;
|
|
117
|
+
for (const node of program.body) {
|
|
118
|
+
if (node.type === 'ExpressionStatement' && node.expression.type === 'CallExpression') {
|
|
119
|
+
const callExpr = node.expression;
|
|
120
|
+
if (isBuilderCall(callExpr, builderAlias)) {
|
|
121
|
+
const arg = callExpr.arguments[0];
|
|
122
|
+
if (arg?.type === 'Identifier') {
|
|
123
|
+
wrappedNames.add(arg.name);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return wrappedNames;
|
|
129
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
2
|
+
* 组件收集模块
|
|
3
|
+
* 负责从 AST 中收集组件函数信息
|
|
4
|
+
* @module utils/component-collect
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
7
|
/**
|
|
@@ -10,8 +10,13 @@ import * as t from '@babel/types';
|
|
|
10
10
|
export interface ComponentInfo {
|
|
11
11
|
name: string;
|
|
12
12
|
node: t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression;
|
|
13
|
+
/** 是否为包装组件(传递给 builder/defineComponent 等函数) */
|
|
14
|
+
isWrapped?: boolean;
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* 收集模块中的组件函数
|
|
18
|
+
* @param program - AST Program 节点
|
|
19
|
+
* @param builderAlias - builder 函数的本地别名(用于识别纯构建组件)
|
|
20
|
+
* @returns 组件信息数组
|
|
16
21
|
*/
|
|
17
|
-
export declare function collectComponentFunctions(program: t.Program): ComponentInfo[];
|
|
22
|
+
export declare function collectComponentFunctions(program: t.Program, builderAlias?: string | null): ComponentInfo[];
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
2
|
+
* 组件收集模块
|
|
3
|
+
* 负责从 AST 中收集组件函数信息
|
|
4
|
+
* @module utils/component-collect
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
|
-
import { DEFAULT_EXPORT_BASE_NAME
|
|
7
|
+
import { DEFAULT_EXPORT_BASE_NAME } from '../constants/index.js';
|
|
8
8
|
import { generateUniqueAlias } from './generate.js';
|
|
9
|
-
|
|
10
|
-
const PURE_COMPILE_COMPONENT_SET = new Set(PURE_COMPILE_COMPONENTS);
|
|
9
|
+
import { collectExportedNames, collectAllBindingNames, collectBuilderWrappedNames, isBuilderCall } from './collect-names.js';
|
|
11
10
|
/**
|
|
12
11
|
* 生成唯一的默认导出组件名称
|
|
13
12
|
* 避免与已存在的名称冲突
|
|
@@ -25,104 +24,22 @@ function generateUniqueDefaultName(exportedNames) {
|
|
|
25
24
|
function isValidComponentName(name) {
|
|
26
25
|
return /^[A-Z]/.test(name);
|
|
27
26
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (node.declaration) {
|
|
33
|
-
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
34
|
-
names.add(node.declaration.id.name);
|
|
35
|
-
}
|
|
36
|
-
else if (node.declaration.type === 'VariableDeclaration') {
|
|
37
|
-
for (const decl of node.declaration.declarations) {
|
|
38
|
-
if (decl.id.type === 'Identifier') {
|
|
39
|
-
names.add(decl.id.name);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (node.specifiers) {
|
|
45
|
-
for (const spec of node.specifiers) {
|
|
46
|
-
if (spec.type === 'ExportSpecifier') {
|
|
47
|
-
names.add(spec.local.name);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* 从默认导出中收集名称
|
|
54
|
-
*/
|
|
55
|
-
function collectFromDefaultExport(node, names) {
|
|
56
|
-
const decl = node.declaration;
|
|
57
|
-
if (decl.type === 'FunctionDeclaration' && decl.id) {
|
|
58
|
-
names.add(decl.id.name);
|
|
59
|
-
}
|
|
60
|
-
else if (decl.type === 'Identifier') {
|
|
61
|
-
names.add(decl.name);
|
|
62
|
-
}
|
|
63
|
-
// 匿名函数不在此处预留名称,而是在处理时动态生成唯一名称
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* 检查函数是否为组件函数
|
|
67
|
-
* 通过遍历 AST 检查是否包含 JSX 元素
|
|
68
|
-
*/
|
|
69
|
-
function isComponentFunction(node) {
|
|
70
|
-
let result = false;
|
|
71
|
-
const check = (n) => {
|
|
72
|
-
if (result)
|
|
73
|
-
return;
|
|
74
|
-
// 直接包含 JSX 元素
|
|
75
|
-
if (n.type === 'JSXElement' || n.type === 'JSXFragment') {
|
|
76
|
-
result = true;
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
// 检查 return 语句中的纯编译组件
|
|
80
|
-
if (n.type === 'ReturnStatement' && n.argument?.type === 'JSXElement') {
|
|
81
|
-
const opening = n.argument.openingElement;
|
|
82
|
-
if (opening.name.type === 'JSXIdentifier' &&
|
|
83
|
-
PURE_COMPILE_COMPONENT_SET.has(opening.name.name)) {
|
|
84
|
-
result = true;
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// 递归遍历子节点
|
|
89
|
-
for (const key of Object.keys(n)) {
|
|
90
|
-
const child = n[key];
|
|
91
|
-
if (child && typeof child === 'object') {
|
|
92
|
-
if (Array.isArray(child)) {
|
|
93
|
-
for (const c of child) {
|
|
94
|
-
if (c && typeof child === 'object')
|
|
95
|
-
check(c);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
check(child);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
check(node);
|
|
105
|
-
return result;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* 尝试添加组件到列表
|
|
109
|
-
* 检查名称有效性、是否导出、是否为组件函数
|
|
110
|
-
*/
|
|
111
|
-
function tryAddComponent(name, node, exportedNames, components) {
|
|
112
|
-
if (isValidComponentName(name) && exportedNames.has(name) && isComponentFunction(node)) {
|
|
113
|
-
components.push({ name, node });
|
|
27
|
+
function tryAddComponent(name, node, exportedNames, components, builderWrappedNames = new Set()) {
|
|
28
|
+
if (isValidComponentName(name) && exportedNames.has(name)) {
|
|
29
|
+
const isWrapped = builderWrappedNames.has(name);
|
|
30
|
+
components.push({ name, node, isWrapped });
|
|
114
31
|
}
|
|
115
32
|
}
|
|
116
33
|
/**
|
|
117
34
|
* 处理变量声明中的组件
|
|
118
35
|
*/
|
|
119
|
-
function processVariableDeclaration(declaration, exportedNames, components) {
|
|
36
|
+
function processVariableDeclaration(declaration, exportedNames, components, builderWrappedNames = new Set()) {
|
|
120
37
|
for (const decl of declaration.declarations) {
|
|
121
38
|
if (decl.id.type !== 'Identifier')
|
|
122
39
|
continue;
|
|
123
40
|
const init = decl.init;
|
|
124
41
|
if (init?.type === 'ArrowFunctionExpression' || init?.type === 'FunctionExpression') {
|
|
125
|
-
tryAddComponent(decl.id.name, init, exportedNames, components);
|
|
42
|
+
tryAddComponent(decl.id.name, init, exportedNames, components, builderWrappedNames);
|
|
126
43
|
}
|
|
127
44
|
}
|
|
128
45
|
}
|
|
@@ -141,91 +58,107 @@ function convertToNamedFunctionDeclaration(func, name) {
|
|
|
141
58
|
funcDecl.loc = func.loc;
|
|
142
59
|
return funcDecl;
|
|
143
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* 从调用表达式中提取函数参数
|
|
63
|
+
* 将 export default builder(()=><div/>) 转换为:
|
|
64
|
+
* const _defaultExport = ()=><div/>
|
|
65
|
+
* export default builder(_defaultExport)
|
|
66
|
+
* @param callExpr - 调用表达式
|
|
67
|
+
* @param exportedNames - 已存在的名称集合
|
|
68
|
+
* @param builderAlias - builder 的本地别名
|
|
69
|
+
* @returns 提取结果,包含名称、函数声明节点和是否是 builder 包装
|
|
70
|
+
*/
|
|
71
|
+
function extractFunctionFromCallExpression(callExpr, exportedNames, builderAlias) {
|
|
72
|
+
const isBuilder = isBuilderCall(callExpr, builderAlias);
|
|
73
|
+
for (let i = 0; i < callExpr.arguments.length; i++) {
|
|
74
|
+
const arg = callExpr.arguments[i];
|
|
75
|
+
if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
|
|
76
|
+
const name = generateUniqueDefaultName(exportedNames);
|
|
77
|
+
const funcNode = convertToNamedFunctionDeclaration(arg, name);
|
|
78
|
+
callExpr.arguments[i] = t.identifier(name);
|
|
79
|
+
return { name, funcNode, isBuilderWrapped: isBuilder };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
144
84
|
/**
|
|
145
85
|
* 处理默认导出的匿名函数
|
|
146
86
|
* 为匿名函数生成唯一名称并转换为命名函数声明
|
|
87
|
+
* @param node - 默认导出节点
|
|
88
|
+
* @param program - AST Program 节点,用于插入提取的函数声明
|
|
89
|
+
* @param exportedNames - 已存在的名称集合
|
|
90
|
+
* @param components - 组件信息数组
|
|
91
|
+
* @param builderAlias - builder 的本地别名
|
|
147
92
|
*/
|
|
148
|
-
function processAnonymousDefaultExport(node, exportedNames, components) {
|
|
93
|
+
function processAnonymousDefaultExport(node, program, exportedNames, components, builderAlias) {
|
|
149
94
|
const decl = node.declaration;
|
|
150
95
|
let funcNode = null;
|
|
151
96
|
let name;
|
|
152
|
-
|
|
97
|
+
let isWrapped = false;
|
|
153
98
|
if (decl.type === 'FunctionDeclaration' && !decl.id) {
|
|
154
|
-
if (!isComponentFunction(decl))
|
|
155
|
-
return;
|
|
156
99
|
name = generateUniqueDefaultName(exportedNames);
|
|
157
100
|
decl.id = t.identifier(name);
|
|
158
101
|
funcNode = decl;
|
|
159
102
|
}
|
|
160
|
-
// 函数表达式
|
|
161
103
|
else if (decl.type === 'FunctionExpression') {
|
|
162
|
-
if (!isComponentFunction(decl))
|
|
163
|
-
return;
|
|
164
104
|
name = generateUniqueDefaultName(exportedNames);
|
|
165
105
|
funcNode = convertToNamedFunctionDeclaration(decl, name);
|
|
166
106
|
node.declaration = funcNode;
|
|
167
107
|
}
|
|
168
|
-
// 箭头函数
|
|
169
108
|
else if (decl.type === 'ArrowFunctionExpression') {
|
|
170
|
-
if (!isComponentFunction(decl))
|
|
171
|
-
return;
|
|
172
109
|
name = generateUniqueDefaultName(exportedNames);
|
|
173
110
|
funcNode = convertToNamedFunctionDeclaration(decl, name);
|
|
174
111
|
node.declaration = funcNode;
|
|
175
112
|
}
|
|
176
|
-
if (
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (node.type === 'ExportNamedDeclaration') {
|
|
187
|
-
collectFromNamedExport(node, names);
|
|
188
|
-
}
|
|
189
|
-
else if (node.type === 'ExportDefaultDeclaration') {
|
|
190
|
-
collectFromDefaultExport(node, names);
|
|
113
|
+
else if (decl.type === 'CallExpression') {
|
|
114
|
+
const result = extractFunctionFromCallExpression(decl, exportedNames, builderAlias);
|
|
115
|
+
if (result) {
|
|
116
|
+
name = result.name;
|
|
117
|
+
funcNode = result.funcNode;
|
|
118
|
+
isWrapped = result.isBuilderWrapped;
|
|
119
|
+
const exportIndex = program.body.indexOf(node);
|
|
120
|
+
if (exportIndex !== -1) {
|
|
121
|
+
program.body.splice(exportIndex, 0, funcNode);
|
|
122
|
+
}
|
|
191
123
|
}
|
|
192
124
|
}
|
|
193
|
-
|
|
125
|
+
if (funcNode) {
|
|
126
|
+
components.push({ name: name, node: funcNode, isWrapped });
|
|
127
|
+
}
|
|
194
128
|
}
|
|
195
129
|
/**
|
|
196
130
|
* 收集模块中的组件函数
|
|
131
|
+
* @param program - AST Program 节点
|
|
132
|
+
* @param builderAlias - builder 函数的本地别名(用于识别纯构建组件)
|
|
133
|
+
* @returns 组件信息数组
|
|
197
134
|
*/
|
|
198
|
-
export function collectComponentFunctions(program) {
|
|
135
|
+
export function collectComponentFunctions(program, builderAlias = null) {
|
|
199
136
|
const exportedNames = collectExportedNames(program);
|
|
137
|
+
const allBindingNames = collectAllBindingNames(program);
|
|
138
|
+
const builderWrappedNames = collectBuilderWrappedNames(program, builderAlias);
|
|
200
139
|
const components = [];
|
|
201
140
|
for (const node of program.body) {
|
|
202
|
-
// 普通函数声明
|
|
203
141
|
if (node.type === 'FunctionDeclaration' && node.id) {
|
|
204
|
-
tryAddComponent(node.id.name, node, exportedNames, components);
|
|
142
|
+
tryAddComponent(node.id.name, node, exportedNames, components, builderWrappedNames);
|
|
205
143
|
}
|
|
206
|
-
// 普通变量声明
|
|
207
144
|
else if (node.type === 'VariableDeclaration') {
|
|
208
|
-
processVariableDeclaration(node, exportedNames, components);
|
|
145
|
+
processVariableDeclaration(node, exportedNames, components, builderWrappedNames);
|
|
209
146
|
}
|
|
210
|
-
// 命名导出
|
|
211
147
|
else if (node.type === 'ExportNamedDeclaration' && node.declaration) {
|
|
212
148
|
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
213
|
-
tryAddComponent(node.declaration.id.name, node.declaration, exportedNames, components);
|
|
149
|
+
tryAddComponent(node.declaration.id.name, node.declaration, exportedNames, components, builderWrappedNames);
|
|
214
150
|
}
|
|
215
151
|
else if (node.declaration.type === 'VariableDeclaration') {
|
|
216
|
-
processVariableDeclaration(node.declaration, exportedNames, components);
|
|
152
|
+
processVariableDeclaration(node.declaration, exportedNames, components, builderWrappedNames);
|
|
217
153
|
}
|
|
218
154
|
}
|
|
219
|
-
// 默认导出
|
|
220
155
|
else if (node.type === 'ExportDefaultDeclaration') {
|
|
221
156
|
const decl = node.declaration;
|
|
222
|
-
// 命名函数声明
|
|
223
157
|
if (decl.type === 'FunctionDeclaration' && decl.id) {
|
|
224
|
-
tryAddComponent(decl.id.name, decl, exportedNames, components);
|
|
158
|
+
tryAddComponent(decl.id.name, decl, exportedNames, components, builderWrappedNames);
|
|
225
159
|
}
|
|
226
|
-
// 匿名函数(函数声明、函数表达式、箭头函数)
|
|
227
160
|
else {
|
|
228
|
-
processAnonymousDefaultExport(node,
|
|
161
|
+
processAnonymousDefaultExport(node, program, allBindingNames, components, builderAlias);
|
|
229
162
|
}
|
|
230
163
|
}
|
|
231
164
|
}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { isWhitespaceJSXText } from './ast-guards.js';
|
|
2
|
-
export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren,
|
|
2
|
+
export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateNoDirectMatchChild } from './jsx-helpers.js';
|
|
3
3
|
export { createUnrefCall, createAccessCall, createDynamicCall, 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';
|
package/dist/utils/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 类型守卫
|
|
2
2
|
export { isWhitespaceJSXText } from './ast-guards.js';
|
|
3
3
|
// JSX 辅助函数
|
|
4
|
-
export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren,
|
|
4
|
+
export { getJSXElementName, isPureCompileComponent, isComponent, isNativeElement, getJSXAttributeByName, hasDirective, getDirectiveValue, isVIfChain, isVIf, isVElseIf, isVElse, removeVDirectives, removeVIfChainDirectives, removeAttribute, filterWhitespaceChildren, hasEffectiveChildren, filterEffectiveChildren, validateNoDirectMatchChild } from './jsx-helpers.js';
|
|
5
5
|
// AST 构建函数
|
|
6
6
|
export { createUnrefCall, createAccessCall, createDynamicCall, createBranchCall, createCreateViewCall, createWithDirectivesCall, createArrowFunction, createLocationObject, addPureComment, getAlias, getDevLocInfo } from './ast-builders.js';
|
|
7
7
|
// 模式处理辅助函数
|
|
@@ -77,7 +77,8 @@ export declare function hasEffectiveChildren(node: JSXElement): boolean;
|
|
|
77
77
|
*/
|
|
78
78
|
export declare function filterEffectiveChildren(node: JSXElement): t.Node[];
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
80
|
+
* 校验子节点中不能直接包含 Match 组件
|
|
81
|
+
* Match 必须在 Switch 内使用,非 Switch 元素的子节点中不允许出现 Match
|
|
81
82
|
* @param children - 子节点数组
|
|
82
83
|
*/
|
|
83
|
-
export declare function
|
|
84
|
+
export declare function validateNoDirectMatchChild(children: t.Node[]): void;
|