bonzai-tree 1.0.113
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 +4 -0
- package/dist/bconfig.js +224 -0
- package/dist/graph-templates/config.js +18 -0
- package/dist/graph-templates/ignore.txt +58 -0
- package/dist/graph-templates/loops/backend/delete.js +20 -0
- package/dist/graph-templates/loops/backend/shutdown.js +16 -0
- package/dist/graph-templates/loops/backend/terminal.js +142 -0
- package/dist/graph-templates/loops/backend/write.js +18 -0
- package/dist/graph-templates/loops/visualization/list.js +18 -0
- package/dist/graph-templates/loops/visualization/read.js +120 -0
- package/dist/graph-templates/receiver.js +85 -0
- package/dist/graph-templates/utils/fileList.js +96 -0
- package/dist/graph-templates/utils/ignore.js +52 -0
- package/dist/graph-templates/utils/parsers.js +720 -0
- package/dist/index.js +257 -0
- package/dist/payload-bonzai/config.json +31 -0
- package/package.json +40 -0
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const { babelParser } = require('../config');
|
|
3
|
+
|
|
4
|
+
// Extract functions, classes, and methods from a Python file
|
|
5
|
+
function extractPythonFunctions(filePath) {
|
|
6
|
+
try {
|
|
7
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
8
|
+
const lines = content.split('\n');
|
|
9
|
+
const functions = [];
|
|
10
|
+
const classes = [];
|
|
11
|
+
let currentFunction = null;
|
|
12
|
+
let currentClass = null;
|
|
13
|
+
let decorators = [];
|
|
14
|
+
let classIndent = -1;
|
|
15
|
+
|
|
16
|
+
for (let i = 0; i < lines.length; i++) {
|
|
17
|
+
const line = lines[i];
|
|
18
|
+
const trimmed = line.trim();
|
|
19
|
+
|
|
20
|
+
// Calculate indentation level
|
|
21
|
+
const match = line.match(/^\s*/);
|
|
22
|
+
const currentIndent = match ? match[0].length : 0;
|
|
23
|
+
|
|
24
|
+
// Check for decorators (only at top level, before function/class)
|
|
25
|
+
if (trimmed.startsWith('@') && currentIndent === 0) {
|
|
26
|
+
decorators.push(line);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Check if this is a top-level class definition
|
|
31
|
+
const classMatch = trimmed.match(/^class\s+(\w+)/);
|
|
32
|
+
if (classMatch && currentIndent === 0) {
|
|
33
|
+
// Save previous function/class if exists
|
|
34
|
+
if (currentFunction) {
|
|
35
|
+
currentFunction.content = currentFunction.content.trim();
|
|
36
|
+
functions.push(currentFunction);
|
|
37
|
+
currentFunction = null;
|
|
38
|
+
}
|
|
39
|
+
if (currentClass) {
|
|
40
|
+
currentClass.content = currentClass.content.trim();
|
|
41
|
+
classes.push(currentClass);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Start new class
|
|
45
|
+
const className = classMatch[1];
|
|
46
|
+
let classContent = '';
|
|
47
|
+
|
|
48
|
+
// Add decorators if any
|
|
49
|
+
if (decorators.length > 0) {
|
|
50
|
+
classContent = decorators.join('\n') + '\n';
|
|
51
|
+
decorators = [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
classContent += line;
|
|
55
|
+
classIndent = currentIndent;
|
|
56
|
+
|
|
57
|
+
currentClass = {
|
|
58
|
+
name: className,
|
|
59
|
+
content: classContent,
|
|
60
|
+
methods: [],
|
|
61
|
+
startLine: i + 1,
|
|
62
|
+
endLine: i + 1
|
|
63
|
+
};
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Check if this is a method definition (inside a class)
|
|
68
|
+
const methodMatch = trimmed.match(/^def\s+(\w+)\s*\(/);
|
|
69
|
+
if (methodMatch && currentClass && currentIndent > classIndent) {
|
|
70
|
+
// Save previous method if exists
|
|
71
|
+
if (currentFunction) {
|
|
72
|
+
currentFunction.content = currentFunction.content.trim();
|
|
73
|
+
currentClass.methods.push(currentFunction);
|
|
74
|
+
currentFunction = null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Start new method
|
|
78
|
+
const methodName = methodMatch[1];
|
|
79
|
+
let methodContent = '';
|
|
80
|
+
|
|
81
|
+
// Add decorators if any
|
|
82
|
+
if (decorators.length > 0) {
|
|
83
|
+
methodContent = decorators.join('\n') + '\n';
|
|
84
|
+
decorators = [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
methodContent += line;
|
|
88
|
+
|
|
89
|
+
currentFunction = {
|
|
90
|
+
name: currentClass.name + '.' + methodName,
|
|
91
|
+
content: methodContent,
|
|
92
|
+
startLine: i + 1,
|
|
93
|
+
endLine: i + 1,
|
|
94
|
+
isMethod: true,
|
|
95
|
+
className: currentClass.name,
|
|
96
|
+
methodName: methodName
|
|
97
|
+
};
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check if this is a top-level function definition
|
|
102
|
+
const funcMatch = trimmed.match(/^def\s+(\w+)\s*\(/);
|
|
103
|
+
|
|
104
|
+
if (funcMatch && currentIndent === 0) {
|
|
105
|
+
// Save previous function/class if exists
|
|
106
|
+
if (currentFunction) {
|
|
107
|
+
currentFunction.content = currentFunction.content.trim();
|
|
108
|
+
if (currentFunction.isMethod && currentClass) {
|
|
109
|
+
currentClass.methods.push(currentFunction);
|
|
110
|
+
} else {
|
|
111
|
+
functions.push(currentFunction);
|
|
112
|
+
}
|
|
113
|
+
currentFunction = null;
|
|
114
|
+
}
|
|
115
|
+
if (currentClass) {
|
|
116
|
+
currentClass.content = currentClass.content.trim();
|
|
117
|
+
classes.push(currentClass);
|
|
118
|
+
currentClass = null;
|
|
119
|
+
classIndent = -1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Start new function
|
|
123
|
+
const functionName = funcMatch[1];
|
|
124
|
+
let functionContent = '';
|
|
125
|
+
|
|
126
|
+
// Add decorators if any
|
|
127
|
+
if (decorators.length > 0) {
|
|
128
|
+
functionContent = decorators.join('\n') + '\n';
|
|
129
|
+
decorators = [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
functionContent += line;
|
|
133
|
+
|
|
134
|
+
currentFunction = {
|
|
135
|
+
name: functionName,
|
|
136
|
+
content: functionContent,
|
|
137
|
+
startLine: i + 1,
|
|
138
|
+
endLine: i + 1
|
|
139
|
+
};
|
|
140
|
+
} else if (currentFunction || currentClass) {
|
|
141
|
+
// We're processing lines after a function/class definition
|
|
142
|
+
if (currentIndent === 0 && trimmed && !trimmed.startsWith('#')) {
|
|
143
|
+
// Back to top level with non-comment content - function/class ended
|
|
144
|
+
if (currentFunction) {
|
|
145
|
+
currentFunction.content = currentFunction.content.trim();
|
|
146
|
+
if (currentFunction.isMethod && currentClass) {
|
|
147
|
+
currentClass.methods.push(currentFunction);
|
|
148
|
+
} else {
|
|
149
|
+
functions.push(currentFunction);
|
|
150
|
+
}
|
|
151
|
+
currentFunction = null;
|
|
152
|
+
}
|
|
153
|
+
if (currentClass) {
|
|
154
|
+
currentClass.content = currentClass.content.trim();
|
|
155
|
+
classes.push(currentClass);
|
|
156
|
+
currentClass = null;
|
|
157
|
+
classIndent = -1;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check if this line starts a new function/class
|
|
161
|
+
if (funcMatch) {
|
|
162
|
+
const functionName = funcMatch[1];
|
|
163
|
+
let functionContent = '';
|
|
164
|
+
|
|
165
|
+
if (decorators.length > 0) {
|
|
166
|
+
functionContent = decorators.join('\n') + '\n';
|
|
167
|
+
decorators = [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
functionContent += line;
|
|
171
|
+
|
|
172
|
+
currentFunction = {
|
|
173
|
+
name: functionName,
|
|
174
|
+
content: functionContent,
|
|
175
|
+
startLine: i + 1,
|
|
176
|
+
endLine: i + 1
|
|
177
|
+
};
|
|
178
|
+
} else if (classMatch) {
|
|
179
|
+
const className = classMatch[1];
|
|
180
|
+
let classContent = '';
|
|
181
|
+
|
|
182
|
+
if (decorators.length > 0) {
|
|
183
|
+
classContent = decorators.join('\n') + '\n';
|
|
184
|
+
decorators = [];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
classContent += line;
|
|
188
|
+
classIndent = currentIndent;
|
|
189
|
+
|
|
190
|
+
currentClass = {
|
|
191
|
+
name: className,
|
|
192
|
+
content: classContent,
|
|
193
|
+
methods: [],
|
|
194
|
+
startLine: i + 1,
|
|
195
|
+
endLine: i + 1
|
|
196
|
+
};
|
|
197
|
+
} else if (trimmed.startsWith('@')) {
|
|
198
|
+
decorators.push(line);
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
// Still inside function/class (indented or empty/comment line)
|
|
202
|
+
if (currentFunction) {
|
|
203
|
+
currentFunction.content += '\n' + line;
|
|
204
|
+
currentFunction.endLine = i + 1;
|
|
205
|
+
}
|
|
206
|
+
if (currentClass) {
|
|
207
|
+
currentClass.content += '\n' + line;
|
|
208
|
+
currentClass.endLine = i + 1;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Don't forget the last function/class
|
|
215
|
+
if (currentFunction) {
|
|
216
|
+
currentFunction.content = currentFunction.content.trim();
|
|
217
|
+
if (currentFunction.isMethod && currentClass) {
|
|
218
|
+
currentClass.methods.push(currentFunction);
|
|
219
|
+
} else {
|
|
220
|
+
functions.push(currentFunction);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (currentClass) {
|
|
224
|
+
currentClass.content = currentClass.content.trim();
|
|
225
|
+
classes.push(currentClass);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { functions, classes };
|
|
229
|
+
} catch (e) {
|
|
230
|
+
// If parsing fails (invalid Python, etc.), return empty arrays
|
|
231
|
+
console.warn('Failed to parse Python file:', filePath, e.message);
|
|
232
|
+
return { functions: [], classes: [] };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Extract functions, classes, and methods from a JavaScript/TypeScript file
|
|
237
|
+
function extractJavaScriptFunctions(filePath) {
|
|
238
|
+
try {
|
|
239
|
+
if (!babelParser) {
|
|
240
|
+
return { functions: [], classes: [] };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Skip .d.ts files, minified files, and node_modules
|
|
244
|
+
if (filePath.endsWith('.d.ts') || filePath.endsWith('.min.js') || filePath.includes('node_modules')) {
|
|
245
|
+
return { functions: [], classes: [] };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
249
|
+
const functions = [];
|
|
250
|
+
const classes = [];
|
|
251
|
+
|
|
252
|
+
// Determine if it's TypeScript
|
|
253
|
+
const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const ast = babelParser.parse(content, {
|
|
257
|
+
sourceType: 'module',
|
|
258
|
+
plugins: [
|
|
259
|
+
'typescript',
|
|
260
|
+
'jsx',
|
|
261
|
+
'decorators-legacy',
|
|
262
|
+
'classProperties',
|
|
263
|
+
'objectRestSpread',
|
|
264
|
+
'asyncGenerators',
|
|
265
|
+
'functionBind',
|
|
266
|
+
'exportDefaultFrom',
|
|
267
|
+
'exportNamespaceFrom',
|
|
268
|
+
'dynamicImport',
|
|
269
|
+
'nullishCoalescingOperator',
|
|
270
|
+
'optionalChaining'
|
|
271
|
+
]
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Helper to extract code snippet from source
|
|
275
|
+
const getCode = (node) => {
|
|
276
|
+
return content.substring(node.start, node.end);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Track visited nodes to avoid duplicates
|
|
280
|
+
const visitedNodes = new Set();
|
|
281
|
+
|
|
282
|
+
// Traverse AST
|
|
283
|
+
function traverse(node, parentType = null) {
|
|
284
|
+
if (!node) return;
|
|
285
|
+
|
|
286
|
+
// Skip if already visited (avoid processing same node twice)
|
|
287
|
+
if (visitedNodes.has(node)) return;
|
|
288
|
+
visitedNodes.add(node);
|
|
289
|
+
|
|
290
|
+
// Function declarations: function myFunc() {}
|
|
291
|
+
// Skip if inside ExportNamedDeclaration (will be handled below)
|
|
292
|
+
if (node.type === 'FunctionDeclaration' && node.id && parentType !== 'ExportNamedDeclaration') {
|
|
293
|
+
functions.push({
|
|
294
|
+
name: node.id.name,
|
|
295
|
+
content: getCode(node),
|
|
296
|
+
startLine: node.loc ? node.loc.start.line : 0,
|
|
297
|
+
endLine: node.loc ? node.loc.end.line : 0
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Arrow functions: const myFunc = () => {}
|
|
302
|
+
if (node.type === 'VariableDeclarator' &&
|
|
303
|
+
node.init &&
|
|
304
|
+
(node.init.type === 'ArrowFunctionExpression' || node.init.type === 'FunctionExpression') &&
|
|
305
|
+
node.id && node.id.type === 'Identifier') {
|
|
306
|
+
const funcContent = getCode(node);
|
|
307
|
+
functions.push({
|
|
308
|
+
name: node.id.name,
|
|
309
|
+
content: funcContent,
|
|
310
|
+
startLine: node.loc ? node.loc.start.line : 0,
|
|
311
|
+
endLine: node.loc ? node.loc.end.line : 0
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Helper function to extract methods from a class body
|
|
316
|
+
const extractClassMethods = (classNode, className) => {
|
|
317
|
+
const methods = [];
|
|
318
|
+
if (classNode.body && classNode.body.body && Array.isArray(classNode.body.body)) {
|
|
319
|
+
for (const member of classNode.body.body) {
|
|
320
|
+
// Handle MethodDefinition (regular methods, constructors, getters, setters, static methods)
|
|
321
|
+
if (member && member.type === 'MethodDefinition' && member.key) {
|
|
322
|
+
let methodName;
|
|
323
|
+
if (member.key.type === 'Identifier') {
|
|
324
|
+
methodName = member.key.name;
|
|
325
|
+
} else if (member.key.type === 'PrivateName') {
|
|
326
|
+
methodName = '#' + member.key.id.name;
|
|
327
|
+
} else if (member.key.type === 'StringLiteral' || member.key.type === 'NumericLiteral') {
|
|
328
|
+
methodName = String(member.key.value);
|
|
329
|
+
} else {
|
|
330
|
+
methodName = String(member.key.value || member.key.name || 'unknown');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Include kind (constructor, get, set, method) in the name for clarity
|
|
334
|
+
const kind = member.kind || 'method';
|
|
335
|
+
const isStatic = member.static || false;
|
|
336
|
+
|
|
337
|
+
// For getters and setters, include the kind in the method name to distinguish them
|
|
338
|
+
// e.g., "value" getter vs "value" setter -> "get value" and "set value"
|
|
339
|
+
let fullMethodName = methodName;
|
|
340
|
+
if (kind === 'get') {
|
|
341
|
+
fullMethodName = 'get ' + methodName;
|
|
342
|
+
} else if (kind === 'set') {
|
|
343
|
+
fullMethodName = 'set ' + methodName;
|
|
344
|
+
} else if (kind === 'constructor') {
|
|
345
|
+
fullMethodName = 'constructor';
|
|
346
|
+
} else if (isStatic) {
|
|
347
|
+
fullMethodName = 'static ' + methodName;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
methods.push({
|
|
351
|
+
name: className + '.' + methodName,
|
|
352
|
+
content: getCode(member),
|
|
353
|
+
startLine: member.loc ? member.loc.start.line : 0,
|
|
354
|
+
endLine: member.loc ? member.loc.end.line : 0,
|
|
355
|
+
isMethod: true,
|
|
356
|
+
className: className,
|
|
357
|
+
methodName: methodName,
|
|
358
|
+
kind: kind,
|
|
359
|
+
static: isStatic
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return methods;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
// Class declarations: class User { ... }
|
|
368
|
+
// Skip if inside ExportNamedDeclaration or ExportDefaultDeclaration (will be handled below)
|
|
369
|
+
if (node.type === 'ClassDeclaration' && node.id &&
|
|
370
|
+
parentType !== 'ExportNamedDeclaration' && parentType !== 'ExportDefaultDeclaration') {
|
|
371
|
+
const className = node.id.name;
|
|
372
|
+
const methods = extractClassMethods(node, className);
|
|
373
|
+
|
|
374
|
+
classes.push({
|
|
375
|
+
name: className,
|
|
376
|
+
content: getCode(node),
|
|
377
|
+
methods: methods,
|
|
378
|
+
startLine: node.loc ? node.loc.start.line : 0,
|
|
379
|
+
endLine: node.loc ? node.loc.end.line : 0
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Export declarations: export function, export class
|
|
384
|
+
if (node.type === 'ExportNamedDeclaration' && node.declaration) {
|
|
385
|
+
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
386
|
+
functions.push({
|
|
387
|
+
name: node.declaration.id.name,
|
|
388
|
+
content: getCode(node.declaration),
|
|
389
|
+
startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
|
|
390
|
+
endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
|
|
391
|
+
isExported: true
|
|
392
|
+
});
|
|
393
|
+
// Mark as visited to avoid duplicate processing
|
|
394
|
+
visitedNodes.add(node.declaration);
|
|
395
|
+
} else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
|
396
|
+
const className = node.declaration.id.name;
|
|
397
|
+
const methods = extractClassMethods(node.declaration, className);
|
|
398
|
+
|
|
399
|
+
classes.push({
|
|
400
|
+
name: className,
|
|
401
|
+
content: getCode(node.declaration),
|
|
402
|
+
methods: methods,
|
|
403
|
+
startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
|
|
404
|
+
endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
|
|
405
|
+
isExported: true
|
|
406
|
+
});
|
|
407
|
+
// Mark as visited to avoid duplicate processing
|
|
408
|
+
visitedNodes.add(node.declaration);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Export default declarations: export default class
|
|
413
|
+
if (node.type === 'ExportDefaultDeclaration' && node.declaration) {
|
|
414
|
+
if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
|
415
|
+
const className = node.declaration.id.name;
|
|
416
|
+
const methods = extractClassMethods(node.declaration, className);
|
|
417
|
+
|
|
418
|
+
classes.push({
|
|
419
|
+
name: className,
|
|
420
|
+
content: getCode(node.declaration),
|
|
421
|
+
methods: methods,
|
|
422
|
+
startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
|
|
423
|
+
endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
|
|
424
|
+
isExported: true,
|
|
425
|
+
isDefaultExport: true
|
|
426
|
+
});
|
|
427
|
+
// Mark as visited to avoid duplicate processing
|
|
428
|
+
visitedNodes.add(node.declaration);
|
|
429
|
+
} else if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
430
|
+
functions.push({
|
|
431
|
+
name: node.declaration.id.name,
|
|
432
|
+
content: getCode(node.declaration),
|
|
433
|
+
startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
|
|
434
|
+
endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
|
|
435
|
+
isExported: true,
|
|
436
|
+
isDefaultExport: true
|
|
437
|
+
});
|
|
438
|
+
visitedNodes.add(node.declaration);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Recursively traverse children
|
|
443
|
+
for (const key in node) {
|
|
444
|
+
if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
|
|
445
|
+
const child = node[key];
|
|
446
|
+
if (Array.isArray(child)) {
|
|
447
|
+
child.forEach(c => traverse(c, node.type));
|
|
448
|
+
} else if (child && typeof child === 'object' && child.type) {
|
|
449
|
+
traverse(child, node.type);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
traverse(ast);
|
|
455
|
+
} catch (parseError) {
|
|
456
|
+
// Silently skip parsing errors - these are expected for some files
|
|
457
|
+
// Only log if it's not in node_modules or a known problematic file type
|
|
458
|
+
if (!filePath.includes('node_modules') && !filePath.endsWith('.d.ts') && !filePath.endsWith('.min.js')) {
|
|
459
|
+
// Suppress warnings for common parsing issues
|
|
460
|
+
const errorMsg = parseError.message || '';
|
|
461
|
+
if (!errorMsg.includes('outside of function') &&
|
|
462
|
+
!errorMsg.includes('Missing initializer') &&
|
|
463
|
+
!errorMsg.includes('Export') &&
|
|
464
|
+
!errorMsg.includes('Unexpected token')) {
|
|
465
|
+
// Only log unexpected errors
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return { functions: [], classes: [] };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return { functions, classes };
|
|
472
|
+
} catch (e) {
|
|
473
|
+
console.warn('Failed to read JavaScript/TypeScript file:', filePath, e.message);
|
|
474
|
+
return { functions: [], classes: [] };
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Extract script content from Vue file and parse it
|
|
479
|
+
function extractVueFunctions(filePath) {
|
|
480
|
+
try {
|
|
481
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
482
|
+
|
|
483
|
+
// Extract <script> section from Vue file
|
|
484
|
+
const scriptMatch = content.match(/<script[^>]*>([\s\S]*?)<\/script>/);
|
|
485
|
+
if (!scriptMatch) {
|
|
486
|
+
return { functions: [], classes: [] };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const scriptContent = scriptMatch[1];
|
|
490
|
+
|
|
491
|
+
// Create a temporary file path for parsing (just for reference)
|
|
492
|
+
// Parse the script content as JavaScript/TypeScript
|
|
493
|
+
if (!babelParser) {
|
|
494
|
+
return { functions: [], classes: [] };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const functions = [];
|
|
498
|
+
const classes = [];
|
|
499
|
+
|
|
500
|
+
// Check if it's TypeScript
|
|
501
|
+
const isTypeScript = scriptMatch[0].includes('lang="ts"') || scriptMatch[0].includes("lang='ts'");
|
|
502
|
+
|
|
503
|
+
try {
|
|
504
|
+
const ast = babelParser.parse(scriptContent, {
|
|
505
|
+
sourceType: 'module',
|
|
506
|
+
plugins: [
|
|
507
|
+
'typescript',
|
|
508
|
+
'jsx',
|
|
509
|
+
'decorators-legacy',
|
|
510
|
+
'classProperties',
|
|
511
|
+
'objectRestSpread',
|
|
512
|
+
'asyncGenerators',
|
|
513
|
+
'functionBind',
|
|
514
|
+
'exportDefaultFrom',
|
|
515
|
+
'exportNamespaceFrom',
|
|
516
|
+
'dynamicImport',
|
|
517
|
+
'nullishCoalescingOperator',
|
|
518
|
+
'optionalChaining'
|
|
519
|
+
]
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
// Helper to extract code snippet from source
|
|
523
|
+
const getCode = (node) => {
|
|
524
|
+
return scriptContent.substring(node.start, node.end);
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
// Track visited nodes to avoid duplicates
|
|
528
|
+
const visitedNodes = new Set();
|
|
529
|
+
|
|
530
|
+
// Traverse AST (same logic as JavaScript parser)
|
|
531
|
+
function traverse(node, parentType = null) {
|
|
532
|
+
if (!node) return;
|
|
533
|
+
|
|
534
|
+
// Skip if already visited (avoid processing same node twice)
|
|
535
|
+
if (visitedNodes.has(node)) return;
|
|
536
|
+
visitedNodes.add(node);
|
|
537
|
+
|
|
538
|
+
// Function declarations: function myFunc() {}
|
|
539
|
+
// Skip if inside ExportNamedDeclaration (will be handled below)
|
|
540
|
+
if (node.type === 'FunctionDeclaration' && node.id && parentType !== 'ExportNamedDeclaration') {
|
|
541
|
+
functions.push({
|
|
542
|
+
name: node.id.name,
|
|
543
|
+
content: getCode(node),
|
|
544
|
+
startLine: node.loc ? node.loc.start.line : 0,
|
|
545
|
+
endLine: node.loc ? node.loc.end.line : 0
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Arrow functions: const myFunc = () => {}
|
|
550
|
+
if (node.type === 'VariableDeclarator' &&
|
|
551
|
+
node.init &&
|
|
552
|
+
(node.init.type === 'ArrowFunctionExpression' || node.init.type === 'FunctionExpression') &&
|
|
553
|
+
node.id && node.id.type === 'Identifier') {
|
|
554
|
+
const funcContent = getCode(node);
|
|
555
|
+
functions.push({
|
|
556
|
+
name: node.id.name,
|
|
557
|
+
content: funcContent,
|
|
558
|
+
startLine: node.loc ? node.loc.start.line : 0,
|
|
559
|
+
endLine: node.loc ? node.loc.end.line : 0
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Helper function to extract methods from a class body
|
|
564
|
+
const extractClassMethods = (classNode, className) => {
|
|
565
|
+
const methods = [];
|
|
566
|
+
if (classNode.body && classNode.body.body && Array.isArray(classNode.body.body)) {
|
|
567
|
+
for (const member of classNode.body.body) {
|
|
568
|
+
// Handle MethodDefinition (regular methods, constructors, getters, setters, static methods)
|
|
569
|
+
if (member && member.type === 'MethodDefinition' && member.key) {
|
|
570
|
+
let methodName;
|
|
571
|
+
if (member.key.type === 'Identifier') {
|
|
572
|
+
methodName = member.key.name;
|
|
573
|
+
} else if (member.key.type === 'PrivateName') {
|
|
574
|
+
methodName = '#' + member.key.id.name;
|
|
575
|
+
} else if (member.key.type === 'StringLiteral' || member.key.type === 'NumericLiteral') {
|
|
576
|
+
methodName = String(member.key.value);
|
|
577
|
+
} else {
|
|
578
|
+
methodName = String(member.key.value || member.key.name || 'unknown');
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Include kind (constructor, get, set, method) in the name for clarity
|
|
582
|
+
const kind = member.kind || 'method';
|
|
583
|
+
const isStatic = member.static || false;
|
|
584
|
+
|
|
585
|
+
// For getters and setters, include the kind in the method name to distinguish them
|
|
586
|
+
// e.g., "value" getter vs "value" setter -> "get value" and "set value"
|
|
587
|
+
let fullMethodName = methodName;
|
|
588
|
+
if (kind === 'get') {
|
|
589
|
+
fullMethodName = 'get ' + methodName;
|
|
590
|
+
} else if (kind === 'set') {
|
|
591
|
+
fullMethodName = 'set ' + methodName;
|
|
592
|
+
} else if (kind === 'constructor') {
|
|
593
|
+
fullMethodName = 'constructor';
|
|
594
|
+
} else if (isStatic) {
|
|
595
|
+
fullMethodName = 'static ' + methodName;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
methods.push({
|
|
599
|
+
name: className + '.' + methodName,
|
|
600
|
+
content: getCode(member),
|
|
601
|
+
startLine: member.loc ? member.loc.start.line : 0,
|
|
602
|
+
endLine: member.loc ? member.loc.end.line : 0,
|
|
603
|
+
isMethod: true,
|
|
604
|
+
className: className,
|
|
605
|
+
methodName: methodName,
|
|
606
|
+
kind: kind,
|
|
607
|
+
static: isStatic
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return methods;
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// Class declarations: class User { ... }
|
|
616
|
+
// Skip if inside ExportNamedDeclaration or ExportDefaultDeclaration (will be handled below)
|
|
617
|
+
if (node.type === 'ClassDeclaration' && node.id &&
|
|
618
|
+
parentType !== 'ExportNamedDeclaration' && parentType !== 'ExportDefaultDeclaration') {
|
|
619
|
+
const className = node.id.name;
|
|
620
|
+
const methods = extractClassMethods(node, className);
|
|
621
|
+
|
|
622
|
+
classes.push({
|
|
623
|
+
name: className,
|
|
624
|
+
content: getCode(node),
|
|
625
|
+
methods: methods,
|
|
626
|
+
startLine: node.loc ? node.loc.start.line : 0,
|
|
627
|
+
endLine: node.loc ? node.loc.end.line : 0
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// Export declarations: export function, export class
|
|
632
|
+
if (node.type === 'ExportNamedDeclaration' && node.declaration) {
|
|
633
|
+
if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
634
|
+
functions.push({
|
|
635
|
+
name: node.declaration.id.name,
|
|
636
|
+
content: getCode(node.declaration),
|
|
637
|
+
startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
|
|
638
|
+
endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
|
|
639
|
+
isExported: true
|
|
640
|
+
});
|
|
641
|
+
// Mark as visited to avoid duplicate processing
|
|
642
|
+
visitedNodes.add(node.declaration);
|
|
643
|
+
} else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
|
644
|
+
const className = node.declaration.id.name;
|
|
645
|
+
const methods = extractClassMethods(node.declaration, className);
|
|
646
|
+
|
|
647
|
+
classes.push({
|
|
648
|
+
name: className,
|
|
649
|
+
content: getCode(node.declaration),
|
|
650
|
+
methods: methods,
|
|
651
|
+
startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
|
|
652
|
+
endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
|
|
653
|
+
isExported: true
|
|
654
|
+
});
|
|
655
|
+
// Mark as visited to avoid duplicate processing
|
|
656
|
+
visitedNodes.add(node.declaration);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Export default declarations: export default class
|
|
661
|
+
if (node.type === 'ExportDefaultDeclaration' && node.declaration) {
|
|
662
|
+
if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
|
|
663
|
+
const className = node.declaration.id.name;
|
|
664
|
+
const methods = extractClassMethods(node.declaration, className);
|
|
665
|
+
|
|
666
|
+
classes.push({
|
|
667
|
+
name: className,
|
|
668
|
+
content: getCode(node.declaration),
|
|
669
|
+
methods: methods,
|
|
670
|
+
startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
|
|
671
|
+
endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
|
|
672
|
+
isExported: true,
|
|
673
|
+
isDefaultExport: true
|
|
674
|
+
});
|
|
675
|
+
// Mark as visited to avoid duplicate processing
|
|
676
|
+
visitedNodes.add(node.declaration);
|
|
677
|
+
} else if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
|
|
678
|
+
functions.push({
|
|
679
|
+
name: node.declaration.id.name,
|
|
680
|
+
content: getCode(node.declaration),
|
|
681
|
+
startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
|
|
682
|
+
endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
|
|
683
|
+
isExported: true,
|
|
684
|
+
isDefaultExport: true
|
|
685
|
+
});
|
|
686
|
+
visitedNodes.add(node.declaration);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Recursively traverse children
|
|
691
|
+
for (const key in node) {
|
|
692
|
+
if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
|
|
693
|
+
const child = node[key];
|
|
694
|
+
if (Array.isArray(child)) {
|
|
695
|
+
child.forEach(c => traverse(c, node.type));
|
|
696
|
+
} else if (child && typeof child === 'object' && child.type) {
|
|
697
|
+
traverse(child, node.type);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
traverse(ast);
|
|
703
|
+
} catch (parseError) {
|
|
704
|
+
// Silently skip parsing errors for Vue files
|
|
705
|
+
return { functions: [], classes: [] };
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return { functions, classes };
|
|
709
|
+
} catch (e) {
|
|
710
|
+
console.warn('Failed to read Vue file:', filePath, e.message);
|
|
711
|
+
return { functions: [], classes: [] };
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
module.exports = {
|
|
716
|
+
extractPythonFunctions,
|
|
717
|
+
extractJavaScriptFunctions,
|
|
718
|
+
extractVueFunctions
|
|
719
|
+
};
|
|
720
|
+
|