mr-sliy 1.0.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/.env.example +145 -0
- package/database/schema.sql +187 -0
- package/package.json +74 -0
- package/scripts/download-tree-sitter.js +171 -0
- package/scripts/postinstall.js +134 -0
- package/src/agent/agent.js +563 -0
- package/src/agent.js +87 -0
- package/src/cli/index.js +1643 -0
- package/src/config/index.js +232 -0
- package/src/engine/dualModeEngine.js +486 -0
- package/src/index.js +165 -0
- package/src/middlewares/errorHandler.js +166 -0
- package/src/middlewares/index.js +23 -0
- package/src/routes/aiRoutes.js +117 -0
- package/src/routes/configRoutes.js +31 -0
- package/src/routes/index.js +75 -0
- package/src/routes/issueRoutes.js +195 -0
- package/src/routes/projectRoutes.js +46 -0
- package/src/routes/reportRoutes.js +40 -0
- package/src/routes/scanRoutes.js +245 -0
- package/src/routes/userRoutes.js +47 -0
- package/src/services/ast/parser.js +503 -0
- package/src/services/detection/detector.js +934 -0
- package/src/services/llm/providers.js +1107 -0
- package/src/services/rag/agent.js +375 -0
- package/src/services/vector/knowledgeBase.js +863 -0
- package/src/skills/Skill.js +38 -0
- package/src/skills/code-analysis/index.js +272 -0
- package/src/skills/code-detection/index.js +166 -0
- package/src/skills/code-detection/rules/console-log.js +45 -0
- package/src/skills/code-detection/rules/deep-nesting.js +76 -0
- package/src/skills/code-detection/rules/duplicate-code.js +57 -0
- package/src/skills/code-detection/rules/high-complexity.js +109 -0
- package/src/skills/code-detection/rules/index.js +59 -0
- package/src/skills/code-detection/rules/long-functions.js +54 -0
- package/src/skills/code-detection/rules/magic-numbers.js +48 -0
- package/src/skills/code-detection/rules/missing-comment.js +64 -0
- package/src/skills/code-detection/rules/null-check.js +71 -0
- package/src/skills/code-detection/rules/unnecessary-else.js +46 -0
- package/src/skills/code-detection/rules/unused-functions.js +57 -0
- package/src/skills/code-detection/rules/unused-imports.js +57 -0
- package/src/skills/code-detection/rules/unused-variables.js +54 -0
- package/src/skills/code-optimization/index.js +319 -0
- package/src/skills/index.js +152 -0
- package/src/utils/crypto.js +212 -0
- package/src/utils/database.js +125 -0
- package/src/utils/helpers.js +226 -0
- package/src/utils/logger.js +202 -0
- package/src/utils/mysql.js +198 -0
- package/src/utils/response.js +124 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter AST解析服务
|
|
3
|
+
* 支持多语言源代码解析为AST语法树
|
|
4
|
+
* 优先使用npm包中的语言解析器,降级策略:无解析器时使用基础正则解析
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const { logger } = require('../../utils/logger');
|
|
10
|
+
const { getFileLanguage } = require('../../utils/helpers');
|
|
11
|
+
|
|
12
|
+
const languageParsers = new Map();
|
|
13
|
+
let Parser = null;
|
|
14
|
+
let parserInitialized = false;
|
|
15
|
+
|
|
16
|
+
const WASM_DIR = path.join(__dirname, '..', '..', '..', 'wasm');
|
|
17
|
+
|
|
18
|
+
const languageMap = {
|
|
19
|
+
javascript: 'javascript',
|
|
20
|
+
typescript: 'typescript',
|
|
21
|
+
python: 'python',
|
|
22
|
+
java: 'java',
|
|
23
|
+
go: 'go',
|
|
24
|
+
rust: 'rust',
|
|
25
|
+
c: 'c',
|
|
26
|
+
cpp: 'cpp',
|
|
27
|
+
csharp: 'c_sharp',
|
|
28
|
+
ruby: 'ruby',
|
|
29
|
+
php: 'php',
|
|
30
|
+
swift: 'swift',
|
|
31
|
+
kotlin: 'kotlin',
|
|
32
|
+
scala: 'scala'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const npmPackageMap = {
|
|
36
|
+
javascript: 'tree-sitter-javascript',
|
|
37
|
+
typescript: 'tree-sitter-typescript',
|
|
38
|
+
python: 'tree-sitter-python',
|
|
39
|
+
java: 'tree-sitter-java',
|
|
40
|
+
go: 'tree-sitter-go',
|
|
41
|
+
rust: 'tree-sitter-rust',
|
|
42
|
+
c: 'tree-sitter-c',
|
|
43
|
+
cpp: 'tree-sitter-cpp',
|
|
44
|
+
csharp: 'tree-sitter-c-sharp',
|
|
45
|
+
ruby: 'tree-sitter-ruby',
|
|
46
|
+
php: 'tree-sitter-php'
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
async function initParser() {
|
|
50
|
+
if (parserInitialized) return;
|
|
51
|
+
try {
|
|
52
|
+
Parser = require('web-tree-sitter');
|
|
53
|
+
await Parser.init();
|
|
54
|
+
parserInitialized = true;
|
|
55
|
+
logger.info('Tree-sitter Parser初始化成功');
|
|
56
|
+
} catch (error) {
|
|
57
|
+
logger.warn('Tree-sitter Parser初始化失败,将使用基础解析模式:', error.message);
|
|
58
|
+
Parser = null;
|
|
59
|
+
parserInitialized = true;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function loadLanguage(languageName) {
|
|
64
|
+
if (languageParsers.has(languageName)) {
|
|
65
|
+
return languageParsers.get(languageName);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!Parser) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const npmPackage = npmPackageMap[languageName];
|
|
74
|
+
if (npmPackage) {
|
|
75
|
+
try {
|
|
76
|
+
const languageModule = require(npmPackage);
|
|
77
|
+
const language = Parser.Language.load(languageModule.wasm || path.join(require.resolve(npmPackage), '../', 'tree-sitter-' + languageMap[languageName] + '.wasm'));
|
|
78
|
+
const parser = new Parser();
|
|
79
|
+
parser.setLanguage(language);
|
|
80
|
+
languageParsers.set(languageName, { parser, language });
|
|
81
|
+
logger.info(`加载语言解析器成功(npm): ${languageName}`);
|
|
82
|
+
return { parser, language };
|
|
83
|
+
} catch (npmError) {
|
|
84
|
+
logger.debug(`npm包加载失败,尝试WASM文件: ${npmError.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const wasmFileName = languageMap[languageName] || languageName;
|
|
89
|
+
const wasmPath = path.join(WASM_DIR, `tree-sitter-${wasmFileName}.wasm`);
|
|
90
|
+
|
|
91
|
+
if (!fs.existsSync(wasmPath)) {
|
|
92
|
+
logger.warn(`语言WASM文件不存在: ${wasmPath}`);
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const language = await Parser.Language.load(wasmPath);
|
|
97
|
+
const parser = new Parser();
|
|
98
|
+
parser.setLanguage(language);
|
|
99
|
+
|
|
100
|
+
languageParsers.set(languageName, { parser, language });
|
|
101
|
+
logger.info(`加载语言解析器成功(wasm): ${languageName}`);
|
|
102
|
+
|
|
103
|
+
return { parser, language };
|
|
104
|
+
} catch (error) {
|
|
105
|
+
logger.error(`加载语言解析器失败: ${languageName}`, error);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 基础解析器(降级模式)
|
|
112
|
+
* 当Tree-sitter不可用时使用正则进行简单解析
|
|
113
|
+
*/
|
|
114
|
+
class FallbackParser {
|
|
115
|
+
parse(sourceCode) {
|
|
116
|
+
const lines = sourceCode.split('\n');
|
|
117
|
+
const nodes = [];
|
|
118
|
+
|
|
119
|
+
lines.forEach((line, index) => {
|
|
120
|
+
// 检测变量声明
|
|
121
|
+
const varMatch = line.match(/(?:const|let|var)\s+(\w+)/);
|
|
122
|
+
if (varMatch) {
|
|
123
|
+
nodes.push({
|
|
124
|
+
type: 'variable_declarator',
|
|
125
|
+
text: line.trim(),
|
|
126
|
+
startPosition: { row: index, column: line.search(/\b(?:const|let|var)\b/) },
|
|
127
|
+
endPosition: { row: index, column: line.length }
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// 检测函数声明
|
|
132
|
+
const funcMatch = line.match(/function\s+(\w+)/);
|
|
133
|
+
if (funcMatch) {
|
|
134
|
+
nodes.push({
|
|
135
|
+
type: 'function_declaration',
|
|
136
|
+
text: line.trim(),
|
|
137
|
+
startPosition: { row: index, column: line.search(/\bfunction\b/) },
|
|
138
|
+
endPosition: { row: index, column: line.length }
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 检测导入语句
|
|
143
|
+
const importMatch = line.match(/import\s+/);
|
|
144
|
+
if (importMatch) {
|
|
145
|
+
nodes.push({
|
|
146
|
+
type: 'import_statement',
|
|
147
|
+
text: line.trim(),
|
|
148
|
+
startPosition: { row: index, column: 0 },
|
|
149
|
+
endPosition: { row: index, column: line.length }
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 检测if语句
|
|
154
|
+
const ifMatch = line.match(/if\s*\(/);
|
|
155
|
+
if (ifMatch) {
|
|
156
|
+
nodes.push({
|
|
157
|
+
type: 'if_statement',
|
|
158
|
+
text: line.trim(),
|
|
159
|
+
startPosition: { row: index, column: line.search(/\bif\b/) },
|
|
160
|
+
endPosition: { row: index, column: line.length }
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 检测for循环
|
|
165
|
+
const forMatch = line.match(/for\s*\(/);
|
|
166
|
+
if (forMatch) {
|
|
167
|
+
nodes.push({
|
|
168
|
+
type: 'for_statement',
|
|
169
|
+
text: line.trim(),
|
|
170
|
+
startPosition: { row: index, column: line.search(/\bfor\b/) },
|
|
171
|
+
endPosition: { row: index, column: line.length }
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 检测while循环
|
|
176
|
+
const whileMatch = line.match(/while\s*\(/);
|
|
177
|
+
if (whileMatch) {
|
|
178
|
+
nodes.push({
|
|
179
|
+
type: 'while_statement',
|
|
180
|
+
text: line.trim(),
|
|
181
|
+
startPosition: { row: index, column: line.search(/\bwhile\b/) },
|
|
182
|
+
endPosition: { row: index, column: line.length }
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 检测console.log调用
|
|
187
|
+
const consoleMatch = line.match(/console\.log\(/);
|
|
188
|
+
if (consoleMatch) {
|
|
189
|
+
nodes.push({
|
|
190
|
+
type: 'call_expression',
|
|
191
|
+
text: line.trim(),
|
|
192
|
+
startPosition: { row: index, column: line.search('console.log') },
|
|
193
|
+
endPosition: { row: index, column: line.length }
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 检测其他函数调用
|
|
198
|
+
const callMatch = line.match(/(\w+)\s*\(/);
|
|
199
|
+
if (callMatch && !line.includes('function') && !line.includes('return')) {
|
|
200
|
+
nodes.push({
|
|
201
|
+
type: 'call_expression',
|
|
202
|
+
text: line.trim(),
|
|
203
|
+
startPosition: { row: index, column: callMatch.index },
|
|
204
|
+
endPosition: { row: index, column: line.length }
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// 检测标识符
|
|
209
|
+
const idMatches = line.matchAll(/\b([a-zA-Z_]\w*)\b/g);
|
|
210
|
+
for (const match of idMatches) {
|
|
211
|
+
const keyword = ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'import', 'from', 'class', 'console'];
|
|
212
|
+
if (!keyword.includes(match[1])) {
|
|
213
|
+
nodes.push({
|
|
214
|
+
type: 'identifier',
|
|
215
|
+
text: match[1],
|
|
216
|
+
startPosition: { row: index, column: match.index },
|
|
217
|
+
endPosition: { row: index, column: match.index + match[1].length }
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 检测数字
|
|
223
|
+
const numMatches = line.matchAll(/\b(\d+(?:\.\d+)?)\b/g);
|
|
224
|
+
for (const match of numMatches) {
|
|
225
|
+
nodes.push({
|
|
226
|
+
type: 'number',
|
|
227
|
+
text: match[1],
|
|
228
|
+
startPosition: { row: index, column: match.index },
|
|
229
|
+
endPosition: { row: index, column: match.index + match[1].length }
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
rootNode: {
|
|
236
|
+
type: 'program',
|
|
237
|
+
text: sourceCode,
|
|
238
|
+
startPosition: { row: 0, column: 0 },
|
|
239
|
+
endPosition: { row: lines.length - 1, column: lines[lines.length - 1]?.length || 0 },
|
|
240
|
+
childCount: nodes.length,
|
|
241
|
+
children: nodes
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 解析源代码为AST
|
|
249
|
+
*/
|
|
250
|
+
async function parseCode(sourceCode, languageName) {
|
|
251
|
+
try {
|
|
252
|
+
await initParser();
|
|
253
|
+
const langParser = await loadLanguage(languageName);
|
|
254
|
+
|
|
255
|
+
if (langParser) {
|
|
256
|
+
const tree = langParser.parser.parse(sourceCode);
|
|
257
|
+
return {
|
|
258
|
+
success: true,
|
|
259
|
+
tree,
|
|
260
|
+
language: languageName,
|
|
261
|
+
rootNode: tree.rootNode
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 降级到基础解析器
|
|
266
|
+
logger.warn(`Tree-sitter不可用,使用基础解析器: ${languageName}`);
|
|
267
|
+
const fallback = new FallbackParser();
|
|
268
|
+
const tree = fallback.parse(sourceCode);
|
|
269
|
+
return {
|
|
270
|
+
success: true,
|
|
271
|
+
tree,
|
|
272
|
+
language: languageName,
|
|
273
|
+
rootNode: tree.rootNode,
|
|
274
|
+
fallback: true
|
|
275
|
+
};
|
|
276
|
+
} catch (error) {
|
|
277
|
+
logger.error('解析代码失败:', error);
|
|
278
|
+
return {
|
|
279
|
+
success: false,
|
|
280
|
+
error: error.message
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* 解析文件
|
|
287
|
+
*/
|
|
288
|
+
async function parseFile(filePath) {
|
|
289
|
+
try {
|
|
290
|
+
const sourceCode = fs.readFileSync(filePath, 'utf-8');
|
|
291
|
+
const languageName = getFileLanguage(filePath);
|
|
292
|
+
|
|
293
|
+
return await parseCode(sourceCode, languageName);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
logger.error(`解析文件失败: ${filePath}`, error);
|
|
296
|
+
return {
|
|
297
|
+
success: false,
|
|
298
|
+
error: error.message,
|
|
299
|
+
filePath
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* 查询AST节点
|
|
306
|
+
*/
|
|
307
|
+
async function queryNodes(tree, language, queryPattern) {
|
|
308
|
+
try {
|
|
309
|
+
const langParser = languageParsers.get(language);
|
|
310
|
+
if (!langParser) {
|
|
311
|
+
throw new Error(`语言解析器未加载: ${language}`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const query = langParser.language.query(queryPattern);
|
|
315
|
+
const matches = query.matches(tree.rootNode);
|
|
316
|
+
|
|
317
|
+
return matches.map(match => ({
|
|
318
|
+
pattern: match.pattern,
|
|
319
|
+
captures: match.captures.map(capture => ({
|
|
320
|
+
name: capture.name,
|
|
321
|
+
node: capture.node,
|
|
322
|
+
text: capture.node.text,
|
|
323
|
+
startPosition: capture.node.startPosition,
|
|
324
|
+
endPosition: capture.node.endPosition,
|
|
325
|
+
startPosition: {
|
|
326
|
+
row: capture.node.startPosition.row,
|
|
327
|
+
column: capture.node.startPosition.column
|
|
328
|
+
},
|
|
329
|
+
endPosition: {
|
|
330
|
+
row: capture.node.endPosition.row,
|
|
331
|
+
column: capture.node.endPosition.column
|
|
332
|
+
}
|
|
333
|
+
}))
|
|
334
|
+
}));
|
|
335
|
+
} catch (error) {
|
|
336
|
+
logger.error('查询AST节点失败:', error);
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* 遍历AST树
|
|
343
|
+
*/
|
|
344
|
+
function traverseAST(node, callback, depth = 0) {
|
|
345
|
+
callback(node, depth);
|
|
346
|
+
|
|
347
|
+
const children = node.children || (typeof node.childCount === 'number' ? [] : []);
|
|
348
|
+
for (let i = 0; i < children.length; i++) {
|
|
349
|
+
traverseAST(children[i], callback, depth + 1);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Tree-sitter原生节点使用child()方法
|
|
353
|
+
if (node.childCount && typeof node.child === 'function') {
|
|
354
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
355
|
+
const child = node.child(i);
|
|
356
|
+
if (child) traverseAST(child, callback, depth + 1);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* 获取所有特定类型的节点
|
|
363
|
+
*/
|
|
364
|
+
function getNodesByType(rootNode, nodeType) {
|
|
365
|
+
const nodes = [];
|
|
366
|
+
|
|
367
|
+
traverseAST(rootNode, (node) => {
|
|
368
|
+
if (node.type === nodeType) {
|
|
369
|
+
nodes.push({
|
|
370
|
+
type: node.type,
|
|
371
|
+
text: node.text,
|
|
372
|
+
startPosition: {
|
|
373
|
+
row: node.startPosition.row,
|
|
374
|
+
column: node.startPosition.column
|
|
375
|
+
},
|
|
376
|
+
endPosition: {
|
|
377
|
+
row: node.endPosition.row,
|
|
378
|
+
column: node.endPosition.column
|
|
379
|
+
},
|
|
380
|
+
children: node.childCount
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
return nodes;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* 提取函数声明节点
|
|
390
|
+
*/
|
|
391
|
+
async function extractFunctions(tree) {
|
|
392
|
+
return getNodesByType(tree.rootNode, 'function_declaration');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* 提取变量声明节点
|
|
397
|
+
*/
|
|
398
|
+
async function extractVariables(tree) {
|
|
399
|
+
return getNodesByType(tree.rootNode, 'variable_declarator');
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* 提取导入语句节点
|
|
404
|
+
*/
|
|
405
|
+
async function extractImports(tree) {
|
|
406
|
+
return getNodesByType(tree.rootNode, 'import_statement');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* 提取类声明节点
|
|
411
|
+
*/
|
|
412
|
+
async function extractClasses(tree) {
|
|
413
|
+
return getNodesByType(tree.rootNode, 'class_declaration');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* 提取标识符节点
|
|
418
|
+
*/
|
|
419
|
+
async function extractIdentifiers(tree) {
|
|
420
|
+
return getNodesByType(tree.rootNode, 'identifier');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* 获取AST树结构摘要
|
|
425
|
+
*/
|
|
426
|
+
function getASTSummary(tree) {
|
|
427
|
+
const summary = {
|
|
428
|
+
totalNodes: 0,
|
|
429
|
+
nodeTypes: {},
|
|
430
|
+
maxDepth: 0
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
traverseAST(tree.rootNode, (node, depth) => {
|
|
434
|
+
summary.totalNodes++;
|
|
435
|
+
|
|
436
|
+
if (!summary.nodeTypes[node.type]) {
|
|
437
|
+
summary.nodeTypes[node.type] = 0;
|
|
438
|
+
}
|
|
439
|
+
summary.nodeTypes[node.type]++;
|
|
440
|
+
|
|
441
|
+
if (depth > summary.maxDepth) {
|
|
442
|
+
summary.maxDepth = depth;
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
return summary;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* 将AST转换为JSON格式(用于存储和传输)
|
|
451
|
+
*/
|
|
452
|
+
function astToJson(node) {
|
|
453
|
+
return {
|
|
454
|
+
type: node.type,
|
|
455
|
+
text: node.text,
|
|
456
|
+
startPosition: {
|
|
457
|
+
row: node.startPosition.row,
|
|
458
|
+
column: node.startPosition.column
|
|
459
|
+
},
|
|
460
|
+
endPosition: {
|
|
461
|
+
row: node.endPosition.row,
|
|
462
|
+
column: node.endPosition.column
|
|
463
|
+
},
|
|
464
|
+
children: node.children ? node.children.map(child => astToJson(child)) : []
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* 释放Parser资源
|
|
470
|
+
*/
|
|
471
|
+
function disposeParser() {
|
|
472
|
+
languageParsers.forEach((langParser, languageName) => {
|
|
473
|
+
if (langParser.parser) {
|
|
474
|
+
langParser.parser.dispose();
|
|
475
|
+
}
|
|
476
|
+
if (langParser.language) {
|
|
477
|
+
langParser.language.dispose();
|
|
478
|
+
}
|
|
479
|
+
logger.info(`释放语言解析器: ${languageName}`);
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
languageParsers.clear();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// 不再在模块加载时自动初始化,改为按需初始化(在parseCode中调用)
|
|
486
|
+
|
|
487
|
+
module.exports = {
|
|
488
|
+
initParser,
|
|
489
|
+
loadLanguage,
|
|
490
|
+
parseCode,
|
|
491
|
+
parseFile,
|
|
492
|
+
queryNodes,
|
|
493
|
+
traverseAST,
|
|
494
|
+
getNodesByType,
|
|
495
|
+
extractFunctions,
|
|
496
|
+
extractVariables,
|
|
497
|
+
extractImports,
|
|
498
|
+
extractClasses,
|
|
499
|
+
extractIdentifiers,
|
|
500
|
+
getASTSummary,
|
|
501
|
+
astToJson,
|
|
502
|
+
disposeParser
|
|
503
|
+
};
|