π― Use case
This file lives under βutils/β, which covers cross-cutting helpers (shell, tempfiles, settings, messages, process input, β¦). On the API surface it exposes which and whichSync β mainly functions, hooks, or classes. Dependencies touch child processes. It composes internal code from execSyncWrapper (relative imports).
Generated from folder role, exports, dependency roots, and inline comments β not hand-reviewed for every path.
π§ Inline summary
import { execa } from 'execa' import { execSync_DEPRECATED } from './execSyncWrapper.js' async function whichNodeAsync(command: string): Promise<string | null> { if (process.platform === 'win32') {
π€ Exports (heuristic)
whichwhichSync
π External import roots
Package roots from from "β¦" (relative paths omitted).
execa
π₯οΈ Source preview
import { execa } from 'execa'
import { execSync_DEPRECATED } from './execSyncWrapper.js'
async function whichNodeAsync(command: string): Promise<string | null> {
if (process.platform === 'win32') {
// On Windows, use where.exe and return the first result
const result = await execa(`where.exe ${command}`, {
shell: true,
stderr: 'ignore',
reject: false,
})
if (result.exitCode !== 0 || !result.stdout) {
return null
}
// where.exe returns multiple paths separated by newlines, return the first
return result.stdout.trim().split(/\r?\n/)[0] || null
}
// On POSIX systems (macOS, Linux, WSL), use which
// Cross-platform safe: Windows is handled above
// eslint-disable-next-line custom-rules/no-cross-platform-process-issues
const result = await execa(`which ${command}`, {
shell: true,
stderr: 'ignore',
reject: false,
})
if (result.exitCode !== 0 || !result.stdout) {
return null
}
return result.stdout.trim()
}
function whichNodeSync(command: string): string | null {
if (process.platform === 'win32') {
try {
const result = execSync_DEPRECATED(`where.exe ${command}`, {
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'ignore'],
})
const output = result.toString().trim()
return output.split(/\r?\n/)[0] || null
} catch {
return null
}
}
try {
const result = execSync_DEPRECATED(`which ${command}`, {
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'ignore'],
})
return result.toString().trim() || null
} catch {
return null
}
}
const bunWhich =
typeof Bun !== 'undefined' && typeof Bun.which === 'function'
? Bun.which
: null
/**
* Finds the full path to a command executable.
* Uses Bun.which when running in Bun (fast, no process spawn),
* otherwise spawns the platform-appropriate command.
*
* @param command - The command name to look up
* @returns The full path to the command, or null if not found
*/
export const which: (command: string) => Promise<string | null> = bunWhich
? async command => bunWhich(command)
: whichNodeAsync
/**
* Synchronous version of `which`.
*
* @param command - The command name to look up
* @returns The full path to the command, or null if not found
*/
export const whichSync: (command: string) => string | null =
bunWhich ?? whichNodeSync