entkapp 5.7.0 β 5.7.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 +1 -1
- package/bin/cli.js +1 -1
- package/bin/cli.mjs +2 -2
- package/package.json +1 -1
- package/src/EngineContext.js +73 -3
- package/src/EngineContext.mjs +55 -2
- package/src/index.js +58 -11
- package/src/index.mjs +1146 -64
- package/src/resolution/EntryPointDetector.js +12 -3
- package/src/resolution/EntryPointDetector.mjs +7 -1
package/README.md
CHANGED
package/bin/cli.js
CHANGED
|
@@ -132,7 +132,7 @@ async function bootstrap() {
|
|
|
132
132
|
}, timeoutMs);
|
|
133
133
|
timeoutTimer.unref(); // Allow process to exit if work finishes
|
|
134
134
|
|
|
135
|
-
console.log(ansis.bold.green(`\nπ¦ entkapp v${packageJsonContent.version || '5.
|
|
135
|
+
console.log(ansis.bold.green(`\nπ¦ entkapp v${packageJsonContent.version || '5.7.1'} Engine Activation`));
|
|
136
136
|
console.log(ansis.dim('------------------------------------------------------------'));
|
|
137
137
|
console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(targetCwd)}`);
|
|
138
138
|
console.log(`${ansis.bold('Refactoring Mode :')} ${options.fix ? ansis.yellow('Active Fixing & Self-Healing Enabled') : ansis.gray('Dry-Run Reporting Only')}`);
|
package/bin/cli.mjs
CHANGED
|
@@ -28,7 +28,7 @@ async function bootstrap() {
|
|
|
28
28
|
program
|
|
29
29
|
.name('entkapp')
|
|
30
30
|
.description(ansis.cyan('The Ultimate Enterprise Codebase Janitor with OXC integration, type-aware analysis, and automated structural healing.'))
|
|
31
|
-
.version(packageJsonContent.version || '5.7.
|
|
31
|
+
.version(packageJsonContent.version || '5.7.1');
|
|
32
32
|
|
|
33
33
|
program
|
|
34
34
|
.option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
|
|
@@ -123,7 +123,7 @@ async function bootstrap() {
|
|
|
123
123
|
}, timeoutMs);
|
|
124
124
|
timeoutTimer.unref();
|
|
125
125
|
|
|
126
|
-
console.log(ansis.bold.green(`\nπ¦ entkapp v${packageJsonContent.version || '5.7.
|
|
126
|
+
console.log(ansis.bold.green(`\nπ¦ entkapp v${packageJsonContent.version || '5.7.1'} Engine Activation`));
|
|
127
127
|
console.log(ansis.dim('------------------------------------------------------------'));
|
|
128
128
|
console.log(`${ansis.bold('Target Workspace Root :')} ${ansis.blue(targetCwd)}`);
|
|
129
129
|
console.log(`${ansis.bold('Refactoring Mode :')} ${options.fix ? ansis.yellow('Active Fixing & Self-Healing Enabled') : ansis.gray('Dry-Run Reporting Only')}`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
3
|
"name": "entkapp",
|
|
4
|
-
"version": "5.7.
|
|
4
|
+
"version": "5.7.1",
|
|
5
5
|
"description": "The Ultimate Enterprise Codebase Janitor. High-speed OXC integration, type-aware analysis, and automated structural healing. Fully standalone architectural orchestrator.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "index.js",
|
package/src/EngineContext.js
CHANGED
|
@@ -214,6 +214,44 @@ export class EngineContext {
|
|
|
214
214
|
} catch (e) {}
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
// FIX (Bug 2): Fallback Entry-Point Seeding for standard TypeScript/JavaScript projects.
|
|
218
|
+
// If no entry points have been discovered via node.isEntry flags or manifest fields
|
|
219
|
+
// (e.g. because EntryPointDetector was not wired in the JS build), we fall back to
|
|
220
|
+
// well-known convention paths so that the BFS traversal is never empty and all
|
|
221
|
+
// reachable files β and their dependencies β are correctly included in the analysis.
|
|
222
|
+
if (reachableFiles.size === 0 && this.projectGraph.size > 0) {
|
|
223
|
+
const conventionEntries = [
|
|
224
|
+
'src/index.ts', 'src/index.tsx', 'src/index.js', 'src/index.jsx',
|
|
225
|
+
'src/main.ts', 'src/main.js',
|
|
226
|
+
'index.ts', 'index.tsx', 'index.js',
|
|
227
|
+
'src/app.ts', 'src/app.js'
|
|
228
|
+
];
|
|
229
|
+
const normalizeCwd = (p) => {
|
|
230
|
+
let n = path.resolve(this.cwd, p).replace(/\\/g, '/');
|
|
231
|
+
if (/^[a-z]:\//.test(n)) n = n.charAt(0).toUpperCase() + n.slice(1);
|
|
232
|
+
return n;
|
|
233
|
+
};
|
|
234
|
+
for (const rel of conventionEntries) {
|
|
235
|
+
const abs = normalizeCwd(rel);
|
|
236
|
+
if (this.projectGraph.has(abs) && !reachableFiles.has(abs)) {
|
|
237
|
+
reachableFiles.add(abs);
|
|
238
|
+
queue.push(abs);
|
|
239
|
+
this.projectGraph.get(abs).isEntry = true;
|
|
240
|
+
if (this.verbose) console.log(`[Reachability] Fallback entry point seeded: ${rel}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Last resort: if still empty, treat every graph node that has no incoming edges
|
|
244
|
+
// as a potential root so that the dependency audit is never skipped entirely.
|
|
245
|
+
if (reachableFiles.size === 0) {
|
|
246
|
+
for (const [fp, node] of this.projectGraph.entries()) {
|
|
247
|
+
if (node.incomingEdges.size === 0) {
|
|
248
|
+
reachableFiles.add(fp);
|
|
249
|
+
queue.push(fp);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
217
255
|
// 2. BFS Traversal
|
|
218
256
|
while (queue.length > 0) {
|
|
219
257
|
const currentPath = queue.shift();
|
|
@@ -362,11 +400,33 @@ export class EngineContext {
|
|
|
362
400
|
|
|
363
401
|
// --- DEPENDENCY ANALYSIS ---
|
|
364
402
|
const usedByReachableFiles = new Set();
|
|
403
|
+
|
|
404
|
+
// FIX (Bug 3): Helper to extract the base package name from an import specifier.
|
|
405
|
+
// Handles scoped packages (@org/pkg), sub-path imports (pkg/sub), and node: prefix.
|
|
406
|
+
const extractBasePkg = (specifier) => {
|
|
407
|
+
if (!specifier || specifier.startsWith('.') || specifier.startsWith('/')) return null;
|
|
408
|
+
const s = specifier.startsWith('node:') ? specifier.slice(5) : specifier;
|
|
409
|
+
return s.startsWith('@') ? s.split('/').slice(0, 2).join('/') : s.split('/')[0];
|
|
410
|
+
};
|
|
411
|
+
|
|
365
412
|
reachableFiles.forEach(filePath => {
|
|
366
413
|
const node = this.projectGraph.get(filePath);
|
|
367
414
|
if (node) {
|
|
415
|
+
// Primary source: externalPackageUsage populated by ASTAnalyzer / OxcAnalyzer
|
|
368
416
|
node.externalPackageUsage.forEach(pkg => usedByReachableFiles.add(pkg));
|
|
369
|
-
|
|
417
|
+
|
|
418
|
+
// FIX (Bug 3): Also derive package names from explicitImports.
|
|
419
|
+
// ASTAnalyzer populates node.explicitImports with raw import specifiers
|
|
420
|
+
// (e.g. "lodash", "axios/lib/core") but these are not always mirrored into
|
|
421
|
+
// externalPackageUsage when the AST pass runs in worker threads or when the
|
|
422
|
+
// OXC parser is unavailable. Extracting them here closes the gap.
|
|
423
|
+
if (node.explicitImports) {
|
|
424
|
+
node.explicitImports.forEach(specifier => {
|
|
425
|
+
const pkg = extractBasePkg(specifier);
|
|
426
|
+
if (pkg) usedByReachableFiles.add(pkg);
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
370
430
|
// Conservative check for dynamic imports in reachable files
|
|
371
431
|
if (node.calculatedDynamicImports) {
|
|
372
432
|
node.calculatedDynamicImports.forEach(entry => {
|
|
@@ -397,11 +457,21 @@ export class EngineContext {
|
|
|
397
457
|
}
|
|
398
458
|
|
|
399
459
|
// UPGRADE: Be more aggressive in detecting unused dependencies.
|
|
400
|
-
// Check
|
|
460
|
+
// FIX (Bug 3): Check externalPackageUsage, rawStringReferences AND explicitImports
|
|
461
|
+
// to ensure packages imported via static import statements are never false-positives.
|
|
401
462
|
const isUsed = usedByReachableFiles.has(dep) ||
|
|
402
463
|
Array.from(reachableFiles).some(f => {
|
|
403
464
|
const node = this.projectGraph.get(f);
|
|
404
|
-
|
|
465
|
+
if (!node) return false;
|
|
466
|
+
if (node.externalPackageUsage.has(dep) || node.rawStringReferences.has(dep)) return true;
|
|
467
|
+
// FIX: check explicitImports for base package name match
|
|
468
|
+
if (node.explicitImports) {
|
|
469
|
+
for (const specifier of node.explicitImports) {
|
|
470
|
+
const base = extractBasePkg(specifier);
|
|
471
|
+
if (base === dep) return true;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return false;
|
|
405
475
|
});
|
|
406
476
|
|
|
407
477
|
if (!isUsed) {
|
package/src/EngineContext.mjs
CHANGED
|
@@ -218,6 +218,38 @@ export class EngineContext {
|
|
|
218
218
|
} catch (e) {}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
// FIX (Bug 2): Fallback Entry-Point Seeding for standard TypeScript/JavaScript projects.
|
|
222
|
+
if (reachableFiles.size === 0 && this.projectGraph.size > 0) {
|
|
223
|
+
const conventionEntries = [
|
|
224
|
+
'src/index.ts', 'src/index.tsx', 'src/index.js', 'src/index.jsx',
|
|
225
|
+
'src/main.ts', 'src/main.js',
|
|
226
|
+
'index.ts', 'index.tsx', 'index.js',
|
|
227
|
+
'src/app.ts', 'src/app.js'
|
|
228
|
+
];
|
|
229
|
+
const normalizeCwd = (p) => {
|
|
230
|
+
let n = path.resolve(this.cwd, p).replace(/\\/g, '/');
|
|
231
|
+
if (/^[a-z]:\//.test(n)) n = n.charAt(0).toUpperCase() + n.slice(1);
|
|
232
|
+
return n;
|
|
233
|
+
};
|
|
234
|
+
for (const rel of conventionEntries) {
|
|
235
|
+
const abs = normalizeCwd(rel);
|
|
236
|
+
if (this.projectGraph.has(abs) && !reachableFiles.has(abs)) {
|
|
237
|
+
reachableFiles.add(abs);
|
|
238
|
+
queue.push(abs);
|
|
239
|
+
this.projectGraph.get(abs).isEntry = true;
|
|
240
|
+
if (this.verbose) console.log(`[Reachability] Fallback entry point seeded: ${rel}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (reachableFiles.size === 0) {
|
|
244
|
+
for (const [fp, node] of this.projectGraph.entries()) {
|
|
245
|
+
if (node.incomingEdges.size === 0) {
|
|
246
|
+
reachableFiles.add(fp);
|
|
247
|
+
queue.push(fp);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
221
253
|
// 2. BFS Traversal
|
|
222
254
|
while (queue.length > 0) {
|
|
223
255
|
const currentPath = queue.shift();
|
|
@@ -366,10 +398,24 @@ export class EngineContext {
|
|
|
366
398
|
|
|
367
399
|
// --- DEPENDENCY ANALYSIS ---
|
|
368
400
|
const usedByReachableFiles = new Set();
|
|
401
|
+
|
|
402
|
+
const extractBasePkg = (specifier) => {
|
|
403
|
+
if (!specifier || specifier.startsWith('.') || specifier.startsWith('/')) return null;
|
|
404
|
+
const s = specifier.startsWith('node:') ? specifier.slice(5) : specifier;
|
|
405
|
+
return s.startsWith('@') ? s.split('/').slice(0, 2).join('/') : s.split('/')[0];
|
|
406
|
+
};
|
|
407
|
+
|
|
369
408
|
reachableFiles.forEach(filePath => {
|
|
370
409
|
const node = this.projectGraph.get(filePath);
|
|
371
410
|
if (node) {
|
|
372
411
|
node.externalPackageUsage.forEach(pkg => usedByReachableFiles.add(pkg));
|
|
412
|
+
|
|
413
|
+
if (node.explicitImports) {
|
|
414
|
+
node.explicitImports.forEach(specifier => {
|
|
415
|
+
const pkg = extractBasePkg(specifier);
|
|
416
|
+
if (pkg) usedByReachableFiles.add(pkg);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
373
419
|
|
|
374
420
|
// Conservative check for dynamic imports in reachable files
|
|
375
421
|
if (node.calculatedDynamicImports) {
|
|
@@ -401,11 +447,18 @@ export class EngineContext {
|
|
|
401
447
|
}
|
|
402
448
|
|
|
403
449
|
// UPGRADE: Be more aggressive in detecting unused dependencies.
|
|
404
|
-
// Check both externalPackageUsage and rawStringReferences.
|
|
405
450
|
const isUsed = usedByReachableFiles.has(dep) ||
|
|
406
451
|
Array.from(reachableFiles).some(f => {
|
|
407
452
|
const node = this.projectGraph.get(f);
|
|
408
|
-
|
|
453
|
+
if (!node) return false;
|
|
454
|
+
if (node.externalPackageUsage.has(dep) || node.rawStringReferences.has(dep)) return true;
|
|
455
|
+
if (node.explicitImports) {
|
|
456
|
+
for (const specifier of node.explicitImports) {
|
|
457
|
+
const base = extractBasePkg(specifier);
|
|
458
|
+
if (base === dep) return true;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return false;
|
|
409
462
|
});
|
|
410
463
|
|
|
411
464
|
if (!isUsed) {
|
package/src/index.js
CHANGED
|
@@ -64,6 +64,24 @@ export class RefactoringEngine {
|
|
|
64
64
|
this.resolver = new DependencyResolver(this.context, this.pathMapper, this.workspaceGraph);
|
|
65
65
|
this.circularDetector = new CircularDetector(this.context);
|
|
66
66
|
|
|
67
|
+
// FIX (Bug 2): Initialize packageJson and EntryPointDetector, mirroring the .mjs twin.
|
|
68
|
+
// Without this, src/index.ts is never registered as an entry point in the JS build,
|
|
69
|
+
// causing all files to be treated as unreachable and suppressing the dependency audit.
|
|
70
|
+
try {
|
|
71
|
+
const pkgPath = path.join(this.context.cwd, 'package.json');
|
|
72
|
+
if (existsSync(pkgPath)) {
|
|
73
|
+
this.context.packageJson = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
74
|
+
}
|
|
75
|
+
} catch (e) {}
|
|
76
|
+
|
|
77
|
+
import('./resolution/EntryPointDetector.js').then(({ EntryPointDetector }) => {
|
|
78
|
+
this.entryPointDetector = new EntryPointDetector(
|
|
79
|
+
this.context.cwd,
|
|
80
|
+
this.context.packageJson,
|
|
81
|
+
this.context.metrics
|
|
82
|
+
);
|
|
83
|
+
}).catch(() => {});
|
|
84
|
+
|
|
67
85
|
// Lazy import DependencyProfiler
|
|
68
86
|
import('./resolution/DependencyProfiler.js').then(({ DependencyProfiler }) => {
|
|
69
87
|
this.dependencyProfiler = new DependencyProfiler(this.context);
|
|
@@ -347,16 +365,33 @@ export class RefactoringEngine {
|
|
|
347
365
|
}
|
|
348
366
|
}
|
|
349
367
|
if (entryCount === 0) {
|
|
350
|
-
console.log(ansis.bold.yellow(' π¨ ALARM: Keine einzige Datei wurde als Entry Point markiert!
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
368
|
+
console.log(ansis.bold.yellow(' π¨ ALARM: Keine einzige Datei wurde als Entry Point markiert! Starte Deep Detection...'));
|
|
369
|
+
|
|
370
|
+
// FIX (Bug 2): Use EntryPointDetector first (mirrors .mjs twin behavior).
|
|
371
|
+
// This ensures src/index.ts and other convention-based roots are found reliably.
|
|
372
|
+
if (this.entryPointDetector) {
|
|
373
|
+
const detected = this.entryPointDetector.detect();
|
|
374
|
+
detected.forEach(absPath => {
|
|
375
|
+
const cleanPath = absPath.replace(/\\/g, '/');
|
|
376
|
+
if (this.context.projectGraph.has(cleanPath)) {
|
|
377
|
+
this.context.projectGraph.get(cleanPath).isEntry = true;
|
|
378
|
+
console.log(ansis.green(` β’ π DETECTED ENTRY: ${path.relative(this.context.cwd, cleanPath)}`));
|
|
379
|
+
entryCount++;
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (entryCount === 0) {
|
|
385
|
+
const fallbackFiles = ['index.js', 'index.mjs', 'src/index.js', 'src/index.mjs', 'src/index.ts', 'src/index.tsx', 'main.js', 'src/main.js', 'src/main.ts'];
|
|
386
|
+
for (const fallback of fallbackFiles) {
|
|
387
|
+
const absFallback = path.resolve(this.context.cwd, fallback).replace(/\\/g, '/');
|
|
388
|
+
if (this.context.projectGraph.has(absFallback)) {
|
|
389
|
+
const node = this.context.projectGraph.get(absFallback);
|
|
390
|
+
node.isEntry = true;
|
|
391
|
+
console.log(ansis.green(` β’ π FALLBACK ENTRY: ${fallback}`));
|
|
392
|
+
entryCount++;
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
360
395
|
}
|
|
361
396
|
}
|
|
362
397
|
}
|
|
@@ -547,10 +582,22 @@ export class RefactoringEngine {
|
|
|
547
582
|
}
|
|
548
583
|
}
|
|
549
584
|
|
|
585
|
+
// FIX: Robust path normalization helper for cross-platform path comparison.
|
|
586
|
+
// Mirrors the normalizePath() logic in OxcAnalyzer.js to handle Windows
|
|
587
|
+
// drive-letter case differences and mixed slash types before path.relative().
|
|
588
|
+
const normalizeForAudit = (p) => {
|
|
589
|
+
if (!p) return '';
|
|
590
|
+
let n = path.resolve(p).replace(/\\/g, '/');
|
|
591
|
+
if (/^[a-z]:\//.test(n)) n = n.charAt(0).toUpperCase() + n.slice(1);
|
|
592
|
+
return n.replace(/\/+/g, '/').replace(/\/$/, '');
|
|
593
|
+
};
|
|
594
|
+
|
|
550
595
|
for (const metadata of auditManifests) {
|
|
551
596
|
if (this.context.projectGraph) {
|
|
597
|
+
const normRoot = normalizeForAudit(metadata.rootDirectory);
|
|
552
598
|
for (const [filePath, fileNode] of this.context.projectGraph.entries()) {
|
|
553
|
-
const
|
|
599
|
+
const normFilePath = normalizeForAudit(filePath);
|
|
600
|
+
const cleanRelative = path.relative(normRoot, normFilePath).replace(/\\/g, '/');
|
|
554
601
|
|
|
555
602
|
// Check if file belongs to this manifest's directory scope
|
|
556
603
|
if (!cleanRelative.startsWith('..') && !cleanRelative.startsWith('/') && fileNode.explicitImports) {
|