From b3130463aa9fe8d5e02c94ed685ec1807d58a714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20J=C3=A4gle?= Date: Tue, 10 Mar 2026 14:01:35 +0100 Subject: [PATCH] fix(search): follow symlinks when walking docset directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit local_folder docsets are stored as symlinked directories. readdir() withFileTypes returns Dirent objects where isDirectory()/isFile() return false for symlinks — only isSymbolicLink() is true. walkFiles() was therefore skipping all symlinked content, causing files_count: 0. Fix: when entry.isSymbolicLink(), use stat() (which follows the link) to determine whether the target is a directory or file, then recurse/yield accordingly. Broken symlinks are silently skipped. --- packages/core/src/search/searcher.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/core/src/search/searcher.ts b/packages/core/src/search/searcher.ts index 117a548..f1fa561 100644 --- a/packages/core/src/search/searcher.ts +++ b/packages/core/src/search/searcher.ts @@ -276,17 +276,29 @@ async function* walkFiles(dir: string): AsyncGenerator { for (const entry of entries) { const absPath = join(dir, entry.name); - if (entry.isDirectory()) { + // For symlinks, stat() follows the link to get the real type. + // entry.isDirectory() / entry.isFile() return false for symlinks. + let isDir = entry.isDirectory(); + let isFile = entry.isFile(); + if (entry.isSymbolicLink()) { + try { + const s = await stat(absPath); + isDir = s.isDirectory(); + isFile = s.isFile(); + } catch { + continue; // broken symlink — skip + } + } + + if (isDir) { if (!IGNORED_NAMES.has(entry.name)) { yield* walkFiles(absPath); } - } else if (entry.isFile()) { + } else if (isFile) { if (!IGNORED_FILES.has(entry.name)) { yield absPath; } } - // symlinks: follow only if they point to files (readdir withFileTypes - // resolves symlinks on most platforms) } }