Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/commands/scan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
})
})
1 change: 1 addition & 0 deletions src/crawlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export {
getNpmGlobalPrefix,
getYarnGlobalPrefix,
getPnpmGlobalPrefix,
getBunGlobalPrefix,
} from './npm-crawler.js'
56 changes: 55 additions & 1 deletion src/crawlers/npm-crawler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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,
}
71 changes: 69 additions & 2 deletions src/utils/global-packages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -65,15 +103,44 @@ 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
*/
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
}

/**
Expand Down