• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MohamedGamil / MapX / 26731444457

01 Jun 2026 02:06AM UTC coverage: 88.416% (+8.5%) from 79.877%
26731444457

push

github

MohamedGamil
fix: update worker initialization to use 'tsx/esm/api' for improved module loading

3466 of 4316 branches covered (80.31%)

Branch coverage included in aggregate %.

5800 of 6164 relevant lines covered (94.09%)

17.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.93
/src/core/codebase-profiler.ts
1
import type { Store } from './store.js';
2
import type { CodebaseProfile, CodebaseArchetype, ArchPattern } from '../types.js';
3
import * as path from 'node:path';
4

5
export class CodebaseProfiler {
6
  constructor(private store: Store) {}
28✔
7

8
  /**
9
   * Profiles the repository based on stored files, extensions, paths, and active frameworks.
10
   */
11
  profile(repoName: string, activeFrameworks: string[] = []): CodebaseProfile {
28✔
12
    const files = this.store.getAllFiles(repoName);
28✔
13
    const filePaths = files.map(f => f.path as string);
37✔
14

15
    // Filter out scratch, benchmark, and test files so they don't skew
16
    // framework confidence or archetype detection.
17
    const scratchTestRe = /\.(test|spec)\.[a-z0-9]+$/i;
28✔
18
    const filteredPaths = filePaths.filter(p => {
28✔
19
      const lower = p.toLowerCase();
37✔
20
      if (lower.startsWith('scratch/') || lower.startsWith('benchmarks/')) return false;
37!
21
      if (lower.includes('/tests/') || lower.includes('/test/')) return false;
37!
22
      if (scratchTestRe.test(lower)) return false;
37!
23
      return true;
37✔
24
    });
25

26
    // 1. Language breakdown & file extensions
27
    const extCount: Record<string, number> = {};
28✔
28
    const langCount: Record<string, number> = {};
28✔
29
    for (const f of files) {
28✔
30
      const filePath = f.path as string;
37✔
31
      const ext = path.extname(filePath).toLowerCase();
37✔
32
      extCount[ext] = (extCount[ext] || 0) + 1;
37✔
33
      const lang = ((f.language as string) || 'unknown').toLowerCase();
37✔
34
      langCount[lang] = (langCount[lang] || 0) + 1;
37✔
35
    }
36

37
    const dominantLanguages = Object.entries(langCount)
28✔
38
      .sort((a, b) => b[1] - a[1])
6✔
39
      .map(entry => entry[0]);
22✔
40

41
    // 2. Directory structure signals (uses filteredPaths to avoid test/scratch noise)
42
    let hasBin = false;
28✔
43
    let hasRoutes = false;
28✔
44
    let hasControllers = false;
28✔
45
    let hasComponents = false;
28✔
46
    let hasPages = false;
28✔
47
    let hasPackages = false;
28✔
48
    let hasMobile = false;
28✔
49
    let hasSrc = false;
28✔
50

51
    for (const p of filteredPaths) {
28✔
52
      const lower = p.toLowerCase();
37✔
53
      const parts = lower.split('/');
37✔
54

55
      if (parts.includes('bin') || parts.includes('commands') || lower.includes('cli.ts') || lower.includes('cli.js')) {
37✔
56
        hasBin = true;
2✔
57
      }
58
      if (parts.includes('routes') || parts.includes('route') || lower.includes('.route.')) {
37✔
59
        hasRoutes = true;
1✔
60
      }
61
      if (parts.includes('controllers') || parts.includes('controller') || lower.includes('.controller.')) {
37✔
62
        hasControllers = true;
2✔
63
      }
64
      if (parts.includes('components') || parts.includes('widgets') || parts.includes('views')) {
37✔
65
        hasComponents = true;
3✔
66
      }
67
      if (parts.includes('pages') || parts.includes('screens')) {
37✔
68
        hasPages = true;
3✔
69
      }
70
      if (parts.includes('packages') || parts.includes('apps')) {
37✔
71
        hasPackages = true;
6✔
72
      }
73
      if (parts.includes('android') || parts.includes('ios') || parts.includes('cordova') || parts.includes('capacitor')) {
37!
74
        hasMobile = true;
×
75
      }
76
      // Flutter: lib/main.dart is the canonical entry point; also detect
77
      // android/ or ios/ sibling directories that Flutter scaffolds
78
      if (p.endsWith('lib/main.dart') || parts.includes('flutter') || (parts.includes('lib') && p.endsWith('.dart'))) {
37✔
79
        hasMobile = true;
4✔
80
      }
81
      if (parts.includes('src')) {
37✔
82
        hasSrc = true;
16✔
83
      }
84
    }
85

86
    // 3. Frontend / Backend checks
87
    const frontendExts = ['.vue', '.svelte', '.jsx', '.tsx', '.html', '.css', '.scss', '.sass', '.less'];
28✔
88
    let frontendFileCount = 0;
28✔
89
    for (const [ext, count] of Object.entries(extCount)) {
28✔
90
      if (frontendExts.includes(ext)) {
23✔
91
        frontendFileCount += count;
4✔
92
      }
93
    }
94

95
    const hasFrontend = frontendFileCount > 0 || hasComponents || hasPages;
28✔
96
    const hasBackend = hasRoutes || hasControllers || filteredPaths.some(p => {
28✔
97
      const base = path.basename(p).toLowerCase();
32✔
98
      return base === 'server.ts' || base === 'server.js' || base === 'app.ts' || base === 'app.js';
32✔
99
    });
100

101
    const isMonorepo = hasPackages && filteredPaths.some(p => p.includes('/package.json') && p.split('/').length > 2);
28✔
102

103
    // 4. Archetype Determination
104
    let archetype: CodebaseArchetype = 'mixed';
28✔
105
    let archetypeConfidence = 0.5;
28✔
106

107
    if (isMonorepo) {
28✔
108
      archetype = 'monorepo';
2✔
109
      archetypeConfidence = 0.85;
2✔
110
    } else if (hasMobile) {
26✔
111
      archetype = 'mobile-app';
1✔
112
      archetypeConfidence = 0.8;
1✔
113
    } else if (hasBin && !hasRoutes && !hasControllers && !hasFrontend) {
25✔
114
      archetype = 'cli-tool';
1✔
115
      archetypeConfidence = 0.9;
1✔
116
    } else if (hasBackend && !hasFrontend) {
24✔
117
      archetype = 'web-api';
1✔
118
      archetypeConfidence = 0.85;
1✔
119
    } else if (hasFrontend && !hasBackend && !hasRoutes && !hasControllers) {
23✔
120
      archetype = 'web-app';
1✔
121
      archetypeConfidence = 0.85;
1✔
122
    } else if (hasBackend && hasFrontend) {
22✔
123
      archetype = 'full-stack';
1✔
124
      archetypeConfidence = 0.8;
1✔
125
    } else if (hasSrc && !hasBin && !hasRoutes && !hasControllers && !hasComponents && !hasPages) {
21✔
126
      archetype = 'library';
3✔
127
      archetypeConfidence = 0.75;
3✔
128
    }
129

130
    // 5. Architecture pattern heuristic
131
    const detectedPatterns: ArchPattern[] = [];
28✔
132
    if (hasControllers && hasComponents) {
28✔
133
      detectedPatterns.push('mvc');
1✔
134
    }
135
    if (hasRoutes && hasControllers) {
28✔
136
      detectedPatterns.push('layered');
1✔
137
    }
138
    // Clean/Hexagonal hints: domain/usecase/repository structure
139
    const hasDomain = filePaths.some(p => p.toLowerCase().includes('/domain/'));
35✔
140
    const hasUsecase = filePaths.some(p => p.toLowerCase().includes('/usecases/') || p.toLowerCase().includes('/usecase/'));
36✔
141
    if (hasDomain && hasUsecase) {
28✔
142
      detectedPatterns.push('clean');
1✔
143
    }
144
    if (detectedPatterns.length === 0) {
28✔
145
      detectedPatterns.push('flat');
25✔
146
    }
147

148
    // Determine package component boundaries
149
    const componentBoundaries: string[] = [];
28✔
150
    if (isMonorepo) {
28✔
151
      const boundaries = new Set<string>();
2✔
152
      for (const p of filePaths) {
2✔
153
        const parts = p.split('/');
7✔
154
        if (parts.length > 2 && (parts[parts.length - 1] === 'package.json' || parts[parts.length - 1] === 'cargo.toml' || parts[parts.length - 1] === 'pubspec.yaml')) {
7✔
155
          boundaries.add(parts.slice(0, -1).join('/'));
4✔
156
        }
157
      }
158
      componentBoundaries.push(...boundaries);
2✔
159
    }
160

161
    return {
28✔
162
      archetype,
163
      archetypeConfidence,
164
      detectedFrameworks: activeFrameworks,
165
      detectedPatterns,
166
      dominantLanguages,
167
      hasBackend: hasBackend || archetype === 'web-api' || archetype === 'full-stack',
80✔
168
      hasFrontend: hasFrontend || archetype === 'web-app' || archetype === 'full-stack' || archetype === 'mobile-app',
100✔
169
      isMonorepo,
170
      componentBoundaries,
171
    };
172
  }
173
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc