entkapp 5.1.0 → 5.2.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 +63 -20
- package/index.js +38 -2241
- package/package.json +1 -1
- package/src/EngineContext.js +1 -1
- package/src/Initializer.js +82 -0
- package/src/analyzers/CodeSmellAnalyzer.js +106 -0
- package/src/ast/OxcAnalyzer.js +33 -415
- package/src/index.js +70 -3
- package/src/performance/WorkerTaskRunner.js +7 -7
- package/src/plugins/BasePlugin.js +171 -2
- package/src/plugins/PluginRegistry.js +193 -81
- package/src/plugins/ecosystems/BackendServices.js +168 -32
- package/src/plugins/ecosystems/GenericPlugins.js +51 -34
- package/src/plugins/ecosystems/ModernFrameworks.js +97 -94
- package/src/plugins/ecosystems/MorePlugins.js +429 -51
- package/src/plugins/ecosystems/NewPlugins.js +526 -0
- package/src/plugins/ecosystems/NextJsPlugin.js +18 -6
- package/src/plugins/ecosystems/PluginLoader.js +190 -17
- package/src/plugins/ecosystems/TypeScriptPlugin.js +10 -10
- package/src/plugins/ecosystems/UltimateBundle.js +168 -0
- package/src/resolution/BuildOrchestrator.js +46 -0
- package/src/resolution/ConfigGenerator.js +83 -0
- package/src/resolution/DependencyFixer.js +88 -0
- package/src/resolution/EntryPointDetector.js +4 -4
- package/src/resolution/GraphAnalyzer.js +80 -0
- package/src/resolution/MigrationAnalyzer.js +60 -0
- package/src/resolution/PathMapper.js +5 -5
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.
|
|
4
|
+
"version": "5.2.0",
|
|
5
5
|
"description": "The Ultimate Enterprise Codebase Janitor. Faster than Knip with OXC integration, type-aware analysis, and automated structural healing. Fully standalone - solving what Knip cannot.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "index.js",
|
package/src/EngineContext.js
CHANGED
|
@@ -189,7 +189,7 @@ export class EngineContext {
|
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
// --- PLUGIN-BASED ECOSYSTEM DETECTION ---
|
|
192
|
-
// Plugins will now handle their own
|
|
192
|
+
// Plugins will now handle their own reachability and dependency validation.
|
|
193
193
|
if (this.pluginRegistry) {
|
|
194
194
|
const activePlugins = await this.pluginRegistry.getActivePlugins(pkgDir);
|
|
195
195
|
for (const plugin of activePlugins) {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Entkapp Initializer: Analyzes the codebase to generate a package.json
|
|
7
|
+
* with correctly categorized dependencies and devDependencies.
|
|
8
|
+
*/
|
|
9
|
+
export class Initializer {
|
|
10
|
+
constructor(context) {
|
|
11
|
+
this.context = context;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async run() {
|
|
15
|
+
console.log("🚀 Starting entkapp intelligent initialization...");
|
|
16
|
+
const cwd = this.context.cwd || process.cwd();
|
|
17
|
+
|
|
18
|
+
// 1. Scan for all JS/TS files
|
|
19
|
+
const files = await glob('**/*.{js,ts,jsx,tsx,vue,svelte,astro}', {
|
|
20
|
+
ignore: ['node_modules/**', 'dist/**', '.git/**'],
|
|
21
|
+
cwd
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const dependencies = new Set();
|
|
25
|
+
const devDependencies = new Set();
|
|
26
|
+
|
|
27
|
+
// Common dev tool indicators
|
|
28
|
+
const devIndicators = ['test', 'spec', 'config', 'stories', 'mock'];
|
|
29
|
+
|
|
30
|
+
console.log(`🔍 Analyzing ${files.length} files for imports...`);
|
|
31
|
+
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
const content = await fs.readFile(path.join(cwd, file), 'utf8');
|
|
34
|
+
const isDevFile = devIndicators.some(ind => file.toLowerCase().includes(ind));
|
|
35
|
+
|
|
36
|
+
// Simple but effective regex for imports
|
|
37
|
+
const importMatches = content.matchAll(/(?:import|from|require)\s*\(?\s*['"]([^' "./][^'"]*)['"]/g);
|
|
38
|
+
|
|
39
|
+
for (const match of importMatches) {
|
|
40
|
+
const pkg = this.extractPackageName(match[1]);
|
|
41
|
+
if (pkg && !this.isBuiltIn(pkg)) {
|
|
42
|
+
if (isDevFile) devDependencies.add(pkg);
|
|
43
|
+
else dependencies.add(pkg);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 2. Generate package.json
|
|
49
|
+
const pkgJson = {
|
|
50
|
+
name: path.basename(cwd),
|
|
51
|
+
version: "1.0.0",
|
|
52
|
+
description: "Initialized with entkapp",
|
|
53
|
+
main: "index.js",
|
|
54
|
+
scripts: {
|
|
55
|
+
"entkapp:run": "entkapp -r",
|
|
56
|
+
"entkapp:check": "entkapp --verbose"
|
|
57
|
+
},
|
|
58
|
+
dependencies: Object.fromEntries([...dependencies].sort().map(d => [d, "latest"])),
|
|
59
|
+
devDependencies: Object.fromEntries([...devDependencies].sort().map(d => [d, "latest"]))
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
await fs.writeFile(path.join(cwd, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
63
|
+
console.log("✅ package.json generated with analyzed dependencies.");
|
|
64
|
+
|
|
65
|
+
// 3. Create /entkapp folder
|
|
66
|
+
await fs.mkdir(path.join(cwd, 'entkapp'), { recursive: true });
|
|
67
|
+
console.log("✅ /entkapp configuration folder created.");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
extractPackageName(specifier) {
|
|
71
|
+
if (specifier.startsWith('@')) {
|
|
72
|
+
const parts = specifier.split('/');
|
|
73
|
+
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : null;
|
|
74
|
+
}
|
|
75
|
+
return specifier.split('/')[0];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
isBuiltIn(pkg) {
|
|
79
|
+
const builtins = ['path', 'fs', 'os', 'crypto', 'http', 'https', 'stream', 'util', 'events', 'module', 'process'];
|
|
80
|
+
return builtins.includes(pkg) || pkg.startsWith('node:');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeSmellAnalyzer: Performs deep static analysis to find runtime risks and logic smells.
|
|
3
|
+
*/
|
|
4
|
+
export class CodeSmellAnalyzer {
|
|
5
|
+
constructor(context) {
|
|
6
|
+
this.context = context;
|
|
7
|
+
this.issues = [];
|
|
8
|
+
this.rules = {
|
|
9
|
+
'potential-null-pointer': {
|
|
10
|
+
message: 'Potential Null Pointer Risk: Accessing property on an object that might be undefined.',
|
|
11
|
+
link: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_access_property'
|
|
12
|
+
},
|
|
13
|
+
'infinite-loop-risk': {
|
|
14
|
+
message: 'Potential Infinite Loop: Loop condition does not seem to change within the body.',
|
|
15
|
+
link: 'https://en.wikipedia.org/wiki/Infinite_loop'
|
|
16
|
+
},
|
|
17
|
+
'implicit-type-coercion': {
|
|
18
|
+
message: 'Implicit Type Coercion: Using loose equality (==) can lead to unexpected runtime behavior.',
|
|
19
|
+
link: 'https://dorey.github.io/JavaScript-Equality-Table/'
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
analyze(fileNode) {
|
|
25
|
+
if (!fileNode.ast) return;
|
|
26
|
+
this.walk(fileNode.ast, fileNode);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
walk(node, fileNode) {
|
|
30
|
+
if (!node) return;
|
|
31
|
+
|
|
32
|
+
// 1. Check for Loose Equality (==)
|
|
33
|
+
if (node.type === 'BinaryExpression' && (node.operator === '==' || node.operator === '!=')) {
|
|
34
|
+
this.addIssue('implicit-type-coercion', node, fileNode);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 2. Check for Potential Infinite Loops (While loops with static conditions)
|
|
38
|
+
if (node.type === 'WhileStatement' && node.test.type === 'Literal' && node.test.value === true) {
|
|
39
|
+
// Check if there is a break or return inside
|
|
40
|
+
if (!this.hasExitStatement(node.body)) {
|
|
41
|
+
this.addIssue('infinite-loop-risk', node, fileNode);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 3. Check for Null Pointer Risks (Accessing properties on potentially uninitialized vars)
|
|
46
|
+
if (node.type === 'MemberExpression' && node.object.type === 'Identifier') {
|
|
47
|
+
const varName = node.object.name;
|
|
48
|
+
if (this.isPotentiallyNull(varName, fileNode)) {
|
|
49
|
+
this.addIssue('potential-null-pointer', node, fileNode);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Recursively walk the AST
|
|
54
|
+
for (const key in node) {
|
|
55
|
+
if (node[key] && typeof node[key] === 'object') {
|
|
56
|
+
if (Array.isArray(node[key])) {
|
|
57
|
+
node[key].forEach(child => this.walk(child, fileNode));
|
|
58
|
+
} else {
|
|
59
|
+
this.walk(node[key], fileNode);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
addIssue(ruleId, node, fileNode) {
|
|
66
|
+
const rule = this.rules[ruleId];
|
|
67
|
+
fileNode.diagnostics = fileNode.diagnostics || [];
|
|
68
|
+
fileNode.diagnostics.push({
|
|
69
|
+
ruleId,
|
|
70
|
+
message: rule.message,
|
|
71
|
+
link: rule.link,
|
|
72
|
+
line: node.start ? this.getLineNumber(node.start, fileNode.content) : 0,
|
|
73
|
+
severity: 'warning'
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
hasExitStatement(node) {
|
|
78
|
+
let found = false;
|
|
79
|
+
const check = (n) => {
|
|
80
|
+
if (!n || found) return;
|
|
81
|
+
if (n.type === 'BreakStatement' || n.type === 'ReturnStatement' || n.type === 'ThrowStatement') {
|
|
82
|
+
found = true;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
for (const key in n) {
|
|
86
|
+
if (n[key] && typeof n[key] === 'object') {
|
|
87
|
+
if (Array.isArray(n[key])) n[key].forEach(check);
|
|
88
|
+
else check(n[key]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
check(node);
|
|
93
|
+
return found;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
isPotentiallyNull(name, fileNode) {
|
|
97
|
+
// Simple heuristic: if it's a variable declared without init or in a try-catch
|
|
98
|
+
const symbol = fileNode.symbolTable?.get(name);
|
|
99
|
+
return symbol && symbol.type === 'variable' && !symbol.initialized;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
getLineNumber(pos, content) {
|
|
103
|
+
if (!content) return 0;
|
|
104
|
+
return content.substring(0, pos).split('\n').length;
|
|
105
|
+
}
|
|
106
|
+
}
|