@actview/plugin-babel 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,64 +1,64 @@
1
- # @actview/plugin-babel
2
-
3
- > **迁移记录**:本包原为 `@actview/babel-plugin-actview`(目录 `plugins/babel-plugin-actview`),现更名为 `@actview/plugin-babel`(目录 `plugins/babel`),与 `@actview/plugin-vite` / `@actview/plugin-scoped` 统一为 `@actview/plugin-*` 命名。旧包名已弃用,请使用新包名安装与引用。
4
-
5
- **ActView 编译核心(Babel 插件)** —— JSX 组件函数自动转换为 `defineComponent`,独立于 Vite 宿主。
6
-
7
- Babel 插件 `defineComponentPlugin` 是 ActView 编译链的**核心**:将大写开头的函数 / 箭头 / 默认导出组件包装为 `defineComponent` 产物(`{ __setup }`),并完成具名插槽提取、props 白名单、早退 return 包装等转换。`@actview/plugin-vite` 与 `@actview/plugin-scoped` 都基于它。
8
-
9
- 同时本包导出**共享 Babel 宿主壳**(`createBabelTransform` / `createBabelItem` / `transformWithBabel`),供各 Vite 插件复用统一的 `transformSync` 调用参数。
10
-
11
- ## 核心功能
12
-
13
- - **组件自动转换**:函数 / 箭头 / 默认导出组件 → `defineComponent`(自动注入 `import { defineComponent } from '@actview/core'`)
14
- - **具名插槽**:`<template slot>` → `slots` prop(编译期提取)
15
- - **props 白名单**:从 `defineComponent({ props })` 声明提取 `__props`
16
- - **早退 return**:组件渲染函数中的 `if / 三元 / &&` 早退分支包装(`isRenderExpr`)
17
- - **共享宿主壳**:`createBabelTransform` 等,统一 `parserOpts` / `retainLines` / `sourceMaps` / `babelrc:false` / `configFile:false`
18
-
19
- ## 安装
20
-
21
- ```bash
22
- pnpm add -D @actview/plugin-babel
23
- ```
24
-
25
- ## 快速开始
26
-
27
- ```ts
28
- import { defineComponentPlugin } from '@actview/plugin-babel'
29
- import * as babel from '@babel/core'
30
-
31
- const result = babel.transformSync(code, {
32
- filename: 'App.tsx',
33
- plugins: [[defineComponentPlugin, {}]],
34
- parserOpts: { plugins: ['jsx', 'typescript'] },
35
- })
36
- ```
37
-
38
- > 大多数场景不需要直接使用 —— 接入 `@actview/plugin-vite`(Vite 项目)即可自动完成转换。
39
-
40
- ## API
41
-
42
- | 导出 | 说明 |
43
- |---|---|
44
- | `defineComponentPlugin`(默认导出同) | Babel 插件工厂:组件 → `defineComponent` 转换 |
45
- | `createBabelTransform(plugin)` | 宿主壳工厂:模块级创建一次 ConfigItem,返回 `(code, filename) => { code, map } \| null` |
46
- | `createBabelItem(plugin)` | 把插件工厂预编译为 ConfigItem(Babel 8 同步) |
47
- | `transformWithBabel(code, filename, pluginItem)` | 统一参数的 `transformSync` |
48
- | 类型:`BabelPlugin` / `BabelHostResult` | 宿主壳类型 |
49
-
50
- ## 依赖关系
51
-
52
- - `@babel/core`(^8)
53
- - 被依赖方:`@actview/plugin-vite`、`@actview/plugin-scoped`(复用宿主壳)
54
-
55
- ## 开发
56
-
57
- ```bash
58
- pnpm build # tsup 打包 dist
59
- pnpm test # vitest(test/plugin.test.ts:组件转换/插槽/props/早退等 30 用例)
60
- ```
61
-
62
- ## License
63
-
64
- MIT
1
+ # @actview/plugin-babel
2
+
3
+ **共享 Babel 宿主壳** —— 统一 `transformSync` 调用参数与排除规则,供 `@actview/plugin-vite` `@actview/plugin-scoped` 复用。
4
+
5
+ (v1 的 `defineComponentPlugin` / `solidPlugin` 已随 ActView v2 移除——v2 JSX 编译由 `@actview/plugin-jsx` 承担。)
6
+
7
+ ## 核心功能
8
+
9
+ - **统一参数**:`parserOpts(jsx+typescript)` / `retainLines` / `sourceMaps` / `babelrc:false` / `configFile:false`
10
+ - **排除规则**:node_modules 硬排除(include 不可覆盖)/ include 白名单 / exclude 黑名单
11
+ - **性能**:`createBabelTransform` 模块级创建一次 ConfigItem(Babel 8 同步),跨多次 `transformSync` 复用
12
+
13
+ ## 安装
14
+
15
+ ```bash
16
+ pnpm add -D @actview/plugin-babel
17
+ ```
18
+
19
+ ## API
20
+
21
+ | 导出 | 说明 |
22
+ |---|---|
23
+ | `createBabelTransform(plugin, options?)` | 宿主壳工厂:模块级创建一次 ConfigItem,返回 `(code, filename) => { code, map } \| null` |
24
+ | `createBabelItem(plugin)` | 把插件工厂预编译为 ConfigItem(Babel 8 同步) |
25
+ | `transformWithBabel(code, filename, pluginItem, options?)` | 统一参数的 `transformSync` |
26
+ | `isExcludedTransform(filename, options?)` | 排除判定:node_modules 硬排除 / 未命中 include / 命中 exclude |
27
+ | 类型:`BabelPlugin` / `BabelHostResult` / `BabelTransformOptions` | 宿主壳类型 |
28
+
29
+ ## 排除规则(node_modules 硬排除)
30
+
31
+ 宿主壳**硬排除 node_modules 下的文件**(返回 `null` 不转换):依赖是第三方代码,属依赖管线(esbuild 预构建),不是源码管线。`BabelTransformOptions`:
32
+
33
+ ```ts
34
+ {
35
+ include?: Array<string | RegExp>, // 白名单:命中任一才转换(默认不过滤;node_modules 硬排除优先)
36
+ exclude?: Array<string | RegExp>, // 黑名单:命中任一即跳过(优先于 include)
37
+ }
38
+ ```
39
+
40
+ **源码分发库包(node_modules 下)需要现场编译时**,在宿主构建配置里做**路径转换**,让文件解析路径脱离 node_modules 段:
41
+
42
+ ```ts
43
+ // vite.config.ts
44
+ resolve: {
45
+ alias: { 'my-lib': 'node_modules/my-lib/src' }, // 路径不再含 node_modules 正常转换
46
+ },
47
+ optimizeDeps: { exclude: ['my-lib'] },
48
+ ```
49
+
50
+ ## 依赖关系
51
+
52
+ - `@babel/core`(^8)
53
+ - 被依赖方:`@actview/plugin-vite`、`@actview/plugin-scoped`(复用宿主壳)
54
+
55
+ ## 开发
56
+
57
+ ```bash
58
+ pnpm build # tsup 打包 dist
59
+ pnpm test # vitest(test/babel-host.test.ts:排除规则/宿主壳行为)
60
+ ```
61
+
62
+ ## License
63
+
64
+ MIT
package/dist/index.d.ts CHANGED
@@ -1,19 +1,5 @@
1
1
  import { PluginObject, PluginItem } from '@babel/core';
2
2
 
3
- declare function defineComponentPlugin(): {
4
- visitor: {
5
- Program: {
6
- enter(): void;
7
- exit(path: any): void;
8
- };
9
- FunctionDeclaration(path: any): void;
10
- VariableDeclarator(path: any): void;
11
- JSXElement(path: any): void;
12
- JSXFragment(path: any): void;
13
- ExportDefaultDeclaration(path: any): void;
14
- };
15
- };
16
-
17
3
  /** Babel 插件工厂形态(() => { visitor }),createConfigItemSync 的标准输入 */
18
4
  type BabelPlugin = () => PluginObject;
19
5
  /** transformSync 统一产物(result.map 类型为 BabelSourceMap | null | undefined) */
@@ -21,23 +7,24 @@ interface BabelHostResult {
21
7
  code: string;
22
8
  map: unknown;
23
9
  }
24
- /** 把插件工厂预编译为 ConfigItem(Babel 8 同步版本,可跨多次 transformSync 复用) */
25
- declare function createBabelItem(plugin: BabelPlugin): PluginItem;
26
- /** 统一参数的 transformSync:失败返回 null,成功返回 { code, map } */
27
- declare function transformWithBabel(code: string, filename: string, pluginItem: PluginItem): BabelHostResult | null;
10
+ /** 宿主壳排除规则(createBabelTransform / transformWithBabel 第二/四参) */
11
+ interface BabelTransformOptions {
12
+ /** 白名单:文件命中任一规则才转换(默认不过滤;node_modules 硬排除优先) */
13
+ include?: Array<string | RegExp>;
14
+ /** 黑名单:命中任一规则即跳过(优先级高于 include) */
15
+ exclude?: Array<string | RegExp>;
16
+ }
17
+ /** 是否应跳过转换:node_modules 硬排除 / 未命中 include / 命中 exclude */
18
+ declare function isExcludedTransform(filename: string, options?: BabelTransformOptions): boolean;
19
+ /** 把插件工厂预编译为 ConfigItem(Babel 8 同步版本,可跨多次 transformSync 复用);
20
+ * pluginOptions 非空时按 [plugin, options] 元组创建(Babel 标准插件选项) */
21
+ declare function createBabelItem(plugin: BabelPlugin, pluginOptions?: object): PluginItem;
22
+ /** 统一参数的 transformSync:排除命中或失败返回 null,成功返回 { code, map } */
23
+ declare function transformWithBabel(code: string, filename: string, pluginItem: PluginItem, options?: BabelTransformOptions): BabelHostResult | null;
28
24
  /**
29
25
  * 静态插件的便捷工厂:模块级调用一次,ConfigItem 只创建一次。
30
26
  * 适用于插件对象不随文件变化的场景(如 defineComponentPlugin)。
31
27
  */
32
- declare function createBabelTransform(plugin: BabelPlugin | BabelPlugin[]): (code: string, filename: string) => BabelHostResult | null;
33
-
34
- declare function solidPlugin(): {
35
- visitor: {
36
- Program: {
37
- enter(): void;
38
- exit(path: any): void;
39
- };
40
- };
41
- };
28
+ declare function createBabelTransform(plugin: BabelPlugin | BabelPlugin[], options?: BabelTransformOptions): (code: string, filename: string) => BabelHostResult | null;
42
29
 
43
- export { type BabelHostResult, createBabelItem, createBabelTransform, defineComponentPlugin as default, defineComponentPlugin, solidPlugin, transformWithBabel };
30
+ export { type BabelHostResult, type BabelTransformOptions, createBabelItem, createBabelTransform, isExcludedTransform, transformWithBabel };
package/dist/index.js CHANGED
@@ -1,446 +1,26 @@
1
- // src/babel-plugin.ts
2
- import { types as t } from "@babel/core";
3
- import generate from "@babel/generator";
4
- function extractNamedSlots(el) {
5
- const nameNode = el.openingElement.name;
6
- if (t.isJSXIdentifier(nameNode)) {
7
- if (!/^[A-Z]/.test(nameNode.name)) return;
8
- } else if (!t.isJSXMemberExpression(nameNode)) {
9
- return;
10
- }
11
- const children = el.children;
12
- const slotProps = [];
13
- const remaining = [];
14
- for (const child of children) {
15
- if (t.isJSXElement(child) && t.isJSXIdentifier(child.openingElement.name, { name: "template" })) {
16
- const attrs = child.openingElement.attributes;
17
- const slotAttr = attrs.find(
18
- (a) => t.isJSXAttribute(a) && t.isJSXIdentifier(a.name, { name: "slot" })
19
- );
20
- if (slotAttr && t.isStringLiteral(slotAttr.value)) {
21
- const scopeParams = attrs.filter(
22
- (a) => t.isJSXAttribute(a) && !t.isJSXIdentifier(a.name, { name: "slot" }) && !a.value
23
- ).map((a) => t.identifier(a.name.name));
24
- const slotBody = t.jsxFragment(
25
- t.jsxOpeningFragment(),
26
- t.jsxClosingFragment(),
27
- child.children
28
- );
29
- slotProps.push(
30
- t.objectProperty(
31
- t.stringLiteral(slotAttr.value.value),
32
- t.arrowFunctionExpression(scopeParams, slotBody)
33
- )
34
- );
35
- continue;
36
- }
37
- }
38
- remaining.push(child);
39
- }
40
- if (!slotProps.length) return;
41
- el.children = remaining;
42
- el.openingElement.attributes.push(
43
- t.jsxAttribute(
44
- t.jsxIdentifier("slots"),
45
- t.jsxExpressionContainer(t.objectExpression(slotProps))
46
- )
47
- );
48
- }
49
- function walkJSX(node) {
50
- if (t.isJSXElement(node)) {
51
- extractNamedSlots(node);
52
- node.children.forEach(walkJSX);
53
- } else if (t.isJSXFragment(node)) {
54
- node.children.forEach(walkJSX);
55
- } else if (t.isJSXExpressionContainer(node)) {
56
- walkExpression(node.expression);
57
- }
58
- }
59
- function walkExpression(expr) {
60
- if (!expr) return;
61
- if (t.isJSXElement(expr) || t.isJSXFragment(expr)) {
62
- walkJSX(expr);
63
- } else if (t.isJSXExpressionContainer(expr)) {
64
- walkExpression(expr.expression);
65
- } else if (t.isLogicalExpression(expr) || t.isBinaryExpression(expr)) {
66
- walkExpression(expr.left);
67
- walkExpression(expr.right);
68
- } else if (t.isConditionalExpression(expr)) {
69
- walkExpression(expr.consequent);
70
- walkExpression(expr.alternate);
71
- } else if (t.isArrowFunctionExpression(expr) || t.isFunctionExpression(expr)) {
72
- walkExpression(expr.body);
73
- } else if (t.isCallExpression(expr)) {
74
- expr.arguments.forEach(walkExpression);
75
- } else if (t.isArrayExpression(expr)) {
76
- expr.elements.forEach(walkExpression);
77
- } else if (t.isObjectExpression(expr)) {
78
- expr.properties.forEach((p) => walkExpression(p.value));
79
- }
80
- }
81
- function defineComponentPlugin() {
82
- let hasTransformed = false;
83
- const jstate = { usedJsx: false, usedFragment: false };
84
- return {
85
- visitor: {
86
- Program: {
87
- enter() {
88
- hasTransformed = false;
89
- jstate.usedJsx = false;
90
- jstate.usedFragment = false;
91
- },
92
- exit(path) {
93
- if (jstate.usedJsx) {
94
- const jsxSpecs = [];
95
- if (jstate.usedJsx) {
96
- jsxSpecs.push(t.importSpecifier(t.identifier("_jsx"), t.identifier("jsx")));
97
- }
98
- if (jstate.usedFragment) {
99
- jsxSpecs.push(
100
- t.importSpecifier(t.identifier("_Fragment"), t.identifier("Fragment"))
101
- );
102
- }
103
- const hasJsxImport = path.node.body.some(
104
- (n) => t.isImportDeclaration(n) && n.source.value === "@actview/jsx/jsx-runtime"
105
- );
106
- if (!hasJsxImport) {
107
- path.node.body.unshift(
108
- t.importDeclaration(jsxSpecs, t.stringLiteral("@actview/jsx/jsx-runtime"))
109
- );
110
- }
111
- }
112
- if (!hasTransformed) return;
113
- const alreadyImported = path.node.body.some(
114
- (n) => t.isImportDeclaration(n) && n.specifiers.some(
115
- (s) => s.imported?.name === "defineComponent"
116
- )
117
- );
118
- if (!alreadyImported) {
119
- path.node.body.unshift(
120
- t.importDeclaration(
121
- [t.importSpecifier(t.identifier("defineComponent"), t.identifier("defineComponent"))],
122
- t.stringLiteral("@actview/core")
123
- )
124
- );
125
- }
126
- }
127
- },
128
- FunctionDeclaration(path) {
129
- const node = path.node;
130
- if (!node.id) return;
131
- const name = node.id.name;
132
- if (!/^[A-Z]/.test(name)) return;
133
- assertNoSetupStyleEarlyReturns(path, name);
134
- const fn = t.functionExpression(null, node.params, node.body, false, false);
135
- if (node.typeParameters) fn.typeParameters = node.typeParameters;
136
- const wrapped = wrapComponentFn(fn, name, path);
137
- if (!wrapped) return;
138
- const call = wrapped;
139
- hasTransformed = true;
140
- wrapEarlyReturns(path);
141
- path.replaceWith(
142
- t.variableDeclaration("const", [
143
- t.variableDeclarator(node.id, call)
144
- ])
145
- );
146
- },
147
- // 缺陷 2 修复:函数表达式 / 箭头函数组件
148
- // const X = function (props) {...}
149
- // const X = (props) => <JSX> / (props) => { ...; return function(){...} }
150
- VariableDeclarator(path) {
151
- const node = path.node;
152
- const id = node.id;
153
- if (!t.isIdentifier(id) || !/^[A-Z]/.test(id.name)) return;
154
- const init = node.init;
155
- const isFn = t.isFunctionExpression(init) || t.isArrowFunctionExpression(init);
156
- if (!isFn) return;
157
- const initPath = path.get("init");
158
- assertNoSetupStyleEarlyReturns(initPath, id.name);
159
- const wrapped = wrapComponentFn(init, id.name, initPath);
160
- if (!wrapped) return;
161
- const call = wrapped;
162
- hasTransformed = true;
163
- wrapEarlyReturns(initPath);
164
- node.init = call;
165
- },
166
- // 顺带支持:export default (props) => <JSX>(默认导出箭头/函数/匿名函数组件)
167
- // JSX 编译:组件转换(含 <solid> 提取)之后剩余 JSX → _jsx 调用
168
- JSXElement(path) {
169
- const name = path.node.openingElement.name;
170
- if (t.isJSXIdentifier(name) && name.name === "solid") {
171
- path.replaceWith(buildSolidMark(path.node, jstate));
172
- return;
173
- }
174
- path.replaceWith(compileJsxElement(path.node, jstate));
175
- },
176
- JSXFragment(path) {
177
- path.replaceWith(compileJsxFragment(path.node, jstate));
178
- },
179
- ExportDefaultDeclaration(path) {
180
- const decl = path.node.declaration;
181
- const isFn = t.isFunctionExpression(decl) || t.isArrowFunctionExpression(decl) || // export default function() {...}(匿名函数声明)
182
- t.isFunctionDeclaration(decl);
183
- if (!isFn) return;
184
- const fn = t.isFunctionDeclaration(decl) ? t.functionExpression(null, decl.params, decl.body, false, false) : decl;
185
- if (t.isFunctionDeclaration(decl) && decl.typeParameters) {
186
- ;
187
- fn.typeParameters = decl.typeParameters;
188
- }
189
- const declPath = path.get("declaration");
190
- assertNoSetupStyleEarlyReturns(declPath);
191
- const wrapped = wrapComponentFn(fn, void 0, path);
192
- if (!wrapped) return;
193
- const call = wrapped;
194
- hasTransformed = true;
195
- wrapEarlyReturns(declPath);
196
- path.node.declaration = call;
197
- }
198
- }
199
- };
200
- }
201
- function wrapComponentFn(fn, name, fnPath) {
202
- const body = fn.body;
203
- const isExprBody = !t.isBlockStatement(body);
204
- let last = null;
205
- let ret;
206
- if (isExprBody) {
207
- ret = body;
208
- } else {
209
- const stmts = body.body;
210
- if (stmts.length === 0) return null;
211
- last = stmts[stmts.length - 1];
212
- if (!t.isReturnStatement(last)) return null;
213
- ret = last.argument;
214
- if (ret == null) return null;
215
- }
216
- const isJsx = t.isJSXElement(ret) || t.isJSXFragment(ret);
217
- const isJsxCall = t.isCallExpression(ret) && t.isIdentifier(ret.callee) && /^_?jsx/.test(ret.callee.name);
218
- const isNullRet = t.isNullLiteral(ret);
219
- const isCondRet = isRenderExpr(ret);
220
- if (!isJsx && !isJsxCall && !isNullRet && !isCondRet) {
221
- const returnsFn = t.isArrowFunctionExpression(ret) || t.isFunctionExpression(ret);
222
- if (returnsFn && (name !== void 0 || containsRenderNode(ret))) {
223
- throw buildSetupStyleError(fnPath, name);
224
- }
225
- return null;
226
- }
227
- if (isJsx) walkJSX(ret);
228
- if (isExprBody) {
229
- fn.body = t.blockStatement([
230
- t.returnStatement(t.arrowFunctionExpression([], ret))
231
- ]);
232
- } else if (isJsx || isJsxCall || isCondRet) {
233
- last.argument = t.arrowFunctionExpression([], ret);
234
- } else if (isNullRet) {
235
- last.argument = t.arrowFunctionExpression([], t.nullLiteral());
236
- }
237
- const args = [fn];
238
- if (name) args.push(t.stringLiteral(name));
239
- return t.callExpression(t.identifier("defineComponent"), args);
240
- }
241
- function assertNoSetupStyleEarlyReturns(fnPath, name) {
242
- const body = fnPath.node.body;
243
- if (!t.isBlockStatement(body)) return;
244
- const stmts = body.body;
245
- const trailing = stmts.length ? stmts[stmts.length - 1] : null;
246
- fnPath.traverse({
247
- ReturnStatement(retPath) {
248
- if (retPath.getFunctionParent() !== fnPath) return;
249
- if (trailing && retPath.node === trailing) return;
250
- const arg = retPath.node.argument;
251
- if (t.isArrowFunctionExpression(arg) || t.isFunctionExpression(arg)) {
252
- throw buildSetupStyleError(retPath, name);
253
- }
254
- }
255
- });
256
- }
257
- function buildSetupStyleError(fnPath, name) {
258
- const label = name ? `\u7EC4\u4EF6 ${name}` : "\u7EC4\u4EF6\u51FD\u6570";
259
- const msg = `[actview] \u5DF2\u5E9F\u5F03\u7684 setup \u98CE\u683C\uFF1A${label}\u4F53\u5185 return \u4E86\u6E32\u67D3\u51FD\u6570\uFF08() => JSX / function () { JSX }\uFF09\u3002
260
- ActView \u53EA\u652F\u6301\u7B80\u5199\u7EC4\u4EF6\u2014\u2014\u76F4\u63A5 return JSX\uFF0C\u63D2\u4EF6\u4F1A\u81EA\u52A8\u5305\u88C5 render \u51FD\u6570\uFF1A
261
-
262
- \u2717 function App() { return () => <div /> }
263
- \u2713 function App() { return <div /> }
264
- `;
265
- try {
266
- return fnPath.buildCodeFrameError(msg);
267
- } catch {
268
- return new Error(msg);
269
- }
270
- }
271
- function containsRenderNode(node, seen = /* @__PURE__ */ new Set()) {
272
- if (!node || typeof node !== "object" || seen.has(node)) return false;
273
- seen.add(node);
274
- if (t.isJSXElement(node) || t.isJSXFragment(node)) return true;
275
- if (t.isCallExpression(node) && t.isIdentifier(node.callee) && /^_?jsx/.test(node.callee.name)) {
1
+ // src/babel-host.ts
2
+ import * as babel from "@babel/core";
3
+ var NODE_MODULES_RE = /(^|[\\/])node_modules[\\/]/;
4
+ function matchesRule(rule, filename) {
5
+ return typeof rule === "string" ? filename.includes(rule) : rule.test(filename);
6
+ }
7
+ function isExcludedTransform(filename, options) {
8
+ const norm = filename.replace(/\\/g, "/");
9
+ if (NODE_MODULES_RE.test(norm)) return true;
10
+ if (options?.include?.length && !options.include.some((r) => matchesRule(r, norm))) {
276
11
  return true;
277
12
  }
278
- for (const k in node) {
279
- if (k === "leadingComments" || k === "trailingComments") continue;
280
- const v = node[k];
281
- if (Array.isArray(v)) {
282
- if (v.some((c) => containsRenderNode(c, seen))) return true;
283
- } else if (v !== null && typeof v === "object") {
284
- if (containsRenderNode(v, seen)) return true;
285
- }
286
- }
13
+ if (options?.exclude?.some((r) => matchesRule(r, norm))) return true;
287
14
  return false;
288
15
  }
289
- function wrapEarlyReturns(fnPath) {
290
- fnPath.traverse({
291
- ReturnStatement(innerPath) {
292
- if (innerPath.getFunctionParent() !== fnPath) return;
293
- const arg = innerPath.node.argument;
294
- if (arg == null) return;
295
- const isStmtJsx = t.isJSXElement(arg) || t.isJSXFragment(arg);
296
- const isStmtJsxCall = t.isCallExpression(arg) && t.isIdentifier(arg.callee) && /^_?jsx/.test(arg.callee.name);
297
- const isStmtNull = t.isNullLiteral(arg);
298
- const isStmtCond = isRenderExpr(arg);
299
- if (isStmtJsx || isStmtJsxCall || isStmtNull || isStmtCond) {
300
- innerPath.node.argument = t.arrowFunctionExpression([], arg);
301
- }
302
- }
303
- });
304
- }
305
- function isRenderExpr(expr) {
306
- if (!expr) return false;
307
- if (t.isJSXElement(expr) || t.isJSXFragment(expr)) return true;
308
- if (t.isCallExpression(expr) && t.isIdentifier(expr.callee) && /^_?jsx/.test(expr.callee.name)) {
309
- return true;
310
- }
311
- if (t.isConditionalExpression(expr)) {
312
- return isRenderExpr(expr.consequent) || isRenderExpr(expr.alternate);
313
- }
314
- if (t.isLogicalExpression(expr)) {
315
- return isRenderExpr(expr.right) || isRenderExpr(expr.left);
316
- }
317
- return false;
318
- }
319
- function compileJsxElement(el, state) {
320
- const name = el.openingElement.name;
321
- let typeExpr;
322
- if (t.isJSXIdentifier(name)) {
323
- typeExpr = /^[a-z]/.test(name.name) ? t.stringLiteral(name.name) : t.identifier(name.name);
324
- } else if (t.isJSXMemberExpression(name)) {
325
- typeExpr = jsxMemberToMember(name);
326
- } else {
327
- typeExpr = t.stringLiteral(name.name);
328
- }
329
- let keyExpr = null;
330
- const propEntries = [];
331
- for (const attr of el.openingElement.attributes) {
332
- if (t.isJSXSpreadAttribute(attr)) {
333
- propEntries.push(t.spreadElement(attr.argument));
334
- continue;
335
- }
336
- const aname = attr.name.name;
337
- let value;
338
- if (attr.value == null) {
339
- value = t.booleanLiteral(true);
340
- } else if (t.isStringLiteral(attr.value)) {
341
- value = attr.value;
342
- } else {
343
- value = attr.value.expression;
344
- }
345
- if (aname === "key") {
346
- keyExpr = t.isStringLiteral(value) ? value : value;
347
- continue;
348
- }
349
- propEntries.push(t.objectProperty(t.stringLiteral(aname), value));
350
- }
351
- const children = compileJsxChildren(el.children, state);
352
- if (children.length === 1) {
353
- propEntries.push(t.objectProperty(t.stringLiteral("children"), children[0]));
354
- } else if (children.length > 1) {
355
- propEntries.push(
356
- t.objectProperty(t.stringLiteral("children"), t.arrayExpression(children))
357
- );
358
- }
359
- state.usedJsx = true;
360
- const args = [typeExpr, t.objectExpression(propEntries)];
361
- args.push(keyExpr ?? t.identifier("undefined"));
362
- return t.callExpression(t.identifier("_jsx"), args);
363
- }
364
- function compileJsxFragment(frag, state) {
365
- const children = compileJsxChildren(frag.children, state);
366
- state.usedJsx = true;
367
- state.usedFragment = true;
368
- const propEntries = [];
369
- if (children.length === 1) {
370
- propEntries.push(t.objectProperty(t.stringLiteral("children"), children[0]));
371
- } else if (children.length > 1) {
372
- propEntries.push(
373
- t.objectProperty(t.stringLiteral("children"), t.arrayExpression(children))
374
- );
375
- }
376
- return t.callExpression(t.identifier("_jsx"), [
377
- t.identifier("_Fragment"),
378
- t.objectExpression(propEntries),
379
- t.identifier("undefined")
380
- ]);
381
- }
382
- function compileJsxChildren(children, state) {
383
- const out = [];
384
- for (const c of children) {
385
- if (t.isJSXText(c)) {
386
- if (c.value.trim() === "") continue;
387
- out.push(t.stringLiteral(processJsxText(c.value)));
388
- } else if (t.isJSXExpressionContainer(c)) {
389
- if (t.isJSXEmptyExpression(c.expression)) continue;
390
- out.push(c.expression);
391
- } else if (t.isJSXElement(c)) {
392
- const cname = c.openingElement.name;
393
- if (t.isJSXIdentifier(cname) && cname.name === "solid") {
394
- out.push(buildSolidMark(c, state));
395
- } else {
396
- out.push(compileJsxElement(c, state));
397
- }
398
- } else if (t.isJSXFragment(c)) {
399
- out.push(compileJsxFragment(c, state));
400
- }
401
- }
402
- return out;
403
- }
404
- function jsxMemberToMember(name) {
405
- const obj = t.isJSXMemberExpression(name.object) ? jsxMemberToMember(name.object) : t.identifier(name.object.name);
406
- return t.memberExpression(obj, t.identifier(name.property.name));
407
- }
408
- function processJsxText(value) {
409
- if (!value.includes("\n")) return value;
410
- return value.replace(/^[ \t]*\n[ \t]*/, "").replace(/[ \t]*\n[ \t]*$/, "").replace(/[ \t]*\n[ \t]*/g, " ");
411
- }
412
- function buildSolidMark(el, state) {
413
- state.usedJsx = true;
414
- const parts = [];
415
- for (const c of el.children) {
416
- if (t.isJSXText(c)) {
417
- if (c.value.trim() === "") continue;
418
- parts.push(c.value);
419
- } else if (t.isJSXExpressionContainer(c)) {
420
- if (t.isJSXEmptyExpression(c.expression)) continue;
421
- parts.push(generate(c.expression).code);
422
- } else {
423
- parts.push(generate(c).code);
424
- }
425
- }
426
- return t.callExpression(t.identifier("_jsx"), [
427
- t.stringLiteral("solid"),
428
- t.objectExpression([
429
- t.objectProperty(
430
- t.stringLiteral("children"),
431
- t.arrayExpression(parts.map((p) => t.stringLiteral(p)))
432
- )
433
- ]),
434
- t.identifier("undefined")
435
- ]);
436
- }
437
-
438
- // src/babel-host.ts
439
- import * as babel from "@babel/core";
440
- function createBabelItem(plugin) {
441
- return babel.createConfigItemSync(plugin, { type: "plugin" });
16
+ function createBabelItem(plugin, pluginOptions) {
17
+ return babel.createConfigItemSync(
18
+ pluginOptions == null ? plugin : [plugin, pluginOptions],
19
+ { type: "plugin" }
20
+ );
442
21
  }
443
- function transformWithBabel(code, filename, pluginItem) {
22
+ function transformWithBabel(code, filename, pluginItem, options) {
23
+ if (isExcludedTransform(filename, options)) return null;
444
24
  const result = babel.transformSync(code, {
445
25
  filename,
446
26
  plugins: [pluginItem],
@@ -453,9 +33,10 @@ function transformWithBabel(code, filename, pluginItem) {
453
33
  if (!result) return null;
454
34
  return { code: result.code || code, map: result.map };
455
35
  }
456
- function createBabelTransform(plugin) {
36
+ function createBabelTransform(plugin, options) {
457
37
  const items = (Array.isArray(plugin) ? plugin : [plugin]).map((p) => createBabelItem(p));
458
38
  return (code, filename) => {
39
+ if (isExcludedTransform(filename, options)) return null;
459
40
  const result = babel.transformSync(code, {
460
41
  filename,
461
42
  plugins: items,
@@ -469,307 +50,9 @@ function createBabelTransform(plugin) {
469
50
  return { code: result.code || code, map: result.map };
470
51
  };
471
52
  }
472
-
473
- // src/solid-plugin.ts
474
- import { types as t2, parseSync } from "@babel/core";
475
- var solidVarCounter = 0;
476
- function solidPlugin() {
477
- let usedSolid = false;
478
- return {
479
- visitor: {
480
- Program: {
481
- enter() {
482
- usedSolid = false;
483
- solidVarCounter = 0;
484
- },
485
- exit(path) {
486
- path.traverse({
487
- CallExpression(p) {
488
- handleSolidCall(p, () => {
489
- usedSolid = true;
490
- });
491
- }
492
- });
493
- if (!usedSolid) return;
494
- const hasImport = path.node.body.some(
495
- (n) => t2.isImportDeclaration(n) && n.source.value === "@actview/core" && n.specifiers.some((s) => s.imported?.name === "solidGet")
496
- );
497
- if (!hasImport) {
498
- path.node.body.unshift(
499
- t2.importDeclaration(
500
- ["solidGet", "createEffect", "mapArray"].map(
501
- (n) => t2.importSpecifier(t2.identifier(n), t2.identifier(n))
502
- ),
503
- t2.stringLiteral("@actview/core")
504
- )
505
- );
506
- }
507
- }
508
- }
509
- }
510
- };
511
- }
512
- function handleSolidCall(path, markUsed) {
513
- const node = path.node;
514
- if (!(t2.isIdentifier(node.callee) && /^_?jsx/.test(node.callee.name))) return;
515
- if (!(node.arguments[0] && t2.isStringLiteral(node.arguments[0], { value: "solid" }))) return;
516
- const propsObj = node.arguments[1];
517
- if (!t2.isObjectExpression(propsObj)) return;
518
- const childrenProp = propsObj.properties.find(
519
- (p) => t2.isObjectProperty(p) && t2.isStringLiteral(p.key, { value: "children" })
520
- );
521
- if (!childrenProp || !t2.isArrayExpression(childrenProp.value)) return;
522
- const srcs = [];
523
- for (const e of childrenProp.value.elements) {
524
- if (e && t2.isStringLiteral(e)) srcs.push(e.value);
525
- }
526
- if (srcs.length === 0 || srcs.every((s) => !s.trim())) return;
527
- const counter = { n: 0 };
528
- const stmts = [];
529
- try {
530
- for (const src of srcs) {
531
- const ast = parseSync(src, { parserOpts: { plugins: ["jsx", "typescript"] } });
532
- if (!ast) return;
533
- for (const st of ast.program.body) {
534
- if (t2.isExpressionStatement(st)) {
535
- compileSolidTop(st.expression, t2.identifier("container"), stmts, counter);
536
- } else {
537
- stmts.push(st);
538
- }
539
- }
540
- }
541
- } catch (e) {
542
- console.error("[solid-plugin] compile error:", e?.message);
543
- return;
544
- }
545
- if (stmts.length === 0) return;
546
- markUsed();
547
- const holderId = t2.identifier("_solid$$" + ++solidVarCounter);
548
- const renderFn = path.getFunctionParent();
549
- const setupFn = renderFn && renderFn.getFunctionParent();
550
- const holderDecl = t2.variableDeclaration("const", [
551
- t2.variableDeclarator(holderId, t2.objectExpression([]))
552
- ]);
553
- if (setupFn && t2.isFunction(setupFn.node)) {
554
- setupFn.get("body").unshiftContainer("body", holderDecl);
555
- } else {
556
- const prog = path.findParent((p) => p.isProgram());
557
- prog?.unshiftContainer("body", holderDecl);
558
- }
559
- path.replaceWith(
560
- t2.callExpression(t2.identifier("solidGet"), [
561
- holderId,
562
- t2.arrowFunctionExpression(
563
- [t2.identifier("container")],
564
- t2.blockStatement(stmts)
565
- )
566
- ])
567
- );
568
- }
569
- function compileSolidTop(expr, containerVar, stmts, counter) {
570
- if (t2.isJSXElement(expr)) {
571
- return compileSolidElement(expr, stmts, counter);
572
- }
573
- if (t2.isJSXFragment(expr)) {
574
- compileSolidChildren(expr.children, containerVar, stmts, counter);
575
- return null;
576
- }
577
- const mapCall = matchMapCall(expr);
578
- if (mapCall) {
579
- stmts.push(
580
- t2.expressionStatement(
581
- t2.callExpression(t2.identifier("mapArray"), [
582
- t2.arrowFunctionExpression([], mapCall.listExpr),
583
- containerVar,
584
- compileMapArrow(mapCall.arrow, counter)
585
- ])
586
- )
587
- );
588
- return null;
589
- }
590
- const textVar = t2.identifier("_t" + ++counter.n);
591
- stmts.push(
592
- t2.variableDeclaration("const", [
593
- t2.variableDeclarator(
594
- textVar,
595
- t2.callExpression(t2.memberExpression(t2.identifier("document"), t2.identifier("createTextNode")), [
596
- t2.stringLiteral("")
597
- ])
598
- )
599
- ])
600
- );
601
- stmts.push(
602
- t2.expressionStatement(
603
- t2.callExpression(t2.identifier("createEffect"), [
604
- t2.arrowFunctionExpression(
605
- [],
606
- t2.assignmentExpression(
607
- "=",
608
- t2.memberExpression(textVar, t2.identifier("textContent")),
609
- expr
610
- )
611
- )
612
- ])
613
- )
614
- );
615
- stmts.push(appendStmt(containerVar, textVar));
616
- return null;
617
- }
618
- function compileSolidChildren(children, containerVar, stmts, counter) {
619
- for (const c of children) {
620
- if (t2.isJSXText(c)) {
621
- if (c.value.trim() === "") continue;
622
- const textVar = t2.identifier("_t" + ++counter.n);
623
- stmts.push(
624
- t2.variableDeclaration("const", [
625
- t2.variableDeclarator(
626
- textVar,
627
- t2.callExpression(t2.memberExpression(t2.identifier("document"), t2.identifier("createTextNode")), [
628
- t2.stringLiteral(c.value)
629
- ])
630
- )
631
- ])
632
- );
633
- stmts.push(appendStmt(containerVar, textVar));
634
- } else if (t2.isJSXExpressionContainer(c)) {
635
- const expr = c.expression;
636
- if (t2.isJSXEmptyExpression(expr)) continue;
637
- const mapCall = matchMapCall(expr);
638
- if (mapCall) {
639
- stmts.push(
640
- t2.expressionStatement(
641
- t2.callExpression(t2.identifier("mapArray"), [
642
- t2.arrowFunctionExpression([], mapCall.listExpr),
643
- containerVar,
644
- compileMapArrow(mapCall.arrow, counter)
645
- ])
646
- )
647
- );
648
- } else {
649
- const textVar = t2.identifier("_t" + ++counter.n);
650
- stmts.push(
651
- t2.variableDeclaration("const", [
652
- t2.variableDeclarator(
653
- textVar,
654
- t2.callExpression(t2.memberExpression(t2.identifier("document"), t2.identifier("createTextNode")), [
655
- t2.stringLiteral("")
656
- ])
657
- )
658
- ])
659
- );
660
- stmts.push(
661
- t2.expressionStatement(
662
- t2.callExpression(t2.identifier("createEffect"), [
663
- t2.arrowFunctionExpression(
664
- [],
665
- t2.assignmentExpression(
666
- "=",
667
- t2.memberExpression(textVar, t2.identifier("textContent")),
668
- expr
669
- )
670
- )
671
- ])
672
- )
673
- );
674
- stmts.push(appendStmt(containerVar, textVar));
675
- }
676
- } else if (t2.isJSXElement(c)) {
677
- const elVar = compileSolidElement(c, stmts, counter);
678
- stmts.push(appendStmt(containerVar, elVar));
679
- } else if (t2.isJSXFragment(c)) {
680
- compileSolidChildren(c.children, containerVar, stmts, counter);
681
- }
682
- }
683
- }
684
- function compileSolidElement(el, stmts, counter) {
685
- const name = el.openingElement.name;
686
- const tag = t2.isJSXIdentifier(name) ? name.name : name.name;
687
- const elVar = t2.identifier("_el" + ++counter.n);
688
- stmts.push(
689
- t2.variableDeclaration("const", [
690
- t2.variableDeclarator(
691
- elVar,
692
- t2.callExpression(t2.memberExpression(t2.identifier("document"), t2.identifier("createElement")), [
693
- t2.stringLiteral(tag)
694
- ])
695
- )
696
- ])
697
- );
698
- for (const attr of el.openingElement.attributes) {
699
- if (!t2.isJSXAttribute(attr)) continue;
700
- const attrName = attr.name.name;
701
- if (attrName === "key" || attrName === "ref" || attrName === "v-memo") continue;
702
- if (/^on[A-Z]/.test(attrName)) {
703
- const evt = attrName[2].toLowerCase() + attrName.slice(3);
704
- const handler = attr.value?.expression ?? attr.value ?? t2.identifier("undefined");
705
- stmts.push(
706
- t2.expressionStatement(
707
- t2.callExpression(t2.memberExpression(elVar, t2.identifier("addEventListener")), [
708
- t2.stringLiteral(evt),
709
- handler
710
- ])
711
- )
712
- );
713
- continue;
714
- }
715
- if (attr.value && t2.isJSXExpressionContainer(attr.value)) {
716
- stmts.push(
717
- t2.expressionStatement(
718
- t2.callExpression(t2.identifier("createEffect"), [
719
- t2.arrowFunctionExpression(
720
- [],
721
- t2.callExpression(t2.memberExpression(elVar, t2.identifier("setAttribute")), [
722
- t2.stringLiteral(attrName),
723
- attr.value.expression
724
- ])
725
- )
726
- ])
727
- )
728
- );
729
- } else {
730
- const value = attr.value ? t2.isStringLiteral(attr.value) ? attr.value.value : true : true;
731
- stmts.push(
732
- t2.expressionStatement(
733
- t2.callExpression(t2.memberExpression(elVar, t2.identifier("setAttribute")), [
734
- t2.stringLiteral(attrName),
735
- value === true ? t2.stringLiteral("true") : t2.stringLiteral(value)
736
- ])
737
- )
738
- );
739
- }
740
- }
741
- compileSolidChildren(el.children, elVar, stmts, counter);
742
- return elVar;
743
- }
744
- function appendStmt(containerVar, childVar) {
745
- return t2.expressionStatement(
746
- t2.callExpression(t2.memberExpression(containerVar, t2.identifier("appendChild")), [childVar])
747
- );
748
- }
749
- function matchMapCall(expr) {
750
- if (t2.isCallExpression(expr) && t2.isMemberExpression(expr.callee) && t2.isIdentifier(expr.callee.property, { name: "map" }) && expr.arguments.length === 1 && t2.isArrowFunctionExpression(expr.arguments[0])) {
751
- return { listExpr: expr.callee.object, arrow: expr.arguments[0] };
752
- }
753
- return null;
754
- }
755
- function compileMapArrow(arrow, counter) {
756
- const stmts = [];
757
- const bodyExpr = arrow.body;
758
- if (t2.isJSXElement(bodyExpr)) {
759
- const elVar = compileSolidElement(bodyExpr, stmts, counter);
760
- stmts.push(t2.returnStatement(elVar));
761
- return t2.arrowFunctionExpression(arrow.params, t2.blockStatement(stmts));
762
- }
763
- if (t2.isJSXFragment(bodyExpr)) {
764
- return arrow;
765
- }
766
- return arrow;
767
- }
768
53
  export {
769
54
  createBabelItem,
770
55
  createBabelTransform,
771
- defineComponentPlugin as default,
772
- defineComponentPlugin,
773
- solidPlugin,
56
+ isExcludedTransform,
774
57
  transformWithBabel
775
58
  };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@actview/plugin-babel",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
- "description": "Babel 插件:JSX 组件自动 defineComponent 转换(ActView 编译核心,独立于 Vite 宿主)",
5
+ "description": "共享 Babel 宿主壳(排除规则/transformSync 参数统一,供 @actview/plugin-vite @actview/plugin-scoped 复用)",
6
6
  "exports": {
7
7
  ".": {
8
8
  "types": "./dist/index.d.ts",