@meituan-nocode/vite-plugin-nocode-compiler 0.1.0-beta.5 → 0.1.0-beta.7
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 +11 -15
- package/dist/index.d.ts +13 -2
- package/dist/index.js +24 -33
- package/dist/{modules → nocode-compiler-core/src/compilers}/base-compiler.d.ts +2 -2
- package/dist/{modules → nocode-compiler-core/src/compilers}/base-compiler.js +3 -3
- package/dist/{modules → nocode-compiler-core/src/compilers}/jsx-compiler.d.ts +11 -3
- package/dist/nocode-compiler-core/src/compilers/jsx-compiler.js +211 -0
- package/dist/nocode-compiler-core/src/compilers/vue-compiler.d.ts +26 -0
- package/dist/nocode-compiler-core/src/compilers/vue-compiler.js +129 -0
- package/dist/nocode-compiler-core/src/types.d.ts +9 -0
- package/dist/{utils → nocode-compiler-core/src/utils}/utils.d.ts +7 -1
- package/dist/{utils → nocode-compiler-core/src/utils}/utils.js +4 -6
- package/dist/vite-plugin-nocode-compiler/src/index.d.ts +42 -0
- package/dist/vite-plugin-nocode-compiler/src/index.js +69 -0
- package/dist/{types.d.ts → vite-plugin-nocode-compiler/src/types.d.ts} +0 -9
- package/dist/vite-plugin-nocode-compiler/src/types.js +2 -0
- package/package.json +3 -6
- package/dist/modules/jsx-compiler.js +0 -124
- /package/dist/{types.js → nocode-compiler-core/src/types.js} +0 -0
- /package/dist/{utils → nocode-compiler-core/src/utils}/constants.d.ts +0 -0
- /package/dist/{utils → nocode-compiler-core/src/utils}/constants.js +0 -0
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## 功能
|
|
6
6
|
|
|
7
|
-
- 🚀 **多框架支持**:
|
|
7
|
+
- 🚀 **多框架支持**: 支持 JSX、TSX 和 Vue 文件
|
|
8
8
|
- 📦 **自动元素标记**: 为非 Three.js Fiber 和非 Drei 的元素自动添加 nocode 标识
|
|
9
9
|
- 🔄 **数组上下文检测**: 智能识别数组映射,添加索引信息
|
|
10
|
-
-
|
|
11
|
-
-
|
|
10
|
+
- 🎯 **简单配置**: 开箱即用,仅需最少配置
|
|
11
|
+
- ⚡️ **高性能**: 优化的编译过程,快速处理
|
|
12
12
|
|
|
13
13
|
## 安装
|
|
14
14
|
|
|
@@ -28,13 +28,15 @@ import { componentCompiler } from '@meituan-nocode/vite-plugin-nocode-compiler';
|
|
|
28
28
|
|
|
29
29
|
export default defineConfig({
|
|
30
30
|
plugins: [
|
|
31
|
-
componentCompiler(),
|
|
31
|
+
componentCompiler(), // 使用默认配置
|
|
32
32
|
// 其他插件...
|
|
33
33
|
],
|
|
34
34
|
});
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
###
|
|
37
|
+
### 启用调试日志
|
|
38
|
+
|
|
39
|
+
如果需要查看编译过程的详细日志:
|
|
38
40
|
|
|
39
41
|
```javascript
|
|
40
42
|
import { defineConfig } from 'vite';
|
|
@@ -43,27 +45,21 @@ import { componentCompiler } from '@meituan-nocode/vite-plugin-nocode-compiler';
|
|
|
43
45
|
export default defineConfig({
|
|
44
46
|
plugins: [
|
|
45
47
|
componentCompiler({
|
|
46
|
-
validExtensions: new Set(['.jsx', '.tsx']), // 支持的文件扩展名
|
|
47
48
|
enableLogging: true, // 启用调试日志
|
|
48
|
-
threeFiberElems: [
|
|
49
|
-
/* 自定义列表 */
|
|
50
|
-
], // 自定义 Three.js 元素
|
|
51
|
-
dreiElems: [
|
|
52
|
-
/* 自定义列表 */
|
|
53
|
-
], // 自定义 Drei 元素
|
|
54
49
|
}),
|
|
50
|
+
// 其他插件...
|
|
55
51
|
],
|
|
56
52
|
});
|
|
57
53
|
```
|
|
58
54
|
|
|
59
55
|
## 工作原理
|
|
60
56
|
|
|
61
|
-
插件会扫描 `.jsx`
|
|
57
|
+
插件会扫描 `.jsx`、`.tsx` 和 `.vue` 文件中的元素,对于不在 Three.js Fiber 元素列表和 Drei 元素列表中的元素,自动添加以下属性:
|
|
62
58
|
|
|
63
59
|
- `data-nocode-id`: 格式为 `文件路径:行号:列号`
|
|
64
60
|
- `data-nocode-name`: 元素名称
|
|
65
61
|
- `data-nocode-array`: 当元素在数组映射中时,包含数组名称
|
|
66
|
-
- `data-nocode-
|
|
62
|
+
- `data-nocode-content`: 元素content内容
|
|
67
63
|
|
|
68
64
|
## 示例
|
|
69
65
|
|
|
@@ -106,7 +102,7 @@ function App() {
|
|
|
106
102
|
该插件采用模块化设计,支持多种前端框架:
|
|
107
103
|
|
|
108
104
|
- **JSX/TSX**: ✅ 已实现
|
|
109
|
-
- **Vue**:
|
|
105
|
+
- **Vue**: ✅ 已实现
|
|
110
106
|
|
|
111
107
|
详细架构说明请参考 [ARCHITECTURE.md](./ARCHITECTURE.md)
|
|
112
108
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
-
import type { CompilerOptions } from './types';
|
|
3
2
|
/**
|
|
4
3
|
* 创建 nocode 编译器插件
|
|
5
4
|
* @param options 编译器选项
|
|
6
5
|
* @returns Vite 插件
|
|
7
6
|
*/
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* 创建 nocode 编译器插件,默认处理所有 jsx、tsx 和 vue 文件
|
|
9
|
+
* @param options 编译器选项
|
|
10
|
+
* @returns Vite 插件
|
|
11
|
+
*/
|
|
12
|
+
export interface CompilerPluginOptions {
|
|
13
|
+
/**
|
|
14
|
+
* 是否启用日志
|
|
15
|
+
* @default false
|
|
16
|
+
*/
|
|
17
|
+
enableLogging?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare function componentCompiler(options?: CompilerPluginOptions): Plugin;
|
package/dist/index.js
CHANGED
|
@@ -1,50 +1,42 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.componentCompiler = componentCompiler;
|
|
4
|
-
const
|
|
5
|
-
const base_compiler_1 = require("./modules/base-compiler");
|
|
6
|
-
const jsx_compiler_1 = require("./modules/jsx-compiler");
|
|
7
|
-
const utils_1 = require("./utils/utils");
|
|
8
|
-
/**
|
|
9
|
-
* 创建 nocode 编译器插件
|
|
10
|
-
* @param options 编译器选项
|
|
11
|
-
* @returns Vite 插件
|
|
12
|
-
*/
|
|
4
|
+
const nocode_compiler_core_1 = require("@mcopilot-nocode/nocode-compiler-core");
|
|
13
5
|
function componentCompiler(options = {}) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
const { enableLogging = false } = options;
|
|
7
|
+
// 构建基础配置
|
|
8
|
+
const baseConfig = {
|
|
9
|
+
validExtensions: new Set(['.jsx', '.tsx', '.vue']),
|
|
10
|
+
threeFiberElems: nocode_compiler_core_1.DEFAULT_CONFIG.threeFiberElems,
|
|
11
|
+
dreiElems: nocode_compiler_core_1.DEFAULT_CONFIG.dreiElems,
|
|
12
|
+
enableLogging,
|
|
21
13
|
};
|
|
22
14
|
// 创建编译器注册表
|
|
23
|
-
const registry = new
|
|
24
|
-
// 注册 JSX 编译器
|
|
15
|
+
const registry = new nocode_compiler_core_1.CompilerRegistry();
|
|
16
|
+
// 注册 JSX/TSX 编译器
|
|
25
17
|
registry.register({
|
|
26
|
-
|
|
27
|
-
supportedExtensions:
|
|
28
|
-
CompilerClass:
|
|
18
|
+
extensionName: 'jsx',
|
|
19
|
+
supportedExtensions: ['.jsx', '.tsx'],
|
|
20
|
+
CompilerClass: nocode_compiler_core_1.JSXCompiler,
|
|
21
|
+
});
|
|
22
|
+
// 注册 Vue 编译器
|
|
23
|
+
registry.register({
|
|
24
|
+
extensionName: 'vue',
|
|
25
|
+
supportedExtensions: ['.vue'],
|
|
26
|
+
CompilerClass: nocode_compiler_core_1.VueCompiler,
|
|
29
27
|
});
|
|
30
|
-
// 未来可以在这里注册 Vue 编译器
|
|
31
|
-
// registry.register({
|
|
32
|
-
// name: 'vue',
|
|
33
|
-
// supportedExtensions: VueCompiler.supportedExtensions,
|
|
34
|
-
// CompilerClass: VueCompiler,
|
|
35
|
-
// });
|
|
36
28
|
// 创建统计管理器
|
|
37
|
-
const statsManager = new
|
|
29
|
+
const statsManager = new nocode_compiler_core_1.StatsManager();
|
|
38
30
|
return {
|
|
39
31
|
name: 'vite-plugin-nocode-compiler',
|
|
40
32
|
enforce: 'pre',
|
|
41
33
|
async transform(code, id) {
|
|
42
34
|
// 获取适合的编译器
|
|
43
35
|
const compiler = registry.getCompilerForFile(id, {
|
|
44
|
-
validExtensions:
|
|
45
|
-
threeFiberElems:
|
|
46
|
-
dreiElems:
|
|
47
|
-
enableLogging:
|
|
36
|
+
validExtensions: baseConfig.validExtensions,
|
|
37
|
+
threeFiberElems: baseConfig.threeFiberElems,
|
|
38
|
+
dreiElems: baseConfig.dreiElems,
|
|
39
|
+
enableLogging: baseConfig.enableLogging,
|
|
48
40
|
});
|
|
49
41
|
if (!compiler) {
|
|
50
42
|
return null;
|
|
@@ -55,7 +47,6 @@ function componentCompiler(options = {}) {
|
|
|
55
47
|
const result = await compiler.compile(code, id);
|
|
56
48
|
if (result) {
|
|
57
49
|
statsManager.incrementProcessedFiles();
|
|
58
|
-
// 这里可以添加元素计数,如果编译器返回相关信息的话
|
|
59
50
|
}
|
|
60
51
|
return result;
|
|
61
52
|
},
|
|
@@ -22,7 +22,7 @@ export type CompilerConstructor = new (options: any) => BaseCompiler;
|
|
|
22
22
|
* 编译器信息接口
|
|
23
23
|
*/
|
|
24
24
|
export interface CompilerInfo {
|
|
25
|
-
|
|
25
|
+
extensionName: string;
|
|
26
26
|
supportedExtensions: string[];
|
|
27
27
|
CompilerClass: CompilerConstructor;
|
|
28
28
|
}
|
|
@@ -39,7 +39,7 @@ export declare class CompilerRegistry {
|
|
|
39
39
|
/**
|
|
40
40
|
* 获取编译器信息
|
|
41
41
|
*/
|
|
42
|
-
getCompilerInfo(
|
|
42
|
+
getCompilerInfo(extensionName: string): CompilerInfo | undefined;
|
|
43
43
|
/**
|
|
44
44
|
* 获取所有支持的文件扩展名
|
|
45
45
|
*/
|
|
@@ -11,13 +11,13 @@ class CompilerRegistry {
|
|
|
11
11
|
* 注册编译器
|
|
12
12
|
*/
|
|
13
13
|
register(info) {
|
|
14
|
-
this.compilers.set(info.
|
|
14
|
+
this.compilers.set(info.extensionName, info);
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* 获取编译器信息
|
|
18
18
|
*/
|
|
19
|
-
getCompilerInfo(
|
|
20
|
-
return this.compilers.get(
|
|
19
|
+
getCompilerInfo(extensionName) {
|
|
20
|
+
return this.compilers.get(extensionName);
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
23
|
* 获取所有支持的文件扩展名
|
|
@@ -6,12 +6,20 @@ export interface JSXCompilerOptions {
|
|
|
6
6
|
enableLogging?: boolean;
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
|
-
* JSX 编译器类
|
|
10
|
-
* 负责处理 JSX 文件的编译逻辑
|
|
9
|
+
* JSX/TSX 编译器类
|
|
10
|
+
* 负责处理 JSX/TSX 文件的编译逻辑
|
|
11
|
+
*
|
|
12
|
+
* 支持的文件类型:
|
|
13
|
+
* - .jsx: React JSX 文件
|
|
14
|
+
* - .tsx: React TypeScript JSX 文件
|
|
15
|
+
*
|
|
16
|
+
* 注意:
|
|
17
|
+
* - TypeScript 类型信息在编译时会被擦除
|
|
18
|
+
* - 编译器关注的是 JSX 结构而不是类型信息
|
|
11
19
|
*/
|
|
12
20
|
export declare class JSXCompiler implements BaseCompiler {
|
|
13
21
|
static readonly supportedExtensions: string[];
|
|
14
|
-
static readonly
|
|
22
|
+
static readonly extensionName = "jsx";
|
|
15
23
|
private options;
|
|
16
24
|
private logger;
|
|
17
25
|
private cwd;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.JSXCompiler = void 0;
|
|
7
|
+
const parser_1 = require("@babel/parser");
|
|
8
|
+
const traverse_1 = __importDefault(require("@babel/traverse"));
|
|
9
|
+
const magic_string_1 = __importDefault(require("magic-string"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const utils_1 = require("../utils/utils");
|
|
12
|
+
/**
|
|
13
|
+
* JSX/TSX 编译器类
|
|
14
|
+
* 负责处理 JSX/TSX 文件的编译逻辑
|
|
15
|
+
*
|
|
16
|
+
* 支持的文件类型:
|
|
17
|
+
* - .jsx: React JSX 文件
|
|
18
|
+
* - .tsx: React TypeScript JSX 文件
|
|
19
|
+
*
|
|
20
|
+
* 注意:
|
|
21
|
+
* - TypeScript 类型信息在编译时会被擦除
|
|
22
|
+
* - 编译器关注的是 JSX 结构而不是类型信息
|
|
23
|
+
*/
|
|
24
|
+
class JSXCompiler {
|
|
25
|
+
static supportedExtensions = ['.jsx', '.tsx'];
|
|
26
|
+
static extensionName = 'jsx';
|
|
27
|
+
options;
|
|
28
|
+
logger;
|
|
29
|
+
cwd;
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.logger = new utils_1.Logger(options.enableLogging);
|
|
33
|
+
this.cwd = process.cwd();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 编译 JSX 代码
|
|
37
|
+
*/
|
|
38
|
+
async compile(code, id) {
|
|
39
|
+
this.logger.log('\nProcessing file:', id);
|
|
40
|
+
if (!(0, utils_1.shouldProcessFile)(id, this.options.validExtensions)) {
|
|
41
|
+
this.logger.log('Skipping file (not .jsx or in node_modules):', id);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const relativePath = path_1.default.relative(this.cwd, id);
|
|
45
|
+
this.logger.log('Relative path:', relativePath);
|
|
46
|
+
try {
|
|
47
|
+
const parserOptions = {
|
|
48
|
+
sourceType: 'module',
|
|
49
|
+
plugins: ['jsx', 'typescript'],
|
|
50
|
+
};
|
|
51
|
+
const ast = (0, parser_1.parse)(code, parserOptions);
|
|
52
|
+
const magicString = new magic_string_1.default(code);
|
|
53
|
+
let changedElementsCount = 0;
|
|
54
|
+
let arrayContext = null;
|
|
55
|
+
// 使用 @babel/traverse 替代 estree-walker
|
|
56
|
+
(0, traverse_1.default)(ast, {
|
|
57
|
+
enter: path => {
|
|
58
|
+
const node = path.node;
|
|
59
|
+
// 检测数组的 map 调用
|
|
60
|
+
if (node.type === 'CallExpression') {
|
|
61
|
+
const callExpr = node;
|
|
62
|
+
if (callExpr.callee.type === 'MemberExpression') {
|
|
63
|
+
const memberExpr = callExpr.callee;
|
|
64
|
+
if (memberExpr.property.type === 'Identifier' && memberExpr.property.name === 'map') {
|
|
65
|
+
this.logger.log('Found array map:', node);
|
|
66
|
+
let arrayName = null;
|
|
67
|
+
// 获取数组名称
|
|
68
|
+
if (memberExpr.object.type === 'Identifier') {
|
|
69
|
+
arrayName = memberExpr.object.name;
|
|
70
|
+
}
|
|
71
|
+
else if (memberExpr.object.type === 'MemberExpression') {
|
|
72
|
+
const innerMember = memberExpr.object;
|
|
73
|
+
if (innerMember.object.type === 'Identifier' && innerMember.property.type === 'Identifier') {
|
|
74
|
+
arrayName = `${innerMember.object.name}.${innerMember.property.name}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 获取参数名(通常是 index)
|
|
78
|
+
let paramName = 'index';
|
|
79
|
+
if (callExpr.arguments.length > 0) {
|
|
80
|
+
const callback = callExpr.arguments[0];
|
|
81
|
+
if (callback.type === 'ArrowFunctionExpression' || callback.type === 'FunctionExpression') {
|
|
82
|
+
const func = callback;
|
|
83
|
+
if (func.params.length > 1 && func.params[1].type === 'Identifier') {
|
|
84
|
+
paramName = func.params[1].name;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (arrayName) {
|
|
89
|
+
arrayContext = { arrayName, paramName };
|
|
90
|
+
this.logger.log('Found array map:', { arrayName, paramName });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// 处理 JSX 开标签
|
|
96
|
+
if (node.type === 'JSXOpeningElement') {
|
|
97
|
+
const jsxOpeningElement = node;
|
|
98
|
+
let elementName;
|
|
99
|
+
// 获取元素名称
|
|
100
|
+
if (jsxOpeningElement.name.type === 'JSXIdentifier') {
|
|
101
|
+
elementName = jsxOpeningElement.name.name;
|
|
102
|
+
}
|
|
103
|
+
else if (jsxOpeningElement.name.type === 'JSXMemberExpression') {
|
|
104
|
+
const memberExpr = jsxOpeningElement.name;
|
|
105
|
+
if (memberExpr.object.type === 'JSXIdentifier' && memberExpr.property.type === 'JSXIdentifier') {
|
|
106
|
+
elementName = `${memberExpr.object.name}.${memberExpr.property.name}`;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// 跳过 Fragment
|
|
116
|
+
if (elementName === 'Fragment' || elementName === 'React.Fragment') {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const shouldTag = (0, utils_1.shouldTagElement)(elementName, this.options.threeFiberElems, this.options.dreiElems);
|
|
120
|
+
this.logger.log('Found JSX element:', {
|
|
121
|
+
elementName,
|
|
122
|
+
shouldTag,
|
|
123
|
+
hasArrayContext: !!arrayContext,
|
|
124
|
+
});
|
|
125
|
+
if (shouldTag) {
|
|
126
|
+
const line = node.loc?.start?.line ?? 0;
|
|
127
|
+
const col = node.loc?.start?.column ?? 0;
|
|
128
|
+
// 收集元素内容
|
|
129
|
+
const content = {};
|
|
130
|
+
// 收集属性
|
|
131
|
+
jsxOpeningElement.attributes.forEach(attr => {
|
|
132
|
+
if (attr.type === 'JSXAttribute') {
|
|
133
|
+
const attrName = attr.name.name;
|
|
134
|
+
if (attrName === 'placeholder' || attrName === 'className') {
|
|
135
|
+
if (attr.value?.type === 'StringLiteral') {
|
|
136
|
+
content[attrName] = attr.value.value;
|
|
137
|
+
}
|
|
138
|
+
else if (attr.value?.type === 'JSXExpressionContainer' && attr.value.expression.type === 'StringLiteral') {
|
|
139
|
+
content[attrName] = attr.value.expression.value;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
// 收集文本内容
|
|
145
|
+
const parentPath = path.parentPath;
|
|
146
|
+
if (parentPath && parentPath.node.type === 'JSXElement') {
|
|
147
|
+
const textContent = parentPath.node.children
|
|
148
|
+
.map((child) => {
|
|
149
|
+
if (child.type === 'JSXText') {
|
|
150
|
+
return child.value.trim();
|
|
151
|
+
}
|
|
152
|
+
else if (child.type === 'JSXExpressionContainer' && child.expression.type === 'StringLiteral') {
|
|
153
|
+
return child.expression.value;
|
|
154
|
+
}
|
|
155
|
+
return '';
|
|
156
|
+
})
|
|
157
|
+
.filter(Boolean)
|
|
158
|
+
.join(' ')
|
|
159
|
+
.trim();
|
|
160
|
+
if (textContent) {
|
|
161
|
+
content.text = textContent;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const attributes = (0, utils_1.generateDataAttributes)(relativePath, line, col, elementName, content, arrayContext);
|
|
165
|
+
this.logger.log('Adding attributes:', attributes);
|
|
166
|
+
// 使用 path 获取准确的节点位置信息
|
|
167
|
+
const nameEndPosition = jsxOpeningElement.name.end;
|
|
168
|
+
if (nameEndPosition !== null) {
|
|
169
|
+
magicString.appendLeft(nameEndPosition, attributes);
|
|
170
|
+
changedElementsCount++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
exit: path => {
|
|
176
|
+
const node = path.node;
|
|
177
|
+
// 离开 map 调用时清除上下文
|
|
178
|
+
if (node.type === 'CallExpression') {
|
|
179
|
+
const callExpr = node;
|
|
180
|
+
if (callExpr.callee.type === 'MemberExpression') {
|
|
181
|
+
const memberExpr = callExpr.callee;
|
|
182
|
+
if (memberExpr.property.type === 'Identifier' && memberExpr.property.name === 'map') {
|
|
183
|
+
if (arrayContext) {
|
|
184
|
+
this.logger.log('Leaving array map context:', arrayContext.arrayName);
|
|
185
|
+
}
|
|
186
|
+
arrayContext = null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
this.logger.log('File processing complete:', {
|
|
193
|
+
file: relativePath,
|
|
194
|
+
elementsChanged: changedElementsCount,
|
|
195
|
+
});
|
|
196
|
+
if (changedElementsCount > 0) {
|
|
197
|
+
this.logger.log('Modified code preview:', magicString.toString().slice(0, 500) + '...');
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
code: magicString.toString(),
|
|
201
|
+
map: magicString.generateMap({ hires: true }),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
this.logger.error('Error processing file:', relativePath);
|
|
206
|
+
this.logger.error(error);
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.JSXCompiler = JSXCompiler;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { BaseCompiler } from './base-compiler';
|
|
2
|
+
export interface VueCompilerOptions {
|
|
3
|
+
validExtensions: Set<string>;
|
|
4
|
+
threeFiberElems: string[];
|
|
5
|
+
dreiElems: string[];
|
|
6
|
+
enableLogging?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Vue 编译器类
|
|
10
|
+
* 负责处理 Vue 文件的编译逻辑
|
|
11
|
+
*/
|
|
12
|
+
export declare class VueCompiler implements BaseCompiler {
|
|
13
|
+
static readonly supportedExtensions: string[];
|
|
14
|
+
static readonly extensionName = "vue";
|
|
15
|
+
private options;
|
|
16
|
+
private logger;
|
|
17
|
+
private cwd;
|
|
18
|
+
constructor(options: VueCompilerOptions);
|
|
19
|
+
/**
|
|
20
|
+
* 编译 Vue 代码
|
|
21
|
+
*/
|
|
22
|
+
compile(code: string, id: string): Promise<{
|
|
23
|
+
code: string;
|
|
24
|
+
map: any;
|
|
25
|
+
} | null>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.VueCompiler = void 0;
|
|
7
|
+
const compiler_sfc_1 = require("@vue/compiler-sfc");
|
|
8
|
+
const magic_string_1 = __importDefault(require("magic-string"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const utils_1 = require("../utils/utils");
|
|
11
|
+
/**
|
|
12
|
+
* Vue 编译器类
|
|
13
|
+
* 负责处理 Vue 文件的编译逻辑
|
|
14
|
+
*/
|
|
15
|
+
class VueCompiler {
|
|
16
|
+
static supportedExtensions = ['.vue'];
|
|
17
|
+
static extensionName = 'vue';
|
|
18
|
+
options;
|
|
19
|
+
logger;
|
|
20
|
+
cwd;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.logger = new utils_1.Logger(options.enableLogging);
|
|
24
|
+
this.cwd = process.cwd();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 编译 Vue 代码
|
|
28
|
+
*/
|
|
29
|
+
async compile(code, id) {
|
|
30
|
+
this.logger.log('\nProcessing file:', id);
|
|
31
|
+
if (!(0, utils_1.shouldProcessFile)(id, this.options.validExtensions)) {
|
|
32
|
+
this.logger.log('Skipping file (not .vue or in node_modules):', id);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const relativePath = path_1.default.relative(this.cwd, id);
|
|
36
|
+
this.logger.log('Relative path:', relativePath);
|
|
37
|
+
try {
|
|
38
|
+
// 解析Vue SFC
|
|
39
|
+
const { descriptor } = (0, compiler_sfc_1.parse)(code);
|
|
40
|
+
if (!descriptor.template) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const magicString = new magic_string_1.default(code);
|
|
44
|
+
let changedElementsCount = 0;
|
|
45
|
+
// 获取template的AST
|
|
46
|
+
const templateAst = descriptor.template.ast;
|
|
47
|
+
if (!templateAst) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
// 递归处理AST节点
|
|
51
|
+
const processNode = (node, arrayContext = null) => {
|
|
52
|
+
if (node.type !== 1) {
|
|
53
|
+
// 1 表示元素节点
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const elementName = node.tag;
|
|
57
|
+
// 跳过内置组件
|
|
58
|
+
if (elementName === 'template' || elementName === 'slot') {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const shouldTag = (0, utils_1.shouldTagElement)(elementName, this.options.threeFiberElems, this.options.dreiElems);
|
|
62
|
+
if (shouldTag) {
|
|
63
|
+
const line = node.loc.start.line;
|
|
64
|
+
const col = node.loc.start.column;
|
|
65
|
+
// 收集元素内容
|
|
66
|
+
const content = {};
|
|
67
|
+
// 收集属性
|
|
68
|
+
if (node.props) {
|
|
69
|
+
node.props.forEach((prop) => {
|
|
70
|
+
if (prop.type === 6) {
|
|
71
|
+
// 6 表示属性
|
|
72
|
+
if (prop.name === 'placeholder' || prop.name === 'class') {
|
|
73
|
+
content[prop.name === 'class' ? 'className' : prop.name] = prop.value?.content;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// 收集文本内容
|
|
79
|
+
if (node.children) {
|
|
80
|
+
const textContent = node.children
|
|
81
|
+
.filter((child) => child.type === 2) // 2 表示文本节点
|
|
82
|
+
.map((child) => child.content.trim())
|
|
83
|
+
.filter(Boolean)
|
|
84
|
+
.join(' ');
|
|
85
|
+
if (textContent) {
|
|
86
|
+
content.text = textContent;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const attributes = (0, utils_1.generateDataAttributes)(relativePath, line, col, elementName, content, arrayContext);
|
|
90
|
+
// 在标签名后插入属性
|
|
91
|
+
const insertPos = node.loc.start.offset + elementName.length + 1;
|
|
92
|
+
magicString.appendLeft(insertPos, attributes);
|
|
93
|
+
changedElementsCount++;
|
|
94
|
+
}
|
|
95
|
+
// 处理v-for上下文
|
|
96
|
+
let vForContext = null;
|
|
97
|
+
const vForProp = node.props?.find((p) => p.type === 7 && p.name === 'for'); // 7 表示指令
|
|
98
|
+
if (vForProp) {
|
|
99
|
+
const match = vForProp.exp?.content.match(/\((.*?),\s*(.*?)\)\s+in\s+(.*)/);
|
|
100
|
+
if (match) {
|
|
101
|
+
vForContext = {
|
|
102
|
+
arrayName: match[3],
|
|
103
|
+
paramName: match[2],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// 递归处理子节点
|
|
108
|
+
if (node.children) {
|
|
109
|
+
node.children.forEach((child) => processNode(child, vForContext || arrayContext));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
processNode(templateAst);
|
|
113
|
+
this.logger.log('File processing complete:', {
|
|
114
|
+
file: relativePath,
|
|
115
|
+
elementsChanged: changedElementsCount,
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
code: magicString.toString(),
|
|
119
|
+
map: magicString.generateMap({ hires: true }),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
this.logger.error('Error processing file:', relativePath);
|
|
124
|
+
this.logger.error(error);
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
exports.VueCompiler = VueCompiler;
|
|
@@ -6,7 +6,12 @@ export declare function shouldTagElement(elementName: string, threeFiberElems: s
|
|
|
6
6
|
/**
|
|
7
7
|
* 生成数据属性字符串
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
interface ElementContent {
|
|
10
|
+
text?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function generateDataAttributes(relativePath: string, line: number, col: number, elementName: string, content?: ElementContent, arrayContext?: ArrayContext | null): string;
|
|
10
15
|
/**
|
|
11
16
|
* 日志记录工具
|
|
12
17
|
*/
|
|
@@ -31,3 +36,4 @@ export declare class StatsManager {
|
|
|
31
36
|
* 检查文件是否应该被处理
|
|
32
37
|
*/
|
|
33
38
|
export declare function shouldProcessFile(id: string, validExtensions: Set<string>): boolean;
|
|
39
|
+
export {};
|
|
@@ -14,16 +14,14 @@ const path_1 = __importDefault(require("path"));
|
|
|
14
14
|
function shouldTagElement(elementName, threeFiberElems, dreiElems) {
|
|
15
15
|
return !threeFiberElems.includes(elementName) && !dreiElems.includes(elementName);
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
* 生成数据属性字符串
|
|
19
|
-
*/
|
|
20
|
-
function generateDataAttributes(relativePath, line, col, elementName, arrayContext) {
|
|
17
|
+
function generateDataAttributes(relativePath, line, col, elementName, content = {}, arrayContext) {
|
|
21
18
|
const dataComponentId = `${relativePath}:${line}:${col}`;
|
|
22
19
|
let arrayAttrs = '';
|
|
23
20
|
if (arrayContext) {
|
|
24
|
-
arrayAttrs = ` data-nocode-array="${arrayContext.arrayName}"
|
|
21
|
+
arrayAttrs = ` data-nocode-array="${arrayContext.arrayName}"`;
|
|
25
22
|
}
|
|
26
|
-
|
|
23
|
+
const contentAttr = Object.keys(content).length > 0 ? ` data-nocode-content="${encodeURIComponent(JSON.stringify(content))}"` : '';
|
|
24
|
+
return ` data-nocode-id="${dataComponentId}" data-nocode-name="${elementName}"${arrayAttrs}${contentAttr}`;
|
|
27
25
|
}
|
|
28
26
|
/**
|
|
29
27
|
* 日志记录工具
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* 创建 nocode 编译器插件
|
|
4
|
+
* @param options 编译器选项
|
|
5
|
+
* @returns Vite 插件
|
|
6
|
+
*/
|
|
7
|
+
export type FileType = 'jsx' | 'tsx' | 'vue';
|
|
8
|
+
export interface CompilerPluginOptions {
|
|
9
|
+
/**
|
|
10
|
+
* 指定要处理的文件类型
|
|
11
|
+
* @default ['jsx']
|
|
12
|
+
* @example ['jsx', 'tsx', 'vue']
|
|
13
|
+
*/
|
|
14
|
+
include?: FileType[];
|
|
15
|
+
/**
|
|
16
|
+
* Three.js 相关组件配置
|
|
17
|
+
*/
|
|
18
|
+
three?: {
|
|
19
|
+
/**
|
|
20
|
+
* Three.js Fiber 组件列表
|
|
21
|
+
*/
|
|
22
|
+
fiberElements?: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Drei 组件列表
|
|
25
|
+
*/
|
|
26
|
+
dreiElements?: string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* 调试选项
|
|
30
|
+
*/
|
|
31
|
+
debug?: {
|
|
32
|
+
/**
|
|
33
|
+
* 是否启用日志
|
|
34
|
+
*/
|
|
35
|
+
enableLogging?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* 是否在开发模式下跳过编译
|
|
38
|
+
*/
|
|
39
|
+
skipInDev?: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export declare function componentCompiler(options?: CompilerPluginOptions): Plugin;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.componentCompiler = componentCompiler;
|
|
4
|
+
const constants_1 = require("@mcopilot-nocode/nocode-compiler-core/utils/constants");
|
|
5
|
+
const base_compiler_1 = require("@mcopilot-nocode/nocode-compiler-core/compilers/base-compiler");
|
|
6
|
+
const jsx_compiler_1 = require("@mcopilot-nocode/nocode-compiler-core/compilers/jsx-compiler");
|
|
7
|
+
const vue_compiler_1 = require("@mcopilot-nocode/nocode-compiler-core/compilers/vue-compiler");
|
|
8
|
+
const utils_1 = require("@mcopilot-nocode/nocode-compiler-core/utils/utils");
|
|
9
|
+
function componentCompiler(options = {}) {
|
|
10
|
+
const { include = ['jsx'], three = {}, debug = {} } = options;
|
|
11
|
+
// 构建基础配置
|
|
12
|
+
const baseConfig = {
|
|
13
|
+
validExtensions: new Set(),
|
|
14
|
+
threeFiberElems: three.fiberElements || constants_1.DEFAULT_CONFIG.threeFiberElems,
|
|
15
|
+
dreiElems: three.dreiElements || constants_1.DEFAULT_CONFIG.dreiElems,
|
|
16
|
+
enableLogging: debug.enableLogging ?? false,
|
|
17
|
+
};
|
|
18
|
+
// 创建编译器注册表
|
|
19
|
+
const registry = new base_compiler_1.CompilerRegistry();
|
|
20
|
+
// 根据include配置注册对应的编译器
|
|
21
|
+
const hasJsxOrTsx = include.includes('jsx') || include.includes('tsx');
|
|
22
|
+
if (hasJsxOrTsx) {
|
|
23
|
+
const extensions = [];
|
|
24
|
+
if (include.includes('jsx'))
|
|
25
|
+
extensions.push('.jsx');
|
|
26
|
+
if (include.includes('tsx'))
|
|
27
|
+
extensions.push('.tsx');
|
|
28
|
+
extensions.forEach(ext => baseConfig.validExtensions.add(ext));
|
|
29
|
+
registry.register({
|
|
30
|
+
extensionName: 'jsx',
|
|
31
|
+
supportedExtensions: extensions,
|
|
32
|
+
CompilerClass: jsx_compiler_1.JSXCompiler,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (include.includes('vue')) {
|
|
36
|
+
baseConfig.validExtensions.add('.vue');
|
|
37
|
+
registry.register({
|
|
38
|
+
extensionName: 'vue',
|
|
39
|
+
supportedExtensions: ['.vue'],
|
|
40
|
+
CompilerClass: vue_compiler_1.VueCompiler,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
// 创建统计管理器
|
|
44
|
+
const statsManager = new utils_1.StatsManager();
|
|
45
|
+
return {
|
|
46
|
+
name: 'vite-plugin-nocode-compiler',
|
|
47
|
+
enforce: 'pre',
|
|
48
|
+
async transform(code, id) {
|
|
49
|
+
// 获取适合的编译器
|
|
50
|
+
const compiler = registry.getCompilerForFile(id, {
|
|
51
|
+
validExtensions: baseConfig.validExtensions,
|
|
52
|
+
threeFiberElems: baseConfig.threeFiberElems,
|
|
53
|
+
dreiElems: baseConfig.dreiElems,
|
|
54
|
+
enableLogging: baseConfig.enableLogging,
|
|
55
|
+
});
|
|
56
|
+
if (!compiler) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// 统计文件处理
|
|
60
|
+
statsManager.incrementTotalFiles();
|
|
61
|
+
// 编译代码
|
|
62
|
+
const result = await compiler.compile(code, id);
|
|
63
|
+
if (result) {
|
|
64
|
+
statsManager.incrementProcessedFiles();
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
-
export interface ArrayContext {
|
|
3
|
-
arrayName: string;
|
|
4
|
-
paramName: string;
|
|
5
|
-
}
|
|
6
|
-
export interface CompilerStats {
|
|
7
|
-
totalFiles: number;
|
|
8
|
-
processedFiles: number;
|
|
9
|
-
totalElements: number;
|
|
10
|
-
}
|
|
11
2
|
export interface CompilerOptions {
|
|
12
3
|
validExtensions?: Set<string>;
|
|
13
4
|
threeFiberElems?: string[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meituan-nocode/vite-plugin-nocode-compiler",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
4
|
-
"description": "nocode
|
|
3
|
+
"version": "0.1.0-beta.7",
|
|
4
|
+
"description": "nocode compiler plugin",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -22,9 +22,6 @@
|
|
|
22
22
|
"vite": "^5.4.0"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"@babel/types": "^7.23.0",
|
|
27
|
-
"magic-string": "^0.30.0",
|
|
28
|
-
"estree-walker": "^3.0.0"
|
|
25
|
+
"@mcopilot-nocode/nocode-compiler-core": "workspace:*"
|
|
29
26
|
}
|
|
30
27
|
}
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.JSXCompiler = void 0;
|
|
7
|
-
const parser_1 = require("@babel/parser");
|
|
8
|
-
const magic_string_1 = __importDefault(require("magic-string"));
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const utils_1 = require("../utils/utils");
|
|
11
|
-
const estree_walker_1 = require("estree-walker");
|
|
12
|
-
/**
|
|
13
|
-
* JSX 编译器类
|
|
14
|
-
* 负责处理 JSX 文件的编译逻辑
|
|
15
|
-
*/
|
|
16
|
-
class JSXCompiler {
|
|
17
|
-
static supportedExtensions = ['.jsx'];
|
|
18
|
-
static name = 'jsx';
|
|
19
|
-
options;
|
|
20
|
-
logger;
|
|
21
|
-
cwd;
|
|
22
|
-
constructor(options) {
|
|
23
|
-
this.options = options;
|
|
24
|
-
this.logger = new utils_1.Logger(options.enableLogging);
|
|
25
|
-
this.cwd = process.cwd();
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* 编译 JSX 代码
|
|
29
|
-
*/
|
|
30
|
-
async compile(code, id) {
|
|
31
|
-
this.logger.log('\nProcessing file:', id);
|
|
32
|
-
if (!(0, utils_1.shouldProcessFile)(id, this.options.validExtensions)) {
|
|
33
|
-
this.logger.log('Skipping file (not .jsx or in node_modules):', id);
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
const relativePath = path_1.default.relative(this.cwd, id);
|
|
37
|
-
this.logger.log('Relative path:', relativePath);
|
|
38
|
-
try {
|
|
39
|
-
const parserOptions = {
|
|
40
|
-
sourceType: 'module',
|
|
41
|
-
plugins: ['jsx'],
|
|
42
|
-
};
|
|
43
|
-
const ast = (0, parser_1.parse)(code, parserOptions);
|
|
44
|
-
const magicString = new magic_string_1.default(code);
|
|
45
|
-
let changedElementsCount = 0;
|
|
46
|
-
let currentElement = null;
|
|
47
|
-
let arrayContext = null;
|
|
48
|
-
(0, estree_walker_1.walk)(ast, {
|
|
49
|
-
enter: (node) => {
|
|
50
|
-
// 检测数组的 map 调用
|
|
51
|
-
if (node.type === 'CallExpression' && node.callee?.type === 'MemberExpression' && node.callee.property?.name === 'map') {
|
|
52
|
-
this.logger.log('Found array map:', node);
|
|
53
|
-
const callee = node.callee;
|
|
54
|
-
const arrayName = callee.object.name || (callee.object.type === 'MemberExpression' ? `${callee.object.object.name}.${callee.object.property.name}` : null);
|
|
55
|
-
if (arrayName) {
|
|
56
|
-
const paramName = node.arguments[0].params?.[1]?.name || 'index';
|
|
57
|
-
arrayContext = { arrayName, paramName };
|
|
58
|
-
this.logger.log('Found array map:', { arrayName, paramName });
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (node.type === 'JSXElement') {
|
|
62
|
-
currentElement = node;
|
|
63
|
-
}
|
|
64
|
-
if (node.type === 'JSXOpeningElement') {
|
|
65
|
-
const jsxNode = node;
|
|
66
|
-
let elementName;
|
|
67
|
-
if (jsxNode.name.type === 'JSXIdentifier') {
|
|
68
|
-
elementName = jsxNode.name.name;
|
|
69
|
-
}
|
|
70
|
-
else if (jsxNode.name.type === 'JSXMemberExpression') {
|
|
71
|
-
const memberExpr = jsxNode.name;
|
|
72
|
-
elementName = `${memberExpr.object.name}.${memberExpr.property.name}`;
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
if (elementName === 'Fragment' || elementName === 'React.Fragment') {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const shouldTag = (0, utils_1.shouldTagElement)(elementName, this.options.threeFiberElems, this.options.dreiElems);
|
|
81
|
-
this.logger.log('Found JSX element:', {
|
|
82
|
-
elementName,
|
|
83
|
-
shouldTag,
|
|
84
|
-
hasArrayContext: !!arrayContext,
|
|
85
|
-
});
|
|
86
|
-
if (shouldTag) {
|
|
87
|
-
const line = jsxNode.loc?.start?.line ?? 0;
|
|
88
|
-
const col = jsxNode.loc?.start?.column ?? 0;
|
|
89
|
-
const attributes = (0, utils_1.generateDataAttributes)(relativePath, line, col, elementName, arrayContext);
|
|
90
|
-
this.logger.log('Adding attributes:', attributes);
|
|
91
|
-
magicString.appendLeft(jsxNode.name.end ?? 0, attributes);
|
|
92
|
-
changedElementsCount++;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
leave: (node) => {
|
|
97
|
-
if (node.type === 'CallExpression' && node.callee?.type === 'MemberExpression' && node.callee.property?.name === 'map') {
|
|
98
|
-
if (arrayContext) {
|
|
99
|
-
this.logger.log('Leaving array map context:', arrayContext.arrayName);
|
|
100
|
-
}
|
|
101
|
-
arrayContext = null;
|
|
102
|
-
}
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
this.logger.log('File processing complete:', {
|
|
106
|
-
file: relativePath,
|
|
107
|
-
elementsChanged: changedElementsCount,
|
|
108
|
-
});
|
|
109
|
-
if (changedElementsCount > 0) {
|
|
110
|
-
this.logger.log('Modified code preview:', magicString.toString().slice(0, 500) + '...');
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
code: magicString.toString(),
|
|
114
|
-
map: magicString.generateMap({ hires: true }),
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
catch (error) {
|
|
118
|
-
this.logger.error('Error processing file:', relativePath);
|
|
119
|
-
this.logger.error(error);
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
exports.JSXCompiler = JSXCompiler;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|