entkapp 5.0.0 → 5.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/bin/cli.js +2 -2
- package/package.json +1 -1
- package/src/ast/ASTAnalyzer.js +29 -14
- package/src/ast/BarrelParser.js +22 -20
- package/src/ast/OxcAnalyzer.js +6 -0
- package/src/index.js +8 -5
- package/src/resolution/CircularDetector.js +64 -25
- package/src/resolution/DepencyResolver.js +12 -1
- package/src/resolution/PathMapper.js +47 -3
- package/src/resolution/WorkSpaceGraph.js +4 -1
- package/docs.zip +0 -0
package/bin/cli.js
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.
|
|
31
|
+
.version(packageJsonContent.version || '5.1.0');
|
|
32
32
|
|
|
33
33
|
program
|
|
34
34
|
.option('-c, --cwd <path>', 'Specify the execution context root directory', process.cwd())
|
|
@@ -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.1.0'} 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/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.1.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/ast/ASTAnalyzer.js
CHANGED
|
@@ -234,7 +234,11 @@ export class ASTAnalyzer {
|
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
236
|
break;
|
|
237
|
+
|
|
237
238
|
case ts.SyntaxKind.CallExpression:
|
|
239
|
+
// FIX: Führe zuerst die generische Aufrufanalyse aus, die vorher blockiert war
|
|
240
|
+
this.handleCallExpression(node, fileNode, sourceFile);
|
|
241
|
+
|
|
238
242
|
if (node.expression.kind === ts.SyntaxKind.ImportKeyword) {
|
|
239
243
|
const arg = node.arguments[0];
|
|
240
244
|
if (arg) {
|
|
@@ -244,14 +248,12 @@ export class ASTAnalyzer {
|
|
|
244
248
|
fileNode.explicitImports.add(specifier);
|
|
245
249
|
} else {
|
|
246
250
|
// UPGRADE: Handle Dynamic Imports with variables/expressions
|
|
247
|
-
// We mark the file as having "calculated" dynamic imports to be conservative
|
|
248
251
|
fileNode.hasCalculatedDynamicImports = true;
|
|
249
252
|
const exprText = arg.getText(sourceFile);
|
|
250
|
-
if (!fileNode.calculatedDynamicImports
|
|
253
|
+
if (!fileNode.calculatedDynamicImports || Array.isArray(fileNode.calculatedDynamicImports)) {
|
|
254
|
+
fileNode.calculatedDynamicImports = new Set();
|
|
255
|
+
}
|
|
251
256
|
fileNode.calculatedDynamicImports.add(exprText);
|
|
252
|
-
|
|
253
|
-
// Conservative approach: If we see `import(path)`, any file in the same or sub directory could be a target.
|
|
254
|
-
// This is a simplified version of what knip/entkapp does for plugins.
|
|
255
257
|
fileNode.dynamicImports.add('__DYNAMIC_PATTERN__');
|
|
256
258
|
}
|
|
257
259
|
}
|
|
@@ -269,12 +271,23 @@ export class ASTAnalyzer {
|
|
|
269
271
|
// Dynamic require with template literal
|
|
270
272
|
fileNode.hasCalculatedDynamicImports = true;
|
|
271
273
|
const exprText = arg.getText(sourceFile);
|
|
272
|
-
|
|
273
|
-
|
|
274
|
+
|
|
275
|
+
// FIX: Typ-Integrität als Set erzwingen (kein Array [])
|
|
276
|
+
if (!fileNode.calculatedDynamicImports || Array.isArray(fileNode.calculatedDynamicImports)) {
|
|
277
|
+
fileNode.calculatedDynamicImports = new Set();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// FIX: .add() statt .push() verwenden, um Absturz in Pass 2 zu verhindern
|
|
281
|
+
fileNode.calculatedDynamicImports.add({
|
|
282
|
+
kind: 'DynamicRequire',
|
|
283
|
+
expression: exprText,
|
|
284
|
+
start: arg.getStart(sourceFile)
|
|
285
|
+
});
|
|
274
286
|
fileNode.dynamicImports.add('__DYNAMIC_PATTERN__');
|
|
275
287
|
}
|
|
276
288
|
}
|
|
277
289
|
break;
|
|
290
|
+
|
|
278
291
|
case ts.SyntaxKind.PropertyAccessExpression:
|
|
279
292
|
{
|
|
280
293
|
const chain = node.getText(sourceFile);
|
|
@@ -284,9 +297,7 @@ export class ASTAnalyzer {
|
|
|
284
297
|
}
|
|
285
298
|
}
|
|
286
299
|
break;
|
|
287
|
-
|
|
288
|
-
this.handleCallExpression(node, fileNode, sourceFile);
|
|
289
|
-
break;
|
|
300
|
+
|
|
290
301
|
case ts.SyntaxKind.JsxElement:
|
|
291
302
|
case ts.SyntaxKind.JsxSelfClosingElement:
|
|
292
303
|
this.handleJsxElement(node, fileNode, sourceFile);
|
|
@@ -580,7 +591,9 @@ export class ASTAnalyzer {
|
|
|
580
591
|
}
|
|
581
592
|
} else {
|
|
582
593
|
// Non-literal dynamic import
|
|
583
|
-
if (!fileNode.calculatedDynamicImports)
|
|
594
|
+
if (!Array.isArray(fileNode.calculatedDynamicImports)) {
|
|
595
|
+
fileNode.calculatedDynamicImports = [];
|
|
596
|
+
}
|
|
584
597
|
fileNode.calculatedDynamicImports.push({ kind: ts.SyntaxKind[arg.kind], start: arg.getStart(sourceFile) });
|
|
585
598
|
|
|
586
599
|
// UPGRADE 2: Try to perform Glob-Analysis on Template Strings
|
|
@@ -605,11 +618,13 @@ export class ASTAnalyzer {
|
|
|
605
618
|
if (ts.isTemplateExpression(node)) {
|
|
606
619
|
const head = node.head.text;
|
|
607
620
|
if (head.startsWith('./') || head.startsWith('../')) {
|
|
608
|
-
//
|
|
621
|
+
// FIX: Typ-Integrität als Set erzwingen, kein Array!
|
|
622
|
+
if (!fileNode.globImports || Array.isArray(fileNode.globImports)) {
|
|
623
|
+
fileNode.globImports = new Set();
|
|
624
|
+
}
|
|
609
625
|
const dirPath = path.dirname(head);
|
|
610
626
|
if (dirPath && dirPath !== '.') {
|
|
611
|
-
|
|
612
|
-
fileNode.globImports.add(dirPath);
|
|
627
|
+
fileNode.globImports.add(dirPath); // Jetzt klappt .add() garantiert!
|
|
613
628
|
}
|
|
614
629
|
}
|
|
615
630
|
}
|
package/src/ast/BarrelParser.js
CHANGED
|
@@ -7,7 +7,7 @@ import fs from 'fs/promises';
|
|
|
7
7
|
*/
|
|
8
8
|
export class BarrelParser {
|
|
9
9
|
constructor(context, resolver) {
|
|
10
|
-
this.context = context;
|
|
10
|
+
this.context = context; // Enthält globale Konfigurationen (z.B. context.entrypoints)
|
|
11
11
|
this.resolver = resolver;
|
|
12
12
|
this.cachedSpecifications = new Map();
|
|
13
13
|
}
|
|
@@ -23,28 +23,43 @@ export class BarrelParser {
|
|
|
23
23
|
|
|
24
24
|
const specification = {
|
|
25
25
|
isBarrelInstance: false,
|
|
26
|
+
isApplicationEntrypoint: false, // FIX: Schutzschicht für Einstiegspunkte gegen Fehldetektion
|
|
26
27
|
wildcardExports: new Set(), // export * from './module';
|
|
27
28
|
namespacedWildcardExports: new Map(), // export * as Utils from './module';
|
|
28
29
|
forwardedNamedExports: new Map(), // export { token as alias } from './module';
|
|
29
30
|
declaredLocalExports: new Set() // export const a = 1;
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
// FIX: Überprüfe, ob die aktuelle Datei als App-Einstiegspunkt deklariert ist
|
|
34
|
+
if (this.context && this.context.entrypoints) {
|
|
35
|
+
const isEntry = this.context.entrypoints.some(entry =>
|
|
36
|
+
this.resolver.resolveModulePath(filePath, entry) === filePath || entry === filePath
|
|
37
|
+
);
|
|
38
|
+
if (isEntry) {
|
|
39
|
+
specification.isApplicationEntrypoint = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
try {
|
|
33
44
|
const code = await fs.readFile(filePath, 'utf8');
|
|
34
45
|
const sourceFile = ts.createSourceFile(filePath, code, ts.ScriptTarget.Latest, true);
|
|
35
46
|
|
|
36
47
|
this.harvestExportSignatures(sourceFile, specification);
|
|
37
48
|
|
|
38
|
-
|
|
49
|
+
// FIX: Eine Datei ist NUR DANN ein rein kosmetisches Barrel, wenn sie Re-Exports besitzt
|
|
50
|
+
// UND NICHT der geschützte Haupteinstiegspunkt (Entrypoint) der Anwendung ist!
|
|
51
|
+
if (!specification.isApplicationEntrypoint && (
|
|
52
|
+
specification.wildcardExports.size > 0 ||
|
|
39
53
|
specification.namespacedWildcardExports.size > 0 ||
|
|
40
|
-
specification.forwardedNamedExports.size > 0
|
|
54
|
+
specification.forwardedNamedExports.size > 0
|
|
55
|
+
)) {
|
|
41
56
|
specification.isBarrelInstance = true;
|
|
42
57
|
}
|
|
43
58
|
|
|
44
59
|
this.cachedSpecifications.set(filePath, specification);
|
|
45
60
|
return specification;
|
|
46
61
|
} catch {
|
|
47
|
-
return specification; // Error
|
|
62
|
+
return specification; // Error-State fällt auf sicheres Standard-Layout zurück
|
|
48
63
|
}
|
|
49
64
|
}
|
|
50
65
|
|
|
@@ -117,14 +132,7 @@ export class BarrelParser {
|
|
|
117
132
|
break;
|
|
118
133
|
}
|
|
119
134
|
|
|
120
|
-
// Track default exports declared directly within the file boundary:
|
|
121
|
-
// Handles both `export default function foo()` and `export default expression`.
|
|
122
|
-
// The canonical symbol name stored is 'default' so that forwardedNamedExports
|
|
123
|
-
// entries whose sourceSymbol is 'default' can resolve correctly via Rule A.
|
|
124
135
|
case ts.SyntaxKind.ExportAssignment: {
|
|
125
|
-
// ExportAssignment covers `export default <expr>` (isExportEquals === false)
|
|
126
|
-
// as well as `export = <expr>` (isExportEquals === true, CommonJS style).
|
|
127
|
-
// We register 'default' for both to ensure the barrel tracer can settle here.
|
|
128
136
|
spec.declaredLocalExports.add('default');
|
|
129
137
|
break;
|
|
130
138
|
}
|
|
@@ -140,10 +148,6 @@ export class BarrelParser {
|
|
|
140
148
|
* @param {Map} activeProjectGraph - Global active module maps directory registry
|
|
141
149
|
* @param {Set} [protectionStack] - Avoids cyclic validation traps inside self-referencing links
|
|
142
150
|
*/
|
|
143
|
-
/**
|
|
144
|
-
* UPGRADE 3: Cross-Barrel Symbol Tracking.
|
|
145
|
-
* Resolves the original source of a symbol through re-exports and aliases.
|
|
146
|
-
*/
|
|
147
151
|
async determineSymbolDeclarationOrigin(contextFilePath, targetSymbolName, activeProjectGraph, protectionStack = new Set()) {
|
|
148
152
|
if (protectionStack.has(contextFilePath)) {
|
|
149
153
|
return { originFile: contextFilePath, originSymbol: targetSymbolName };
|
|
@@ -172,7 +176,6 @@ export class BarrelParser {
|
|
|
172
176
|
// Rule C: Evaluate structural namespace alias groupings (export * as name from 'module')
|
|
173
177
|
for (const [namespaceAlias, relativeModule] of spec.namespacedWildcardExports.entries()) {
|
|
174
178
|
if (targetSymbolName === namespaceAlias) {
|
|
175
|
-
// The symbol IS the namespace itself
|
|
176
179
|
const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, relativeModule);
|
|
177
180
|
return { originFile: fullyResolvedPath, originSymbol: '*' };
|
|
178
181
|
}
|
|
@@ -190,7 +193,7 @@ export class BarrelParser {
|
|
|
190
193
|
const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, relativePath);
|
|
191
194
|
|
|
192
195
|
if (fullyResolvedPath) {
|
|
193
|
-
//
|
|
196
|
+
// Verbinde Graph-Kanten direkt während der Wildcard-Traversierung
|
|
194
197
|
const contextNode = activeProjectGraph.get(contextFilePath);
|
|
195
198
|
if (contextNode) {
|
|
196
199
|
contextNode.outgoingEdges.add(fullyResolvedPath);
|
|
@@ -204,13 +207,12 @@ export class BarrelParser {
|
|
|
204
207
|
fullyResolvedPath,
|
|
205
208
|
targetSymbolName,
|
|
206
209
|
activeProjectGraph,
|
|
207
|
-
new Set(protectionStack) //
|
|
210
|
+
new Set(protectionStack) // Kopie verhindert geschwisterliche Branch-Blockaden
|
|
208
211
|
);
|
|
209
212
|
|
|
210
213
|
if (!continuousResolutionTrace) continue;
|
|
211
214
|
if (continuousResolutionTrace.originFile === contextFilePath) continue;
|
|
212
215
|
|
|
213
|
-
// Verify that the resolved origin actually declares the symbol.
|
|
214
216
|
const originSpec = await this.parseBarrelSpecification(continuousResolutionTrace.originFile);
|
|
215
217
|
if (originSpec.declaredLocalExports.has(continuousResolutionTrace.originSymbol) ||
|
|
216
218
|
continuousResolutionTrace.originSymbol === '*') {
|
|
@@ -225,4 +227,4 @@ export class BarrelParser {
|
|
|
225
227
|
|
|
226
228
|
return { originFile: contextFilePath, originSymbol: targetSymbolName };
|
|
227
229
|
}
|
|
228
|
-
}
|
|
230
|
+
}
|
package/src/ast/OxcAnalyzer.js
CHANGED
|
@@ -250,6 +250,9 @@ export class OxcAnalyzer {
|
|
|
250
250
|
} else if (node.source.type === "TemplateLiteral") {
|
|
251
251
|
// Conservative Dynamic Analysis: Treat template literal imports as "potentially anything"
|
|
252
252
|
const quasis = node.source.quasis.map(q => q.value.cooked).join('*');
|
|
253
|
+
if (!Array.isArray(fileNode.calculatedDynamicImports)) {
|
|
254
|
+
fileNode.calculatedDynamicImports = [];
|
|
255
|
+
}
|
|
253
256
|
fileNode.calculatedDynamicImports.push({
|
|
254
257
|
kind: 'TemplateLiteral',
|
|
255
258
|
pattern: quasis,
|
|
@@ -437,6 +440,9 @@ export class OxcAnalyzer {
|
|
|
437
440
|
handleDynamicRequire(node, fileNode) {
|
|
438
441
|
if (node.type === "TemplateLiteral") {
|
|
439
442
|
const quasis = node.quasis.map(q => q.value.cooked).join('*');
|
|
443
|
+
if (!Array.isArray(fileNode.calculatedDynamicImports)) {
|
|
444
|
+
fileNode.calculatedDynamicImports = [];
|
|
445
|
+
}
|
|
440
446
|
fileNode.calculatedDynamicImports.push({
|
|
441
447
|
kind: 'DynamicRequire',
|
|
442
448
|
pattern: quasis,
|
package/src/index.js
CHANGED
|
@@ -563,10 +563,13 @@ export class RefactoringEngine {
|
|
|
563
563
|
if (node && (node.isLibraryEntry || node.isEntry)) return false;
|
|
564
564
|
|
|
565
565
|
let isPackageEntryPoint = false;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
566
|
+
const manifests = this.workspaceGraph?.packageManifests;
|
|
567
|
+
if (manifests) {
|
|
568
|
+
for (const [_, metadata] of manifests.entries()) {
|
|
569
|
+
if (metadata.entryPoints && metadata.entryPoints.map(p => slashifyLocal(p)).includes(cleanAbsPath)) {
|
|
570
|
+
isPackageEntryPoint = true;
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
570
573
|
}
|
|
571
574
|
}
|
|
572
575
|
return !isPackageEntryPoint;
|
|
@@ -574,7 +577,7 @@ export class RefactoringEngine {
|
|
|
574
577
|
}
|
|
575
578
|
analysisSummary.unlistedDependencies = this.context.unlistedDependencies || [];
|
|
576
579
|
|
|
577
|
-
const advancedResults = this.advancedAnalysis.runAll(this.context.projectGraph, this.workspaceGraph
|
|
580
|
+
const advancedResults = this.advancedAnalysis.runAll(this.context.projectGraph, this.workspaceGraph?.packageManifests || new Map());
|
|
578
581
|
const cycles = cyclesResult;
|
|
579
582
|
|
|
580
583
|
const structuralModificationsStaged =
|
|
@@ -6,47 +6,86 @@ export class CircularDetector {
|
|
|
6
6
|
this.cycles = [];
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
detectCycles(projectGraph
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
9
|
+
detectCycles(projectGraph) {
|
|
10
|
+
const stack = [];
|
|
11
|
+
const onStack = new Set();
|
|
12
|
+
const indices = new Map();
|
|
13
|
+
const lowlink = new Map();
|
|
14
|
+
const sccs = [];
|
|
15
|
+
let index = 0;
|
|
13
16
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const strongConnect = (v) => {
|
|
18
|
+
indices.set(v, index);
|
|
19
|
+
lowlink.set(v, index);
|
|
20
|
+
index++;
|
|
21
|
+
stack.push(v);
|
|
22
|
+
onStack.add(v);
|
|
18
23
|
|
|
19
|
-
const node = projectGraph.get(
|
|
24
|
+
const node = projectGraph.get(v);
|
|
20
25
|
if (node && node.outgoingEdges) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
// Ensure outgoingEdges is treated as an array-like structure
|
|
27
|
+
const edges = Array.isArray(node.outgoingEdges) ? node.outgoingEdges : Array.from(node.outgoingEdges);
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < edges.length; i++) {
|
|
30
|
+
const w = edges[i];
|
|
31
|
+
if (!indices.has(w)) {
|
|
32
|
+
strongConnect(w);
|
|
33
|
+
lowlink.set(v, Math.min(lowlink.get(v), lowlink.get(w)));
|
|
34
|
+
} else if (onStack.has(w)) {
|
|
35
|
+
lowlink.set(v, Math.min(lowlink.get(v), indices.get(w)));
|
|
29
36
|
}
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
39
|
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
if (lowlink.get(v) === indices.get(v)) {
|
|
41
|
+
const scc = [];
|
|
42
|
+
let w;
|
|
43
|
+
do {
|
|
44
|
+
w = stack.pop();
|
|
45
|
+
onStack.delete(w);
|
|
46
|
+
scc.push(w);
|
|
47
|
+
} while (w !== v);
|
|
48
|
+
|
|
49
|
+
if (scc.length > 1) {
|
|
50
|
+
const cycle = scc.slice().reverse();
|
|
51
|
+
cycle.push(cycle[0]);
|
|
52
|
+
sccs.push(cycle);
|
|
53
|
+
} else if (scc.length === 1) {
|
|
54
|
+
const singleNode = projectGraph.get(scc[0]);
|
|
55
|
+
if (singleNode && singleNode.outgoingEdges) {
|
|
56
|
+
const edges = Array.isArray(singleNode.outgoingEdges) ? singleNode.outgoingEdges : Array.from(singleNode.outgoingEdges);
|
|
57
|
+
let hasSelfLoop = false;
|
|
58
|
+
for (let i = 0; i < edges.length; i++) {
|
|
59
|
+
if (edges[i] === scc[0]) {
|
|
60
|
+
hasSelfLoop = true;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (hasSelfLoop) {
|
|
65
|
+
sccs.push([scc[0], scc[0]]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
35
70
|
};
|
|
36
71
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
const keys = Array.from(projectGraph.keys());
|
|
73
|
+
for (let i = 0; i < keys.length; i++) {
|
|
74
|
+
if (!indices.has(keys[i])) {
|
|
75
|
+
strongConnect(keys[i]);
|
|
40
76
|
}
|
|
41
77
|
}
|
|
42
78
|
|
|
43
|
-
this.cycles =
|
|
44
|
-
return
|
|
79
|
+
this.cycles = sccs;
|
|
80
|
+
return sccs;
|
|
45
81
|
}
|
|
46
82
|
|
|
47
83
|
formatCycles() {
|
|
48
84
|
return this.cycles.map(cycle => {
|
|
49
|
-
return cycle.map(p =>
|
|
85
|
+
return cycle.map(p => {
|
|
86
|
+
const relativePath = path.relative(this.context.cwd, p);
|
|
87
|
+
return relativePath.replace(/\\/g, '/');
|
|
88
|
+
}).join(' -> ');
|
|
50
89
|
});
|
|
51
90
|
}
|
|
52
91
|
}
|
|
@@ -24,12 +24,23 @@ export class DependencyResolver {
|
|
|
24
24
|
|
|
25
25
|
resolveModulePath(sourceFile, specifier) {
|
|
26
26
|
const cleanSource = this.normalizePath(sourceFile);
|
|
27
|
+
|
|
28
|
+
// UPGRADE: Use PathMapper for sophisticated resolution (TS-to-JS, aliases, etc.)
|
|
29
|
+
if (this.pathMapper) {
|
|
30
|
+
const dir = path.dirname(cleanSource);
|
|
31
|
+
const target = path.resolve(dir, specifier);
|
|
32
|
+
const resolved = this.pathMapper.resolvePath(target);
|
|
33
|
+
if (resolved && existsSync(resolved)) {
|
|
34
|
+
return this.normalizePath(resolved);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
if (specifier.startsWith('.')) {
|
|
28
39
|
const dir = path.dirname(cleanSource);
|
|
29
40
|
const target = path.resolve(dir, specifier);
|
|
30
41
|
const normalizedTarget = this.normalizePath(target);
|
|
31
42
|
|
|
32
|
-
const extensions = ['', '.js', '.ts', '.tsx', '.jsx', '/index.js', '/index.ts'];
|
|
43
|
+
const extensions = ['', '.js', '.ts', '.tsx', '.jsx', '/index.js', '/index.ts', '/index.tsx'];
|
|
33
44
|
for (const ext of extensions) {
|
|
34
45
|
const p = normalizedTarget + ext;
|
|
35
46
|
if (existsSync(p)) return this.normalizePath(p);
|
|
@@ -1,5 +1,49 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
1
4
|
export class PathMapper {
|
|
2
|
-
constructor(context) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
constructor(context) {
|
|
6
|
+
this.context = context;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
async loadMappings() {
|
|
10
|
+
// Hier können später tsconfig-Pfade geladen werden
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolves physical module paths on disk, translating modern .js imports
|
|
15
|
+
* back to their actual TypeScript source files.
|
|
16
|
+
* @param {string} p - The target module specifier or absolute path
|
|
17
|
+
*/
|
|
18
|
+
resolvePath(p) {
|
|
19
|
+
if (!p || typeof p !== 'string') return p;
|
|
20
|
+
|
|
21
|
+
// FIX 1: Wenn der Import auf .js endet, übersetze ihn für die Suche auf .ts
|
|
22
|
+
if (p.endsWith('.js')) {
|
|
23
|
+
const tsPath = p.slice(0, -3) + '.ts';
|
|
24
|
+
if (fs.existsSync(tsPath)) return tsPath;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// FIX 2: Wenn der Import auf .jsx endet, übersetze ihn für die Suche auf .tsx
|
|
28
|
+
if (p.endsWith('.jsx')) {
|
|
29
|
+
const tsxPath = p.slice(0, -4) + '.tsx';
|
|
30
|
+
if (fs.existsSync(tsxPath)) return tsxPath;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// FIX 3: Unterstützung für Verzeichnis-Imports (z.B. ./adapters -> ./adapters/index.ts)
|
|
34
|
+
try {
|
|
35
|
+
const stat = fs.statSync(p);
|
|
36
|
+
if (stat.isDirectory()) {
|
|
37
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx'];
|
|
38
|
+
for (const ext of extensions) {
|
|
39
|
+
const indexPath = path.join(p, `index${ext}`);
|
|
40
|
+
if (fs.existsSync(indexPath)) return indexPath;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
// Datei existiert nicht oder ist kein Verzeichnis, fahre mit Standard fort
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return p;
|
|
48
|
+
}
|
|
5
49
|
}
|
package/docs.zip
DELETED
|
Binary file
|