entkapp 5.3.0 β 5.4.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 +1 -7
- package/bin/cli.js +58 -117
- package/package.json +1 -1
- package/src/EngineContext.js +6 -0
- package/src/ast/ASTAnalyzer.js +94 -6
- package/src/ast/MagicDetector.js +23 -43
- package/src/ast/OxcAnalyzer.js +190 -27
- package/src/ast/SecretScanner.js +112 -50
- package/src/index.js +87 -4
- package/src/performance/GraphCache.js +27 -0
- package/src/performance/WorkerPool.js +22 -4
- package/src/performance/WorkerTaskRunner.js +26 -0
- package/src/plugins/BasePlugin.js +16 -27
- package/src/plugins/PluginRegistry.js +44 -0
- package/src/plugins/ecosystems/UltimateBundle.js +259 -0
- package/src/resolution/ConfigLoader.js +190 -26
- package/src/resolution/FrameworkConfigParser.js +119 -0
- package/src/resolution/GraphAnalyzer.js +65 -1
- package/src/resolution/PathMapper.js +81 -0
- package/src/resolution/TSConfigLoader.js +3 -1
- package/src/resolution/WorkSpaceGraph.js +260 -89
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# πΈοΈ entkapp Ultimate v5.
|
|
1
|
+
# πΈοΈ entkapp Ultimate v5.4.0
|
|
2
2
|
|
|
3
3
|
> **The Ultimate Enterprise Codebase Janitor.** High-speed OXC integration, type-aware analysis, and automated structural healing. Fully standalone architectural orchestrator.
|
|
4
4
|
|
|
@@ -35,12 +35,6 @@ Starts the interactive analysis:
|
|
|
35
35
|
npx entkapp -r
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
### Headless Analysis Mode
|
|
39
|
-
Ideal for CI/CD pipelines. Performs analysis without prompts and outputs JSON:
|
|
40
|
-
```bash
|
|
41
|
-
npx entkapp --analyze
|
|
42
|
-
```
|
|
43
|
-
|
|
44
38
|
### Auto-Fix Mode
|
|
45
39
|
Automatically resolves dependency conflicts and structural issues:
|
|
46
40
|
```bash
|
package/bin/cli.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* ============================================================================
|
|
5
|
-
*
|
|
5
|
+
* π entkapp: The High-Performance Codebase Orchestrator
|
|
6
6
|
* ============================================================================
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Command-line interface for executing architectural audits, structural
|
|
8
|
+
* refactoring cycles, and autonomous codebase healing.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { Command } from 'commander';
|
|
@@ -13,163 +13,104 @@ import ansis from 'ansis';
|
|
|
13
13
|
import path from 'path';
|
|
14
14
|
import fs from 'fs/promises';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
|
-
import
|
|
16
|
+
import { ConfigLoader } from '../src/resolution/ConfigLoader.js';
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
const
|
|
18
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const pkg = JSON.parse(await fs.readFile(path.join(__dirname, '../package.json'), 'utf8'));
|
|
20
20
|
|
|
21
21
|
const program = new Command();
|
|
22
22
|
|
|
23
|
-
async function
|
|
23
|
+
async function main() {
|
|
24
24
|
try {
|
|
25
|
-
const packageJsonPath = path.resolve(__dirname, '../package.json');
|
|
26
|
-
const packageJsonContent = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
|
|
27
|
-
|
|
28
25
|
program
|
|
29
26
|
.name('entkapp')
|
|
30
|
-
.description(
|
|
31
|
-
.version(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
|
|
35
|
-
.option('-d, --debug', 'Developer`s comprehensive telemetry debug diagnostics', false)
|
|
36
|
-
.option('--fix', 'Enable atomic code updates, structural file pruning, and active type sanitization', false)
|
|
27
|
+
.description('Autonomous architectural refactoring and codebase optimization engine')
|
|
28
|
+
.version(pkg.version)
|
|
29
|
+
.argument('[path]', 'Target directory for codebase analysis and refactoring', '.')
|
|
30
|
+
.option('-f, --fix', 'Automatically apply identified structural optimizations and healing', false)
|
|
37
31
|
.option('--tsconfig <filename>', 'Specify path to custom layout configurations', 'tsconfig.json')
|
|
38
32
|
.option('--test-command <command>', 'Integrated continuous safety test validation script execution path', 'npm test')
|
|
39
33
|
.option('--workspace', 'Enable high-density workspace workspace/monorepo cluster mesh evaluation parsing', false)
|
|
40
34
|
.option('--verbose', 'Toggle expanded trace telemetry for debug operational diagnostics', false)
|
|
41
35
|
.option('--visualize', 'Generate an interactive execution graph visualization', false)
|
|
36
|
+
.option('-ef, --entry-file <path>', 'Manually specify a primary entry file for analysis')
|
|
42
37
|
.option('-r, --run', 'Execute the primary operational pipeline loop', false)
|
|
43
38
|
.option('-y, --yes', 'Skip confirmation prompts and execute planned structural modifications automatically', false)
|
|
44
39
|
.option('--timeout <ms>', 'Set execution timeout in milliseconds', '30000');
|
|
45
40
|
|
|
46
41
|
program.parse(process.argv);
|
|
47
|
-
const options = program.opts();
|
|
48
|
-
|
|
49
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
50
|
-
|
|
51
|
-
// --- Onboarding Check (Skipped in Non-Interactive Mode) ---
|
|
52
|
-
// FIX: Ensure options.cwd is always a string, never undefined
|
|
53
|
-
const targetCwd = path.resolve(options.cwd || process.cwd());
|
|
54
|
-
const pkgJsonPath = path.join(targetCwd, 'package.json');
|
|
55
|
-
const configDirPath = path.join(targetCwd, 'entkapp');
|
|
56
|
-
|
|
57
|
-
let pkgJson;
|
|
58
|
-
try {
|
|
59
|
-
pkgJson = JSON.parse(await fs.readFile(pkgJsonPath, 'utf8'));
|
|
60
|
-
} catch (e) {}
|
|
61
|
-
|
|
62
|
-
let configInstalled = false;
|
|
63
|
-
if (!options.yes && !options.run) {
|
|
64
|
-
// 1. Ask to install script
|
|
65
|
-
if (pkgJson && !pkgJson.scripts?.['entkapp:run']) {
|
|
66
|
-
const answer = await rl.question(ansis.bold.yellow('β No "entkapp:run" script found in package.json. Install it? (y/n): '));
|
|
67
|
-
if (answer.toLowerCase() === 'y') {
|
|
68
|
-
pkgJson.scripts = pkgJson.scripts || {};
|
|
69
|
-
pkgJson.scripts['entkapp:run'] = 'npx entkapp --fix';
|
|
70
|
-
await fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
|
|
71
|
-
console.log(ansis.green('β
"entkapp:run" script added to package.json.'));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 2. Ask to install config folder
|
|
76
|
-
try {
|
|
77
|
-
await fs.access(configDirPath);
|
|
78
|
-
configInstalled = true;
|
|
79
|
-
} catch (e) {
|
|
80
|
-
const answer = await rl.question(ansis.bold.yellow('β No "/entkapp" configuration folder found. Create it with defaults? (y/n): '));
|
|
81
|
-
if (answer.toLowerCase() === 'y') {
|
|
82
|
-
await fs.mkdir(configDirPath, { recursive: true });
|
|
83
|
-
await fs.mkdir(path.join(configDirPath, 'plugins'), { recursive: true });
|
|
84
|
-
const defaultConfig = {
|
|
85
|
-
interface: "CLI",
|
|
86
|
-
useBuiltinPlugins: true,
|
|
87
|
-
useCustomPlugins: true,
|
|
88
|
-
|
|
89
|
-
options: { verbose: false, fastMode: true, selfHealing: true },
|
|
90
|
-
enabledPlugins: ["nextjs", "nuxt", "remix", "sveltekit", "astro"]
|
|
91
|
-
};
|
|
92
|
-
await fs.writeFile(path.join(configDirPath, 'config.json'), JSON.stringify(defaultConfig, null, 2));
|
|
93
|
-
console.log(ansis.green('β
"/entkapp" folder? and default config created.'));
|
|
94
|
-
configInstalled = true;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (pkgJson?.scripts?.['entkapp:run'] || configInstalled) {
|
|
99
|
-
console.log(ansis.bold.cyan('\nπ Setup complete! To start the engine, run:'));
|
|
100
|
-
console.log(ansis.white(` - npx entkapp -r`));
|
|
101
|
-
console.log(ansis.white(` - npm run entkapp:run\n`));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
42
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
console.log(ansis.
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Only proceed to execution if -r/--run is provided
|
|
123
|
-
if (!options.run) {
|
|
124
|
-
return;
|
|
43
|
+
const options = program.opts();
|
|
44
|
+
const targetCwd = path.resolve(process.cwd(), program.args[0] || '.');
|
|
45
|
+
|
|
46
|
+
// Load configuration
|
|
47
|
+
const configLoader = new ConfigLoader(targetCwd);
|
|
48
|
+
const localConfig = await configLoader.loadConfig(options);
|
|
49
|
+
|
|
50
|
+
if (options.verbose) {
|
|
51
|
+
const summary = {
|
|
52
|
+
packages: localConfig.workspacePackages?.length || 0,
|
|
53
|
+
tsconfigs: localConfig.workspaceTsConfigs?.length || 0,
|
|
54
|
+
configs: localConfig.workspaceConfigFiles?.length || 0
|
|
55
|
+
};
|
|
56
|
+
console.log(ansis.dim(`[ConfigLoader] Monorepo detected β ${summary.packages} package(s), ${summary.tsconfigs} tsconfig(s), ${summary.configs} *.config file(s) loaded`));
|
|
125
57
|
}
|
|
126
58
|
|
|
127
|
-
// --- Timeout Handling ---
|
|
128
|
-
const timeoutMs = parseInt(options.timeout);
|
|
129
59
|
const timeoutTimer = setTimeout(() => {
|
|
130
|
-
console.error(ansis.
|
|
60
|
+
console.error(ansis.red(`\nβ Execution Timeout: Operation exceeded ${options.timeout}ms limit.`));
|
|
131
61
|
process.exit(1);
|
|
132
|
-
},
|
|
133
|
-
timeoutTimer.unref(); // Allow process to exit if work finishes
|
|
62
|
+
}, parseInt(options.timeout));
|
|
134
63
|
|
|
135
|
-
console.log(ansis.bold.
|
|
64
|
+
console.log('\n' + ansis.bold.cyan(`π¦ entkapp v${pkg.version} Engine Activation`));
|
|
136
65
|
console.log(ansis.dim('------------------------------------------------------------'));
|
|
137
|
-
console.log(`${ansis.bold('Target Workspace Root
|
|
138
|
-
console.log(`${ansis.bold('Refactoring Mode
|
|
139
|
-
console.log(`${ansis.bold('Validation Sandbox
|
|
66
|
+
console.log(`${ansis.bold('Target Workspace Root')} : ${targetCwd}`);
|
|
67
|
+
console.log(`${ansis.bold('Refactoring Mode')} : ${options.fix ? ansis.yellow('Automated Healing Active') : 'Dry-Run Reporting Only'}`);
|
|
68
|
+
console.log(`${ansis.bold('Validation Sandbox')} : ${options.testCommand}`);
|
|
140
69
|
console.log(ansis.dim('------------------------------------------------------------\n'));
|
|
141
70
|
|
|
142
71
|
const { RefactoringEngine } = await import('../src/index.js');
|
|
143
72
|
|
|
73
|
+
// Prepare entry points - EXTREMELY ROBUST ARRAY HANDLING
|
|
74
|
+
let entryPoints = [];
|
|
75
|
+
if (localConfig.entryPoints && typeof localConfig.entryPoints[Symbol.iterator] === 'function') {
|
|
76
|
+
entryPoints = [...localConfig.entryPoints];
|
|
77
|
+
} else if (localConfig.entryPoints) {
|
|
78
|
+
entryPoints = [localConfig.entryPoints];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (options.entryFile) {
|
|
82
|
+
const absEntry = path.resolve(targetCwd, options.entryFile).replace(/\\/g, '/');
|
|
83
|
+
if (!entryPoints.includes(absEntry)) {
|
|
84
|
+
entryPoints.push(absEntry);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
144
88
|
const engine = new RefactoringEngine({
|
|
145
89
|
cwd: targetCwd,
|
|
146
90
|
autoFix: options.fix,
|
|
147
91
|
tsconfig: options.tsconfig,
|
|
148
92
|
testCommand: options.testCommand,
|
|
149
|
-
workspace: options.workspace,
|
|
93
|
+
workspace: options.workspace || localConfig.workspace || false,
|
|
150
94
|
verbose: options.verbose,
|
|
151
95
|
skipConfirm: options.yes,
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
rules: localConfig.rules,
|
|
96
|
+
entryPoints: entryPoints,
|
|
97
|
+
exclude: localConfig.exclude || [],
|
|
98
|
+
rules: localConfig.rules || {},
|
|
156
99
|
debug: options.debug,
|
|
157
100
|
visualize: options.visualize,
|
|
101
|
+
workspacePackages: localConfig.workspacePackages || [],
|
|
102
|
+
workspaceTsConfigs: localConfig.workspaceTsConfigs || [],
|
|
103
|
+
workspaceConfigFiles: localConfig.workspaceConfigFiles || [],
|
|
158
104
|
});
|
|
159
105
|
|
|
160
106
|
await engine.run();
|
|
161
107
|
|
|
162
108
|
clearTimeout(timeoutTimer);
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
} catch (criticalBootError) {
|
|
167
|
-
console.error(ansis.bold.red(`\nπ¨ Critical Lifecycle Boot Instability: ${criticalBootError.message}`));
|
|
168
|
-
if (criticalBootError.stack) {
|
|
169
|
-
console.error(ansis.dim(criticalBootError.stack));
|
|
170
|
-
}
|
|
109
|
+
} catch (err) {
|
|
110
|
+
console.error(ansis.red(`\nβ Engine Failure: ${err.message}`));
|
|
111
|
+
if (program.opts().verbose) console.error(err);
|
|
171
112
|
process.exit(1);
|
|
172
113
|
}
|
|
173
114
|
}
|
|
174
115
|
|
|
175
|
-
|
|
116
|
+
main();
|
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.4.0",
|
|
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
|
@@ -419,6 +419,12 @@ export class EngineContext {
|
|
|
419
419
|
report.importedUnusedPackages = Array.from(this.importedUnusedPackages);
|
|
420
420
|
report.unusedBinaries = Array.from(this.unusedBinaries);
|
|
421
421
|
|
|
422
|
+
// UPGRADE 5.4.3: Integrate enhanced results from GraphAnalyzer
|
|
423
|
+
const analyzer = new (await import('./resolution/GraphAnalyzer.js')).GraphAnalyzer(this);
|
|
424
|
+
const { unusedImports, boundaryViolations } = await analyzer.findDeadCode();
|
|
425
|
+
report.unusedImports = unusedImports;
|
|
426
|
+
report.boundaryViolations = boundaryViolations;
|
|
427
|
+
|
|
422
428
|
return report;
|
|
423
429
|
}
|
|
424
430
|
}
|
package/src/ast/ASTAnalyzer.js
CHANGED
|
@@ -265,7 +265,16 @@ export class ASTAnalyzer {
|
|
|
265
265
|
fileNode.explicitImports.add(specifier);
|
|
266
266
|
fileNode.importedSymbols.add(`${specifier}:*`);
|
|
267
267
|
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
268
|
-
|
|
268
|
+
// UPGRADE: Apply alias/workspace filtering before adding to externalPackageUsage
|
|
269
|
+
const pkgNameReq = this._extractPackageName(specifier);
|
|
270
|
+
let isInternalReq = false;
|
|
271
|
+
if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
|
|
272
|
+
if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgNameReq)) isInternalReq = true;
|
|
273
|
+
}
|
|
274
|
+
if (!isInternalReq && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
|
|
275
|
+
if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgNameReq)) isInternalReq = true;
|
|
276
|
+
}
|
|
277
|
+
if (!isInternalReq) fileNode.externalPackageUsage.add(pkgNameReq);
|
|
269
278
|
}
|
|
270
279
|
} else if (arg && ts.isTemplateExpression(arg)) {
|
|
271
280
|
// Dynamic require with template literal
|
|
@@ -351,8 +360,40 @@ export class ASTAnalyzer {
|
|
|
351
360
|
const specifier = node.moduleSpecifier.text;
|
|
352
361
|
fileNode.explicitImports.add(specifier);
|
|
353
362
|
|
|
363
|
+
// UPGRADE 5.4.2: Distinguish between local aliases and workspace packages
|
|
354
364
|
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
355
|
-
|
|
365
|
+
let isInternalAlias = false;
|
|
366
|
+
const pkgName = this._extractPackageName(specifier);
|
|
367
|
+
|
|
368
|
+
// UPGRADE: First check via isTsconfigAlias() β works even when the target file doesn't exist yet
|
|
369
|
+
if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
|
|
370
|
+
if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgName)) {
|
|
371
|
+
isInternalAlias = true;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// UPGRADE: Check workspace packages (pnpm-workspace.yaml / package.json workspaces)
|
|
376
|
+
if (!isInternalAlias && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
|
|
377
|
+
if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgName)) {
|
|
378
|
+
isInternalAlias = true;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Fallback: try resolvePath() β works when the target file exists on disk
|
|
383
|
+
if (!isInternalAlias && this.context.pathMapper && typeof this.context.pathMapper.resolvePath === 'function') {
|
|
384
|
+
const resolved = this.context.pathMapper.resolvePath(specifier);
|
|
385
|
+
if (resolved !== specifier && (resolved.startsWith('/') || resolved.includes(':'))) {
|
|
386
|
+
// Only mark as internal alias if it's NOT a registered workspace package
|
|
387
|
+
const isWorkspacePkg = this.context.workspaceGraph && this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgName);
|
|
388
|
+
if (!isWorkspacePkg) {
|
|
389
|
+
isInternalAlias = true;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (!isInternalAlias) {
|
|
395
|
+
fileNode.externalPackageUsage.add(pkgName);
|
|
396
|
+
}
|
|
356
397
|
}
|
|
357
398
|
|
|
358
399
|
if (this.context.workspaceGraph && typeof this.context.workspaceGraph.auditImportSpecifier === 'function') {
|
|
@@ -373,14 +414,29 @@ export class ASTAnalyzer {
|
|
|
373
414
|
if (ts.isNamespaceImport(node.importClause.namedBindings)) {
|
|
374
415
|
fileNode.importedSymbols.add(`${specifier}:*`);
|
|
375
416
|
if (targetFile) this.context.importUsageRegistry.add(`${targetFile}:*`);
|
|
417
|
+
|
|
418
|
+
// Track local name for unused import detection
|
|
419
|
+
if (!fileNode.localImportBindings) fileNode.localImportBindings = new Map();
|
|
420
|
+
fileNode.localImportBindings.set(node.importClause.namedBindings.name.text, { specifier, originalName: '*' });
|
|
376
421
|
} else if (ts.isNamedImports(node.importClause.namedBindings)) {
|
|
377
422
|
node.importClause.namedBindings.elements.forEach(element => {
|
|
378
423
|
const importedName = element.propertyName ? element.propertyName.text : element.name.text;
|
|
424
|
+
const localName = element.name.text;
|
|
379
425
|
fileNode.importedSymbols.add(`${specifier}:${importedName}`);
|
|
380
426
|
if (targetFile) this.context.importUsageRegistry.add(`${targetFile}:${importedName}`);
|
|
427
|
+
|
|
428
|
+
// Track local name for unused import detection
|
|
429
|
+
if (!fileNode.localImportBindings) fileNode.localImportBindings = new Map();
|
|
430
|
+
fileNode.localImportBindings.set(localName, { specifier, originalName: importedName });
|
|
381
431
|
});
|
|
382
432
|
}
|
|
383
433
|
}
|
|
434
|
+
|
|
435
|
+
// Handle default import binding
|
|
436
|
+
if (node.importClause.name) {
|
|
437
|
+
if (!fileNode.localImportBindings) fileNode.localImportBindings = new Map();
|
|
438
|
+
fileNode.localImportBindings.set(node.importClause.name.text, { specifier, originalName: 'default' });
|
|
439
|
+
}
|
|
384
440
|
} else {
|
|
385
441
|
// Side-Effect Import (import '...')
|
|
386
442
|
// FIX: Mark the target file as used even if no symbols are imported
|
|
@@ -423,7 +479,22 @@ export class ASTAnalyzer {
|
|
|
423
479
|
if (specifier) {
|
|
424
480
|
fileNode.explicitImports.add(specifier);
|
|
425
481
|
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
426
|
-
|
|
482
|
+
// UPGRADE: Apply the same alias/workspace filtering as handleImportDeclaration
|
|
483
|
+
const pkgNameExport = this._extractPackageName(specifier);
|
|
484
|
+
let isInternalAliasExport = false;
|
|
485
|
+
if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
|
|
486
|
+
if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgNameExport)) {
|
|
487
|
+
isInternalAliasExport = true;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (!isInternalAliasExport && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
|
|
491
|
+
if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgNameExport)) {
|
|
492
|
+
isInternalAliasExport = true;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
if (!isInternalAliasExport) {
|
|
496
|
+
fileNode.externalPackageUsage.add(pkgNameExport);
|
|
497
|
+
}
|
|
427
498
|
}
|
|
428
499
|
|
|
429
500
|
if (!node.exportClause) {
|
|
@@ -587,7 +658,16 @@ export class ASTAnalyzer {
|
|
|
587
658
|
fileNode.dynamicImports.add(specifier);
|
|
588
659
|
fileNode.importedSymbols.add(`${specifier}:*`);
|
|
589
660
|
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
590
|
-
|
|
661
|
+
// UPGRADE: Apply alias/workspace filtering before adding to externalPackageUsage
|
|
662
|
+
const pkgNameDyn = this._extractPackageName(specifier);
|
|
663
|
+
let isInternalDyn = false;
|
|
664
|
+
if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
|
|
665
|
+
if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgNameDyn)) isInternalDyn = true;
|
|
666
|
+
}
|
|
667
|
+
if (!isInternalDyn && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
|
|
668
|
+
if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgNameDyn)) isInternalDyn = true;
|
|
669
|
+
}
|
|
670
|
+
if (!isInternalDyn) fileNode.externalPackageUsage.add(pkgNameDyn);
|
|
591
671
|
}
|
|
592
672
|
} else {
|
|
593
673
|
// Non-literal dynamic import
|
|
@@ -608,9 +688,17 @@ export class ASTAnalyzer {
|
|
|
608
688
|
if (this.context.verbose) console.log(`[AST-DEBUG] Added import: ${specifier}`);
|
|
609
689
|
fileNode.explicitImports.add(specifier);
|
|
610
690
|
|
|
611
|
-
// UPGRADE:
|
|
691
|
+
// UPGRADE: Apply alias/workspace filtering before adding to externalPackageUsage
|
|
612
692
|
if (!specifier.startsWith('.') && !specifier.startsWith('/')) {
|
|
613
|
-
|
|
693
|
+
const pkgNameCE = this._extractPackageName(specifier);
|
|
694
|
+
let isInternalCE = false;
|
|
695
|
+
if (this.context.pathMapper && typeof this.context.pathMapper.isTsconfigAlias === 'function') {
|
|
696
|
+
if (this.context.pathMapper.isTsconfigAlias(specifier) || this.context.pathMapper.isTsconfigAlias(pkgNameCE)) isInternalCE = true;
|
|
697
|
+
}
|
|
698
|
+
if (!isInternalCE && this.context.workspaceGraph && typeof this.context.workspaceGraph.isLocalWorkspaceSpecifier === 'function') {
|
|
699
|
+
if (this.context.workspaceGraph.isLocalWorkspaceSpecifier(specifier) || this.context.workspaceGraph.isLocalWorkspaceSpecifier(pkgNameCE)) isInternalCE = true;
|
|
700
|
+
}
|
|
701
|
+
if (!isInternalCE) fileNode.externalPackageUsage.add(pkgNameCE);
|
|
614
702
|
}
|
|
615
703
|
}
|
|
616
704
|
}
|
package/src/ast/MagicDetector.js
CHANGED
|
@@ -6,15 +6,7 @@ import { PluginRegistry } from '../plugins/PluginRegistry.js';
|
|
|
6
6
|
* Ecosystem Entry Point Manifest & Dynamic Framework Router Heuristic Validator
|
|
7
7
|
* Intercepts implicit conventions to handle cases where direct import statements are absent.
|
|
8
8
|
* Now refactored to use a pluggable architecture.
|
|
9
|
-
*
|
|
10
|
-
* Improvements over v1:
|
|
11
|
-
* - Extended config-file detection list (Biome, Oxlint, tsup, unbuild, etc.)
|
|
12
|
-
* - Next.js App Router conventions (page.tsx, layout.tsx, loading.tsx, error.tsx, etc.)
|
|
13
|
-
* - Remix conventions (route files under app/routes/)
|
|
14
|
-
* - SvelteKit conventions (+page.svelte, +layout.svelte, etc.)
|
|
15
|
-
* - Astro page/layout conventions
|
|
16
|
-
* - Common entry-point patterns (bin/, cli.ts, server.ts, main.ts, app.ts)
|
|
17
|
-
* - Test file patterns extended to cover Vitest workspace files
|
|
9
|
+
* Version 5.4.0: Added detectEntryPointsFromPlugins() for dynamic framework entry detection.
|
|
18
10
|
*/
|
|
19
11
|
export class MagicDetector {
|
|
20
12
|
constructor(context) {
|
|
@@ -31,18 +23,26 @@ export class MagicDetector {
|
|
|
31
23
|
|
|
32
24
|
/**
|
|
33
25
|
* Audits the project context to map active micro-framework ecosystems.
|
|
34
|
-
* @param {string} baseContextDirectory - Root file workspace context execution vector path
|
|
35
26
|
*/
|
|
36
27
|
async identifyActiveProjectEcosystems(baseContextDirectory) {
|
|
37
28
|
await this.ensureInitialized(baseContextDirectory);
|
|
38
29
|
const activePlugins = await this.registry.getActivePlugins(baseContextDirectory);
|
|
39
30
|
const activeFrameworkFlags = activePlugins.map(p => p.name);
|
|
40
|
-
|
|
41
|
-
// Universal infrastructure overrides (testing platforms and common bundlers)
|
|
42
31
|
activeFrameworkFlags.push('universal-tooling-vectors');
|
|
43
32
|
return activeFrameworkFlags;
|
|
44
33
|
}
|
|
45
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Version 5.4.0: Delegates entry point detection to the plugin registry.
|
|
37
|
+
* @param {string} content - The file content
|
|
38
|
+
* @param {string} filePath - The absolute path to the file
|
|
39
|
+
* @returns {Array<string>} List of detected entry points
|
|
40
|
+
*/
|
|
41
|
+
async detectEntryPointsFromPlugins(content, filePath) {
|
|
42
|
+
await this.ensureInitialized();
|
|
43
|
+
return this.registry.detectEntryPointsFromContent(content, filePath);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
46
|
/**
|
|
47
47
|
* Assesses if a file path acts as an implicit route entry point.
|
|
48
48
|
*/
|
|
@@ -60,7 +60,6 @@ export class MagicDetector {
|
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
// Apply baseline platform rules (Test suites, lint parameters, continuous integration files)
|
|
64
63
|
if (this.isCoreToolingSuiteElement(normalizedSystemPath)) {
|
|
65
64
|
return true;
|
|
66
65
|
}
|
|
@@ -68,49 +67,46 @@ export class MagicDetector {
|
|
|
68
67
|
return false;
|
|
69
68
|
}
|
|
70
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Version 5.3.0: Delegates content analysis to the plugin registry.
|
|
72
|
+
*/
|
|
73
|
+
async runPluginContentAnalysis(node, absolutePath) {
|
|
74
|
+
await this.ensureInitialized();
|
|
75
|
+
await this.registry.runPluginAnalysis(node, absolutePath);
|
|
76
|
+
}
|
|
77
|
+
|
|
71
78
|
isCoreToolingSuiteElement(normalizedPath) {
|
|
72
|
-
// ββ Test / spec files ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
73
79
|
if (/\.(test|spec|e2e|cy)\.(js|ts|tsx|jsx)$/i.test(normalizedPath)) return true;
|
|
74
80
|
if (/\.stories\.(js|ts|tsx|jsx)$/i.test(normalizedPath)) return true;
|
|
75
81
|
|
|
76
|
-
// ββ Build / bundler config files βββββββββββββββββββββββββββββββββββββββββββ
|
|
77
82
|
const configFragments = [
|
|
78
|
-
// Test runners
|
|
79
83
|
'jest.config.', 'vitest.config.', 'vitest.workspace.',
|
|
80
84
|
'playwright.config.', 'cypress.config.',
|
|
81
|
-
// Bundlers
|
|
82
85
|
'webpack.config.', 'vite.config.', 'rollup.config.',
|
|
83
86
|
'esbuild.config.', 'parcel.config.',
|
|
84
87
|
'tsup.config.', 'unbuild.config.', 'pkgroll.config.',
|
|
85
|
-
// CSS / styling
|
|
86
88
|
'tailwind.config.', 'postcss.config.', '.postcssrc.',
|
|
87
|
-
// Linters / formatters
|
|
88
89
|
'.eslintrc.', 'eslint.config.', 'prettier.config.', '.prettierrc.',
|
|
89
90
|
'.stylelintrc.', 'stylelint.config.',
|
|
90
91
|
'biome.json', '.oxlintrc.',
|
|
91
|
-
// Babel / transpilation
|
|
92
92
|
'.babelrc.', 'babel.config.',
|
|
93
|
-
// Commit / git hooks
|
|
94
93
|
'.commitlintrc.', 'commitlint.config.',
|
|
95
94
|
'.lintstagedrc.', 'lint-staged.config.',
|
|
96
|
-
// Documentation
|
|
97
95
|
'typedoc.config.', 'typedoc.json',
|
|
98
|
-
// Monorepo tools
|
|
99
96
|
'turbo.json', 'nx.json', 'lerna.json',
|
|
100
|
-
// Misc tooling
|
|
101
97
|
'knip.config.', 'knip.json',
|
|
102
98
|
'syncpack.config.',
|
|
103
|
-
// Internal worker
|
|
104
99
|
'WorkerTaskRunner.js'
|
|
105
100
|
];
|
|
106
101
|
if (configFragments.some(f => normalizedPath.includes(f))) return true;
|
|
107
102
|
|
|
108
|
-
// ββ Common application entry points βββββββββββββββββββββββββββββββββββββββ
|
|
109
103
|
const entryPatterns = [
|
|
110
104
|
// CLI binaries
|
|
111
105
|
'/bin/cli.js', '/bin/cli.ts', '/bin/cli.mjs',
|
|
112
106
|
'/bin/index.js', '/bin/index.ts',
|
|
113
|
-
//
|
|
107
|
+
// Standard package entry points (poly-extension support)
|
|
108
|
+
'/index.js', '/index.ts', '/index.jsx', '/index.tsx', '/index.mjs', '/index.cjs',
|
|
109
|
+
// Server / app entry points
|
|
114
110
|
'/src/main.ts', '/src/main.js',
|
|
115
111
|
'/src/app.ts', '/src/app.js',
|
|
116
112
|
'/src/api/HeadlessAPI.js', '/src/api/PluginSDK.js',
|
|
@@ -119,50 +115,37 @@ export class MagicDetector {
|
|
|
119
115
|
];
|
|
120
116
|
if (entryPatterns.some(p => normalizedPath.endsWith(p))) return true;
|
|
121
117
|
|
|
122
|
-
// ββ Next.js App Router conventions ββββββββββββββββββββββββββββββββββββββββ
|
|
123
|
-
// Files under app/ directory with Next.js special names
|
|
124
118
|
if (/\/app\/(page|layout|loading|error|not-found|template|default|route|middleware)\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
|
|
125
|
-
// Next.js Pages Router
|
|
126
119
|
if (/\/pages\/.*\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
|
|
127
|
-
// Next.js API routes
|
|
128
120
|
if (/\/pages\/api\/.*\.(js|ts)$/.test(normalizedPath)) return true;
|
|
129
|
-
// Next.js middleware
|
|
130
121
|
if (/\/middleware\.(js|ts)$/.test(normalizedPath)) return true;
|
|
131
|
-
// Next.js config
|
|
132
122
|
if (/\/next\.config\.(js|ts|mjs|cjs)$/.test(normalizedPath)) return true;
|
|
133
123
|
|
|
134
|
-
// ββ Remix conventions βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
135
124
|
if (/\/app\/routes\/.*\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
|
|
136
125
|
if (/\/app\/root\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
|
|
137
126
|
if (/\/app\/entry\.(client|server)\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
|
|
138
127
|
|
|
139
|
-
// ββ SvelteKit conventions βββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
140
128
|
if (/\/\+page(\.(server|client))?\.(svelte|js|ts)$/.test(normalizedPath)) return true;
|
|
141
129
|
if (/\/\+layout(\.(server|client))?\.(svelte|js|ts)$/.test(normalizedPath)) return true;
|
|
142
130
|
if (/\/\+error\.(svelte|js|ts)$/.test(normalizedPath)) return true;
|
|
143
131
|
if (/\/\+server\.(js|ts)$/.test(normalizedPath)) return true;
|
|
144
132
|
if (/\/svelte\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
|
|
145
133
|
|
|
146
|
-
// ββ Astro conventions βββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
147
134
|
if (/\/src\/pages\/.*\.astro$/.test(normalizedPath)) return true;
|
|
148
135
|
if (/\/src\/layouts\/.*\.astro$/.test(normalizedPath)) return true;
|
|
149
136
|
if (/\/astro\.config\.(mjs|js|ts)$/.test(normalizedPath)) return true;
|
|
150
137
|
|
|
151
|
-
// ββ Nuxt conventions ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
152
138
|
if (/\/pages\/.*\.vue$/.test(normalizedPath)) return true;
|
|
153
139
|
if (/\/layouts\/.*\.vue$/.test(normalizedPath)) return true;
|
|
154
140
|
if (/\/server\/api\/.*\.(js|ts)$/.test(normalizedPath)) return true;
|
|
155
141
|
if (/\/nuxt\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
|
|
156
142
|
|
|
157
|
-
// ββ React / Vite entry points βββββββββββββββββββββββββββββββββββββββββββββ
|
|
158
143
|
if (/\/vite\.config\.(js|ts|mjs)$/.test(normalizedPath)) return true;
|
|
159
144
|
|
|
160
|
-
// ββ Angular conventions βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
161
145
|
if (/\/main\.(ts|js)$/.test(normalizedPath)) return true;
|
|
162
146
|
if (/\/app\.module\.(ts|js)$/.test(normalizedPath)) return true;
|
|
163
147
|
if (/\/angular\.json$/.test(normalizedPath)) return true;
|
|
164
148
|
|
|
165
|
-
// ββ Expo / React Native βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
166
149
|
if (/\/app\/_layout\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
|
|
167
150
|
if (/\/app\/index\.(js|ts|tsx|jsx)$/.test(normalizedPath)) return true;
|
|
168
151
|
|
|
@@ -176,10 +159,8 @@ export class MagicDetector {
|
|
|
176
159
|
await this.ensureInitialized();
|
|
177
160
|
if (!await this.isImplicitlyRequiredByEcosystem(filePath, activeFrameworks)) return;
|
|
178
161
|
|
|
179
|
-
// Retain entry point elements within memory to keep verification safe
|
|
180
162
|
fileNode.isEntry = true;
|
|
181
163
|
|
|
182
|
-
// Apply dynamic exports coverage metrics based on active platform contracts
|
|
183
164
|
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
184
165
|
const plugins = this.registry.getPlugins();
|
|
185
166
|
|
|
@@ -192,7 +173,6 @@ export class MagicDetector {
|
|
|
192
173
|
const contracts = plugin.getRequiredSystemContracts();
|
|
193
174
|
contracts.forEach(contractMethodToken => {
|
|
194
175
|
if (fileNode.internalExports.has(contractMethodToken)) {
|
|
195
|
-
// Emulate active local reference linkages to protect the export
|
|
196
176
|
fileNode.instantiatedIdentifiers.add(contractMethodToken);
|
|
197
177
|
}
|
|
198
178
|
});
|