devbonzai 1.8.1 → 2.0.2

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 (3) hide show
  1. package/README.md +1 -69
  2. package/cli.js +162 -472
  3. package/package.json +3 -4
package/README.md CHANGED
@@ -13,72 +13,4 @@ npm publish
13
13
  - node cli.js
14
14
  - this will setup a receiver in this directory
15
15
 
16
- Used for Bonzai's linking functionality from web to local development environment.
17
-
18
- ## API Endpoints
19
-
20
- The server exposes several endpoints for file operations and code analysis:
21
-
22
- ### Import Validation
23
-
24
- **POST /validate-imports** - Validate imports in a single file
25
-
26
- Request body:
27
- ```json
28
- {
29
- "filePath": "src/components/Button.js",
30
- "imports": [
31
- {
32
- "path": "./utils",
33
- "line": 5,
34
- "symbols": [
35
- { "name": "formatDate", "isDefault": false },
36
- { "name": "default", "isDefault": true }
37
- ]
38
- }
39
- ]
40
- }
41
- ```
42
-
43
- Response:
44
- ```json
45
- {
46
- "file": "src/components/Button.js",
47
- "errors": [
48
- {
49
- "line": 5,
50
- "import": "./utils",
51
- "symbol": "formatDate",
52
- "error": "'formatDate' is not exported from './utils'"
53
- }
54
- ],
55
- "errorCount": 1
56
- }
57
- ```
58
-
59
- **POST /validate-imports-batch** - Validate imports for multiple files
60
-
61
- Request body:
62
- ```json
63
- {
64
- "files": [
65
- {
66
- "filePath": "src/components/Button.js",
67
- "imports": [...]
68
- },
69
- {
70
- "filePath": "src/components/Input.js",
71
- "imports": [...]
72
- }
73
- ]
74
- }
75
- ```
76
-
77
- Response: Array of validation results (same format as single file endpoint)
78
-
79
- ### Supported Features
80
-
81
- - **JavaScript/TypeScript**: Validates default exports, named exports, and namespace imports
82
- - **Python**: Validates function, class, and variable imports
83
- - **File Resolution**: Automatically resolves relative imports and checks for files with common extensions (.js, .jsx, .ts, .tsx, .py, index files)
84
- - **External Packages**: Skips validation for node_modules imports
16
+ Used for Bonzai's linking functionality from web to local development environment.
package/cli.js CHANGED
@@ -92,8 +92,6 @@ app.get('/', (req, res) => {
92
92
  'POST /move': 'Move file or folder (body: {source, destination})',
93
93
  'POST /open-cursor': 'Open Cursor (body: {path, line?})',
94
94
  'POST /prompt_agent': 'Execute cursor-agent command (body: {prompt})',
95
- 'POST /validate-imports': 'Validate imports in a file (body: {filePath, imports})',
96
- 'POST /validate-imports-batch': 'Validate imports for multiple files (body: {files})',
97
95
  'POST /shutdown': 'Gracefully shutdown the server'
98
96
  },
99
97
  example: 'Try: /list or /read?path=README.md'
@@ -456,19 +454,41 @@ function extractJavaScriptFunctions(filePath) {
456
454
  });
457
455
  }
458
456
 
459
- // Class declarations: class User { ... }
460
- // Skip if inside ExportNamedDeclaration (will be handled below)
461
- if (node.type === 'ClassDeclaration' && node.id && parentType !== 'ExportNamedDeclaration') {
462
- const className = node.id.name;
457
+ // Helper function to extract methods from a class body
458
+ const extractClassMethods = (classNode, className) => {
463
459
  const methods = [];
464
-
465
- // Extract methods
466
- if (node.body && node.body.body) {
467
- for (const member of node.body.body) {
468
- if (member.type === 'MethodDefinition' && member.key) {
469
- const methodName = member.key.type === 'Identifier' ? member.key.name :
470
- member.key.type === 'PrivateName' ? '#' + member.key.id.name :
471
- String(member.key.value || member.key.name);
460
+ if (classNode.body && classNode.body.body && Array.isArray(classNode.body.body)) {
461
+ for (const member of classNode.body.body) {
462
+ // Handle MethodDefinition (regular methods, constructors, getters, setters, static methods)
463
+ if (member && member.type === 'MethodDefinition' && member.key) {
464
+ let methodName;
465
+ if (member.key.type === 'Identifier') {
466
+ methodName = member.key.name;
467
+ } else if (member.key.type === 'PrivateName') {
468
+ methodName = '#' + member.key.id.name;
469
+ } else if (member.key.type === 'StringLiteral' || member.key.type === 'NumericLiteral') {
470
+ methodName = String(member.key.value);
471
+ } else {
472
+ methodName = String(member.key.value || member.key.name || 'unknown');
473
+ }
474
+
475
+ // Include kind (constructor, get, set, method) in the name for clarity
476
+ const kind = member.kind || 'method';
477
+ const isStatic = member.static || false;
478
+
479
+ // For getters and setters, include the kind in the method name to distinguish them
480
+ // e.g., "value" getter vs "value" setter -> "get value" and "set value"
481
+ let fullMethodName = methodName;
482
+ if (kind === 'get') {
483
+ fullMethodName = 'get ' + methodName;
484
+ } else if (kind === 'set') {
485
+ fullMethodName = 'set ' + methodName;
486
+ } else if (kind === 'constructor') {
487
+ fullMethodName = 'constructor';
488
+ } else if (isStatic) {
489
+ fullMethodName = 'static ' + methodName;
490
+ }
491
+
472
492
  methods.push({
473
493
  name: className + '.' + methodName,
474
494
  content: getCode(member),
@@ -476,11 +496,22 @@ function extractJavaScriptFunctions(filePath) {
476
496
  endLine: member.loc ? member.loc.end.line : 0,
477
497
  isMethod: true,
478
498
  className: className,
479
- methodName: methodName
499
+ methodName: methodName,
500
+ kind: kind,
501
+ static: isStatic
480
502
  });
481
503
  }
482
504
  }
483
505
  }
506
+ return methods;
507
+ };
508
+
509
+ // Class declarations: class User { ... }
510
+ // Skip if inside ExportNamedDeclaration or ExportDefaultDeclaration (will be handled below)
511
+ if (node.type === 'ClassDeclaration' && node.id &&
512
+ parentType !== 'ExportNamedDeclaration' && parentType !== 'ExportDefaultDeclaration') {
513
+ const className = node.id.name;
514
+ const methods = extractClassMethods(node, className);
484
515
 
485
516
  classes.push({
486
517
  name: className,
@@ -491,7 +522,7 @@ function extractJavaScriptFunctions(filePath) {
491
522
  });
492
523
  }
493
524
 
494
- // Export declarations: export function, export default
525
+ // Export declarations: export function, export class
495
526
  if (node.type === 'ExportNamedDeclaration' && node.declaration) {
496
527
  if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
497
528
  functions.push({
@@ -505,26 +536,7 @@ function extractJavaScriptFunctions(filePath) {
505
536
  visitedNodes.add(node.declaration);
506
537
  } else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
507
538
  const className = node.declaration.id.name;
508
- const methods = [];
509
-
510
- if (node.declaration.body && node.declaration.body.body) {
511
- for (const member of node.declaration.body.body) {
512
- if (member.type === 'MethodDefinition' && member.key) {
513
- const methodName = member.key.type === 'Identifier' ? member.key.name :
514
- member.key.type === 'PrivateName' ? '#' + member.key.id.name :
515
- String(member.key.value || member.key.name);
516
- methods.push({
517
- name: className + '.' + methodName,
518
- content: getCode(member),
519
- startLine: member.loc ? member.loc.start.line : 0,
520
- endLine: member.loc ? member.loc.end.line : 0,
521
- isMethod: true,
522
- className: className,
523
- methodName: methodName
524
- });
525
- }
526
- }
527
- }
539
+ const methods = extractClassMethods(node.declaration, className);
528
540
 
529
541
  classes.push({
530
542
  name: className,
@@ -539,6 +551,36 @@ function extractJavaScriptFunctions(filePath) {
539
551
  }
540
552
  }
541
553
 
554
+ // Export default declarations: export default class
555
+ if (node.type === 'ExportDefaultDeclaration' && node.declaration) {
556
+ if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
557
+ const className = node.declaration.id.name;
558
+ const methods = extractClassMethods(node.declaration, className);
559
+
560
+ classes.push({
561
+ name: className,
562
+ content: getCode(node.declaration),
563
+ methods: methods,
564
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
565
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
566
+ isExported: true,
567
+ isDefaultExport: true
568
+ });
569
+ // Mark as visited to avoid duplicate processing
570
+ visitedNodes.add(node.declaration);
571
+ } else if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
572
+ functions.push({
573
+ name: node.declaration.id.name,
574
+ content: getCode(node.declaration),
575
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
576
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
577
+ isExported: true,
578
+ isDefaultExport: true
579
+ });
580
+ visitedNodes.add(node.declaration);
581
+ }
582
+ }
583
+
542
584
  // Recursively traverse children
543
585
  for (const key in node) {
544
586
  if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
@@ -660,18 +702,41 @@ function extractVueFunctions(filePath) {
660
702
  });
661
703
  }
662
704
 
663
- // Class declarations: class User { ... }
664
- // Skip if inside ExportNamedDeclaration (will be handled below)
665
- if (node.type === 'ClassDeclaration' && node.id && parentType !== 'ExportNamedDeclaration') {
666
- const className = node.id.name;
705
+ // Helper function to extract methods from a class body
706
+ const extractClassMethods = (classNode, className) => {
667
707
  const methods = [];
668
-
669
- if (node.body && node.body.body) {
670
- for (const member of node.body.body) {
671
- if (member.type === 'MethodDefinition' && member.key) {
672
- const methodName = member.key.type === 'Identifier' ? member.key.name :
673
- member.key.type === 'PrivateName' ? '#' + member.key.id.name :
674
- String(member.key.value || member.key.name);
708
+ if (classNode.body && classNode.body.body && Array.isArray(classNode.body.body)) {
709
+ for (const member of classNode.body.body) {
710
+ // Handle MethodDefinition (regular methods, constructors, getters, setters, static methods)
711
+ if (member && member.type === 'MethodDefinition' && member.key) {
712
+ let methodName;
713
+ if (member.key.type === 'Identifier') {
714
+ methodName = member.key.name;
715
+ } else if (member.key.type === 'PrivateName') {
716
+ methodName = '#' + member.key.id.name;
717
+ } else if (member.key.type === 'StringLiteral' || member.key.type === 'NumericLiteral') {
718
+ methodName = String(member.key.value);
719
+ } else {
720
+ methodName = String(member.key.value || member.key.name || 'unknown');
721
+ }
722
+
723
+ // Include kind (constructor, get, set, method) in the name for clarity
724
+ const kind = member.kind || 'method';
725
+ const isStatic = member.static || false;
726
+
727
+ // For getters and setters, include the kind in the method name to distinguish them
728
+ // e.g., "value" getter vs "value" setter -> "get value" and "set value"
729
+ let fullMethodName = methodName;
730
+ if (kind === 'get') {
731
+ fullMethodName = 'get ' + methodName;
732
+ } else if (kind === 'set') {
733
+ fullMethodName = 'set ' + methodName;
734
+ } else if (kind === 'constructor') {
735
+ fullMethodName = 'constructor';
736
+ } else if (isStatic) {
737
+ fullMethodName = 'static ' + methodName;
738
+ }
739
+
675
740
  methods.push({
676
741
  name: className + '.' + methodName,
677
742
  content: getCode(member),
@@ -679,11 +744,22 @@ function extractVueFunctions(filePath) {
679
744
  endLine: member.loc ? member.loc.end.line : 0,
680
745
  isMethod: true,
681
746
  className: className,
682
- methodName: methodName
747
+ methodName: methodName,
748
+ kind: kind,
749
+ static: isStatic
683
750
  });
684
751
  }
685
752
  }
686
753
  }
754
+ return methods;
755
+ };
756
+
757
+ // Class declarations: class User { ... }
758
+ // Skip if inside ExportNamedDeclaration or ExportDefaultDeclaration (will be handled below)
759
+ if (node.type === 'ClassDeclaration' && node.id &&
760
+ parentType !== 'ExportNamedDeclaration' && parentType !== 'ExportDefaultDeclaration') {
761
+ const className = node.id.name;
762
+ const methods = extractClassMethods(node, className);
687
763
 
688
764
  classes.push({
689
765
  name: className,
@@ -694,7 +770,7 @@ function extractVueFunctions(filePath) {
694
770
  });
695
771
  }
696
772
 
697
- // Export declarations: export function, export default
773
+ // Export declarations: export function, export class
698
774
  if (node.type === 'ExportNamedDeclaration' && node.declaration) {
699
775
  if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
700
776
  functions.push({
@@ -708,26 +784,7 @@ function extractVueFunctions(filePath) {
708
784
  visitedNodes.add(node.declaration);
709
785
  } else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
710
786
  const className = node.declaration.id.name;
711
- const methods = [];
712
-
713
- if (node.declaration.body && node.declaration.body.body) {
714
- for (const member of node.declaration.body.body) {
715
- if (member.type === 'MethodDefinition' && member.key) {
716
- const methodName = member.key.type === 'Identifier' ? member.key.name :
717
- member.key.type === 'PrivateName' ? '#' + member.key.id.name :
718
- String(member.key.value || member.key.name);
719
- methods.push({
720
- name: className + '.' + methodName,
721
- content: getCode(member),
722
- startLine: member.loc ? member.loc.start.line : 0,
723
- endLine: member.loc ? member.loc.end.line : 0,
724
- isMethod: true,
725
- className: className,
726
- methodName: methodName
727
- });
728
- }
729
- }
730
- }
787
+ const methods = extractClassMethods(node.declaration, className);
731
788
 
732
789
  classes.push({
733
790
  name: className,
@@ -742,6 +799,36 @@ function extractVueFunctions(filePath) {
742
799
  }
743
800
  }
744
801
 
802
+ // Export default declarations: export default class
803
+ if (node.type === 'ExportDefaultDeclaration' && node.declaration) {
804
+ if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
805
+ const className = node.declaration.id.name;
806
+ const methods = extractClassMethods(node.declaration, className);
807
+
808
+ classes.push({
809
+ name: className,
810
+ content: getCode(node.declaration),
811
+ methods: methods,
812
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
813
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
814
+ isExported: true,
815
+ isDefaultExport: true
816
+ });
817
+ // Mark as visited to avoid duplicate processing
818
+ visitedNodes.add(node.declaration);
819
+ } else if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
820
+ functions.push({
821
+ name: node.declaration.id.name,
822
+ content: getCode(node.declaration),
823
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
824
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
825
+ isExported: true,
826
+ isDefaultExport: true
827
+ });
828
+ visitedNodes.add(node.declaration);
829
+ }
830
+ }
831
+
745
832
  // Recursively traverse children
746
833
  for (const key in node) {
747
834
  if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
@@ -820,10 +907,12 @@ function listAllFiles(dir, base = '', ignorePatterns = null) {
820
907
  results.push(classFilePath);
821
908
 
822
909
  // Add methods nested under the class: ClassName.methodName
823
- for (const method of cls.methods) {
824
- const methodFileName = method.name + '.method';
825
- const methodFilePath = path.join(classFilePath, methodFileName).replace(/\\\\/g, '/');
826
- results.push(methodFilePath);
910
+ if (cls.methods && cls.methods.length > 0) {
911
+ for (const method of cls.methods) {
912
+ const methodFileName = method.name + '.method';
913
+ const methodFilePath = path.join(classFilePath, methodFileName).replace(/\\\\/g, '/');
914
+ results.push(methodFilePath);
915
+ }
827
916
  }
828
917
  }
829
918
  };
@@ -1296,405 +1385,6 @@ app.post('/prompt_agent', (req, res) => {
1296
1385
  });
1297
1386
  });
1298
1387
 
1299
- // Helper function to resolve import path relative to source file
1300
- function resolveImportPath(importPath, sourceFilePath) {
1301
- // Remove root folder name if present
1302
- let relativeSourcePath = sourceFilePath;
1303
- const rootName = path.basename(ROOT);
1304
- if (relativeSourcePath.startsWith(rootName + '/')) {
1305
- relativeSourcePath = relativeSourcePath.substring(rootName.length + 1);
1306
- }
1307
-
1308
- const sourceDir = path.dirname(path.join(ROOT, relativeSourcePath));
1309
-
1310
- // Handle relative imports
1311
- if (importPath.startsWith('./') || importPath.startsWith('../')) {
1312
- const resolved = path.resolve(sourceDir, importPath);
1313
- return path.relative(ROOT, resolved).replace(/\\\\/g, '/');
1314
- }
1315
-
1316
- // Handle absolute imports (node_modules, etc.)
1317
- // For now, just return as-is - we'll check node_modules separately
1318
- return importPath;
1319
- }
1320
-
1321
- // Helper function to check if a file exists (with various extensions)
1322
- function findFileWithExtensions(basePath) {
1323
- const extensions = ['', '.js', '.jsx', '.ts', '.tsx', '.py', '/index.js', '/index.ts', '/index.tsx'];
1324
- const rootName = path.basename(ROOT);
1325
-
1326
- for (const ext of extensions) {
1327
- const testPath = basePath + ext;
1328
-
1329
- // Remove root folder name if present
1330
- let relativePath = testPath;
1331
- if (relativePath.startsWith(rootName + '/')) {
1332
- relativePath = relativePath.substring(rootName.length + 1);
1333
- }
1334
- const actualPath = path.join(ROOT, relativePath);
1335
-
1336
- if (fs.existsSync(actualPath) && fs.statSync(actualPath).isFile()) {
1337
- return relativePath;
1338
- }
1339
- }
1340
-
1341
- return null;
1342
- }
1343
-
1344
- // Helper function to extract exports from JavaScript/TypeScript file
1345
- function getJavaScriptExports(filePath) {
1346
- try {
1347
- if (!babelParser) {
1348
- return { default: false, named: [], all: false };
1349
- }
1350
-
1351
- const content = fs.readFileSync(filePath, 'utf8');
1352
- const isTypeScript = filePath.endsWith('.ts') || filePath.endsWith('.tsx');
1353
-
1354
- const ast = babelParser.parse(content, {
1355
- sourceType: 'module',
1356
- plugins: [
1357
- 'typescript',
1358
- 'jsx',
1359
- 'decorators-legacy',
1360
- 'classProperties',
1361
- 'objectRestSpread',
1362
- 'asyncGenerators',
1363
- 'functionBind',
1364
- 'exportDefaultFrom',
1365
- 'exportNamespaceFrom',
1366
- 'dynamicImport',
1367
- 'nullishCoalescingOperator',
1368
- 'optionalChaining'
1369
- ]
1370
- });
1371
-
1372
- const exports = { default: false, named: [], all: false };
1373
-
1374
- function traverse(node) {
1375
- if (!node) return;
1376
-
1377
- // export default
1378
- if (node.type === 'ExportDefaultDeclaration') {
1379
- exports.default = true;
1380
- }
1381
-
1382
- // export { A, B }
1383
- if (node.type === 'ExportNamedDeclaration') {
1384
- if (node.specifiers) {
1385
- node.specifiers.forEach(spec => {
1386
- if (spec.type === 'ExportSpecifier') {
1387
- const name = spec.exported.name || spec.exported.value;
1388
- if (name === '*') {
1389
- exports.all = true;
1390
- } else {
1391
- exports.named.push(name);
1392
- }
1393
- }
1394
- });
1395
- }
1396
- // export function/class
1397
- if (node.declaration) {
1398
- if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
1399
- exports.named.push(node.declaration.id.name);
1400
- } else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
1401
- exports.named.push(node.declaration.id.name);
1402
- }
1403
- }
1404
- }
1405
-
1406
- // export * from './file'
1407
- if (node.type === 'ExportAllDeclaration') {
1408
- exports.all = true;
1409
- }
1410
-
1411
- // Recursively traverse
1412
- for (const key in node) {
1413
- if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
1414
- const child = node[key];
1415
- if (Array.isArray(child)) {
1416
- child.forEach(c => traverse(c));
1417
- } else if (child && typeof child === 'object' && child.type) {
1418
- traverse(child);
1419
- }
1420
- }
1421
- }
1422
-
1423
- traverse(ast);
1424
- return exports;
1425
- } catch (e) {
1426
- // If parsing fails, return empty exports
1427
- return { default: false, named: [], all: false };
1428
- }
1429
- }
1430
-
1431
- // Helper function to check if symbol exists in Python file
1432
- function checkPythonSymbol(filePath, symbolName) {
1433
- try {
1434
- const content = fs.readFileSync(filePath, 'utf8');
1435
- const lines = content.split('\\n');
1436
-
1437
- // Check for function definitions
1438
- const funcPattern = new RegExp(\`^\\\\s*def\\\\s+\${symbolName}\\\\s*\\\\(\`);
1439
- // Check for class definitions
1440
- const classPattern = new RegExp(\`^\\\\s*class\\\\s+\${symbolName}\\\\s*[(:]\`);
1441
- // Check for variable assignments
1442
- const varPattern = new RegExp(\`^\\\\s*\${symbolName}\\\\s*=\`);
1443
-
1444
- for (const line of lines) {
1445
- if (funcPattern.test(line) || classPattern.test(line) || varPattern.test(line)) {
1446
- return true;
1447
- }
1448
- }
1449
-
1450
- // Check __all__ if present
1451
- const allMatch = content.match(/^__all__\\s*=\\s*\\[(.*?)\\]/m);
1452
- if (allMatch) {
1453
- const allExports = allMatch[1].split(',').map(s => s.trim().replace(/['"]/g, ''));
1454
- return allExports.includes(symbolName);
1455
- }
1456
-
1457
- return false;
1458
- } catch (e) {
1459
- return false;
1460
- }
1461
- }
1462
-
1463
- // Validate imports for a single file
1464
- app.post('/validate-imports', (req, res) => {
1465
- try {
1466
- const { filePath, imports } = req.body;
1467
-
1468
- if (!filePath || !imports || !Array.isArray(imports)) {
1469
- return res.status(400).json({ error: 'filePath and imports array required' });
1470
- }
1471
-
1472
- const errors = [];
1473
- const rootName = path.basename(ROOT);
1474
-
1475
- // Remove root folder name if present
1476
- let relativeFilePath = filePath;
1477
- if (relativeFilePath.startsWith(rootName + '/')) {
1478
- relativeFilePath = relativeFilePath.substring(rootName.length + 1);
1479
- }
1480
-
1481
- for (const imp of imports) {
1482
- const importPath = imp.path;
1483
- const symbols = imp.symbols || [];
1484
-
1485
- // Resolve import path
1486
- const resolvedPath = resolveImportPath(importPath, filePath);
1487
-
1488
- // Check if file exists
1489
- const foundFile = findFileWithExtensions(resolvedPath);
1490
-
1491
- if (!foundFile) {
1492
- // Check if it's a node_modules import (skip validation for external packages)
1493
- if (!importPath.startsWith('./') && !importPath.startsWith('../') && !importPath.startsWith('/')) {
1494
- // Likely a node_modules import, skip
1495
- continue;
1496
- }
1497
-
1498
- errors.push({
1499
- line: imp.line,
1500
- import: importPath,
1501
- error: \`Cannot find module '\${importPath}'\`
1502
- });
1503
- continue;
1504
- }
1505
-
1506
- // Check exports for each symbol
1507
- const fullFilePath = path.join(ROOT, foundFile);
1508
- const extension = foundFile.split('.').pop()?.toLowerCase();
1509
-
1510
- if (['js', 'jsx', 'ts', 'tsx'].includes(extension)) {
1511
- const exports = getJavaScriptExports(fullFilePath);
1512
-
1513
- for (const symbol of symbols) {
1514
- if (symbol.isNamespace || symbol.name === '*') {
1515
- // Namespace import - check if file has any exports
1516
- if (!exports.default && exports.named.length === 0 && !exports.all) {
1517
- errors.push({
1518
- line: imp.line,
1519
- import: importPath,
1520
- symbol: symbol.name,
1521
- error: \`Module '\${importPath}' has no exports\`
1522
- });
1523
- }
1524
- } else if (symbol.isDefault) {
1525
- if (!exports.default) {
1526
- errors.push({
1527
- line: imp.line,
1528
- import: importPath,
1529
- symbol: symbol.name,
1530
- error: \`'\${symbol.name}' is not exported as default from '\${importPath}'\`
1531
- });
1532
- }
1533
- } else {
1534
- // Named export
1535
- if (!exports.named.includes(symbol.name) && !exports.all) {
1536
- errors.push({
1537
- line: imp.line,
1538
- import: importPath,
1539
- symbol: symbol.name,
1540
- error: \`'\${symbol.name}' is not exported from '\${importPath}'\`
1541
- });
1542
- }
1543
- }
1544
- }
1545
- } else if (extension === 'py') {
1546
- // Python validation
1547
- for (const symbol of symbols) {
1548
- if (symbol.name === '*' || symbol.isNamespace) {
1549
- // Wildcard import - just check if file exists (already done)
1550
- continue;
1551
- }
1552
-
1553
- // Check if symbol exists in Python file
1554
- if (!checkPythonSymbol(fullFilePath, symbol.name)) {
1555
- errors.push({
1556
- line: imp.line,
1557
- import: importPath,
1558
- symbol: symbol.name,
1559
- error: \`'\${symbol.name}' is not defined in '\${importPath}'\`
1560
- });
1561
- }
1562
- }
1563
- }
1564
- }
1565
-
1566
- res.json({
1567
- file: filePath,
1568
- errors,
1569
- errorCount: errors.length
1570
- });
1571
- } catch (e) {
1572
- console.error('Error validating imports:', e);
1573
- res.status(500).json({ error: e.message });
1574
- }
1575
- });
1576
-
1577
- // Validate imports for multiple files (batch)
1578
- app.post('/validate-imports-batch', (req, res) => {
1579
- try {
1580
- const { files } = req.body;
1581
-
1582
- if (!files || !Array.isArray(files)) {
1583
- return res.status(400).json({ error: 'files array required' });
1584
- }
1585
-
1586
- const results = [];
1587
-
1588
- for (const file of files) {
1589
- const { filePath, imports } = file;
1590
-
1591
- if (!filePath || !imports || !Array.isArray(imports)) {
1592
- results.push({ file: filePath || 'unknown', errors: [], errorCount: 0 });
1593
- continue;
1594
- }
1595
-
1596
- const errors = [];
1597
- const rootName = path.basename(ROOT);
1598
-
1599
- // Remove root folder name if present
1600
- let relativeFilePath = filePath;
1601
- if (relativeFilePath.startsWith(rootName + '/')) {
1602
- relativeFilePath = relativeFilePath.substring(rootName.length + 1);
1603
- }
1604
-
1605
- for (const imp of imports) {
1606
- const importPath = imp.path;
1607
- const symbols = imp.symbols || [];
1608
-
1609
- // Resolve import path
1610
- const resolvedPath = resolveImportPath(importPath, filePath);
1611
-
1612
- // Check if file exists
1613
- const foundFile = findFileWithExtensions(resolvedPath);
1614
-
1615
- if (!foundFile) {
1616
- // Check if it's a node_modules import
1617
- if (!importPath.startsWith('./') && !importPath.startsWith('../') && !importPath.startsWith('/')) {
1618
- continue;
1619
- }
1620
-
1621
- errors.push({
1622
- line: imp.line,
1623
- import: importPath,
1624
- error: \`Cannot find module '\${importPath}'\`
1625
- });
1626
- continue;
1627
- }
1628
-
1629
- // Check exports
1630
- const fullFilePath = path.join(ROOT, foundFile);
1631
- const extension = foundFile.split('.').pop()?.toLowerCase();
1632
-
1633
- if (['js', 'jsx', 'ts', 'tsx'].includes(extension)) {
1634
- const exports = getJavaScriptExports(fullFilePath);
1635
-
1636
- for (const symbol of symbols) {
1637
- if (symbol.isNamespace || symbol.name === '*') {
1638
- if (!exports.default && exports.named.length === 0 && !exports.all) {
1639
- errors.push({
1640
- line: imp.line,
1641
- import: importPath,
1642
- symbol: symbol.name,
1643
- error: \`Module '\${importPath}' has no exports\`
1644
- });
1645
- }
1646
- } else if (symbol.isDefault) {
1647
- if (!exports.default) {
1648
- errors.push({
1649
- line: imp.line,
1650
- import: importPath,
1651
- symbol: symbol.name,
1652
- error: \`'\${symbol.name}' is not exported as default from '\${importPath}'\`
1653
- });
1654
- }
1655
- } else {
1656
- if (!exports.named.includes(symbol.name) && !exports.all) {
1657
- errors.push({
1658
- line: imp.line,
1659
- import: importPath,
1660
- symbol: symbol.name,
1661
- error: \`'\${symbol.name}' is not exported from '\${importPath}'\`
1662
- });
1663
- }
1664
- }
1665
- }
1666
- } else if (extension === 'py') {
1667
- for (const symbol of symbols) {
1668
- if (symbol.name === '*' || symbol.isNamespace) {
1669
- continue;
1670
- }
1671
-
1672
- if (!checkPythonSymbol(fullFilePath, symbol.name)) {
1673
- errors.push({
1674
- line: imp.line,
1675
- import: importPath,
1676
- symbol: symbol.name,
1677
- error: \`'\${symbol.name}' is not defined in '\${importPath}'\`
1678
- });
1679
- }
1680
- }
1681
- }
1682
- }
1683
-
1684
- results.push({
1685
- file: filePath,
1686
- errors,
1687
- errorCount: errors.length
1688
- });
1689
- }
1690
-
1691
- res.json(results);
1692
- } catch (e) {
1693
- console.error('Error validating imports batch:', e);
1694
- res.status(500).json({ error: e.message });
1695
- }
1696
- });
1697
-
1698
1388
  // Shutdown endpoint to kill the server
1699
1389
  app.post('/shutdown', (req, res) => {
1700
1390
  console.log('🛑 Shutdown endpoint called - terminating server...');
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "devbonzai",
3
- "version": "1.8.1",
3
+ "version": "2.0.2",
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": {
7
- "devbonzai": "./cli.js"
7
+ "devchart": "./cli.js"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -21,7 +21,6 @@
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",
25
- "@babel/parser": "^7.23.0"
24
+ "raw-body": "^2.5.2"
26
25
  }
27
26
  }