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.
@@ -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
 
@@ -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
- // If the targetSymbolName is prefixed with the namespaceAlias, then it's a member of this namespace re-export
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
- // The origin spec is itself a barrel (isBarrelInstance = true) and the
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
- if (node.isEntry || node.isNextJsRoute || node.isSvelteComponent || node.isAstroPage) {
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
- // Traverse from entry points to find used files
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 queue = Array.from(entryPoints);
21
-
22
- while (queue.length > 0) {
23
- const current = queue.shift();
24
- if (!usedFiles.has(current)) {
25
- usedFiles.add(current);
26
- const node = graph.get(current);
27
- if (node && node.outgoingEdges) {
28
- for (const edge of node.outgoingEdges) {
29
- queue.push(edge);
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 (node.isEntry) continue;
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 incoming edges if they import this specific symbol
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
- if (!isUsed && (node.isLibraryEntry || filePath.toLowerCase().includes('index'))) {
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
 
@@ -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.isLibraryEntry = true;
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, '/');