devbonzai 1.8.1 → 2.0.1

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 +128 -466
  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,28 @@ 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) {
460
+ if (classNode.body && classNode.body.body) {
461
+ for (const member of classNode.body.body) {
462
+ // Handle MethodDefinition (regular methods, constructors, getters, setters, static methods)
468
463
  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);
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
+
472
479
  methods.push({
473
480
  name: className + '.' + methodName,
474
481
  content: getCode(member),
@@ -476,11 +483,22 @@ function extractJavaScriptFunctions(filePath) {
476
483
  endLine: member.loc ? member.loc.end.line : 0,
477
484
  isMethod: true,
478
485
  className: className,
479
- methodName: methodName
486
+ methodName: methodName,
487
+ kind: kind,
488
+ static: isStatic
480
489
  });
481
490
  }
482
491
  }
483
492
  }
493
+ return methods;
494
+ };
495
+
496
+ // Class declarations: class User { ... }
497
+ // Skip if inside ExportNamedDeclaration or ExportDefaultDeclaration (will be handled below)
498
+ if (node.type === 'ClassDeclaration' && node.id &&
499
+ parentType !== 'ExportNamedDeclaration' && parentType !== 'ExportDefaultDeclaration') {
500
+ const className = node.id.name;
501
+ const methods = extractClassMethods(node, className);
484
502
 
485
503
  classes.push({
486
504
  name: className,
@@ -491,7 +509,7 @@ function extractJavaScriptFunctions(filePath) {
491
509
  });
492
510
  }
493
511
 
494
- // Export declarations: export function, export default
512
+ // Export declarations: export function, export class
495
513
  if (node.type === 'ExportNamedDeclaration' && node.declaration) {
496
514
  if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
497
515
  functions.push({
@@ -505,26 +523,7 @@ function extractJavaScriptFunctions(filePath) {
505
523
  visitedNodes.add(node.declaration);
506
524
  } else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
507
525
  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
- }
526
+ const methods = extractClassMethods(node.declaration, className);
528
527
 
529
528
  classes.push({
530
529
  name: className,
@@ -539,6 +538,36 @@ function extractJavaScriptFunctions(filePath) {
539
538
  }
540
539
  }
541
540
 
541
+ // Export default declarations: export default class
542
+ if (node.type === 'ExportDefaultDeclaration' && node.declaration) {
543
+ if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
544
+ const className = node.declaration.id.name;
545
+ const methods = extractClassMethods(node.declaration, className);
546
+
547
+ classes.push({
548
+ name: className,
549
+ content: getCode(node.declaration),
550
+ methods: methods,
551
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
552
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
553
+ isExported: true,
554
+ isDefaultExport: true
555
+ });
556
+ // Mark as visited to avoid duplicate processing
557
+ visitedNodes.add(node.declaration);
558
+ } else if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
559
+ functions.push({
560
+ name: node.declaration.id.name,
561
+ content: getCode(node.declaration),
562
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
563
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
564
+ isExported: true,
565
+ isDefaultExport: true
566
+ });
567
+ visitedNodes.add(node.declaration);
568
+ }
569
+ }
570
+
542
571
  // Recursively traverse children
543
572
  for (const key in node) {
544
573
  if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
@@ -660,18 +689,28 @@ function extractVueFunctions(filePath) {
660
689
  });
661
690
  }
662
691
 
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;
692
+ // Helper function to extract methods from a class body
693
+ const extractClassMethods = (classNode, className) => {
667
694
  const methods = [];
668
-
669
- if (node.body && node.body.body) {
670
- for (const member of node.body.body) {
695
+ if (classNode.body && classNode.body.body) {
696
+ for (const member of classNode.body.body) {
697
+ // Handle MethodDefinition (regular methods, constructors, getters, setters, static methods)
671
698
  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);
699
+ let methodName;
700
+ if (member.key.type === 'Identifier') {
701
+ methodName = member.key.name;
702
+ } else if (member.key.type === 'PrivateName') {
703
+ methodName = '#' + member.key.id.name;
704
+ } else if (member.key.type === 'StringLiteral' || member.key.type === 'NumericLiteral') {
705
+ methodName = String(member.key.value);
706
+ } else {
707
+ methodName = String(member.key.value || member.key.name || 'unknown');
708
+ }
709
+
710
+ // Include kind (constructor, get, set, method) in the name for clarity
711
+ const kind = member.kind || 'method';
712
+ const isStatic = member.static || false;
713
+
675
714
  methods.push({
676
715
  name: className + '.' + methodName,
677
716
  content: getCode(member),
@@ -679,11 +718,22 @@ function extractVueFunctions(filePath) {
679
718
  endLine: member.loc ? member.loc.end.line : 0,
680
719
  isMethod: true,
681
720
  className: className,
682
- methodName: methodName
721
+ methodName: methodName,
722
+ kind: kind,
723
+ static: isStatic
683
724
  });
684
725
  }
685
726
  }
686
727
  }
728
+ return methods;
729
+ };
730
+
731
+ // Class declarations: class User { ... }
732
+ // Skip if inside ExportNamedDeclaration or ExportDefaultDeclaration (will be handled below)
733
+ if (node.type === 'ClassDeclaration' && node.id &&
734
+ parentType !== 'ExportNamedDeclaration' && parentType !== 'ExportDefaultDeclaration') {
735
+ const className = node.id.name;
736
+ const methods = extractClassMethods(node, className);
687
737
 
688
738
  classes.push({
689
739
  name: className,
@@ -694,7 +744,7 @@ function extractVueFunctions(filePath) {
694
744
  });
695
745
  }
696
746
 
697
- // Export declarations: export function, export default
747
+ // Export declarations: export function, export class
698
748
  if (node.type === 'ExportNamedDeclaration' && node.declaration) {
699
749
  if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
700
750
  functions.push({
@@ -708,26 +758,7 @@ function extractVueFunctions(filePath) {
708
758
  visitedNodes.add(node.declaration);
709
759
  } else if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
710
760
  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
- }
761
+ const methods = extractClassMethods(node.declaration, className);
731
762
 
732
763
  classes.push({
733
764
  name: className,
@@ -742,6 +773,36 @@ function extractVueFunctions(filePath) {
742
773
  }
743
774
  }
744
775
 
776
+ // Export default declarations: export default class
777
+ if (node.type === 'ExportDefaultDeclaration' && node.declaration) {
778
+ if (node.declaration.type === 'ClassDeclaration' && node.declaration.id) {
779
+ const className = node.declaration.id.name;
780
+ const methods = extractClassMethods(node.declaration, className);
781
+
782
+ classes.push({
783
+ name: className,
784
+ content: getCode(node.declaration),
785
+ methods: methods,
786
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
787
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
788
+ isExported: true,
789
+ isDefaultExport: true
790
+ });
791
+ // Mark as visited to avoid duplicate processing
792
+ visitedNodes.add(node.declaration);
793
+ } else if (node.declaration.type === 'FunctionDeclaration' && node.declaration.id) {
794
+ functions.push({
795
+ name: node.declaration.id.name,
796
+ content: getCode(node.declaration),
797
+ startLine: node.declaration.loc ? node.declaration.loc.start.line : 0,
798
+ endLine: node.declaration.loc ? node.declaration.loc.end.line : 0,
799
+ isExported: true,
800
+ isDefaultExport: true
801
+ });
802
+ visitedNodes.add(node.declaration);
803
+ }
804
+ }
805
+
745
806
  // Recursively traverse children
746
807
  for (const key in node) {
747
808
  if (key === 'parent' || key === 'leadingComments' || key === 'trailingComments') continue;
@@ -1296,405 +1357,6 @@ app.post('/prompt_agent', (req, res) => {
1296
1357
  });
1297
1358
  });
1298
1359
 
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
1360
  // Shutdown endpoint to kill the server
1699
1361
  app.post('/shutdown', (req, res) => {
1700
1362
  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.1",
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
  }