UNPKG

@skarab/detect-package-manager

Version:

Detects which package manager (bun, pnpm, yarn, npm) is used based on the current working directory.

131 lines (130 loc) 4.68 kB
import { basename } from 'node:path'; import { readFileSync } from 'node:fs'; import { findFile, walkDirectoryUp } from './fs.js'; import { tryGetCommandVersion } from './version.js'; export const agents = ['bun', 'pnpm', 'yarn', 'npm']; /** * Detects all (known) agents installed on the system. */ export async function detectInstalledAgents() { const agentsFound = new Map(); for (const name of agents) { const version = await tryGetCommandVersion(name); if (version) { agentsFound.set(name, { name, version }); } } return agentsFound; } /** * Try to find the used agent from a directory with the following rules: * * - Try to find the agent in `packageManager` field from `package.json`. * - If not found, try to find the agent from lock file name. * - If not found, stats again in parent directory. * * @param directory If no directory is provided, the current working directory will be used. */ export function detectAgent(directory) { return walkDirectoryUp(async (directory) => { let agent = await detectAgentFromPackageJSON(directory); if (!agent) { agent = await detectAgentFromLockFile(directory); } return agent; }, directory); } /** * Try to find the used agent defined in `packageManager` field from `package.json`. * * @param directory If no directory is provided, the current working directory will be used. */ export async function detectAgentFromPackageJSON(directory = process.cwd()) { const packageJSONPath = await findFile('package.json', directory); if (!packageJSONPath) { return; } const packageJSONContent = readFileSync(packageJSONPath, 'utf8'); const { packageManager } = JSON.parse(packageJSONContent); if (!packageManager) { return; } const [name, version] = packageManager.split('@'); return { name, version }; } export const lockFiles = [ 'pnpm-lock.yaml', 'yarn.lock', 'package-lock.json', 'bun.lockb', 'shrinkwrap.yaml', 'npm-shrinkwrap.json', ]; export function detectNPMVersionFromLockFile(path) { // https://docs.npmjs.com/cli/v8/configuring-npm/package-lock-json#lockfileversion const { lockfileVersion } = JSON.parse(readFileSync(path, 'utf8')); switch (lockfileVersion) { case 1: return { name: 'npm', version: '5 - 6' }; case 2: return { name: 'npm', version: '>=5' }; case 3: return { name: 'npm', version: '>=7' }; default: return { name: 'npm', version: '<5' }; } } export const pnpmLockFileVersionToVersion = { '5.4': '>=7.0.0', '5.3': '>=6.0.0', '5.2': '>=5.10.0', '5.1': '>=3.5.0', '5.0': '>=3.0.0', '4.0': '>=2.17.0', '3.9': '>=2.13.3', '3.8': '>=2.8.0', '3.7': '>=2.0.0', '3.6': '>=1.43.0', '3.5': '>=1.40.0', '3.4': '>=1.23.0', '3.3': '>=1.22.0', '3.2': '>=1.18.1', '3.1': '1.17 - 1.18.0', '3': '1 - 1.16', '2': '0.62 - 0', }; export function detectPNPMVersionFromLockFile(path) { var _a; // https://github.com/pnpm/spec/tree/master/lockfile const content = readFileSync(path, 'utf8'); const [, lockfileVersion] = (_a = content.match(/^lockfileVersion: (\d(\.\d)?)$/m)) !== null && _a !== void 0 ? _a : []; if (!lockfileVersion) { return { name: 'pnpm', version: '<0.62' }; } const version = pnpmLockFileVersionToVersion[lockfileVersion]; return { name: 'pnpm', version: version !== null && version !== void 0 ? version : Object.values(pnpmLockFileVersionToVersion)[0] }; } export const lockFileVersionDetector = { 'pnpm-lock.yaml': detectPNPMVersionFromLockFile, 'shrinkwrap.yaml': detectPNPMVersionFromLockFile, 'package-lock.json': detectNPMVersionFromLockFile, 'npm-shrinkwrap.json': detectNPMVersionFromLockFile, 'bun.lockb': () => ({ name: 'bun', version: '>=0' }), 'yarn.lock': (path) => { // https://github.com/yarnpkg/berry/blob/635ed55d7582fe6ee1af4c4b2e033f0fdc33fd2f/packages/yarnpkg-core/sources/scriptUtils.ts#L87 const version = /^__metadata:$/m.test(readFileSync(path, 'utf8')) ? '2' : '1'; return { name: 'yarn', version }; }, }; /** * Try to find the used agent from lock file name and/or content. * * @param directory If no directory is provided, the current working directory will be used. */ export async function detectAgentFromLockFile(directory = process.cwd()) { const lockFilePath = await findFile(lockFiles, directory); if (!lockFilePath) { return; } return lockFileVersionDetector[basename(lockFilePath)](lockFilePath); }