diff --git a/src/commands/scan.test.ts b/src/commands/scan.test.ts index 08345af..067d131 100644 --- a/src/commands/scan.test.ts +++ b/src/commands/scan.test.ts @@ -436,11 +436,13 @@ describe('crawlers module', () => { getNpmGlobalPrefix, getYarnGlobalPrefix, getPnpmGlobalPrefix, + getBunGlobalPrefix, } = await import('../crawlers/index.js') assert.equal(typeof getNpmGlobalPrefix, 'function') assert.equal(typeof getYarnGlobalPrefix, 'function') assert.equal(typeof getPnpmGlobalPrefix, 'function') + assert.equal(typeof getBunGlobalPrefix, 'function') }) }) }) diff --git a/src/crawlers/index.ts b/src/crawlers/index.ts index 78235e9..094936a 100644 --- a/src/crawlers/index.ts +++ b/src/crawlers/index.ts @@ -4,4 +4,5 @@ export { getNpmGlobalPrefix, getYarnGlobalPrefix, getPnpmGlobalPrefix, + getBunGlobalPrefix, } from './npm-crawler.js' diff --git a/src/crawlers/npm-crawler.ts b/src/crawlers/npm-crawler.ts index 3c2727e..a8ec43a 100644 --- a/src/crawlers/npm-crawler.ts +++ b/src/crawlers/npm-crawler.ts @@ -104,6 +104,42 @@ function getPnpmGlobalPrefix(): string | null { } } +/** + * Get the bun global node_modules path + */ +function getBunGlobalPrefix(): string | null { + try { + const binPath = execSync('bun pm bin -g', { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }).trim() + const bunRoot = path.dirname(binPath) + return path.join(bunRoot, 'install', 'global', 'node_modules') + } catch { + return null + } +} + +/** + * Get the deno global npm cache path + */ +function getDenoGlobalPrefix(): string | null { + try { + const result = execSync('deno info --json', { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }) + const info = JSON.parse(result) + // Deno stores npm packages at: denoDir/npm/registry.npmjs.org/ + if (info.denoDir) { + return path.join(info.denoDir, 'npm', 'registry.npmjs.org') + } + return null + } catch { + return null + } +} + /** * NPM ecosystem crawler for discovering packages in node_modules */ @@ -150,6 +186,18 @@ export class NpmCrawler { paths.push(yarnPath) } + // Try bun global path + const bunPath = getBunGlobalPrefix() + if (bunPath) { + paths.push(bunPath) + } + + // Try deno global path + const denoPath = getDenoGlobalPrefix() + if (denoPath) { + paths.push(denoPath) + } + return paths } @@ -498,4 +546,10 @@ export class NpmCrawler { } // Re-export global prefix functions for backward compatibility -export { getNpmGlobalPrefix, getYarnGlobalPrefix, getPnpmGlobalPrefix } +export { + getNpmGlobalPrefix, + getYarnGlobalPrefix, + getPnpmGlobalPrefix, + getBunGlobalPrefix, + getDenoGlobalPrefix, +} diff --git a/src/utils/global-packages.ts b/src/utils/global-packages.ts index 5f14441..f581a7e 100644 --- a/src/utils/global-packages.ts +++ b/src/utils/global-packages.ts @@ -51,6 +51,44 @@ export function getPnpmGlobalPrefix(): string | null { } } +/** + * Get the bun global node_modules path + * @returns The path to bun's global node_modules directory, or null if not available + */ +export function getBunGlobalPrefix(): string | null { + try { + const binPath = execSync('bun pm bin -g', { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }).trim() + const bunRoot = path.dirname(binPath) + return path.join(bunRoot, 'install', 'global', 'node_modules') + } catch { + return null + } +} + +/** + * Get the deno global npm cache path + * @returns The path to deno's npm cache directory, or null if not available + */ +export function getDenoGlobalPrefix(): string | null { + try { + const result = execSync('deno info --json', { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }) + const info = JSON.parse(result) + // Deno stores npm packages at: denoDir/npm/registry.npmjs.org/ + if (info.denoDir) { + return path.join(info.denoDir, 'npm', 'registry.npmjs.org') + } + return null + } catch { + return null + } +} + /** * Get the global node_modules path, with support for custom override * @param customPrefix - Optional custom path to use instead of auto-detection @@ -65,7 +103,7 @@ export function getGlobalPrefix(customPrefix?: string): string { /** * Get all global node_modules paths for package lookup - * Currently returns npm global path, but could be extended for yarn global, etc. + * Returns paths from all detected package managers (npm, pnpm, yarn, bun) * @param customPrefix - Optional custom path to use instead of auto-detection * @returns Array of global node_modules paths */ @@ -73,7 +111,36 @@ export function getGlobalNodeModulesPaths(customPrefix?: string): string[] { if (customPrefix) { return [customPrefix] } - return [getNpmGlobalPrefix()] + + const paths: string[] = [] + + try { + paths.push(getNpmGlobalPrefix()) + } catch { + // npm not available + } + + const pnpmPath = getPnpmGlobalPrefix() + if (pnpmPath) { + paths.push(pnpmPath) + } + + const yarnPath = getYarnGlobalPrefix() + if (yarnPath) { + paths.push(yarnPath) + } + + const bunPath = getBunGlobalPrefix() + if (bunPath) { + paths.push(bunPath) + } + + const denoPath = getDenoGlobalPrefix() + if (denoPath) { + paths.push(denoPath) + } + + return paths } /**