arcvision 0.1.0 → 0.1.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ArcVision CLI
2
2
 
3
- Architecture scanner for modern codebases. ArcVision analyzes your codebase and creates visual representations of file relationships and dependencies.
3
+ Architecture scanner for modern codebases that generates dependency graphs and visualizes your project structure.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,82 +10,56 @@ npm install -g arcvision
10
10
 
11
11
  ## Usage
12
12
 
13
- ### Link to a Project
13
+ ### Quick Start
14
+ 1. Sign up at the ArcVision dashboard
15
+ 2. Create a project and name it
16
+ 3. Generate a CLI token
17
+ 4. Run: `arcvision link <token>`
18
+ 5. Run: `arcvision scan --upload`
19
+ 6. Open dashboard to see results
14
20
 
21
+ ### Commands
22
+
23
+ #### Link to Project
15
24
  ```bash
16
- arcvision link <upload-token>
25
+ arcvision link <token>
17
26
  ```
27
+ Link this CLI to a project via upload token.
18
28
 
19
- ### Scan a Directory
20
-
29
+ #### Scan Project
21
30
  ```bash
22
- # Scan current directory
23
- arcvision scan
24
-
25
- # Scan specific directory
26
- arcvision scan path/to/directory
27
-
28
- # Scan and upload to dashboard
29
- arcvision scan --upload
31
+ arcvision scan [directory] [options]
30
32
  ```
33
+ Scan the current directory and generate architecture map.
31
34
 
32
- ## Commands
33
-
34
- - `arcvision link <token>` - Link the CLI to a project using an upload token
35
- - `arcvision scan [directory]` - Scan a directory and generate architecture map
36
- - `-u, --upload` - Upload the scan results to the dashboard
35
+ Options:
36
+ - `-u, --upload`: Upload to database
37
37
 
38
38
  ## Features
39
39
 
40
- - Scans JavaScript, TypeScript, JSX, and TSX files
41
- - Detects import/export relationships
42
- - Supports plugin system for enhanced analysis (React components, Express routes, etc.)
43
- - Uploads results to ArcVision dashboard
44
- - Cross-platform support
45
-
46
- ## Plugin System
47
-
48
- The CLI includes a plugin system that enhances code analysis:
49
-
50
- - **React Plugin**: Detects React components and hooks
51
- - **Express Plugin**: Identifies Express routes and endpoints
52
- - **Custom Plugins**: Extend functionality with your own plugins
53
-
54
- ## Development
55
-
56
- To build the CLI from source:
40
+ - **Modern Import Resolution**: Handles path aliases (`@/components/*`), barrel files (`./utils` → `./utils/index.ts`), and directory imports
41
+ - **AST-based Parsing**: Uses Babel parser for accurate import detection
42
+ - **Multi-framework Support**: Works with React, Next.js, and other modern JavaScript frameworks
43
+ - **Dependency Graph Generation**: Creates comprehensive node-edge relationships
44
+ - **Cloud Integration**: Uploads results to ArcVision dashboard for visualization
57
45
 
58
- ```bash
59
- # Clone the repository
60
- git clone <repository-url>
61
-
62
- # Navigate to CLI directory
63
- cd cli
64
-
65
- # Install dependencies
66
- npm install
67
-
68
- # Build the CLI
69
- npm run build
70
-
71
- # Run in development mode
72
- npm run dev
73
- ```
74
-
75
- ## Configuration
76
-
77
- The CLI stores authentication tokens in `~/.arcvisionrc` in your home directory. This file is created automatically when you run `arcvision link <token>`.
46
+ ## Supported Import Patterns
78
47
 
79
- ## API Integration
48
+ - Relative imports: `./utils`, `../components`
49
+ - Path aliases: `@/components/Button`, `~/lib/utils`
50
+ - Barrel files: `./utils` resolves to `./utils/index.ts(x)`
51
+ - Directory imports: `@/utils` resolves to `src/utils/index.ts(x)`
52
+ - Implicit extensions: `.ts`, `.tsx`, `.js`, `.jsx`
80
53
 
81
- The CLI uploads scan results to the ArcVision dashboard API. You can configure the API endpoint using the `ARCVISION_API_URL` environment variable:
54
+ ## Architecture
82
55
 
83
- ```bash
84
- export ARCVISION_API_URL="https://your-deployment-url.com"
85
- ```
56
+ The ArcVision CLI runs locally on your machine and:
57
+ 1. Scans your project files using AST parsing
58
+ 2. Detects import relationships between files
59
+ 3. Resolves path aliases using tsconfig.json/jsconfig.json
60
+ 4. Generates a dependency graph in JSON format
61
+ 5. Uploads the graph to the ArcVision dashboard via API
86
62
 
87
- ## Troubleshooting
63
+ ## License
88
64
 
89
- - **Permission errors**: Ensure you have write permissions to the scan directory and config file location
90
- - **Network errors**: Check your internet connection and API endpoint accessibility
91
- - **Parse errors**: The scanner skips TypeScript declaration files (.d.ts) by default
65
+ MIT
Binary file
package/dist/index.js CHANGED
@@ -56388,6 +56388,118 @@ var require_plugin_manager = __commonJS({
56388
56388
  }
56389
56389
  });
56390
56390
 
56391
+ // src/core/tsconfig-utils.js
56392
+ var require_tsconfig_utils = __commonJS({
56393
+ "src/core/tsconfig-utils.js"(exports2, module2) {
56394
+ var fs2 = require("fs");
56395
+ var path2 = require("path");
56396
+ function loadTSConfig(projectRoot) {
56397
+ const tsconfigPaths = [
56398
+ path2.join(projectRoot, "tsconfig.json"),
56399
+ path2.join(projectRoot, "jsconfig.json")
56400
+ ];
56401
+ for (const tsconfigPath of tsconfigPaths) {
56402
+ if (fs2.existsSync(tsconfigPath)) {
56403
+ try {
56404
+ const raw = JSON.parse(fs2.readFileSync(tsconfigPath, "utf-8"));
56405
+ return raw.compilerOptions || {};
56406
+ } catch (error) {
56407
+ console.warn(`Warning: Could not parse ${tsconfigPath}:`, error.message);
56408
+ return null;
56409
+ }
56410
+ }
56411
+ }
56412
+ return null;
56413
+ }
56414
+ module2.exports = { loadTSConfig };
56415
+ }
56416
+ });
56417
+
56418
+ // src/core/path-resolver.js
56419
+ var require_path_resolver = __commonJS({
56420
+ "src/core/path-resolver.js"(exports2, module2) {
56421
+ var fs2 = require("fs");
56422
+ var path2 = require("path");
56423
+ function resolveImport(importPath, fromFile, projectRoot, tsconfig) {
56424
+ if (importPath.startsWith("node_modules/") || importPath.includes("node_modules/") || importPath.startsWith("http") || importPath.startsWith("//")) {
56425
+ return null;
56426
+ }
56427
+ if (tsconfig && tsconfig.paths && importPath in tsconfig.paths) {
56428
+ const pathMappings = tsconfig.paths[importPath];
56429
+ if (Array.isArray(pathMappings) && pathMappings.length > 0) {
56430
+ const mappedPath = pathMappings[0].replace("*", importPath.substring(0, importPath.lastIndexOf("/")));
56431
+ const realPath = path2.resolve(projectRoot, tsconfig.baseUrl || ".", mappedPath);
56432
+ return resolveFile(realPath);
56433
+ }
56434
+ }
56435
+ if (tsconfig && tsconfig.paths) {
56436
+ const pathKeys = Object.keys(tsconfig.paths);
56437
+ for (const pathKey of pathKeys) {
56438
+ if (pathKey.endsWith("*")) {
56439
+ const prefix = pathKey.slice(0, -1);
56440
+ if (importPath.startsWith(prefix)) {
56441
+ const suffix = importPath.substring(prefix.length);
56442
+ const pathMappings = tsconfig.paths[pathKey];
56443
+ if (Array.isArray(pathMappings) && pathMappings.length > 0) {
56444
+ const mappedPath = pathMappings[0].replace("*", suffix);
56445
+ const realPath = path2.resolve(projectRoot, tsconfig.baseUrl || ".", mappedPath);
56446
+ const resolved = resolveFile(realPath);
56447
+ if (resolved)
56448
+ return resolved;
56449
+ }
56450
+ }
56451
+ }
56452
+ }
56453
+ }
56454
+ if (tsconfig && tsconfig.baseUrl && !importPath.startsWith(".")) {
56455
+ const baseUrlPath = path2.resolve(projectRoot, tsconfig.baseUrl, importPath);
56456
+ const resolved = resolveFile(baseUrlPath);
56457
+ if (resolved)
56458
+ return resolved;
56459
+ }
56460
+ if (importPath.startsWith("@") && tsconfig && tsconfig.baseUrl) {
56461
+ const relativePath = importPath.substring(1);
56462
+ const realPath = path2.resolve(projectRoot, tsconfig.baseUrl, relativePath);
56463
+ const resolved = resolveFile(realPath);
56464
+ if (resolved)
56465
+ return resolved;
56466
+ }
56467
+ if (importPath.startsWith(".")) {
56468
+ const realPath = path2.resolve(path2.dirname(fromFile), importPath);
56469
+ return resolveFile(realPath);
56470
+ }
56471
+ if (!importPath.startsWith(".")) {
56472
+ const realPath = path2.resolve(projectRoot, importPath);
56473
+ return resolveFile(realPath);
56474
+ }
56475
+ return null;
56476
+ }
56477
+ function resolveFile(basePath) {
56478
+ const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
56479
+ const barrelFiles = ["index.ts", "index.tsx", "index.js", "index.jsx"];
56480
+ if (fs2.existsSync(basePath) && fs2.statSync(basePath).isFile()) {
56481
+ return basePath;
56482
+ }
56483
+ for (const ext of extensions) {
56484
+ const pathWithExt = basePath + ext;
56485
+ if (fs2.existsSync(pathWithExt) && fs2.statSync(pathWithExt).isFile()) {
56486
+ return pathWithExt;
56487
+ }
56488
+ }
56489
+ if (fs2.existsSync(basePath) && fs2.statSync(basePath).isDirectory()) {
56490
+ for (const barrelFile of barrelFiles) {
56491
+ const barrelPath = path2.join(basePath, barrelFile);
56492
+ if (fs2.existsSync(barrelPath)) {
56493
+ return barrelPath;
56494
+ }
56495
+ }
56496
+ }
56497
+ return null;
56498
+ }
56499
+ module2.exports = { resolveImport, resolveFile };
56500
+ }
56501
+ });
56502
+
56391
56503
  // src/core/scanner.js
56392
56504
  var require_scanner = __commonJS({
56393
56505
  "src/core/scanner.js"(exports2, module2) {
@@ -56395,6 +56507,8 @@ var require_scanner = __commonJS({
56395
56507
  var path2 = require("path");
56396
56508
  var parser = require_parser();
56397
56509
  var pluginManager = require_plugin_manager();
56510
+ var { loadTSConfig } = require_tsconfig_utils();
56511
+ var { resolveImport } = require_path_resolver();
56398
56512
  async function scan(directory) {
56399
56513
  const options = {
56400
56514
  ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**", "**/build/**"],
@@ -56427,25 +56541,26 @@ var require_scanner = __commonJS({
56427
56541
  }
56428
56542
  }
56429
56543
  const normalize = (p) => p.replace(/\\/g, "/");
56430
- const normalizedFileMap = /* @__PURE__ */ new Set();
56431
- architectureMap.nodes.forEach((n) => normalizedFileMap.add(normalize(n.id)));
56544
+ const tsconfig = loadTSConfig(directory);
56545
+ const normalizedFileMap = /* @__PURE__ */ new Map();
56546
+ architectureMap.nodes.forEach((n) => normalizedFileMap.set(normalize(n.id), n.id));
56432
56547
  architectureMap.nodes.forEach((node) => {
56433
56548
  node.metadata.imports.forEach((imp) => {
56434
- let target = imp.source;
56435
- if (target.startsWith(".")) {
56436
- const dir = path2.dirname(node.id);
56437
- let resolved = path2.join(dir, target);
56438
- const extensions = ["", ".js", ".jsx", ".ts", ".tsx"];
56439
- for (const ext of extensions) {
56440
- const probe = normalize(resolved + ext);
56441
- if (normalizedFileMap.has(probe)) {
56442
- architectureMap.edges.push({
56443
- source: normalize(node.id),
56444
- target: probe,
56445
- type: "import"
56446
- });
56447
- break;
56448
- }
56549
+ const resolvedPath = resolveImport(
56550
+ imp.source,
56551
+ path2.join(directory, node.id),
56552
+ directory,
56553
+ tsconfig
56554
+ );
56555
+ if (resolvedPath) {
56556
+ const relativeResolvedPath = path2.relative(directory, resolvedPath);
56557
+ const normalizedResolvedPath = normalize(relativeResolvedPath);
56558
+ if (normalizedFileMap.has(normalizedResolvedPath)) {
56559
+ architectureMap.edges.push({
56560
+ source: normalize(node.id),
56561
+ target: normalizedResolvedPath,
56562
+ type: "import"
56563
+ });
56449
56564
  }
56450
56565
  }
56451
56566
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcvision",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Architecture scanner for modern codebases",
5
5
  "bin": {
6
6
  "arcvision": "./dist/index.js"
@@ -34,4 +34,4 @@
34
34
  ],
35
35
  "author": "ArcVision",
36
36
  "license": "MIT"
37
- }
37
+ }
@@ -0,0 +1,120 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Resolve an import path to an actual file on disk
6
+ * @param {string} importPath - The import string from the code (e.g., '@/components/Button', './utils')
7
+ * @param {string} fromFile - The file that contains the import
8
+ * @param {string} projectRoot - The root directory of the project
9
+ * @param {Object|null} tsconfig - The compilerOptions from tsconfig.json
10
+ * @returns {string|null} The resolved absolute file path or null if not found
11
+ */
12
+ function resolveImport(importPath, fromFile, projectRoot, tsconfig) {
13
+ // Skip external packages and node_modules
14
+ if (importPath.startsWith('node_modules/') ||
15
+ importPath.includes('node_modules/') ||
16
+ importPath.startsWith('http') ||
17
+ importPath.startsWith('//')) {
18
+ return null;
19
+ }
20
+
21
+ // Handle path aliases (e.g., '@/components/Button', '@/lib/utils')
22
+ if (tsconfig && tsconfig.paths && importPath in tsconfig.paths) {
23
+ // Direct path mapping
24
+ const pathMappings = tsconfig.paths[importPath];
25
+ if (Array.isArray(pathMappings) && pathMappings.length > 0) {
26
+ const mappedPath = pathMappings[0].replace('*', importPath.substring(0, importPath.lastIndexOf('/')));
27
+ const realPath = path.resolve(projectRoot, tsconfig.baseUrl || '.', mappedPath);
28
+ return resolveFile(realPath);
29
+ }
30
+ }
31
+
32
+ // Handle wildcard path aliases (e.g., '@/components/*' -> './src/components/*')
33
+ if (tsconfig && tsconfig.paths) {
34
+ const pathKeys = Object.keys(tsconfig.paths);
35
+ for (const pathKey of pathKeys) {
36
+ if (pathKey.endsWith('*')) {
37
+ const prefix = pathKey.slice(0, -1); // Remove the '*'
38
+ if (importPath.startsWith(prefix)) {
39
+ const suffix = importPath.substring(prefix.length);
40
+ const pathMappings = tsconfig.paths[pathKey];
41
+ if (Array.isArray(pathMappings) && pathMappings.length > 0) {
42
+ const mappedPath = pathMappings[0].replace('*', suffix);
43
+ const realPath = path.resolve(projectRoot, tsconfig.baseUrl || '.', mappedPath);
44
+ const resolved = resolveFile(realPath);
45
+ if (resolved) return resolved;
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ // Handle baseUrl resolution (e.g., 'components/Button' -> './src/components/Button')
53
+ if (tsconfig && tsconfig.baseUrl && !importPath.startsWith('.')) {
54
+ const baseUrlPath = path.resolve(projectRoot, tsconfig.baseUrl, importPath);
55
+ const resolved = resolveFile(baseUrlPath);
56
+ if (resolved) return resolved;
57
+ }
58
+
59
+ // Handle @ imports that don't match specific paths mappings
60
+ if (importPath.startsWith('@') && tsconfig && tsconfig.baseUrl) {
61
+ // Remove @ and resolve relative to baseUrl
62
+ const relativePath = importPath.substring(1); // Remove @
63
+ const realPath = path.resolve(projectRoot, tsconfig.baseUrl, relativePath);
64
+ const resolved = resolveFile(realPath);
65
+ if (resolved) return resolved;
66
+ }
67
+
68
+ // Handle relative paths (e.g., './utils', '../components')
69
+ if (importPath.startsWith('.')) {
70
+ const realPath = path.resolve(path.dirname(fromFile), importPath);
71
+ return resolveFile(realPath);
72
+ }
73
+
74
+ // For absolute paths within the project that don't match tsconfig paths
75
+ if (!importPath.startsWith('.')) {
76
+ // Try resolving relative to project root
77
+ const realPath = path.resolve(projectRoot, importPath);
78
+ return resolveFile(realPath);
79
+ }
80
+
81
+ return null;
82
+ }
83
+
84
+ /**
85
+ * Resolve a potential file path to an actual existing file
86
+ * Handles implicit extensions and barrel files (index files)
87
+ * @param {string} basePath - The base path to resolve
88
+ * @returns {string|null} The resolved file path or null if no file exists
89
+ */
90
+ function resolveFile(basePath) {
91
+ const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
92
+ const barrelFiles = ['index.ts', 'index.tsx', 'index.js', 'index.jsx'];
93
+
94
+ // First, try the exact path
95
+ if (fs.existsSync(basePath) && fs.statSync(basePath).isFile()) {
96
+ return basePath;
97
+ }
98
+
99
+ // Try with extensions
100
+ for (const ext of extensions) {
101
+ const pathWithExt = basePath + ext;
102
+ if (fs.existsSync(pathWithExt) && fs.statSync(pathWithExt).isFile()) {
103
+ return pathWithExt;
104
+ }
105
+ }
106
+
107
+ // Try as a directory with barrel files
108
+ if (fs.existsSync(basePath) && fs.statSync(basePath).isDirectory()) {
109
+ for (const barrelFile of barrelFiles) {
110
+ const barrelPath = path.join(basePath, barrelFile);
111
+ if (fs.existsSync(barrelPath)) {
112
+ return barrelPath;
113
+ }
114
+ }
115
+ }
116
+
117
+ return null;
118
+ }
119
+
120
+ module.exports = { resolveImport, resolveFile };
@@ -2,6 +2,8 @@ const { glob } = require('glob');
2
2
  const path = require('path');
3
3
  const parser = require('./parser');
4
4
  const pluginManager = require('../plugins/plugin-manager');
5
+ const { loadTSConfig } = require('./tsconfig-utils');
6
+ const { resolveImport } = require('./path-resolver');
5
7
 
6
8
  async function scan(directory) {
7
9
  const options = {
@@ -50,27 +52,34 @@ async function scan(directory) {
50
52
  // Normalize helper
51
53
  const normalize = p => p.replace(/\\/g, '/');
52
54
 
53
- const normalizedFileMap = new Set();
54
- architectureMap.nodes.forEach(n => normalizedFileMap.add(normalize(n.id)));
55
+ // Load tsconfig for path resolution
56
+ const tsconfig = loadTSConfig(directory);
57
+
58
+ const normalizedFileMap = new Map();
59
+ architectureMap.nodes.forEach(n => normalizedFileMap.set(normalize(n.id), n.id));
55
60
 
56
61
  architectureMap.nodes.forEach(node => {
57
62
  node.metadata.imports.forEach(imp => {
58
- let target = imp.source;
59
- if (target.startsWith('.')) {
60
- const dir = path.dirname(node.id);
61
- let resolved = path.join(dir, target);
62
- const extensions = ['', '.js', '.jsx', '.ts', '.tsx'];
63
-
64
- for (const ext of extensions) {
65
- const probe = normalize(resolved + ext);
66
- if (normalizedFileMap.has(probe)) {
67
- architectureMap.edges.push({
68
- source: normalize(node.id),
69
- target: probe,
70
- type: 'import'
71
- });
72
- break;
73
- }
63
+ // Resolve the import path using the new resolver
64
+ const resolvedPath = resolveImport(
65
+ imp.source,
66
+ path.join(directory, node.id),
67
+ directory,
68
+ tsconfig
69
+ );
70
+
71
+ if (resolvedPath) {
72
+ // Convert resolved absolute path back to relative path
73
+ const relativeResolvedPath = path.relative(directory, resolvedPath);
74
+ const normalizedResolvedPath = normalize(relativeResolvedPath);
75
+
76
+ // Check if the resolved file exists in our scanned files
77
+ if (normalizedFileMap.has(normalizedResolvedPath)) {
78
+ architectureMap.edges.push({
79
+ source: normalize(node.id),
80
+ target: normalizedResolvedPath,
81
+ type: 'import'
82
+ });
74
83
  }
75
84
  }
76
85
  });
@@ -83,4 +92,4 @@ async function scan(directory) {
83
92
  }
84
93
  }
85
94
 
86
- module.exports = { scan };
95
+ module.exports = { scan };
@@ -0,0 +1,30 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Load and parse tsconfig.json from project root
6
+ * @param {string} projectRoot - The root directory of the project
7
+ * @returns {Object|null} The compilerOptions from tsconfig.json or null if not found
8
+ */
9
+ function loadTSConfig(projectRoot) {
10
+ const tsconfigPaths = [
11
+ path.join(projectRoot, 'tsconfig.json'),
12
+ path.join(projectRoot, 'jsconfig.json')
13
+ ];
14
+
15
+ for (const tsconfigPath of tsconfigPaths) {
16
+ if (fs.existsSync(tsconfigPath)) {
17
+ try {
18
+ const raw = JSON.parse(fs.readFileSync(tsconfigPath, 'utf-8'));
19
+ return raw.compilerOptions || {};
20
+ } catch (error) {
21
+ console.warn(`Warning: Could not parse ${tsconfigPath}:`, error.message);
22
+ return null;
23
+ }
24
+ }
25
+ }
26
+
27
+ return null;
28
+ }
29
+
30
+ module.exports = { loadTSConfig };
package/jest.config.json DELETED
@@ -1,6 +0,0 @@
1
- {
2
- "testEnvironment": "node",
3
- "testMatch": [
4
- "**/tests/**/*.test.js"
5
- ]
6
- }
@@ -1,49 +0,0 @@
1
- const pluginManager = require('./src/plugins/plugin-manager');
2
- const path = require('path');
3
-
4
- describe('Plugin System', () => {
5
- test('should register a plugin', () => {
6
- const testPlugin = {
7
- name: 'test-plugin',
8
- process: async (filePath, metadata) => {
9
- return { testField: 'test-value' };
10
- }
11
- };
12
-
13
- pluginManager.register(testPlugin);
14
- expect(pluginManager.plugins.length).toBeGreaterThan(0);
15
- });
16
-
17
- test('should process file with plugin', async () => {
18
- const testPlugin = {
19
- name: 'test-enhancer',
20
- process: async (filePath, metadata) => {
21
- return { enhanced: true };
22
- }
23
- };
24
-
25
- pluginManager.register(testPlugin);
26
-
27
- const metadata = { id: 'test.js', imports: [] };
28
- const result = await pluginManager.processFile('test.js', metadata);
29
-
30
- expect(result.enhanced).toBe(true);
31
- });
32
-
33
- test('should handle plugin errors gracefully', async () => {
34
- const faultyPlugin = {
35
- name: 'faulty-plugin',
36
- process: async () => {
37
- throw new Error('Plugin error');
38
- }
39
- };
40
-
41
- pluginManager.register(faultyPlugin);
42
-
43
- const metadata = { id: 'test.js' };
44
- const result = await pluginManager.processFile('test.js', metadata);
45
-
46
- // Should not crash
47
- expect(result).toBeDefined();
48
- });
49
- });
@@ -1,52 +0,0 @@
1
- const scanner = require('./src/core/scanner');
2
- const parser = require('./src/core/parser');
3
- const path = require('path');
4
-
5
- describe('ArcVision Scanner', () => {
6
- test('should parse a simple JavaScript file', () => {
7
- const testFile = path.join(__dirname, '../example-project/utils.js');
8
- const metadata = parser.parseFile(testFile);
9
-
10
- expect(metadata).toBeDefined();
11
- expect(metadata.exports).toContain('helper');
12
- expect(metadata.functions).toBeDefined();
13
- });
14
-
15
- test('should scan a directory and return architecture map', async () => {
16
- const testDir = path.join(__dirname, '../example-project');
17
- const map = await scanner.scan(testDir);
18
-
19
- expect(map).toBeDefined();
20
- expect(map.nodes).toBeDefined();
21
- expect(map.edges).toBeDefined();
22
- expect(map.nodes.length).toBeGreaterThan(0);
23
- }, 30000);
24
-
25
- test('should detect imports and create edges', async () => {
26
- const testDir = path.join(__dirname, '../example-project');
27
- const map = await scanner.scan(testDir);
28
-
29
- expect(map.edges.length).toBeGreaterThan(0);
30
- const hasImportEdge = map.edges.some(e => e.type === 'import');
31
- expect(hasImportEdge).toBe(true);
32
- });
33
-
34
- test('should detect API calls', async () => {
35
- const testDir = path.join(__dirname, '../example-project');
36
- const map = await scanner.scan(testDir);
37
-
38
- const apiFile = map.nodes.find(n => n.id.includes('api.js'));
39
- expect(apiFile).toBeDefined();
40
- expect(apiFile.metadata.apiCalls).toBeDefined();
41
- expect(apiFile.metadata.apiCalls.length).toBeGreaterThan(0);
42
- });
43
-
44
- test('should handle circular dependencies', async () => {
45
- // This is a basic test - in real scenarios we'd create files with circular deps
46
- const testDir = path.join(__dirname, '../example-project');
47
- const map = await scanner.scan(testDir);
48
-
49
- // Should not crash
50
- expect(map).toBeDefined();
51
- });
52
- });