swynx-lite 1.0.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/README.md +113 -0
- package/bin/swynx-lite +3 -0
- package/package.json +47 -0
- package/src/clean.mjs +280 -0
- package/src/cli.mjs +264 -0
- package/src/config.mjs +121 -0
- package/src/output/console.mjs +298 -0
- package/src/output/json.mjs +76 -0
- package/src/output/progress.mjs +57 -0
- package/src/scan.mjs +143 -0
- package/src/security.mjs +62 -0
- package/src/shared/fixer/barrel-cleaner.mjs +192 -0
- package/src/shared/fixer/import-cleaner.mjs +237 -0
- package/src/shared/fixer/quarantine.mjs +218 -0
- package/src/shared/scanner/analysers/buildSystems.mjs +647 -0
- package/src/shared/scanner/analysers/configParsers.mjs +1086 -0
- package/src/shared/scanner/analysers/deadcode.mjs +6194 -0
- package/src/shared/scanner/analysers/entryPointDetector.mjs +634 -0
- package/src/shared/scanner/analysers/generatedCode.mjs +297 -0
- package/src/shared/scanner/analysers/imports.mjs +60 -0
- package/src/shared/scanner/discovery.mjs +240 -0
- package/src/shared/scanner/parse-worker.mjs +82 -0
- package/src/shared/scanner/parsers/assets.mjs +44 -0
- package/src/shared/scanner/parsers/csharp.mjs +400 -0
- package/src/shared/scanner/parsers/css.mjs +60 -0
- package/src/shared/scanner/parsers/go.mjs +445 -0
- package/src/shared/scanner/parsers/java.mjs +364 -0
- package/src/shared/scanner/parsers/javascript.mjs +823 -0
- package/src/shared/scanner/parsers/kotlin.mjs +350 -0
- package/src/shared/scanner/parsers/python.mjs +497 -0
- package/src/shared/scanner/parsers/registry.mjs +233 -0
- package/src/shared/scanner/parsers/rust.mjs +427 -0
- package/src/shared/scanner/scan-dead-code.mjs +316 -0
- package/src/shared/security/patterns.mjs +349 -0
- package/src/shared/security/proximity.mjs +84 -0
- package/src/shared/security/scanner.mjs +269 -0
|
@@ -0,0 +1,634 @@
|
|
|
1
|
+
// src/scanner/analysers/entryPointDetector.mjs
|
|
2
|
+
// Unified entry point detection for multi-language dead code analysis
|
|
3
|
+
|
|
4
|
+
import { readFileSync, existsSync } from 'fs';
|
|
5
|
+
import { join, dirname, basename, extname, relative } from 'path';
|
|
6
|
+
import { globSync } from 'glob';
|
|
7
|
+
import { collectConfigEntryPoints } from './configParsers.mjs';
|
|
8
|
+
import { detectBuildSystems, getPackageDirectories } from './buildSystems.mjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Default entry point file patterns (language-agnostic)
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_ENTRY_PATTERNS = [
|
|
14
|
+
// JavaScript/TypeScript
|
|
15
|
+
/^index\.(m?js|jsx?|tsx?)$/,
|
|
16
|
+
/^main\.(m?js|jsx?|tsx?)$/,
|
|
17
|
+
/^app\.(m?js|jsx?|tsx?)$/,
|
|
18
|
+
/^server\.(m?js|jsx?|tsx?)$/,
|
|
19
|
+
/^cli\.(m?js|jsx?|tsx?)$/,
|
|
20
|
+
/^entry\.(m?js|jsx?|tsx?)$/,
|
|
21
|
+
/src\/index\.(m?js|jsx?|tsx?)$/,
|
|
22
|
+
/src\/main\.(m?js|jsx?|tsx?)$/,
|
|
23
|
+
/src\/app\.(m?js|jsx?|tsx?)$/,
|
|
24
|
+
|
|
25
|
+
// Python
|
|
26
|
+
/^__main__\.py$/,
|
|
27
|
+
/^main\.py$/,
|
|
28
|
+
/^app\.py$/,
|
|
29
|
+
/^manage\.py$/,
|
|
30
|
+
/^wsgi\.py$/,
|
|
31
|
+
/^asgi\.py$/,
|
|
32
|
+
|
|
33
|
+
// Go
|
|
34
|
+
/^main\.go$/,
|
|
35
|
+
/cmd\/[^/]+\/main\.go$/,
|
|
36
|
+
|
|
37
|
+
// Java
|
|
38
|
+
/^Main\.java$/,
|
|
39
|
+
/Application\.java$/,
|
|
40
|
+
/SpringBootApplication/,
|
|
41
|
+
|
|
42
|
+
// C#
|
|
43
|
+
/^Program\.cs$/,
|
|
44
|
+
/^Startup\.cs$/,
|
|
45
|
+
|
|
46
|
+
// Ruby
|
|
47
|
+
/^Rakefile$/,
|
|
48
|
+
/^config\.ru$/,
|
|
49
|
+
/^application\.rb$/,
|
|
50
|
+
|
|
51
|
+
// PHP
|
|
52
|
+
/^index\.php$/,
|
|
53
|
+
/^artisan$/,
|
|
54
|
+
|
|
55
|
+
// Rust
|
|
56
|
+
/^main\.rs$/,
|
|
57
|
+
/src\/main\.rs$/,
|
|
58
|
+
/src\/bin\/[^/]+\.rs$/
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* DI decorator patterns by language/framework
|
|
63
|
+
*/
|
|
64
|
+
const DI_DECORATORS_BY_LANGUAGE = {
|
|
65
|
+
javascript: [
|
|
66
|
+
'Service', 'Injectable', 'Controller', 'Module', 'Component',
|
|
67
|
+
'Entity', 'Repository', 'Resolver', 'Guard', 'Pipe',
|
|
68
|
+
'EventSubscriber', 'Subscriber', 'Singleton'
|
|
69
|
+
],
|
|
70
|
+
java: [
|
|
71
|
+
'Service', 'Component', 'Repository', 'Controller', 'RestController',
|
|
72
|
+
'Configuration', 'Bean', 'Autowired', 'Inject', 'Named', 'Singleton',
|
|
73
|
+
'Entity', 'ManagedBean', 'Stateless', 'Stateful'
|
|
74
|
+
],
|
|
75
|
+
csharp: [
|
|
76
|
+
'Controller', 'ApiController', 'Service', 'Scoped', 'Singleton', 'Transient',
|
|
77
|
+
'Entity', 'Table', 'DbContext', 'Injectable'
|
|
78
|
+
],
|
|
79
|
+
python: [
|
|
80
|
+
'app.route', 'router.get', 'router.post', 'router.put', 'router.delete',
|
|
81
|
+
'task', 'shared_task', 'celery.task'
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Unified entry point detection result
|
|
87
|
+
*/
|
|
88
|
+
class EntryPointResult {
|
|
89
|
+
constructor() {
|
|
90
|
+
this.entryPoints = new Map(); // filePath -> { reason, source, confidence, isDynamic }
|
|
91
|
+
this.sources = {
|
|
92
|
+
packageJson: [],
|
|
93
|
+
html: [],
|
|
94
|
+
bundlerConfig: [],
|
|
95
|
+
ciConfig: [],
|
|
96
|
+
diAnnotation: [],
|
|
97
|
+
convention: [],
|
|
98
|
+
buildSystem: []
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
add(filePath, info) {
|
|
103
|
+
const existing = this.entryPoints.get(filePath);
|
|
104
|
+
if (!existing || info.confidence > (existing.confidence || 0)) {
|
|
105
|
+
this.entryPoints.set(filePath, info);
|
|
106
|
+
}
|
|
107
|
+
if (info.source && this.sources[info.source]) {
|
|
108
|
+
this.sources[info.source].push(filePath);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
has(filePath) {
|
|
113
|
+
return this.entryPoints.has(filePath);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get(filePath) {
|
|
117
|
+
return this.entryPoints.get(filePath);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
getAll() {
|
|
121
|
+
return [...this.entryPoints.entries()].map(([file, info]) => ({
|
|
122
|
+
file,
|
|
123
|
+
...info
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getFiles() {
|
|
128
|
+
return new Set(this.entryPoints.keys());
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Extract entry points from package.json
|
|
134
|
+
*/
|
|
135
|
+
function extractPackageJsonEntries(packageJson, projectPath = '') {
|
|
136
|
+
const entries = [];
|
|
137
|
+
|
|
138
|
+
if (!packageJson) return entries;
|
|
139
|
+
|
|
140
|
+
// main field
|
|
141
|
+
if (packageJson.main) {
|
|
142
|
+
entries.push({
|
|
143
|
+
path: packageJson.main.replace(/^\.\//, ''),
|
|
144
|
+
reason: 'Package main entry',
|
|
145
|
+
source: 'packageJson',
|
|
146
|
+
confidence: 0.9
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// module field (ESM entry)
|
|
151
|
+
if (packageJson.module) {
|
|
152
|
+
entries.push({
|
|
153
|
+
path: packageJson.module.replace(/^\.\//, ''),
|
|
154
|
+
reason: 'Package module entry (ESM)',
|
|
155
|
+
source: 'packageJson',
|
|
156
|
+
confidence: 0.9
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// bin field
|
|
161
|
+
if (packageJson.bin) {
|
|
162
|
+
const bins = typeof packageJson.bin === 'string'
|
|
163
|
+
? [packageJson.bin]
|
|
164
|
+
: Object.values(packageJson.bin);
|
|
165
|
+
for (const bin of bins) {
|
|
166
|
+
entries.push({
|
|
167
|
+
path: bin.replace(/^\.\//, ''),
|
|
168
|
+
reason: 'Package bin entry',
|
|
169
|
+
source: 'packageJson',
|
|
170
|
+
confidence: 0.95
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// exports field
|
|
176
|
+
if (packageJson.exports) {
|
|
177
|
+
const extractExports = (exp, path = '') => {
|
|
178
|
+
if (typeof exp === 'string') {
|
|
179
|
+
entries.push({
|
|
180
|
+
path: exp.replace(/^\.\//, ''),
|
|
181
|
+
reason: `Package exports entry${path ? ` (${path})` : ''}`,
|
|
182
|
+
source: 'packageJson',
|
|
183
|
+
confidence: 0.9
|
|
184
|
+
});
|
|
185
|
+
} else if (typeof exp === 'object' && exp !== null) {
|
|
186
|
+
for (const [key, value] of Object.entries(exp)) {
|
|
187
|
+
extractExports(value, key);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
extractExports(packageJson.exports);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// scripts (extract referenced files)
|
|
195
|
+
if (packageJson.scripts) {
|
|
196
|
+
for (const [name, script] of Object.entries(packageJson.scripts)) {
|
|
197
|
+
// Match node/npx/ts-node commands
|
|
198
|
+
const matches = script.matchAll(/(?:node|npx|ts-node|tsx)\s+([^\s&|;]+)/g);
|
|
199
|
+
for (const match of matches) {
|
|
200
|
+
const file = match[1].replace(/^\.\//, '');
|
|
201
|
+
if (file.match(/\.(m?js|ts|tsx?)$/)) {
|
|
202
|
+
entries.push({
|
|
203
|
+
path: file,
|
|
204
|
+
reason: `Referenced in npm script "${name}"`,
|
|
205
|
+
source: 'packageJson',
|
|
206
|
+
confidence: 0.85
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return entries;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Extract entry points from HTML files
|
|
218
|
+
*/
|
|
219
|
+
function extractHtmlEntries(projectPath) {
|
|
220
|
+
const entries = [];
|
|
221
|
+
|
|
222
|
+
if (!projectPath) return entries;
|
|
223
|
+
|
|
224
|
+
const htmlPatterns = [
|
|
225
|
+
'*.html',
|
|
226
|
+
'public/*.html',
|
|
227
|
+
'src/*.html',
|
|
228
|
+
'static/*.html',
|
|
229
|
+
'views/**/*.html',
|
|
230
|
+
'templates/**/*.html'
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
for (const pattern of htmlPatterns) {
|
|
234
|
+
try {
|
|
235
|
+
const htmlFiles = globSync(pattern, {
|
|
236
|
+
cwd: projectPath,
|
|
237
|
+
nodir: true,
|
|
238
|
+
ignore: ['node_modules/**', 'dist/**', 'build/**']
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
for (const htmlFile of htmlFiles) {
|
|
242
|
+
const fullPath = join(projectPath, htmlFile);
|
|
243
|
+
try {
|
|
244
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
245
|
+
const htmlDir = dirname(htmlFile);
|
|
246
|
+
|
|
247
|
+
// Match script tags with src attribute
|
|
248
|
+
const scriptPattern = /<script[^>]*\ssrc=["']([^"']+)["'][^>]*>/gi;
|
|
249
|
+
let match;
|
|
250
|
+
while ((match = scriptPattern.exec(content)) !== null) {
|
|
251
|
+
let src = match[1];
|
|
252
|
+
|
|
253
|
+
// Skip external scripts
|
|
254
|
+
if (src.startsWith('http://') || src.startsWith('https://') || src.startsWith('//')) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Resolve relative paths
|
|
259
|
+
if (src.startsWith('./')) {
|
|
260
|
+
src = join(htmlDir, src.slice(2));
|
|
261
|
+
} else if (src.startsWith('/')) {
|
|
262
|
+
src = src.slice(1);
|
|
263
|
+
} else if (!src.includes('://')) {
|
|
264
|
+
src = join(htmlDir, src);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
src = src.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
268
|
+
|
|
269
|
+
entries.push({
|
|
270
|
+
path: src,
|
|
271
|
+
reason: `Referenced in ${htmlFile}`,
|
|
272
|
+
source: 'html',
|
|
273
|
+
confidence: 0.9
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
// Ignore read errors
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} catch {
|
|
281
|
+
// Ignore glob errors
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return entries;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Check if a file matches entry point patterns
|
|
290
|
+
*/
|
|
291
|
+
function matchesEntryPattern(filePath, customPatterns = []) {
|
|
292
|
+
const allPatterns = [...DEFAULT_ENTRY_PATTERNS, ...customPatterns];
|
|
293
|
+
|
|
294
|
+
for (const pattern of allPatterns) {
|
|
295
|
+
if (pattern.test(filePath)) {
|
|
296
|
+
return {
|
|
297
|
+
matches: true,
|
|
298
|
+
pattern: pattern.toString(),
|
|
299
|
+
confidence: 0.7
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return { matches: false };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Detect entry points from multi-language build systems
|
|
309
|
+
*/
|
|
310
|
+
function detectBuildSystemEntries(projectPath) {
|
|
311
|
+
const entries = [];
|
|
312
|
+
|
|
313
|
+
if (!projectPath) return entries;
|
|
314
|
+
|
|
315
|
+
const buildSystems = detectBuildSystems(projectPath);
|
|
316
|
+
|
|
317
|
+
for (const system of buildSystems) {
|
|
318
|
+
// Each build system may define entry points differently
|
|
319
|
+
switch (system.type) {
|
|
320
|
+
case 'gradle':
|
|
321
|
+
case 'maven':
|
|
322
|
+
// Java: look for src/main/java/**/Application.java
|
|
323
|
+
try {
|
|
324
|
+
const javaFiles = globSync('src/main/java/**/*Application.java', {
|
|
325
|
+
cwd: projectPath,
|
|
326
|
+
nodir: true
|
|
327
|
+
});
|
|
328
|
+
for (const file of javaFiles) {
|
|
329
|
+
entries.push({
|
|
330
|
+
path: file,
|
|
331
|
+
reason: `Spring Boot application (${system.type})`,
|
|
332
|
+
source: 'buildSystem',
|
|
333
|
+
confidence: 0.9
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
} catch { /* ignore */ }
|
|
337
|
+
break;
|
|
338
|
+
|
|
339
|
+
case 'cargo':
|
|
340
|
+
// Rust: src/main.rs and src/bin/*.rs
|
|
341
|
+
if (existsSync(join(projectPath, 'src/main.rs'))) {
|
|
342
|
+
entries.push({
|
|
343
|
+
path: 'src/main.rs',
|
|
344
|
+
reason: 'Cargo binary entry',
|
|
345
|
+
source: 'buildSystem',
|
|
346
|
+
confidence: 0.95
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
try {
|
|
350
|
+
const binFiles = globSync('src/bin/*.rs', {
|
|
351
|
+
cwd: projectPath,
|
|
352
|
+
nodir: true
|
|
353
|
+
});
|
|
354
|
+
for (const file of binFiles) {
|
|
355
|
+
entries.push({
|
|
356
|
+
path: file,
|
|
357
|
+
reason: 'Cargo binary entry',
|
|
358
|
+
source: 'buildSystem',
|
|
359
|
+
confidence: 0.95
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
} catch { /* ignore */ }
|
|
363
|
+
break;
|
|
364
|
+
|
|
365
|
+
case 'go':
|
|
366
|
+
// Go: main.go and cmd/*/main.go
|
|
367
|
+
if (existsSync(join(projectPath, 'main.go'))) {
|
|
368
|
+
entries.push({
|
|
369
|
+
path: 'main.go',
|
|
370
|
+
reason: 'Go main entry',
|
|
371
|
+
source: 'buildSystem',
|
|
372
|
+
confidence: 0.95
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
try {
|
|
376
|
+
const cmdFiles = globSync('cmd/*/main.go', {
|
|
377
|
+
cwd: projectPath,
|
|
378
|
+
nodir: true
|
|
379
|
+
});
|
|
380
|
+
for (const file of cmdFiles) {
|
|
381
|
+
entries.push({
|
|
382
|
+
path: file,
|
|
383
|
+
reason: 'Go cmd entry',
|
|
384
|
+
source: 'buildSystem',
|
|
385
|
+
confidence: 0.95
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
} catch { /* ignore */ }
|
|
389
|
+
break;
|
|
390
|
+
|
|
391
|
+
case 'dotnet':
|
|
392
|
+
// C#: Program.cs
|
|
393
|
+
if (existsSync(join(projectPath, 'Program.cs'))) {
|
|
394
|
+
entries.push({
|
|
395
|
+
path: 'Program.cs',
|
|
396
|
+
reason: '.NET Program entry',
|
|
397
|
+
source: 'buildSystem',
|
|
398
|
+
confidence: 0.95
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
|
|
403
|
+
case 'python':
|
|
404
|
+
// Python: __main__.py, manage.py
|
|
405
|
+
for (const file of ['__main__.py', 'manage.py', 'app.py', 'main.py']) {
|
|
406
|
+
if (existsSync(join(projectPath, file))) {
|
|
407
|
+
entries.push({
|
|
408
|
+
path: file,
|
|
409
|
+
reason: 'Python entry point',
|
|
410
|
+
source: 'buildSystem',
|
|
411
|
+
confidence: 0.9
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return entries;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Unified entry point detector
|
|
424
|
+
*/
|
|
425
|
+
export class EntryPointDetector {
|
|
426
|
+
constructor(options = {}) {
|
|
427
|
+
this.projectPath = options.projectPath || process.cwd();
|
|
428
|
+
this.packageJson = options.packageJson || {};
|
|
429
|
+
this.customPatterns = options.customPatterns || [];
|
|
430
|
+
this.diDecorators = options.diDecorators || DI_DECORATORS_BY_LANGUAGE.javascript;
|
|
431
|
+
this.result = new EntryPointResult();
|
|
432
|
+
this.initialized = false;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Initialize detection - collect all entry points from various sources
|
|
437
|
+
*/
|
|
438
|
+
initialize() {
|
|
439
|
+
if (this.initialized) return this.result;
|
|
440
|
+
|
|
441
|
+
// 1. Package.json entries
|
|
442
|
+
const pkgEntries = extractPackageJsonEntries(this.packageJson, this.projectPath);
|
|
443
|
+
for (const entry of pkgEntries) {
|
|
444
|
+
this.result.add(entry.path, entry);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// 2. HTML entries
|
|
448
|
+
const htmlEntries = extractHtmlEntries(this.projectPath);
|
|
449
|
+
for (const entry of htmlEntries) {
|
|
450
|
+
this.result.add(entry.path, entry);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// 3. Bundler/CI config entries
|
|
454
|
+
const configData = collectConfigEntryPoints(this.projectPath);
|
|
455
|
+
for (const entryPath of configData.entries) {
|
|
456
|
+
this.result.add(entryPath, {
|
|
457
|
+
reason: 'Bundler/CI config entry',
|
|
458
|
+
source: 'bundlerConfig',
|
|
459
|
+
confidence: 0.85
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// 4. Build system entries
|
|
464
|
+
const buildEntries = detectBuildSystemEntries(this.projectPath);
|
|
465
|
+
for (const entry of buildEntries) {
|
|
466
|
+
this.result.add(entry.path, entry);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
this.initialized = true;
|
|
470
|
+
return this.result;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Check if a file is an entry point
|
|
475
|
+
* @param {string} filePath - Relative file path
|
|
476
|
+
* @param {Object} options - Additional context (classes, decorators, etc.)
|
|
477
|
+
*/
|
|
478
|
+
isEntryPoint(filePath, options = {}) {
|
|
479
|
+
this.initialize();
|
|
480
|
+
|
|
481
|
+
const normalizedPath = filePath.replace(/^\.\//, '');
|
|
482
|
+
|
|
483
|
+
// 1. Check pre-collected entries
|
|
484
|
+
const preCollected = this.result.get(normalizedPath);
|
|
485
|
+
if (preCollected) {
|
|
486
|
+
return { isEntry: true, ...preCollected };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// 2. Check pattern matches
|
|
490
|
+
const patternMatch = matchesEntryPattern(normalizedPath, this.customPatterns);
|
|
491
|
+
if (patternMatch.matches) {
|
|
492
|
+
return {
|
|
493
|
+
isEntry: true,
|
|
494
|
+
reason: 'Matches entry point pattern',
|
|
495
|
+
source: 'convention',
|
|
496
|
+
confidence: patternMatch.confidence
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// 3. Check DI decorators on classes
|
|
501
|
+
if (options.classes?.length) {
|
|
502
|
+
for (const cls of options.classes) {
|
|
503
|
+
if (cls.decorators?.length) {
|
|
504
|
+
const diMatch = cls.decorators.find(d =>
|
|
505
|
+
this.diDecorators.includes(d.name)
|
|
506
|
+
);
|
|
507
|
+
if (diMatch) {
|
|
508
|
+
return {
|
|
509
|
+
isEntry: true,
|
|
510
|
+
reason: `Class ${cls.name} has DI decorator: @${diMatch.name}`,
|
|
511
|
+
source: 'diAnnotation',
|
|
512
|
+
confidence: 0.9,
|
|
513
|
+
isDynamic: true
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// 4. Check for framework-specific patterns in file metadata
|
|
521
|
+
if (options.metadata) {
|
|
522
|
+
// Python frameworks
|
|
523
|
+
if (options.metadata.hasMainBlock) {
|
|
524
|
+
return {
|
|
525
|
+
isEntry: true,
|
|
526
|
+
reason: 'Has __main__ block',
|
|
527
|
+
source: 'convention',
|
|
528
|
+
confidence: 0.95
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (options.metadata.isCelery) {
|
|
532
|
+
return {
|
|
533
|
+
isEntry: true,
|
|
534
|
+
reason: 'Celery task file',
|
|
535
|
+
source: 'diAnnotation',
|
|
536
|
+
confidence: 0.9,
|
|
537
|
+
isDynamic: true
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Go frameworks
|
|
542
|
+
if (options.metadata.hasMainFunction && options.metadata.isMainPackage) {
|
|
543
|
+
return {
|
|
544
|
+
isEntry: true,
|
|
545
|
+
reason: 'Go main package with main()',
|
|
546
|
+
source: 'convention',
|
|
547
|
+
confidence: 0.95
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
if (options.metadata.usesWire || options.metadata.usesFx || options.metadata.usesDig) {
|
|
551
|
+
return {
|
|
552
|
+
isEntry: true,
|
|
553
|
+
reason: 'Uses Go DI framework',
|
|
554
|
+
source: 'diAnnotation',
|
|
555
|
+
confidence: 0.9,
|
|
556
|
+
isDynamic: true
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Java frameworks
|
|
561
|
+
if (options.metadata.isSpringComponent) {
|
|
562
|
+
return {
|
|
563
|
+
isEntry: true,
|
|
564
|
+
reason: 'Spring component',
|
|
565
|
+
source: 'diAnnotation',
|
|
566
|
+
confidence: 0.9,
|
|
567
|
+
isDynamic: true
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// C# frameworks
|
|
572
|
+
if (options.metadata.hasMainMethod || options.metadata.hasTopLevelStatements) {
|
|
573
|
+
return {
|
|
574
|
+
isEntry: true,
|
|
575
|
+
reason: 'C# entry point',
|
|
576
|
+
source: 'convention',
|
|
577
|
+
confidence: 0.95
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return { isEntry: false };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Get all detected entry point files
|
|
587
|
+
*/
|
|
588
|
+
getEntryPointFiles() {
|
|
589
|
+
this.initialize();
|
|
590
|
+
return this.result.getFiles();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Get detailed entry point information
|
|
595
|
+
*/
|
|
596
|
+
getEntryPointDetails() {
|
|
597
|
+
this.initialize();
|
|
598
|
+
return this.result.getAll();
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Get entry points grouped by source
|
|
603
|
+
*/
|
|
604
|
+
getEntryPointsBySource() {
|
|
605
|
+
this.initialize();
|
|
606
|
+
return this.result.sources;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Create a detector with default configuration
|
|
612
|
+
*/
|
|
613
|
+
export function createEntryPointDetector(projectPath, packageJson = {}, options = {}) {
|
|
614
|
+
return new EntryPointDetector({
|
|
615
|
+
projectPath,
|
|
616
|
+
packageJson,
|
|
617
|
+
...options
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Quick check if a file is likely an entry point (without full initialization)
|
|
623
|
+
*/
|
|
624
|
+
export function isLikelyEntryPoint(filePath) {
|
|
625
|
+
return matchesEntryPattern(filePath).matches;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export default {
|
|
629
|
+
EntryPointDetector,
|
|
630
|
+
createEntryPointDetector,
|
|
631
|
+
isLikelyEntryPoint,
|
|
632
|
+
DEFAULT_ENTRY_PATTERNS,
|
|
633
|
+
DI_DECORATORS_BY_LANGUAGE
|
|
634
|
+
};
|