@ace-sdk/core 2.0.0 → 2.1.0
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/dist/cache/project-index.d.ts +152 -0
- package/dist/cache/project-index.d.ts.map +1 -0
- package/dist/cache/project-index.js +290 -0
- package/dist/cache/project-index.js.map +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +15 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/services/bootstrap-stream.d.ts +113 -0
- package/dist/services/bootstrap-stream.d.ts.map +1 -0
- package/dist/services/bootstrap-stream.js +261 -0
- package/dist/services/bootstrap-stream.js.map +1 -0
- package/dist/services/import-graph.d.ts +111 -0
- package/dist/services/import-graph.d.ts.map +1 -0
- package/dist/services/import-graph.js +292 -0
- package/dist/services/import-graph.js.map +1 -0
- package/dist/services/language-detector.d.ts +120 -0
- package/dist/services/language-detector.d.ts.map +1 -0
- package/dist/services/language-detector.js +210 -0
- package/dist/services/language-detector.js.map +1 -0
- package/dist/types/bootstrap-events.d.ts +251 -0
- package/dist/types/bootstrap-events.d.ts.map +1 -0
- package/dist/types/bootstrap-events.js +103 -0
- package/dist/types/bootstrap-events.js.map +1 -0
- package/dist/types/config.d.ts +5 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/project-dna.d.ts +151 -0
- package/dist/types/project-dna.d.ts.map +1 -0
- package/dist/types/project-dna.js +42 -0
- package/dist/types/project-dna.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import Graph Analysis Service
|
|
3
|
+
*
|
|
4
|
+
* Uses Skott for JS/TS dependency graph analysis with dead code detection.
|
|
5
|
+
* Routes to appropriate analyzer based on primary language.
|
|
6
|
+
*
|
|
7
|
+
* @package @ace-sdk/core
|
|
8
|
+
*/
|
|
9
|
+
import skott from 'skott';
|
|
10
|
+
import { LanguageDetector } from './language-detector.js';
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// =============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Default paths to ignore during analysis
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_IGNORE_PATHS = [
|
|
18
|
+
'node_modules',
|
|
19
|
+
'dist',
|
|
20
|
+
'build',
|
|
21
|
+
'.git',
|
|
22
|
+
'.next',
|
|
23
|
+
'coverage',
|
|
24
|
+
'__tests__',
|
|
25
|
+
'__mocks__',
|
|
26
|
+
'*.test.*',
|
|
27
|
+
'*.spec.*'
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Minimum number of importers to be considered a hub
|
|
31
|
+
*/
|
|
32
|
+
export const HUB_THRESHOLD = 5;
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// Import Graph Builder
|
|
35
|
+
// =============================================================================
|
|
36
|
+
/**
|
|
37
|
+
* Build import graph using Skott for JS/TS projects
|
|
38
|
+
*/
|
|
39
|
+
async function buildSkottGraph(options) {
|
|
40
|
+
const { repoPath, ignorePaths = DEFAULT_IGNORE_PATHS } = options;
|
|
41
|
+
const graph = {
|
|
42
|
+
nodes: new Map(),
|
|
43
|
+
entryPoints: [],
|
|
44
|
+
hubFiles: [],
|
|
45
|
+
leafFiles: [],
|
|
46
|
+
circularDeps: [],
|
|
47
|
+
deadCode: [],
|
|
48
|
+
unusedDeps: []
|
|
49
|
+
};
|
|
50
|
+
try {
|
|
51
|
+
// Run Skott analysis with config
|
|
52
|
+
const skottInstance = await skott({
|
|
53
|
+
cwd: repoPath,
|
|
54
|
+
entrypoint: options.entryPoints?.[0],
|
|
55
|
+
includeBaseDir: false,
|
|
56
|
+
ignorePatterns: ignorePaths,
|
|
57
|
+
dependencyTracking: {
|
|
58
|
+
thirdParty: true,
|
|
59
|
+
builtin: false,
|
|
60
|
+
typeOnly: true
|
|
61
|
+
},
|
|
62
|
+
fileExtensions: ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'],
|
|
63
|
+
tsConfigPath: 'tsconfig.json',
|
|
64
|
+
manifestPath: 'package.json',
|
|
65
|
+
incremental: false,
|
|
66
|
+
circularMaxDepth: 10,
|
|
67
|
+
verbose: false
|
|
68
|
+
});
|
|
69
|
+
// Get structure from the instance
|
|
70
|
+
const structure = skottInstance.getStructure();
|
|
71
|
+
const { files, graph: skottGraph } = structure;
|
|
72
|
+
// Get circular dependencies and unused deps from graph API
|
|
73
|
+
const graphApi = skottInstance.useGraph();
|
|
74
|
+
const circularDependencies = graphApi.findCircularDependencies();
|
|
75
|
+
const unusedDependencies = await skottInstance.findUnusedDependencies();
|
|
76
|
+
// Build our graph from Skott's output
|
|
77
|
+
// SkottNode has: { id, adjacentTo: string[], body: { size, thirdPartyDependencies, builtinDependencies } }
|
|
78
|
+
for (const file of files) {
|
|
79
|
+
const node = skottGraph[file];
|
|
80
|
+
const deps = node?.adjacentTo || [];
|
|
81
|
+
graph.nodes.set(file, {
|
|
82
|
+
path: file,
|
|
83
|
+
imports: deps,
|
|
84
|
+
importedBy: [],
|
|
85
|
+
isEntryPoint: false,
|
|
86
|
+
isHub: false,
|
|
87
|
+
isLeaf: deps.length === 0
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// Build reverse mapping (who imports this file)
|
|
91
|
+
for (const [filePath, node] of graph.nodes) {
|
|
92
|
+
for (const dep of node.imports) {
|
|
93
|
+
const depNode = graph.nodes.get(dep);
|
|
94
|
+
if (depNode) {
|
|
95
|
+
depNode.importedBy.push(filePath);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Identify entry points, hubs, and dead code
|
|
100
|
+
for (const [filePath, node] of graph.nodes) {
|
|
101
|
+
// Entry point: no one imports this file
|
|
102
|
+
if (node.importedBy.length === 0) {
|
|
103
|
+
node.isEntryPoint = true;
|
|
104
|
+
graph.entryPoints.push(filePath);
|
|
105
|
+
}
|
|
106
|
+
// Hub: many files import this
|
|
107
|
+
if (node.importedBy.length >= HUB_THRESHOLD) {
|
|
108
|
+
node.isHub = true;
|
|
109
|
+
graph.hubFiles.push(filePath);
|
|
110
|
+
}
|
|
111
|
+
// Leaf: no dependencies
|
|
112
|
+
if (node.isLeaf) {
|
|
113
|
+
graph.leafFiles.push(filePath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Dead code: files that are never imported (but not entry points)
|
|
117
|
+
// Use Skott's circular dependencies detection
|
|
118
|
+
graph.circularDeps = circularDependencies || [];
|
|
119
|
+
// Unused npm dependencies (thirdParty is the property containing unused deps)
|
|
120
|
+
graph.unusedDeps = unusedDependencies?.thirdParty || [];
|
|
121
|
+
// Find orphan files (not entry points but never imported)
|
|
122
|
+
for (const [filePath, node] of graph.nodes) {
|
|
123
|
+
if (node.importedBy.length === 0 && !isLikelyEntryPoint(filePath)) {
|
|
124
|
+
graph.deadCode.push(filePath);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return graph;
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error('Skott analysis failed:', error);
|
|
131
|
+
return graph;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Check if a file is likely an entry point
|
|
136
|
+
*/
|
|
137
|
+
function isLikelyEntryPoint(filePath) {
|
|
138
|
+
const entryPatterns = [
|
|
139
|
+
/^index\.[jt]sx?$/,
|
|
140
|
+
/^main\.[jt]sx?$/,
|
|
141
|
+
/^app\.[jt]sx?$/,
|
|
142
|
+
/^server\.[jt]sx?$/,
|
|
143
|
+
/^cli\.[jt]sx?$/,
|
|
144
|
+
/\/index\.[jt]sx?$/,
|
|
145
|
+
/\/main\.[jt]sx?$/,
|
|
146
|
+
/src\/index\.[jt]sx?$/,
|
|
147
|
+
/src\/main\.[jt]sx?$/
|
|
148
|
+
];
|
|
149
|
+
return entryPatterns.some(pattern => pattern.test(filePath));
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Build import graph using git co-occurrence for non-JS/TS projects
|
|
153
|
+
* (Fallback when no static analyzer is available)
|
|
154
|
+
*/
|
|
155
|
+
async function buildGitCooccurrenceGraph(_options) {
|
|
156
|
+
// For non-JS/TS projects, we can't do static analysis
|
|
157
|
+
// Return empty graph - future: implement git co-occurrence analysis
|
|
158
|
+
return {
|
|
159
|
+
nodes: new Map(),
|
|
160
|
+
entryPoints: [],
|
|
161
|
+
hubFiles: [],
|
|
162
|
+
leafFiles: [],
|
|
163
|
+
circularDeps: [],
|
|
164
|
+
deadCode: [],
|
|
165
|
+
unusedDeps: []
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// =============================================================================
|
|
169
|
+
// Main Export
|
|
170
|
+
// =============================================================================
|
|
171
|
+
/**
|
|
172
|
+
* Build import graph with language-based routing
|
|
173
|
+
*
|
|
174
|
+
* Routes to the best analyzer based on primary language:
|
|
175
|
+
* - TypeScript/JavaScript: Skott (fast, dead code detection)
|
|
176
|
+
* - Other languages: Git co-occurrence fallback
|
|
177
|
+
*
|
|
178
|
+
* @param options - Import graph options
|
|
179
|
+
* @returns Complete import graph analysis
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* const graph = await buildImportGraph({ repoPath: '/path/to/repo' });
|
|
184
|
+
*
|
|
185
|
+
* console.log(`Entry points: ${graph.entryPoints.length}`);
|
|
186
|
+
* console.log(`Hub files: ${graph.hubFiles.length}`);
|
|
187
|
+
* console.log(`Dead code: ${graph.deadCode.length}`);
|
|
188
|
+
* console.log(`Circular deps: ${graph.circularDeps.length}`);
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export async function buildImportGraph(options) {
|
|
192
|
+
const detector = new LanguageDetector();
|
|
193
|
+
const primaryLang = await detector.getPrimaryLanguage(options.repoPath);
|
|
194
|
+
// Route to best analyzer for this language
|
|
195
|
+
switch (primaryLang) {
|
|
196
|
+
case 'TypeScript':
|
|
197
|
+
case 'JavaScript':
|
|
198
|
+
case 'TSX':
|
|
199
|
+
case 'JSX':
|
|
200
|
+
return buildSkottGraph(options);
|
|
201
|
+
// Future: Add support for other languages
|
|
202
|
+
// case 'Java':
|
|
203
|
+
// case 'Python':
|
|
204
|
+
// case 'Go':
|
|
205
|
+
// return buildDependsGraph(options);
|
|
206
|
+
default:
|
|
207
|
+
// Fallback to git co-occurrence
|
|
208
|
+
return buildGitCooccurrenceGraph(options);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Select priority files from import graph for bootstrap
|
|
213
|
+
*
|
|
214
|
+
* Priority order:
|
|
215
|
+
* 1. Entry points (always include)
|
|
216
|
+
* 2. Hub files (most imported - high value)
|
|
217
|
+
* 3. Files in circular deps (need attention)
|
|
218
|
+
* 4. Leaf files with many exports (API surface)
|
|
219
|
+
* 5. Fill remaining with diverse sampling
|
|
220
|
+
*
|
|
221
|
+
* @param graph - Import graph to select from
|
|
222
|
+
* @param maxFiles - Maximum files to select
|
|
223
|
+
* @returns Array of priority file paths
|
|
224
|
+
*/
|
|
225
|
+
export function selectPriorityFiles(graph, maxFiles) {
|
|
226
|
+
const priority = [];
|
|
227
|
+
const seen = new Set();
|
|
228
|
+
const addUnique = (files, limit) => {
|
|
229
|
+
let added = 0;
|
|
230
|
+
for (const file of files) {
|
|
231
|
+
if (!seen.has(file)) {
|
|
232
|
+
seen.add(file);
|
|
233
|
+
priority.push(file);
|
|
234
|
+
added++;
|
|
235
|
+
if (limit && added >= limit)
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
// 1. Entry points (always include)
|
|
241
|
+
addUnique(graph.entryPoints, 10);
|
|
242
|
+
// 2. Hub files (most imported - high value)
|
|
243
|
+
const sortedHubs = [...graph.hubFiles].sort((a, b) => {
|
|
244
|
+
const aNode = graph.nodes.get(a);
|
|
245
|
+
const bNode = graph.nodes.get(b);
|
|
246
|
+
return (bNode?.importedBy.length || 0) - (aNode?.importedBy.length || 0);
|
|
247
|
+
});
|
|
248
|
+
addUnique(sortedHubs, 20);
|
|
249
|
+
// 3. Files in circular deps (need attention)
|
|
250
|
+
const circularFiles = graph.circularDeps.flat();
|
|
251
|
+
addUnique(circularFiles, 10);
|
|
252
|
+
// 4. Leaf files with many exports (API surface)
|
|
253
|
+
const apiSurface = [...graph.nodes.entries()]
|
|
254
|
+
.filter(([_, n]) => n.isLeaf && n.importedBy.length > 0)
|
|
255
|
+
.sort((a, b) => b[1].importedBy.length - a[1].importedBy.length)
|
|
256
|
+
.map(([p]) => p);
|
|
257
|
+
addUnique(apiSurface, 20);
|
|
258
|
+
// 5. Fill remaining with diverse sampling
|
|
259
|
+
if (priority.length < maxFiles) {
|
|
260
|
+
const remaining = [...graph.nodes.keys()].filter(p => !seen.has(p));
|
|
261
|
+
addUnique(remaining, maxFiles - priority.length);
|
|
262
|
+
}
|
|
263
|
+
return priority.slice(0, maxFiles);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Convert import graph to GraphMetrics for ProjectDNA
|
|
267
|
+
*/
|
|
268
|
+
export function graphToMetrics(graph) {
|
|
269
|
+
return {
|
|
270
|
+
totalFiles: graph.nodes.size,
|
|
271
|
+
hubFiles: graph.hubFiles.slice(0, 20),
|
|
272
|
+
entryPoints: graph.entryPoints.slice(0, 10),
|
|
273
|
+
leafNodes: graph.leafFiles.slice(0, 20),
|
|
274
|
+
circularDeps: graph.circularDeps.slice(0, 10)
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Calculate code health metrics from import graph
|
|
279
|
+
*/
|
|
280
|
+
export function calculateHealthMetrics(graph, totalFiles, avgFileSize, maxFileSize) {
|
|
281
|
+
const deadCodePercentage = totalFiles > 0
|
|
282
|
+
? (graph.deadCode.length / totalFiles) * 100
|
|
283
|
+
: 0;
|
|
284
|
+
return {
|
|
285
|
+
deadCodePercentage,
|
|
286
|
+
circularDepsCount: graph.circularDeps.length,
|
|
287
|
+
unusedDepsCount: graph.unusedDeps.length,
|
|
288
|
+
avgFileSize,
|
|
289
|
+
maxFileSize
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
//# sourceMappingURL=import-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import-graph.js","sourceRoot":"","sources":["../../src/services/import-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AA2D1D,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,cAAc;IACd,MAAM;IACN,OAAO;IACP,MAAM;IACN,OAAO;IACP,UAAU;IACV,WAAW;IACX,WAAW;IACX,UAAU;IACV,UAAU;CACX,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAE/B,gFAAgF;AAChF,uBAAuB;AACvB,gFAAgF;AAEhF;;GAEG;AACH,KAAK,UAAU,eAAe,CAAC,OAA2B;IACxD,MAAM,EAAE,QAAQ,EAAE,WAAW,GAAG,oBAAoB,EAAE,GAAG,OAAO,CAAC;IAEjE,MAAM,KAAK,GAAgB;QACzB,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE;KACf,CAAC;IAEF,IAAI,CAAC;QACH,iCAAiC;QACjC,MAAM,aAAa,GAAG,MAAM,KAAK,CAAC;YAChC,GAAG,EAAE,QAAQ;YACb,UAAU,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;YACpC,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,WAAW;YAC3B,kBAAkB,EAAE;gBAClB,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,KAAK;gBACd,QAAQ,EAAE,IAAI;aACf;YACD,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC9D,YAAY,EAAE,eAAe;YAC7B,YAAY,EAAE,cAAc;YAC5B,WAAW,EAAE,KAAK;YAClB,gBAAgB,EAAE,EAAE;YACpB,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QAEH,kCAAkC;QAClC,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,EAAE,CAAC;QAC/C,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC;QAE/C,2DAA2D;QAC3D,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,CAAC;QAC1C,MAAM,oBAAoB,GAAG,QAAQ,CAAC,wBAAwB,EAAE,CAAC;QACjE,MAAM,kBAAkB,GAAG,MAAM,aAAa,CAAC,sBAAsB,EAAE,CAAC;QAExE,sCAAsC;QACtC,2GAA2G;QAC3G,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC9B,MAAM,IAAI,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC;YAEpC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE;gBACpB,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,IAAI;gBACb,UAAU,EAAE,EAAE;gBACd,YAAY,EAAE,KAAK;gBACnB,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,IAAI,CAAC,MAAM,KAAK,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC;QAED,gDAAgD;QAChD,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC3C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACrC,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACpC,CAAC;YACH,CAAC;QACH,CAAC;QAED,6CAA6C;QAC7C,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC3C,wCAAwC;YACxC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;gBACzB,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACnC,CAAC;YAED,8BAA8B;YAC9B,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;gBAC5C,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBAClB,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;YAED,wBAAwB;YACxB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChB,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QAED,kEAAkE;QAClE,8CAA8C;QAC9C,KAAK,CAAC,YAAY,GAAG,oBAAoB,IAAI,EAAE,CAAC;QAEhD,8EAA8E;QAC9E,KAAK,CAAC,UAAU,GAAG,kBAAkB,EAAE,UAAU,IAAI,EAAE,CAAC;QAExD,0DAA0D;QAC1D,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC3C,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAClE,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,kBAAkB,CAAC,QAAgB;IAC1C,MAAM,aAAa,GAAG;QACpB,kBAAkB;QAClB,iBAAiB;QACjB,gBAAgB;QAChB,mBAAmB;QACnB,gBAAgB;QAChB,mBAAmB;QACnB,kBAAkB;QAClB,sBAAsB;QACtB,qBAAqB;KACtB,CAAC;IAEF,OAAO,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,yBAAyB,CAAC,QAA4B;IACnE,sDAAsD;IACtD,oEAAoE;IACpE,OAAO;QACL,KAAK,EAAE,IAAI,GAAG,EAAE;QAChB,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE;KACf,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,cAAc;AACd,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAA2B;IAChE,MAAM,QAAQ,GAAG,IAAI,gBAAgB,EAAE,CAAC;IACxC,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAExE,2CAA2C;IAC3C,QAAQ,WAAW,EAAE,CAAC;QACpB,KAAK,YAAY,CAAC;QAClB,KAAK,YAAY,CAAC;QAClB,KAAK,KAAK,CAAC;QACX,KAAK,KAAK;YACR,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;QAElC,0CAA0C;QAC1C,eAAe;QACf,iBAAiB;QACjB,aAAa;QACb,uCAAuC;QAEvC;YACE,gCAAgC;YAChC,OAAO,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAkB,EAAE,QAAgB;IACtE,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,SAAS,GAAG,CAAC,KAAe,EAAE,KAAc,EAAE,EAAE;QACpD,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACf,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACpB,KAAK,EAAE,CAAC;gBACR,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK;oBAAE,MAAM;YACrC,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IAEF,mCAAmC;IACnC,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAEjC,4CAA4C;IAC5C,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACnD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACjC,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAE1B,6CAA6C;IAC7C,MAAM,aAAa,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;IAChD,SAAS,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAE7B,gDAAgD;IAChD,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;SAC1C,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;SACvD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;SAC/D,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;IACnB,SAAS,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAE1B,0CAA0C;IAC1C,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,SAAS,CAAC,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;QAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACrC,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC3C,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACvC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;KAC9C,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAkB,EAClB,UAAkB,EAClB,WAAmB,EACnB,WAAmB;IAEnB,MAAM,kBAAkB,GAAG,UAAU,GAAG,CAAC;QACvC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,GAAG;QAC5C,CAAC,CAAC,CAAC,CAAC;IAEN,OAAO;QACL,kBAAkB;QAClB,iBAAiB,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM;QAC5C,eAAe,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM;QACxC,WAAW;QACX,WAAW;KACZ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language Detection Service using GitHub Linguist
|
|
3
|
+
*
|
|
4
|
+
* Dynamically detects programming languages instead of hardcoding file extensions.
|
|
5
|
+
* Uses linguist-js which is a JavaScript port of GitHub's Linguist library.
|
|
6
|
+
*
|
|
7
|
+
* @package @ace-sdk/core
|
|
8
|
+
*/
|
|
9
|
+
import type { Category } from 'linguist-js/dist/types.js';
|
|
10
|
+
import type { LanguageStats as ProjectLanguageStats } from '../types/project-dna.js';
|
|
11
|
+
/**
|
|
12
|
+
* Raw language statistics from linguist-js
|
|
13
|
+
*/
|
|
14
|
+
export interface LanguageStats {
|
|
15
|
+
files: {
|
|
16
|
+
count: number;
|
|
17
|
+
results: Record<string, string | null>;
|
|
18
|
+
};
|
|
19
|
+
languages: {
|
|
20
|
+
count: number;
|
|
21
|
+
results: Record<string, {
|
|
22
|
+
type: string;
|
|
23
|
+
bytes: number;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Options for language analysis
|
|
29
|
+
*/
|
|
30
|
+
export interface LanguageAnalysisOptions {
|
|
31
|
+
/** Use quick mode (skip complex analysis) */
|
|
32
|
+
quick?: boolean;
|
|
33
|
+
/** Glob patterns for files to ignore */
|
|
34
|
+
ignoredFiles?: string[];
|
|
35
|
+
/** File categories to include */
|
|
36
|
+
categories?: Category[];
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Default ignored patterns (vendored, generated, lock files)
|
|
40
|
+
*/
|
|
41
|
+
export declare const DEFAULT_IGNORED_PATTERNS: string[];
|
|
42
|
+
/**
|
|
43
|
+
* Default categories (exclude prose/documentation)
|
|
44
|
+
*/
|
|
45
|
+
export declare const DEFAULT_CATEGORIES: Category[];
|
|
46
|
+
/**
|
|
47
|
+
* Language detection service using GitHub Linguist
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* const detector = new LanguageDetector();
|
|
52
|
+
*
|
|
53
|
+
* // Get primary language
|
|
54
|
+
* const primary = await detector.getPrimaryLanguage('/path/to/repo');
|
|
55
|
+
* console.log(primary); // "TypeScript"
|
|
56
|
+
*
|
|
57
|
+
* // Get language breakdown
|
|
58
|
+
* const breakdown = await detector.getLanguageBreakdown('/path/to/repo');
|
|
59
|
+
* console.log(breakdown); // { TypeScript: 75.5, JavaScript: 20.3, ... }
|
|
60
|
+
*
|
|
61
|
+
* // Get all programming files
|
|
62
|
+
* const files = await detector.getProgrammingFiles('/path/to/repo', 1000);
|
|
63
|
+
* console.log(files); // ["src/index.ts", "src/utils.ts", ...]
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare class LanguageDetector {
|
|
67
|
+
/**
|
|
68
|
+
* Analyze a directory and detect all programming languages
|
|
69
|
+
*
|
|
70
|
+
* @param repoPath - Path to repository
|
|
71
|
+
* @param options - Analysis options
|
|
72
|
+
* @returns Language statistics with file mappings
|
|
73
|
+
*/
|
|
74
|
+
analyzeDirectory(repoPath: string, options?: LanguageAnalysisOptions): Promise<LanguageStats>;
|
|
75
|
+
/**
|
|
76
|
+
* Get all source files of programming languages (excluding vendor, generated, docs)
|
|
77
|
+
*
|
|
78
|
+
* @param repoPath - Path to repository
|
|
79
|
+
* @param maxFiles - Maximum files to return (-1 for unlimited)
|
|
80
|
+
* @returns Array of file paths
|
|
81
|
+
*/
|
|
82
|
+
getProgrammingFiles(repoPath: string, maxFiles?: number): Promise<string[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Get language breakdown for repository
|
|
85
|
+
*
|
|
86
|
+
* @param repoPath - Path to repository
|
|
87
|
+
* @returns Map of language name to percentage
|
|
88
|
+
*/
|
|
89
|
+
getLanguageBreakdown(repoPath: string): Promise<Record<string, number>>;
|
|
90
|
+
/**
|
|
91
|
+
* Get detailed language stats for ProjectDNA
|
|
92
|
+
*
|
|
93
|
+
* @param repoPath - Path to repository
|
|
94
|
+
* @returns Array of LanguageStats for ProjectDNA
|
|
95
|
+
*/
|
|
96
|
+
getLanguageStatsForDNA(repoPath: string): Promise<ProjectLanguageStats[]>;
|
|
97
|
+
/**
|
|
98
|
+
* Check if file is a programming language file
|
|
99
|
+
*
|
|
100
|
+
* @param filepath - Full path to file
|
|
101
|
+
* @returns Language name or null
|
|
102
|
+
*/
|
|
103
|
+
detectFileLanguage(filepath: string): Promise<string | null>;
|
|
104
|
+
/**
|
|
105
|
+
* Get primary language of repository
|
|
106
|
+
*
|
|
107
|
+
* @param repoPath - Path to repository
|
|
108
|
+
* @returns Primary language name or null
|
|
109
|
+
*/
|
|
110
|
+
getPrimaryLanguage(repoPath: string): Promise<string | null>;
|
|
111
|
+
/**
|
|
112
|
+
* Get files by language
|
|
113
|
+
*
|
|
114
|
+
* @param repoPath - Path to repository
|
|
115
|
+
* @param language - Language to filter by
|
|
116
|
+
* @returns Array of file paths for that language
|
|
117
|
+
*/
|
|
118
|
+
getFilesByLanguage(repoPath: string, language: string): Promise<string[]>;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=language-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-detector.d.ts","sourceRoot":"","sources":["../../src/services/language-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,IAAI,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAMrF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;KACxC,CAAC;IACF,SAAS,EAAE;QACT,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;YACtB,IAAI,EAAE,MAAM,CAAC;YACb,KAAK,EAAE,MAAM,CAAC;SACf,CAAC,CAAC;KACJ,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,6CAA6C;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,iCAAiC;IACjC,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAC;CACzB;AAMD;;GAEG;AACH,eAAO,MAAM,wBAAwB,UAcpC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAQ,EAAsC,CAAC;AAMhF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,gBAAgB;IAC3B;;;;;;OAMG;IACG,gBAAgB,CACpB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,aAAa,CAAC;IA8BzB;;;;;;OAMG;IACG,mBAAmB,CACvB,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAAa,GACtB,OAAO,CAAC,MAAM,EAAE,CAAC;IAgBpB;;;;;OAKG;IACG,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAmB7E;;;;;OAKG;IACG,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAoC/E;;;;;OAKG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAalE;;;;;OAKG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IASlE;;;;;;OAMG;IACG,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;CAOhF"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language Detection Service using GitHub Linguist
|
|
3
|
+
*
|
|
4
|
+
* Dynamically detects programming languages instead of hardcoding file extensions.
|
|
5
|
+
* Uses linguist-js which is a JavaScript port of GitHub's Linguist library.
|
|
6
|
+
*
|
|
7
|
+
* @package @ace-sdk/core
|
|
8
|
+
*/
|
|
9
|
+
import linguist from 'linguist-js';
|
|
10
|
+
// =============================================================================
|
|
11
|
+
// Default Options
|
|
12
|
+
// =============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Default ignored patterns (vendored, generated, lock files)
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_IGNORED_PATTERNS = [
|
|
17
|
+
'node_modules/**',
|
|
18
|
+
'dist/**',
|
|
19
|
+
'build/**',
|
|
20
|
+
'.git/**',
|
|
21
|
+
'.next/**',
|
|
22
|
+
'target/**',
|
|
23
|
+
'__pycache__/**',
|
|
24
|
+
'venv/**',
|
|
25
|
+
'*.min.js',
|
|
26
|
+
'*.bundle.js',
|
|
27
|
+
'package-lock.json',
|
|
28
|
+
'yarn.lock',
|
|
29
|
+
'pnpm-lock.yaml'
|
|
30
|
+
];
|
|
31
|
+
/**
|
|
32
|
+
* Default categories (exclude prose/documentation)
|
|
33
|
+
*/
|
|
34
|
+
export const DEFAULT_CATEGORIES = ['programming', 'markup', 'data'];
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Language Detector Class
|
|
37
|
+
// =============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* Language detection service using GitHub Linguist
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const detector = new LanguageDetector();
|
|
44
|
+
*
|
|
45
|
+
* // Get primary language
|
|
46
|
+
* const primary = await detector.getPrimaryLanguage('/path/to/repo');
|
|
47
|
+
* console.log(primary); // "TypeScript"
|
|
48
|
+
*
|
|
49
|
+
* // Get language breakdown
|
|
50
|
+
* const breakdown = await detector.getLanguageBreakdown('/path/to/repo');
|
|
51
|
+
* console.log(breakdown); // { TypeScript: 75.5, JavaScript: 20.3, ... }
|
|
52
|
+
*
|
|
53
|
+
* // Get all programming files
|
|
54
|
+
* const files = await detector.getProgrammingFiles('/path/to/repo', 1000);
|
|
55
|
+
* console.log(files); // ["src/index.ts", "src/utils.ts", ...]
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export class LanguageDetector {
|
|
59
|
+
/**
|
|
60
|
+
* Analyze a directory and detect all programming languages
|
|
61
|
+
*
|
|
62
|
+
* @param repoPath - Path to repository
|
|
63
|
+
* @param options - Analysis options
|
|
64
|
+
* @returns Language statistics with file mappings
|
|
65
|
+
*/
|
|
66
|
+
async analyzeDirectory(repoPath, options = {}) {
|
|
67
|
+
const mergedOptions = {
|
|
68
|
+
quick: true,
|
|
69
|
+
ignoredFiles: DEFAULT_IGNORED_PATTERNS,
|
|
70
|
+
categories: DEFAULT_CATEGORIES,
|
|
71
|
+
...options
|
|
72
|
+
};
|
|
73
|
+
try {
|
|
74
|
+
const result = await linguist(repoPath, mergedOptions);
|
|
75
|
+
return {
|
|
76
|
+
files: {
|
|
77
|
+
count: result.files.count,
|
|
78
|
+
results: result.files.results
|
|
79
|
+
},
|
|
80
|
+
languages: {
|
|
81
|
+
count: result.languages.count,
|
|
82
|
+
results: result.languages.results
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error('Language detection failed:', error);
|
|
88
|
+
return {
|
|
89
|
+
files: { count: 0, results: {} },
|
|
90
|
+
languages: { count: 0, results: {} }
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get all source files of programming languages (excluding vendor, generated, docs)
|
|
96
|
+
*
|
|
97
|
+
* @param repoPath - Path to repository
|
|
98
|
+
* @param maxFiles - Maximum files to return (-1 for unlimited)
|
|
99
|
+
* @returns Array of file paths
|
|
100
|
+
*/
|
|
101
|
+
async getProgrammingFiles(repoPath, maxFiles = 5000) {
|
|
102
|
+
const stats = await this.analyzeDirectory(repoPath);
|
|
103
|
+
// Filter for programming and markup files
|
|
104
|
+
const programmingFiles = Object.entries(stats.files.results)
|
|
105
|
+
.filter(([_, lang]) => lang !== null) // Has detected language
|
|
106
|
+
.map(([filepath]) => filepath);
|
|
107
|
+
// Respect maxFiles limit
|
|
108
|
+
if (maxFiles === -1) {
|
|
109
|
+
return programmingFiles;
|
|
110
|
+
}
|
|
111
|
+
return programmingFiles.slice(0, maxFiles);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get language breakdown for repository
|
|
115
|
+
*
|
|
116
|
+
* @param repoPath - Path to repository
|
|
117
|
+
* @returns Map of language name to percentage
|
|
118
|
+
*/
|
|
119
|
+
async getLanguageBreakdown(repoPath) {
|
|
120
|
+
const stats = await this.analyzeDirectory(repoPath);
|
|
121
|
+
const totalBytes = Object.values(stats.languages.results)
|
|
122
|
+
.reduce((sum, lang) => sum + lang.bytes, 0);
|
|
123
|
+
if (totalBytes === 0) {
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
const breakdown = {};
|
|
127
|
+
for (const [langName, langData] of Object.entries(stats.languages.results)) {
|
|
128
|
+
breakdown[langName] = (langData.bytes / totalBytes) * 100;
|
|
129
|
+
}
|
|
130
|
+
return breakdown;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get detailed language stats for ProjectDNA
|
|
134
|
+
*
|
|
135
|
+
* @param repoPath - Path to repository
|
|
136
|
+
* @returns Array of LanguageStats for ProjectDNA
|
|
137
|
+
*/
|
|
138
|
+
async getLanguageStatsForDNA(repoPath) {
|
|
139
|
+
const stats = await this.analyzeDirectory(repoPath);
|
|
140
|
+
const totalBytes = Object.values(stats.languages.results)
|
|
141
|
+
.reduce((sum, lang) => sum + lang.bytes, 0);
|
|
142
|
+
if (totalBytes === 0) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
// Count files per language
|
|
146
|
+
const fileCountByLang = {};
|
|
147
|
+
for (const [_, lang] of Object.entries(stats.files.results)) {
|
|
148
|
+
if (lang) {
|
|
149
|
+
fileCountByLang[lang] = (fileCountByLang[lang] || 0) + 1;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Build language stats
|
|
153
|
+
const languageStats = [];
|
|
154
|
+
for (const [langName, langData] of Object.entries(stats.languages.results)) {
|
|
155
|
+
languageStats.push({
|
|
156
|
+
name: langName,
|
|
157
|
+
percentage: (langData.bytes / totalBytes) * 100,
|
|
158
|
+
files: fileCountByLang[langName] || 0,
|
|
159
|
+
bytes: langData.bytes
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
// Sort by percentage descending
|
|
163
|
+
languageStats.sort((a, b) => b.percentage - a.percentage);
|
|
164
|
+
return languageStats;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check if file is a programming language file
|
|
168
|
+
*
|
|
169
|
+
* @param filepath - Full path to file
|
|
170
|
+
* @returns Language name or null
|
|
171
|
+
*/
|
|
172
|
+
async detectFileLanguage(filepath) {
|
|
173
|
+
try {
|
|
174
|
+
const result = await linguist([filepath], {
|
|
175
|
+
quick: true,
|
|
176
|
+
categories: DEFAULT_CATEGORIES
|
|
177
|
+
});
|
|
178
|
+
return result.files.results[filepath] || null;
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get primary language of repository
|
|
186
|
+
*
|
|
187
|
+
* @param repoPath - Path to repository
|
|
188
|
+
* @returns Primary language name or null
|
|
189
|
+
*/
|
|
190
|
+
async getPrimaryLanguage(repoPath) {
|
|
191
|
+
const breakdown = await this.getLanguageBreakdown(repoPath);
|
|
192
|
+
const sorted = Object.entries(breakdown)
|
|
193
|
+
.sort((a, b) => b[1] - a[1]);
|
|
194
|
+
return sorted.length > 0 ? sorted[0][0] : null;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get files by language
|
|
198
|
+
*
|
|
199
|
+
* @param repoPath - Path to repository
|
|
200
|
+
* @param language - Language to filter by
|
|
201
|
+
* @returns Array of file paths for that language
|
|
202
|
+
*/
|
|
203
|
+
async getFilesByLanguage(repoPath, language) {
|
|
204
|
+
const stats = await this.analyzeDirectory(repoPath);
|
|
205
|
+
return Object.entries(stats.files.results)
|
|
206
|
+
.filter(([_, lang]) => lang === language)
|
|
207
|
+
.map(([filepath]) => filepath);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=language-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-detector.js","sourceRoot":"","sources":["../../src/services/language-detector.ts"],"names":[],"mappings":"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"}
|