UNPKG

packmate

Version:

Your smart and friendly interactive assistant for dependency updates, security advisories, and cleanup.

139 lines (117 loc) 4.01 kB
import { execSync } from 'child_process'; import fs from 'fs'; import path from 'path'; import process from 'process'; let cachedPackageManager = null; function getProjectRoot() { return process.cwd(); } function findLockFileUpward(root, lockFile) { let current = root; for (let i = 0; i < 3; i++) { // 3단계까지만 탐색 (원하면 늘릴 수 있음) const filePath = path.join(current, lockFile); if (fs.existsSync(filePath)) return filePath; const parent = path.dirname(current); if (parent === current) break; current = parent; } return null; } /** * 패키지 매니저가 실제로 설치되어 사용 가능한지 확인합니다 */ function isPackageManagerAvailable(pm) { try { // Windows와 Unix-like 시스템 모두에서 작동하도록 개선 // shell 옵션을 사용하여 PATH 환경변수 올바르게 해석 const options = { encoding: 'utf-8', stdio: 'pipe', shell: true // Windows와 Unix-like 모두에서 안정적으로 작동 }; execSync(`${pm} --version`, options); return true; } catch (error) { return false; } } /** * 사용 가능한 패키지 매니저 목록을 우선순위대로 가져옵니다 */ function getAvailablePackageManagers() { const available = []; const managers = ['pnpm', 'yarn', 'npm']; for (const pm of managers) { if (isPackageManagerAvailable(pm)) { available.push(pm); } } return available; } export function getPackageManagerInfo() { const root = getProjectRoot(); const lockFiles = { pnpm: findLockFileUpward(root, 'pnpm-lock.yaml'), yarn: findLockFileUpward(root, 'yarn.lock'), npm: findLockFileUpward(root, 'package-lock.json'), }; const found = Object.entries(lockFiles) .filter(([, v]) => v) .map(([name, filePath]) => ({ name, filePath, available: isPackageManagerAvailable(name) })); const available = getAvailablePackageManagers(); return { found, available }; } export function setDetectedPackageManager(packageManager) { cachedPackageManager = packageManager; } export function detectPackageManager({ silent = false } = {}) { if (cachedPackageManager) return cachedPackageManager; const { found, available } = getPackageManagerInfo(); // 사용 가능한 패키지 매니저가 전혀 없음 if (available.length === 0) { console.error('\x1b[31m%s\x1b[0m', '❌ No package manager found. Please install npm, yarn, or pnpm.'); process.exit(1); } // 여러 락 파일 감지됨 if (!silent && found.length > 1) { console.log( '\x1b[33m%s\x1b[0m', `⚠️ Multiple lock files detected: ${found.map((item) => item.name).join(', ')}.`, ); } // 락 파일 없음 - 첫 번째 사용 가능한 패키지 매니저 사용 if (found.length === 0) { if (!silent) { console.log('\x1b[33m%s\x1b[0m', `⚠️ No lock file detected. Using: ${available[0]}`); } cachedPackageManager = available[0]; return cachedPackageManager; } // 감지된 패키지 매니저가 실제로 사용 가능한지 확인 const detectedPm = found[0].name; if (isPackageManagerAvailable(detectedPm)) { // 완벽한 매칭 - 락 파일이 존재하고 패키지 매니저가 설치됨 if (!silent && found.length > 1) { console.log('\x1b[32m%s\x1b[0m', `✓ Using: ${detectedPm}`); } cachedPackageManager = detectedPm; } else { // 락 파일은 존재하지만 패키지 매니저가 설치되지 않음 - 폴백 const fallbackPm = available[0]; console.log( '\x1b[33m%s\x1b[0m', `⚠️ ${detectedPm} lock file found, but ${detectedPm} is not installed.`, ); console.log( '\x1b[33m%s\x1b[0m', `⚠️ Falling back to: ${fallbackPm}`, ); console.log( '\x1b[36m%s\x1b[0m', `💡 Tip: Install ${detectedPm} globally with: npm install -g ${detectedPm}`, ); cachedPackageManager = fallbackPm; } return cachedPackageManager; }