Skip to content

Commit 8da0f91

Browse files
aksOpsclaude
andcommitted
fix: file tree drops files when directory-like filePaths exist (monorepo bug)
SERVICE/MODULE nodes with directory-like filePaths (e.g. "packages/api") caused the tree builder to create those path segments as type=FILE. When actual source files under that path (e.g. "packages/api/src/index.ts") were processed, their children were silently dropped because the parent was a FILE node rendered with empty children. Fix: upgrade FILE→DIRECTORY when a path segment is later used as an intermediate directory. Verified on contoso-real-estate monorepo: before 61 files visible, after 282 files visible (231 were silently lost). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 91803c4 commit 8da0f91

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

src/main/java/io/github/randomcodespace/iq/query/QueryService.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,12 @@ public Map<String, Object> getFileTree(Integer maxDepth, int maxFiles) {
375375
boolean isFile = (i == parts.length - 1);
376376
String type = isFile ? PROP_FILE : PROP_DIRECTORY;
377377
TreeNode child = current.children.computeIfAbsent(part, k -> new TreeNode(k, type));
378+
// Upgrade FILE→DIRECTORY if this segment is used as an intermediate path.
379+
// This happens when e.g. "packages/api" (SERVICE node, file) exists AND
380+
// "packages/api/src/index.ts" (source file) also exists.
381+
if (!isFile && PROP_FILE.equals(child.type)) {
382+
child.type = PROP_DIRECTORY;
383+
}
378384
if (isFile) {
379385
child.nodeCount += count;
380386
}
@@ -439,7 +445,7 @@ private long aggregateCount(TreeNode node) {
439445

440446
private static class TreeNode {
441447
final String name;
442-
final String type;
448+
String type; // mutable: FILE may be upgraded to DIRECTORY if later paths use it as an intermediate
443449
long nodeCount = 0;
444450
final Map<String, TreeNode> children = new TreeMap<>();
445451

src/test/java/io/github/randomcodespace/iq/query/QueryServiceTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,48 @@ void getFileTreeShouldBeDeterministic() {
761761
assertEquals(first.toString(), second.toString());
762762
}
763763

764+
@Test
765+
@SuppressWarnings("unchecked")
766+
void getFileTreeShouldUpgradeFileToDirectoryWhenUsedAsIntermediate() {
767+
// Simulates a monorepo where SERVICE nodes have directory-like filePaths
768+
// e.g., "packages/api" (SERVICE) AND "packages/api/src/index.ts" (source file)
769+
when(graphStore.getFilePathsWithCounts(anyInt())).thenReturn(new GraphStore.FilePathResult(List.of(
770+
Map.of("filePath", "packages/api", "nodeCount", 1L),
771+
Map.of("filePath", "packages/api/src/index.ts", "nodeCount", 5L),
772+
Map.of("filePath", "packages/api/src/users.ts", "nodeCount", 3L),
773+
Map.of("filePath", "packages/web/src/App.tsx", "nodeCount", 2L)), false));
774+
775+
Map<String, Object> result = service.getFileTree(null);
776+
List<Map<String, Object>> tree = (List<Map<String, Object>>) result.get("tree");
777+
778+
// packages should be a directory
779+
assertEquals(1, tree.size());
780+
Map<String, Object> packages = tree.get(0);
781+
assertEquals("packages", packages.get("name"));
782+
assertEquals("directory", packages.get("type"));
783+
784+
List<Map<String, Object>> pkgChildren = (List<Map<String, Object>>) packages.get("children");
785+
// api and web directories
786+
assertEquals(2, pkgChildren.size());
787+
788+
// "api" should be upgraded to directory (not file) even though "packages/api" was first
789+
Map<String, Object> api = pkgChildren.get(0);
790+
assertEquals("api", api.get("name"));
791+
assertEquals("directory", api.get("type"));
792+
793+
// api should have children (src directory), not be empty
794+
List<Map<String, Object>> apiChildren = (List<Map<String, Object>>) api.get("children");
795+
assertFalse(apiChildren.isEmpty(), "api directory should have children");
796+
// nodeCount should aggregate: 1 (api itself) + 5 + 3 = 9
797+
assertEquals(9L, api.get("nodeCount"));
798+
799+
// src should contain index.ts and users.ts
800+
Map<String, Object> src = apiChildren.get(0);
801+
assertEquals("src", src.get("name"));
802+
List<Map<String, Object>> srcChildren = (List<Map<String, Object>>) src.get("children");
803+
assertEquals(2, srcChildren.size());
804+
}
805+
764806
// --- findRelatedEndpoints ---
765807

766808
@Test

0 commit comments

Comments
 (0)