Skip to content

Commit a1ffad7

Browse files
invalidclaude
andcommitted
fix: rebuild module dependencies during incremental update, fix --depth 0
- Add rebuildDependencies() in differ.js that reconstructs module-level dependsOn/dependedBy from file-level import data after mergeGraphUpdate - Update recalculateSummary() to also rebuild entryPoints and config.languages - Fix impact command: --depth 0 was silently treated as 3 due to falsy || fallback Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8591f83 commit a1ffad7

2 files changed

Lines changed: 72 additions & 2 deletions

File tree

cli/src/commands/impact.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ export function registerImpactCommand(program) {
2525
process.exit(1);
2626
}
2727

28-
const depth = parseInt(options.depth, 10) || 3;
28+
const parsed = parseInt(options.depth, 10);
29+
const depth = Number.isNaN(parsed) ? 3 : parsed;
2930
const result = analyzeImpact(graph, target, { depth });
3031

3132
console.log(`Impact analysis for: ${target}`);

cli/src/differ.js

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* and provides a function to merge partial updates into the full graph.
66
*/
77

8+
import path from 'path';
9+
810
/**
911
* Compare two file hash maps to detect changes.
1012
*
@@ -111,8 +113,9 @@ export function mergeGraphUpdate(graph, updatedFiles, removedFiles) {
111113
}
112114
}
113115

114-
// Step 4: Recalculate summary
116+
// Step 4: Recalculate summary and rebuild module dependencies
115117
recalculateSummary(graph);
118+
rebuildDependencies(graph);
116119
}
117120

118121
/**
@@ -140,4 +143,70 @@ function recalculateSummary(graph) {
140143
graph.summary.totalClasses = totalClasses;
141144
graph.summary.languages = languages;
142145
graph.summary.modules = Object.keys(graph.modules).sort();
146+
graph.summary.entryPoints = Object.entries(graph.files)
147+
.filter(([, f]) => f.isEntryPoint)
148+
.map(([relPath]) => relPath)
149+
.sort();
150+
if (graph.config) {
151+
graph.config.languages = Object.keys(languages);
152+
}
153+
}
154+
155+
/**
156+
* Rebuild module-level dependsOn / dependedBy from file-level import data.
157+
*
158+
* Iterates every file in the graph, resolves each relative import to a target
159+
* module via a normalised-path lookup (mirroring scanner.js logic), and
160+
* overwrites the dependency arrays on every module.
161+
*
162+
* @param {object} graph - The full code graph (mutated in place).
163+
*/
164+
function rebuildDependencies(graph) {
165+
// Build normalised relative-path → moduleName lookup (O(1) resolution)
166+
const pathLookup = new Map();
167+
for (const [relPath, fileData] of Object.entries(graph.files)) {
168+
const norm = relPath.replace(/\\/g, '/');
169+
pathLookup.set(norm, fileData.module);
170+
const withoutExt = norm.replace(/\.[^/.]+$/, '');
171+
if (!pathLookup.has(withoutExt)) {
172+
pathLookup.set(withoutExt, fileData.module);
173+
}
174+
}
175+
176+
// Collect dependencies using Sets
177+
const depSets = {};
178+
for (const modName of Object.keys(graph.modules)) {
179+
depSets[modName] = { dependsOn: new Set(), dependedBy: new Set() };
180+
}
181+
182+
for (const [relPath, fileData] of Object.entries(graph.files)) {
183+
const moduleName = fileData.module;
184+
if (!depSets[moduleName]) continue;
185+
186+
for (const imp of fileData.imports) {
187+
if (imp.isExternal) continue;
188+
if (!imp.source.startsWith('.')) continue;
189+
190+
const importerDir = path.posix.dirname(relPath.replace(/\\/g, '/'));
191+
const resolved = path.posix.normalize(importerDir + '/' + imp.source);
192+
193+
let targetModule = pathLookup.get(resolved);
194+
if (!targetModule) {
195+
targetModule = pathLookup.get(resolved + '/index');
196+
}
197+
198+
if (targetModule && targetModule !== moduleName && depSets[targetModule]) {
199+
depSets[moduleName].dependsOn.add(targetModule);
200+
depSets[targetModule].dependedBy.add(moduleName);
201+
}
202+
}
203+
}
204+
205+
// Apply rebuilt dependencies to graph
206+
for (const [modName, deps] of Object.entries(depSets)) {
207+
if (graph.modules[modName]) {
208+
graph.modules[modName].dependsOn = [...deps.dependsOn].sort();
209+
graph.modules[modName].dependedBy = [...deps.dependedBy].sort();
210+
}
211+
}
143212
}

0 commit comments

Comments
 (0)