entkapp 4.5.0 → 5.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/bin/cli.js +4 -4
- package/docs.zip +0 -0
- package/entkapp/config.json +0 -1
- package/package.json +5 -6
- package/src/EngineContext.js +276 -78
- package/src/analyzers/OxcAnalyzer.js +8 -380
- package/src/api/HeadlessAPI.js +1 -1
- package/src/api/PluginSDK.js +23 -187
- package/src/ast/ASTAnalyzer.js +467 -253
- package/src/ast/AdvancedAnalysis.js +6 -5
- package/src/ast/BarrelParser.js +23 -16
- package/src/ast/DeadCodeDetector.js +30 -18
- package/src/ast/MagicDetector.js +1 -1
- package/src/ast/OxcAnalyzer.js +328 -264
- package/src/index.js +272 -361
- package/src/performance/GraphCache.js +21 -2
- package/src/performance/WorkerPool.js +11 -1
- package/src/performance/WorkerTaskRunner.js +72 -25
- package/src/plugins/PluginRegistry.js +5 -16
- package/src/plugins/ecosystems/GenericPlugins.js +61 -0
- package/src/refractor/TransactionManager.js +3 -136
- package/src/refractor/TypeIntegrity.js +2 -73
- package/src/resolution/CircularDetector.js +27 -66
- package/src/resolution/ConfigLoader.js +2 -85
- package/src/resolution/DepencyResolver.js +20 -124
- package/src/resolution/EntryPointDetector.js +134 -0
- package/src/resolution/PathMapper.js +3 -123
- package/src/resolution/TSConfigLoader.js +76 -0
- package/src/resolution/WorkSpaceGraph.js +4 -473
- package/src/resolution/WorkspaceDiagnostic.js +3 -57
- package/src/plugins/KnipAdapter.js +0 -106
|
@@ -112,6 +112,7 @@ export class AdvancedAnalysis {
|
|
|
112
112
|
* Package.json Export Reachability
|
|
113
113
|
*/
|
|
114
114
|
analyzeExportReachability(projectGraph, packageManifests) {
|
|
115
|
+
if (!packageManifests) return;
|
|
115
116
|
for (const [pkgName, metadata] of packageManifests.entries()) {
|
|
116
117
|
const publicEntries = new Set(metadata.entryPoints);
|
|
117
118
|
|
|
@@ -557,13 +558,13 @@ export class AdvancedAnalysis {
|
|
|
557
558
|
this.analyzeDataIntegrity(filePath, node.ast);
|
|
558
559
|
this.analyzeEventLeaks(filePath, node.ast);
|
|
559
560
|
this.analyzeBinaryShaking(filePath, node.ast);
|
|
561
|
+
this.analyzeDereferences(filePath, node.ast);
|
|
562
|
+
this.analyzeClones(filePath, node.ast);
|
|
563
|
+
this.analyzeEscapes(filePath, node.ast);
|
|
564
|
+
this.analyzeLoops(filePath, node.ast);
|
|
565
|
+
this.analyzeConfigSanity(filePath, node.ast);
|
|
560
566
|
}
|
|
561
567
|
this.analyzeSandboxing(filePath, projectGraph);
|
|
562
|
-
this.analyzeDereferences(filePath, node.ast);
|
|
563
|
-
this.analyzeClones(filePath, node.ast);
|
|
564
|
-
this.analyzeEscapes(filePath, node.ast);
|
|
565
|
-
this.analyzeLoops(filePath, node.ast);
|
|
566
|
-
this.analyzeConfigSanity(filePath, node.ast);
|
|
567
568
|
}
|
|
568
569
|
this.analyzeExportReachability(projectGraph, packageManifests);
|
|
569
570
|
|
package/src/ast/BarrelParser.js
CHANGED
|
@@ -140,6 +140,10 @@ export class BarrelParser {
|
|
|
140
140
|
* @param {Map} activeProjectGraph - Global active module maps directory registry
|
|
141
141
|
* @param {Set} [protectionStack] - Avoids cyclic validation traps inside self-referencing links
|
|
142
142
|
*/
|
|
143
|
+
/**
|
|
144
|
+
* UPGRADE 3: Cross-Barrel Symbol Tracking.
|
|
145
|
+
* Resolves the original source of a symbol through re-exports and aliases.
|
|
146
|
+
*/
|
|
143
147
|
async determineSymbolDeclarationOrigin(contextFilePath, targetSymbolName, activeProjectGraph, protectionStack = new Set()) {
|
|
144
148
|
if (protectionStack.has(contextFilePath)) {
|
|
145
149
|
return { originFile: contextFilePath, originSymbol: targetSymbolName };
|
|
@@ -156,7 +160,7 @@ export class BarrelParser {
|
|
|
156
160
|
return { originFile: contextFilePath, originSymbol: targetSymbolName };
|
|
157
161
|
}
|
|
158
162
|
|
|
159
|
-
// Rule B: Evaluate explicit named re-export mappings
|
|
163
|
+
// Rule B: Evaluate explicit named re-export mappings (export { A as B } from 'module')
|
|
160
164
|
if (spec.forwardedNamedExports.has(targetSymbolName)) {
|
|
161
165
|
const routingRule = spec.forwardedNamedExports.get(targetSymbolName);
|
|
162
166
|
const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, routingRule.targetModule);
|
|
@@ -167,7 +171,11 @@ export class BarrelParser {
|
|
|
167
171
|
|
|
168
172
|
// Rule C: Evaluate structural namespace alias groupings (export * as name from 'module')
|
|
169
173
|
for (const [namespaceAlias, relativeModule] of spec.namespacedWildcardExports.entries()) {
|
|
170
|
-
|
|
174
|
+
if (targetSymbolName === namespaceAlias) {
|
|
175
|
+
// The symbol IS the namespace itself
|
|
176
|
+
const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, relativeModule);
|
|
177
|
+
return { originFile: fullyResolvedPath, originSymbol: '*' };
|
|
178
|
+
}
|
|
171
179
|
if (targetSymbolName.startsWith(`${namespaceAlias}.`)) {
|
|
172
180
|
const originalSymbol = targetSymbolName.substring(namespaceAlias.length + 1);
|
|
173
181
|
const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, relativeModule);
|
|
@@ -178,19 +186,20 @@ export class BarrelParser {
|
|
|
178
186
|
}
|
|
179
187
|
|
|
180
188
|
// Rule D: Sweep through anonymous star re-exports vectors (export * from 'module')
|
|
181
|
-
//
|
|
182
|
-
// Algorithm:
|
|
183
|
-
// 1. For each `export * from './child'`, recursively resolve the symbol in the child.
|
|
184
|
-
// 2. A non-barrel child immediately returns `{ originFile: childPath }` regardless of
|
|
185
|
-
// whether it actually declares the symbol. We therefore must verify that the
|
|
186
|
-
// returned origin file actually contains the symbol in its declaredLocalExports
|
|
187
|
-
// before accepting the result.
|
|
188
|
-
// 3. If the child is itself a barrel, the recursive call already performs the full
|
|
189
|
-
// chain walk, so we only need to verify the final origin.
|
|
190
189
|
for (const relativePath of spec.wildcardExports) {
|
|
191
190
|
const fullyResolvedPath = this.resolver.resolveModulePath(contextFilePath, relativePath);
|
|
192
191
|
|
|
193
192
|
if (fullyResolvedPath) {
|
|
193
|
+
// FIX: Mark the target module as active immediately when export * is found
|
|
194
|
+
const contextNode = activeProjectGraph.get(contextFilePath);
|
|
195
|
+
if (contextNode) {
|
|
196
|
+
contextNode.outgoingEdges.add(fullyResolvedPath);
|
|
197
|
+
const targetNode = activeProjectGraph.get(fullyResolvedPath);
|
|
198
|
+
if (targetNode) {
|
|
199
|
+
targetNode.incomingEdges.add(contextFilePath);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
194
203
|
const continuousResolutionTrace = await this.determineSymbolDeclarationOrigin(
|
|
195
204
|
fullyResolvedPath,
|
|
196
205
|
targetSymbolName,
|
|
@@ -202,14 +211,12 @@ export class BarrelParser {
|
|
|
202
211
|
if (continuousResolutionTrace.originFile === contextFilePath) continue;
|
|
203
212
|
|
|
204
213
|
// Verify that the resolved origin actually declares the symbol.
|
|
205
|
-
// This prevents a non-barrel sibling (e.g. constants.ts) from being
|
|
206
|
-
// incorrectly returned for a symbol it does not export (e.g. formatData).
|
|
207
214
|
const originSpec = await this.parseBarrelSpecification(continuousResolutionTrace.originFile);
|
|
208
|
-
if (originSpec.declaredLocalExports.has(continuousResolutionTrace.originSymbol)
|
|
215
|
+
if (originSpec.declaredLocalExports.has(continuousResolutionTrace.originSymbol) ||
|
|
216
|
+
continuousResolutionTrace.originSymbol === '*') {
|
|
209
217
|
return continuousResolutionTrace;
|
|
210
218
|
}
|
|
211
|
-
|
|
212
|
-
// recursive call already resolved through it – accept the result.
|
|
219
|
+
|
|
213
220
|
if (originSpec.isBarrelInstance) {
|
|
214
221
|
return continuousResolutionTrace;
|
|
215
222
|
}
|
|
@@ -10,26 +10,32 @@ export class DeadCodeDetector {
|
|
|
10
10
|
// Find all entry points
|
|
11
11
|
const entryPoints = new Set();
|
|
12
12
|
for (const [filePath, node] of graph.entries()) {
|
|
13
|
-
|
|
13
|
+
// NIGHTMARE FIX: node.isEntry must be respected, and also check for library entries
|
|
14
|
+
if (node.isEntry || node.isLibraryEntry || node.isNextJsRoute || node.isSvelteComponent || node.isAstroPage) {
|
|
14
15
|
entryPoints.add(filePath);
|
|
15
16
|
}
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
//
|
|
19
|
+
// UPGRADE 5: Cycle-tolerant Graph Traversal.
|
|
20
|
+
// We use a robust recursive traversal with a visited set to handle cycles.
|
|
19
21
|
const usedFiles = new Set();
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
const visited = new Set();
|
|
23
|
+
|
|
24
|
+
const traverse = (filePath) => {
|
|
25
|
+
if (visited.has(filePath)) return;
|
|
26
|
+
visited.add(filePath);
|
|
27
|
+
usedFiles.add(filePath);
|
|
28
|
+
|
|
29
|
+
const node = graph.get(filePath);
|
|
30
|
+
if (node && node.outgoingEdges) {
|
|
31
|
+
for (const edge of node.outgoingEdges) {
|
|
32
|
+
traverse(edge);
|
|
31
33
|
}
|
|
32
34
|
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
for (const entry of entryPoints) {
|
|
38
|
+
traverse(entry);
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
// Identify dead files
|
|
@@ -45,25 +51,31 @@ export class DeadCodeDetector {
|
|
|
45
51
|
if (!node) continue;
|
|
46
52
|
|
|
47
53
|
// If it's an entry point, we consider its exports used (unless strictly configured otherwise)
|
|
48
|
-
if
|
|
54
|
+
// NIGHTMARE FIX: Even in entry points, we might want to find unused exports if they are not re-exported
|
|
55
|
+
if (node.isEntry && !node.isLibraryEntry) {
|
|
56
|
+
// If it's a main entry but not a library, its exports are usually unused
|
|
57
|
+
// unless it's a barrel file. Let's proceed to check usage.
|
|
58
|
+
} else if (node.isLibraryEntry) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
49
61
|
|
|
50
62
|
for (const [exportName, exportInfo] of node.internalExports.entries()) {
|
|
51
63
|
if (exportName === '*' || exportName === 'default') continue; // Skip wildcards for now
|
|
52
64
|
|
|
53
65
|
let isUsed = false;
|
|
54
|
-
// Check
|
|
66
|
+
// Check ALL nodes in the graph for usage of this symbol, not just those with edges
|
|
67
|
+
// (Handles cases where edges might be missing or complex)
|
|
55
68
|
for (const [otherPath, otherNode] of graph.entries()) {
|
|
56
|
-
if (otherNode.outgoingEdges.has(filePath)) {
|
|
57
69
|
if (otherNode.importedSymbols.has(`${filePath}:${exportName}`) ||
|
|
58
70
|
otherNode.importedSymbols.has(`${filePath}:*`)) {
|
|
59
71
|
isUsed = true;
|
|
60
72
|
break;
|
|
61
73
|
}
|
|
62
|
-
}
|
|
63
74
|
}
|
|
64
75
|
|
|
65
76
|
// Additional check for library entries or index files
|
|
66
|
-
|
|
77
|
+
// NIGHTMARE FIX: Don't blindly protect 'index' files if they are not real entries
|
|
78
|
+
if (!isUsed && (node.isLibraryEntry)) {
|
|
67
79
|
isUsed = true;
|
|
68
80
|
}
|
|
69
81
|
|
package/src/ast/MagicDetector.js
CHANGED
|
@@ -177,7 +177,7 @@ export class MagicDetector {
|
|
|
177
177
|
if (!await this.isImplicitlyRequiredByEcosystem(filePath, activeFrameworks)) return;
|
|
178
178
|
|
|
179
179
|
// Retain entry point elements within memory to keep verification safe
|
|
180
|
-
fileNode.
|
|
180
|
+
fileNode.isEntry = true;
|
|
181
181
|
|
|
182
182
|
// Apply dynamic exports coverage metrics based on active platform contracts
|
|
183
183
|
const normalizedPath = filePath.replace(/\\/g, '/');
|