UNPKG

@bfra.me/eslint-config

Version:
112 lines (99 loc) 3.24 kB
import {spawnSync} from 'node:child_process' import {existsSync} from 'node:fs' import {resolve} from 'node:path' import process from 'node:process' import {packageDirectorySync} from 'package-directory' import {resolveCommand, type AgentName} from 'package-manager-detector' const installedModules = new Set<string>() export function tryInstall(module: string, targetFile: string = process.cwd()) { if (installedModules.has(module)) { return null } const cwd = packageDirectorySync({cwd: targetFile}) ?? '' if (cwd) { installedModules.add(module) const {moduleName, version = ''} = parseModule(module) const result = installPackageSync(moduleName + (version ? `@^${version}` : ''), { cwd, dev: true, }) return result } return null } export function getPackageInstallCommand(module: string, targetFile: string = process.cwd()) { const cwd = packageDirectorySync({cwd: targetFile}) ?? '' if (!cwd) { return null } const agent = detectPackageManagerSync(cwd) if (!agent) { return null } const command = resolveCommand(agent, 'install', ['-D', module]) return command ? `${command.command} ${command.args.join(' ')}` : null } function parseModule(name: string) { const parts = name.split(/@/u) if (parts.length > 1 && parts.slice(0, -1).join('@')) { return { moduleName: parts.slice(0, -1).join('@'), version: parts.at(-1), } } return {moduleName: name} } function installPackageSync(packages: string | string[], options: {cwd: string; dev: boolean}) { const manager = (detectPackageManagerSync(options.cwd)?.split('@')[0] ?? '') || 'npm' if (!Array.isArray(packages)) { packages = [packages] } const args = [] if ( manager === 'pnpm' && existsSync(resolve(options.cwd ?? process.cwd(), 'pnpm-workspace.yaml')) ) { args.unshift( '-w', /** * Prevent pnpm from removing installed development deps when `NODE_ENV` is `production` * @see https://pnpm.io/cli/install#--prod--p */ '--prod=false', ) } const result = spawnSync( manager, [manager === 'yarn' ? 'add' : 'install', options.dev ? '-D' : '', ...args, ...packages].filter( Boolean, ), {cwd: options.cwd, maxBuffer: Infinity, stdio: 'inherit', windowsHide: true}, ) if (result.error || result.status !== 0) { const errorMessage = (result.error?.message ?? '') || `Package installation failed with status ${result.status}` throw new Error(errorMessage) } return `${result.output?.toString() ?? ''}` } function detectPackageManagerSync(cwd = process.cwd()): AgentName | null { // We cannot reliably convert an async detect() to sync detectSync() // without potentially blocking. The proper fix would be to make this // function async, but that would break API compatibility. // For now, we'll use a simple fallback approach: try { // Look for lockfiles directly if (existsSync(resolve(cwd, 'package-lock.json'))) { return 'npm' } if (existsSync(resolve(cwd, 'yarn.lock'))) { return 'yarn' } if (existsSync(resolve(cwd, 'pnpm-lock.yaml'))) { return 'pnpm' } return null } catch { return null } }