devbonzai 1.6.8 → 1.6.9

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.
Files changed (2) hide show
  1. package/cli.js +597 -35
  2. package/package.json +3 -2
package/cli.js CHANGED
@@ -65,6 +65,12 @@ const cors = require('./node_modules/cors');
65
65
  const fs = require('fs');
66
66
  const path = require('path');
67
67
  const { exec, spawn } = require('child_process');
68
+ let babelParser = null;
69
+ try {
70
+ babelParser = require('./node_modules/@babel/parser');
71
+ } catch (e) {
72
+ // Babel parser not available, will fall back gracefully
73
+ }
68
74
 
69
75
  const app = express();
70
76
  const ROOT = path.join(__dirname, '..');
@@ -137,14 +143,17 @@ function shouldIgnore(relativePath, ignorePatterns) {
137
143
  return ignorePatterns.some(pattern => pattern.test(relativePath));
138
144
  }
139
145
 
140
- // Extract top-level functions from a Python file
146
+ // Extract functions, classes, and methods from a Python file
141
147
  function extractPythonFunctions(filePath) {
142
148
  try {
143
149
  const content = fs.readFileSync(filePath, 'utf8');
144
150
  const lines = content.split('\\n');
145
151
  const functions = [];
152
+ const classes = [];
146
153
  let currentFunction = null;
154
+ let currentClass = null;
147
155
  let decorators = [];
156
+ let classIndent = -1;
148
157
 
149
158
  for (let i = 0; i < lines.length; i++) {
150
159
  const line = lines[i];
@@ -154,20 +163,102 @@ function extractPythonFunctions(filePath) {
154
163
  const match = line.match(/^\\s*/);
155
164
  const currentIndent = match ? match[0].length : 0;
156
165
 
157
- // Check for decorators (only at top level, before function)
166
+ // Check for decorators (only at top level, before function/class)
158
167
  if (trimmed.startsWith('@') && currentIndent === 0) {
159
168
  decorators.push(line);
160
169
  continue;
161
170
  }
162
171
 
172
+ // Check if this is a top-level class definition
173
+ const classMatch = trimmed.match(/^class\\s+(\\w+)/);
174
+ if (classMatch && currentIndent === 0) {
175
+ // Save previous function/class if exists
176
+ if (currentFunction) {
177
+ currentFunction.content = currentFunction.content.trim();
178
+ functions.push(currentFunction);
179
+ currentFunction = null;
180
+ }
181
+ if (currentClass) {
182
+ currentClass.content = currentClass.content.trim();
183
+ classes.push(currentClass);
184
+ }
185
+
186
+ // Start new class
187
+ const className = classMatch[1];
188
+ let classContent = '';
189
+
190
+ // Add decorators if any
191
+ if (decorators.length > 0) {
192
+ classContent = decorators.join('\\n') + '\\n';
193
+ decorators = [];
194
+ }
195
+
196
+ classContent += line;
197
+ classIndent = currentIndent;
198
+
199
+ currentClass = {
200
+ name: className,
201
+ content: classContent,
202
+ methods: [],
203
+ startLine: i + 1,
204
+ endLine: i + 1
205
+ };
206
+ continue;
207
+ }
208
+
209
+ // Check if this is a method definition (inside a class)
210
+ const methodMatch = trimmed.match(/^def\\s+(\\w+)\\s*\\(/);
211
+ if (methodMatch && currentClass && currentIndent > classIndent) {
212
+ // Save previous method if exists
213
+ if (currentFunction) {
214
+ currentFunction.content = currentFunction.content.trim();
215
+ currentClass.methods.push(currentFunction);
216
+ currentFunction = null;
217
+ }
218
+
219
+ // Start new method
220
+ const methodName = methodMatch[1];
221
+ let methodContent = '';
222
+
223
+ // Add decorators if any
224
+ if (decorators.length > 0) {
225
+ methodContent = decorators.join('\\n') + '\\n';
226
+ decorators = [];
227
+ }
228
+
229
+ methodContent += line;
230
+
231
+ currentFunction = {
232
+ name: currentClass.name + '.' + methodName,
233
+ content: methodContent,
234
+ startLine: i + 1,
235
+ endLine: i + 1,
236
+ isMethod: true,
237
+ className: currentClass.name,
238
+ methodName: methodName
239
+ };
240
+ continue;
241
+ }
242
+
163
243
  // Check if this is a top-level function definition
164
244
  const funcMatch = trimmed.match(/^def\\s+(\\w+)\\s*\\(/);
165
245
 
166
246
  if (funcMatch && currentIndent === 0) {
167
- // Save previous function if exists
247
+ // Save previous function/class if exists
168
248
  if (currentFunction) {
169
249
  currentFunction.content = currentFunction.content.trim();
250
+ if (currentFunction.isMethod && currentClass) {
251
+ currentClass.methods.push(currentFunction);
252
+ } else {
170
253
  functions.push(currentFunction);
254
+ }
255
+ currentFunction = null;
256
+ }
257
+ if (currentClass) {
258
+ currentClass.content = currentClass.content.trim();
259
+ classes.push(currentClass);
260
+ currentClass = null;
261
+ classIndent = -1;
171
262
  }
172
263
 
173
264
  // Start new function
@@ -188,15 +279,27 @@ function extractPythonFunctions(filePath) {
188
279
  startLine: i + 1,
189
280
  endLine: i + 1
190
281
  };
191
- } else if (currentFunction) {
192
- // We're processing lines after a function definition
282
+ } else if (currentFunction || currentClass) {
283
+ // We're processing lines after a function/class definition
193
284
  if (currentIndent === 0 && trimmed && !trimmed.startsWith('#')) {
194
- // Back to top level with non-comment content - function ended
285
+ // Back to top level with non-comment content - function/class ended
286
+ if (currentFunction) {
195
287
  currentFunction.content = currentFunction.content.trim();
288
+ if (currentFunction.isMethod && currentClass) {
289
+ currentClass.methods.push(currentFunction);
290
+ } else {
196
291
  functions.push(currentFunction);
292
+ }
197
293
  currentFunction = null;
294
+ }
295
+ if (currentClass) {
296
+ currentClass.content = currentClass.content.trim();
297
+ classes.push(currentClass);
298
+ currentClass = null;
299
+ classIndent = -1;
300
+ }
198
301
 
199
- // Check if this line starts a new function
302
+ // Check if this line starts a new function/class
200
303
  if (funcMatch) {
201
304
  const functionName = funcMatch[1];
202
305
  let functionContent = '';
@@ -214,28 +317,405 @@ function extractPythonFunctions(filePath) {
214
317
  startLine: i + 1,
215
318
  endLine: i + 1
216
319
  };
320
+ } else if (classMatch) {
321
+ const className = classMatch[1];
322
+ let classContent = '';
323
+
324
+ if (decorators.length > 0) {
325
+ classContent = decorators.join('\\n') + '\\n';
326
+ decorators = [];
327
+ }
328
+
329
+ classContent += line;
330
+ classIndent = currentIndent;
331
+
332
+ currentClass = {
333
+ name: className,
334
+ content: classContent,
335
+ methods: [],
336
+ startLine: i + 1,
337
+ endLine: i + 1
338
+ };
217
339
  } else if (trimmed.startsWith('@')) {
218
340
  decorators.push(line);
219
341
  }
220
342
  } else {
221
- // Still inside function (indented or empty/comment line)
343
+ // Still inside function/class (indented or empty/comment line)
344
+ if (currentFunction) {
222
345
  currentFunction.content += '\\n' + line;
223
346
  currentFunction.endLine = i + 1;
347
+ }
348
+ if (currentClass) {
349
+ currentClass.content += '\\n' + line;
350
+ currentClass.endLine = i + 1;
351
+ }
224
352
  }
225
353
  }
226
354
  }
227
355
 
228
- // Don't forget the last function
356
+ // Don't forget the last function/class
229
357
  if (currentFunction) {
230
358
  currentFunction.content = currentFunction.content.trim();
359
+ if (currentFunction.isMethod && currentClass) {
360
+ currentClass.methods.push(currentFunction);
361
+ } else {
231
362
  functions.push(currentFunction);
363
+ }
364
+ }
365
+ if (currentClass) {
366
+ currentClass.content = currentClass.content.trim();
367
+ classes.push(currentClass);
232
368
  }
233
369
 
234
- return functions;
370
+ return { functions, classes };
235
371
  } catch (e) {
236
- // If parsing fails (invalid Python, etc.), return empty array
372
+ // If parsing fails (invalid Python, etc.), return empty arrays
237
373
  console.warn('Failed to parse Python file:', filePath, e.message);
238
- return [];
374
+ return { functions: [], classes: [] };
375
+ }
376
+ }
377
+
378
+ // Extract functions, classes, and methods from a JavaScript/TypeScript file
379
+ function extractJavaScriptFunctions(filePath) {
380
+ try {
381
+ if (!babelParser) {
382
+ return { functions: [], classes: [] };
383
+ }
384
+
385
+ const content = fs.readFileSync(filePath, 'utf8');
386
+ const functions = [];
387
+ const classes = [];
388
+
389
+ // Determine if it's TypeScript
390
+ const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
391
+
392
+ try {
393
+ const ast = babelParser.parse(content, {
394
+ sourceType: 'module',
395
+ plugins: [
396
+ 'typescript',
397
+ 'jsx',
398
+ 'decorators-legacy',
399
+ 'classProperties',
400
+ 'objectRestSpread',
401
+ 'asyncGenerators',
402
+ 'functionBind',
403
+ 'exportDefaultFrom',
404
+ 'exportNamespaceFrom',
405
+ 'dynamicImport',
406
+ 'nullishCoalescingOperator',
407
+ 'optionalChaining'
408
+ ]
409
+ });
410
+
411
+ // Helper to extract code snippet from source
412
+ const getCode = (node) => {
413
+ return content.substring(node.start, node.end);
414
+ };
415
+
416
+ // Traverse AST
417
+ function traverse(node) {
418
+ if (!node) return;
419
+
420
+ // Function declarations: function myFunc() {}
421
+ if (node.type === 'FunctionDeclaration' && node.id) {
422
+ functions.push({
423
+ name: node.id.name,
424
+ content: getCode(node),
425
+ startLine: node.loc ? node.loc.start.line : 0,
426
+ endLine: node.loc ? node.loc.end.line : 0
427
+ });
428
+ }
429
+
430
+ // Arrow functions: const myFunc = () => {}
431
+ if (node.type === 'VariableDeclarator' &&
432
+ node.init &&
433
+ (node.init.type === 'ArrowFunctionExpression' || node.init.type === 'FunctionExpression') &&
434
+ node.id && node.id.type === 'Identifier') {
435
+ const funcNode = node.init;
436
+ const funcContent = getCode(node);
437
+ functions.push({
438
+ name: node.id.name,
439
+ content: funcContent,
440
+ startLine: node.loc ? node.loc.start.line : 0,
441
+ endLine: node.loc ? node.loc.end.line : 0
442
+ });
443
+ }
444
+
445
+ // Class declarations: class User { ... }
446
+ if (node.type === 'ClassDeclaration' && node.id) {
447
+ const className = node.id.name;
448
+ const methods = [];
449
+
450
+ // Extract methods
451
+ if (node.body && node.body.body) {
452
+ for (const member of node.body.body) {
453
+ if (member.type === 'MethodDefinition' && member.key) {
454
+ const methodName = member.key.type === 'Identifier' ? member.key.name :
455
+ member.key.type === 'PrivateName' ? '#' + member.key.id.name :
456
+ String(member.key.value || member.key.name);
457
+ methods.push({
458
+ name: className + '.' + methodName,
459
+ content: getCode(member),
460
+ startLine: member.loc ? member.loc.start.line : 0,
461
+ endLine: member.loc ? member.loc.end.line : 0,
462
+ isMethod: true,
463
+ className: className,
464
+ methodName: methodName
465
+ });
466
+ }
467
+ }
468
+ }
469
+
470
+ classes.push({
471
+ name: className,
472
+ content: getCode(node),
473
+ methods: methods,
474
+ startLine: node.loc ? node.loc.start.line : 0,
475
+ endLine: node.loc ? node.loc.end.line : 0
476
+ });
477
+ }
478
+
479
+ // Export declarations: export function, export default
480
+ if (node.type === 'ExportNamedDeclaration' && node.declaration) {
481
+ if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
482
+ functions.push({
483
+ name: node.declaration.id.name,
484
+ content: getCode(node.declaration),
485
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
486
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
487
+ isExported: true
488
+ });
489
+ } else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
490
+ const className = node.declaration.id.name;
491
+ const methods = [];
492
+
493
+ if (node.declaration.body && node.declaration.body.body) {
494
+ for (const member of node.declaration.body.body) {
495
+ if (member.type === 'MethodDefinition' && member.key) {
496
+ const methodName = member.key.type === 'Identifier' ? member.key.name :
497
+ member.key.type === 'PrivateName' ? '#' + member.key.id.name :
498
+ String(member.key.value || member.key.name);
499
+ methods.push({
500
+ name: className + '.' + methodName,
501
+ content: getCode(member),
502
+ startLine: member.loc ? member.loc.start.line : 0,
503
+ endLine: member.loc ? member.loc.end.line : 0,
504
+ isMethod: true,
505
+ className: className,
506
+ methodName: methodName
507
+ });
508
+ }
509
+ }
510
+ }
511
+
512
+ classes.push({
513
+ name: className,
514
+ content: getCode(node.declaration),
515
+ methods: methods,
516
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
517
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
518
+ isExported: true
519
+ });
520
+ }
521
+ }
522
+
523
+ // Recursively traverse children
524
+ for (const key in node) {
525
+ if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
526
+ const child = node[key];
527
+ if (Array.isArray(child)) {
528
+ child.forEach(traverse);
529
+ } else if (child && typeof child === 'object' && child.type) {
530
+ traverse(child);
531
+ }
532
+ }
533
+ }
534
+
535
+ traverse(ast);
536
+ } catch (parseError) {
537
+ console.warn('Failed to parse JavaScript/TypeScript file:', filePath, parseError.message);
538
+ return { functions: [], classes: [] };
539
+ }
540
+
541
+ return { functions, classes };
542
+ } catch (e) {
543
+ console.warn('Failed to read JavaScript/TypeScript file:', filePath, e.message);
544
+ return { functions: [], classes: [] };
545
+ }
546
+ }
547
+
548
+ // Extract script content from Vue file and parse it
549
+ function extractVueFunctions(filePath) {
550
+ try {
551
+ const content = fs.readFileSync(filePath, 'utf8');
552
+
553
+ // Extract <script> section from Vue file
554
+ const scriptMatch = content.match(/<script[^>]*>([\\s\\S]*?)<\\/script>/);
555
+ if (!scriptMatch) {
556
+ return { functions: [], classes: [] };
557
+ }
558
+
559
+ const scriptContent = scriptMatch[1];
560
+
561
+ // Create a temporary file path for parsing (just for reference)
562
+ // Parse the script content as JavaScript/TypeScript
563
+ if (!babelParser) {
564
+ return { functions: [], classes: [] };
565
+ }
566
+
567
+ const functions = [];
568
+ const classes = [];
569
+
570
+ // Check if it's TypeScript
571
+ const isTypeScript = scriptMatch[0].includes('lang="ts"') || scriptMatch[0].includes("lang='ts'");
572
+
573
+ try {
574
+ const ast = babelParser.parse(scriptContent, {
575
+ sourceType: 'module',
576
+ plugins: [
577
+ 'typescript',
578
+ 'jsx',
579
+ 'decorators-legacy',
580
+ 'classProperties',
581
+ 'objectRestSpread',
582
+ 'asyncGenerators',
583
+ 'functionBind',
584
+ 'exportDefaultFrom',
585
+ 'exportNamespaceFrom',
586
+ 'dynamicImport',
587
+ 'nullishCoalescingOperator',
588
+ 'optionalChaining'
589
+ ]
590
+ });
591
+
592
+ // Helper to extract code snippet from source
593
+ const getCode = (node) => {
594
+ return scriptContent.substring(node.start, node.end);
595
+ };
596
+
597
+ // Traverse AST (same logic as JavaScript parser)
598
+ function traverse(node) {
599
+ if (!node) return;
600
+
601
+ if (node.type === 'FunctionDeclaration' && node.id) {
602
+ functions.push({
603
+ name: node.id.name,
604
+ content: getCode(node),
605
+ startLine: node.loc ? node.loc.start.line : 0,
606
+ endLine: node.loc ? node.loc.end.line : 0
607
+ });
608
+ }
609
+
610
+ if (node.type === 'VariableDeclarator' &&
611
+ node.init &&
612
+ (node.init.type === 'ArrowFunctionExpression' || node.init.type === 'FunctionExpression') &&
613
+ node.id && node.id.type === 'Identifier') {
614
+ const funcContent = getCode(node);
615
+ functions.push({
616
+ name: node.id.name,
617
+ content: funcContent,
618
+ startLine: node.loc ? node.loc.start.line : 0,
619
+ endLine: node.loc ? node.loc.end.line : 0
620
+ });
621
+ }
622
+
623
+ if (node.type === 'ClassDeclaration' && node.id) {
624
+ const className = node.id.name;
625
+ const methods = [];
626
+
627
+ if (node.body && node.body.body) {
628
+ for (const member of node.body.body) {
629
+ if (member.type === 'MethodDefinition' && member.key) {
630
+ const methodName = member.key.type === 'Identifier' ? member.key.name :
631
+ member.key.type === 'PrivateName' ? '#' + member.key.id.name :
632
+ String(member.key.value || member.key.name);
633
+ methods.push({
634
+ name: className + '.' + methodName,
635
+ content: getCode(member),
636
+ startLine: member.loc ? member.loc.start.line : 0,
637
+ endLine: member.loc ? member.loc.end.line : 0,
638
+ isMethod: true,
639
+ className: className,
640
+ methodName: methodName
641
+ });
642
+ }
643
+ }
644
+ }
645
+
646
+ classes.push({
647
+ name: className,
648
+ content: getCode(node),
649
+ methods: methods,
650
+ startLine: node.loc ? node.loc.start.line : 0,
651
+ endLine: node.loc ? node.loc.end.line : 0
652
+ });
653
+ }
654
+
655
+ if (node.type === 'ExportNamedDeclaration' && node.declaration) {
656
+ if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
657
+ functions.push({
658
+ name: node.declaration.id.name,
659
+ content: getCode(node.declaration),
660
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
661
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
662
+ isExported: true
663
+ });
664
+ } else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
665
+ const className = node.declaration.id.name;
666
+ const methods = [];
667
+
668
+ if (node.declaration.body && node.declaration.body.body) {
669
+ for (const member of node.declaration.body.body) {
670
+ if (member.type === 'MethodDefinition' && member.key) {
671
+ const methodName = member.key.type === 'Identifier' ? member.key.name :
672
+ member.key.type === 'PrivateName' ? '#' + member.key.id.name :
673
+ String(member.key.value || member.key.name);
674
+ methods.push({
675
+ name: className + '.' + methodName,
676
+ content: getCode(member),
677
+ startLine: member.loc ? member.loc.start.line : 0,
678
+ endLine: member.loc ? member.loc.end.line : 0,
679
+ isMethod: true,
680
+ className: className,
681
+ methodName: methodName
682
+ });
683
+ }
684
+ }
685
+ }
686
+
687
+ classes.push({
688
+ name: className,
689
+ content: getCode(node.declaration),
690
+ methods: methods,
691
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
692
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
693
+ isExported: true
694
+ });
695
+ }
696
+ }
697
+
698
+ for (const key in node) {
699
+ if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
700
+ const child = node[key];
701
+ if (Array.isArray(child)) {
702
+ child.forEach(traverse);
703
+ } else if (child && typeof child === 'object' && child.type) {
704
+ traverse(child);
705
+ }
706
+ }
707
+ }
708
+
709
+ traverse(ast);
710
+ } catch (parseError) {
711
+ console.warn('Failed to parse Vue script:', filePath, parseError.message);
712
+ return { functions: [], classes: [] };
713
+ }
714
+
715
+ return { functions, classes };
716
+ } catch (e) {
717
+ console.warn('Failed to read Vue file:', filePath, e.message);
718
+ return { functions: [], classes: [] };
239
719
  }
240
720
  }
241
721
 
@@ -266,16 +746,47 @@ function listAllFiles(dir, base = '', ignorePatterns = null) {
266
746
  } else {
267
747
  results.push(relativePath);
268
748
 
269
- // If this is a Python file, extract functions and add them as virtual files
270
- if (file.endsWith('.py')) {
271
- const functions = extractPythonFunctions(fullPath);
272
- for (const func of functions) {
273
- // Add function file as downstream from the Python file
274
- // Format: <python_file_path>/<function_name.function>
749
+ // Helper function to add functions, classes, and methods as virtual files
750
+ const addVirtualFiles = (parseResult, filePath) => {
751
+ // Add functions
752
+ for (const func of parseResult.functions) {
275
753
  const functionFileName = func.name + '.function';
276
- const functionFilePath = relativePath + '/' + functionFileName;
754
+ const functionFilePath = filePath + '/' + functionFileName;
277
755
  results.push(functionFilePath);
278
756
  }
757
+
758
+ // Add classes and their methods
759
+ for (const cls of parseResult.classes) {
760
+ // Add class itself (optional, but useful)
761
+ const className = cls.name + '.class';
762
+ const classFilePath = filePath + '/' + className;
763
+ results.push(classFilePath);
764
+
765
+ // Add methods with dot notation: ClassName.methodName
766
+ for (const method of cls.methods) {
767
+ const methodFileName = method.name + '.method';
768
+ const methodFilePath = filePath + '/' + methodFileName;
769
+ results.push(methodFilePath);
770
+ }
771
+ }
772
+ };
773
+
774
+ // Handle Python files
775
+ if (file.endsWith('.py')) {
776
+ const parseResult = extractPythonFunctions(fullPath);
777
+ addVirtualFiles(parseResult, relativePath);
778
+ }
779
+
780
+ // Handle JavaScript/TypeScript files
781
+ if (file.endsWith('.js') || file.endsWith('.jsx') || file.endsWith('.ts') || file.endsWith('.tsx')) {
782
+ const parseResult = extractJavaScriptFunctions(fullPath);
783
+ addVirtualFiles(parseResult, relativePath);
784
+ }
785
+
786
+ // Handle Vue files
787
+ if (file.endsWith('.vue')) {
788
+ const parseResult = extractVueFunctions(fullPath);
789
+ addVirtualFiles(parseResult, relativePath);
279
790
  }
280
791
  }
281
792
  }
@@ -301,30 +812,80 @@ app.get('/read', (req, res) => {
301
812
  return res.status(400).send('Invalid path');
302
813
  }
303
814
 
304
- // Check if this is a .function file request
305
- if (requestedPath.endsWith('.function')) {
306
- // Extract function name and Python file path
307
- // Path format: <python_file_path>/<function_name.function>
308
- const functionFileName = path.basename(requestedPath, '.function');
309
- const pythonFilePath = path.dirname(filePath);
815
+ // Helper function to find and return content from parse result
816
+ const findAndReturn = (parseResult, name, type) => {
817
+ if (type === 'function') {
818
+ const target = parseResult.functions.find(f => f.name === name);
819
+ if (target) return target.content;
820
+ } else if (type === 'method') {
821
+ // Method name format: ClassName.methodName
822
+ for (const cls of parseResult.classes) {
823
+ const method = cls.methods.find(m => m.name === name);
824
+ if (method) return method.content;
825
+ }
826
+ } else if (type === 'class') {
827
+ const target = parseResult.classes.find(c => c.name === name);
828
+ if (target) return target.content;
829
+ }
830
+ return null;
831
+ };
832
+
833
+ // Check if this is a virtual file request (.function, .method, or .class)
834
+ if (requestedPath.endsWith('.function') || requestedPath.endsWith('.method') || requestedPath.endsWith('.class')) {
835
+ const parentFilePath = path.dirname(filePath);
836
+ const parentRelativePath = path.relative(ROOT, parentFilePath);
837
+ const fileName = path.basename(parentRelativePath);
838
+
839
+ // Determine file type and parser
840
+ let parseResult = null;
841
+ let parser = null;
842
+
843
+ if (fileName.endsWith('.py')) {
844
+ parser = extractPythonFunctions;
845
+ } else if (fileName.endsWith('.js') || fileName.endsWith('.jsx') || fileName.endsWith('.ts') || fileName.endsWith('.tsx')) {
846
+ parser = extractJavaScriptFunctions;
847
+ } else if (fileName.endsWith('.vue')) {
848
+ parser = extractVueFunctions;
849
+ } else {
850
+ return res.status(404).send('Parent file type not supported');
851
+ }
310
852
 
311
- // Check if the Python file exists
853
+ // Check if the parent file exists
312
854
  try {
313
- if (!fs.existsSync(pythonFilePath) || !pythonFilePath.endsWith('.py')) {
314
- return res.status(404).send('Parent Python file not found');
855
+ if (!fs.existsSync(parentFilePath)) {
856
+ return res.status(404).send('Parent file not found');
857
+ }
858
+
859
+ // Parse the file
860
+ parseResult = parser(parentFilePath);
861
+
862
+ // Extract the requested item name
863
+ let itemName = '';
864
+ let itemType = '';
865
+
866
+ if (requestedPath.endsWith('.function')) {
867
+ itemName = path.basename(requestedPath, '.function');
868
+ itemType = 'function';
869
+ } else if (requestedPath.endsWith('.method')) {
870
+ itemName = path.basename(requestedPath, '.method');
871
+ itemType = 'method';
872
+ } else if (requestedPath.endsWith('.class')) {
873
+ itemName = path.basename(requestedPath, '.class');
874
+ itemType = 'class';
315
875
  }
316
876
 
317
- // Extract functions from the Python file
318
- const functions = extractPythonFunctions(pythonFilePath);
319
- const targetFunction = functions.find(f => f.name === functionFileName);
877
+ // Find and return the content
878
+ const content = findAndReturn(parseResult, itemName, itemType);
320
879
 
321
- if (!targetFunction) {
322
- return res.status(404).send('Function not found in Python file');
880
+ if (!content) {
881
+ return res.status(404).send(\`\${itemType} '\${itemName}' not found in file\`);
323
882
  }
324
883
 
325
- return res.json({ content: targetFunction.content });
884
+ return res.json({ content });
326
885
  } catch (e) {
327
- return res.status(500).send('Error reading function: ' + e.message);
886
+ const errorType = requestedPath.endsWith('.function') ? 'function' :
887
+ requestedPath.endsWith('.method') ? 'method' : 'class';
888
+ return res.status(500).send('Error reading ' + errorType + ': ' + e.message);
328
889
  }
329
890
  }
330
891
 
@@ -736,6 +1297,7 @@ async function main() {
736
1297
  packageJson.dependencies.cors = "^2.8.5";
737
1298
  packageJson.dependencies["body-parser"] = "^1.20.2";
738
1299
  packageJson.dependencies["raw-body"] = "^2.5.2";
1300
+ packageJson.dependencies["@babel/parser"] = "^7.23.0";
739
1301
 
740
1302
  // Add script to run receiver
741
1303
  if (!packageJson.scripts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devbonzai",
3
- "version": "1.6.8",
3
+ "version": "1.6.9",
4
4
  "description": "Quickly set up a local file server in any repository for browser-based file access",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "express": "^4.18.2",
22
22
  "cors": "^2.8.5",
23
23
  "body-parser": "^1.20.2",
24
- "raw-body": "^2.5.2"
24
+ "raw-body": "^2.5.2",
25
+ "@babel/parser": "^7.23.0"
25
26
  }
26
27
  }