process->run( Command::fromArray(['npm', '--version']) ); if (! $result->isSuccess()) { return 'unknown'; } return trim($result->stdout); } /** * Installiert Dependencies. */ public function install(bool $production = false): bool { $args = ['npm', 'install']; if ($production) { $args[] = '--production'; } $result = $this->process->run( command: Command::fromArray($args), workingDirectory: $this->projectRoot, timeout: Duration::fromMinutes(5) ); return $result->isSuccess(); } /** * Aktualisiert Dependencies. */ public function update(bool $production = false): bool { $args = ['npm', 'update']; if ($production) { $args[] = '--production'; } $result = $this->process->run( command: Command::fromArray($args), workingDirectory: $this->projectRoot, timeout: Duration::fromMinutes(10) ); return $result->isSuccess(); } /** * Führt Security Audit durch. */ public function audit(): NpmAuditResult { $result = $this->process->run( command: Command::fromArray(['npm', 'audit', '--json']), workingDirectory: $this->projectRoot, timeout: Duration::fromSeconds(60) ); if (! $result->isSuccess()) { // Auch bei Vulnerabilities ist Exit-Code != 0 // Versuche trotzdem JSON zu parsen } $data = json_decode($result->stdout, true); if (! is_array($data)) { return NpmAuditResult::safe(); } return NpmAuditResult::fromAuditJson($data); } /** * Versucht automatisches Fixing von Vulnerabilities. */ public function auditFix(bool $force = false): bool { $args = ['npm', 'audit', 'fix']; if ($force) { $args[] = '--force'; } $result = $this->process->run( command: Command::fromArray($args), workingDirectory: $this->projectRoot, timeout: Duration::fromMinutes(5) ); return $result->isSuccess(); } /** * Listet veraltete Packages auf. * * @return NpmPackage[] */ public function getOutdatedPackages(): array { $result = $this->process->run( command: Command::fromArray(['npm', 'outdated', '--json']), workingDirectory: $this->projectRoot, timeout: Duration::fromSeconds(60) ); // npm outdated gibt Exit-Code != 0 wenn es outdated packages gibt $data = json_decode($result->stdout, true); if (! is_array($data)) { return []; } $packages = []; foreach ($data as $name => $info) { $packages[] = NpmPackage::fromNpmOutput( name: $name, currentVersion: $info['current'] ?? 'unknown', wantedVersion: $info['wanted'] ?? null, latestVersion: $info['latest'] ?? null, type: $info['type'] ?? null ); } return $packages; } /** * Listet installierte Packages auf. * * @return NpmPackage[] */ public function getInstalledPackages(): array { $result = $this->process->run( command: Command::fromArray(['npm', 'list', '--json', '--depth=0']), workingDirectory: $this->projectRoot, timeout: Duration::fromSeconds(30) ); if (! $result->isSuccess()) { return []; } $data = json_decode($result->stdout, true); if (! is_array($data)) { return []; } $packages = []; // Dependencies foreach ($data['dependencies'] ?? [] as $name => $info) { $packages[] = new NpmPackage( name: $name, currentVersion: $info['version'] ?? 'unknown', type: 'dependencies' ); } // DevDependencies (wenn --depth=0 nicht nur dependencies zurückgibt) foreach ($data['devDependencies'] ?? [] as $name => $info) { $packages[] = new NpmPackage( name: $name, currentVersion: $info['version'] ?? 'unknown', type: 'devDependencies' ); } return $packages; } /** * Führt NPM Script aus. */ public function runScript(string $script): bool { $result = $this->process->run( command: Command::fromArray(['npm', 'run', $script]), workingDirectory: $this->projectRoot, timeout: Duration::fromMinutes(10) ); return $result->isSuccess(); } /** * Cleaned node_modules und package-lock.json. */ public function clean(): bool { if ($this->projectRoot === null) { return false; } $nodeModules = $this->projectRoot->toString() . '/node_modules'; $packageLock = $this->projectRoot->toString() . '/package-lock.json'; // Remove node_modules if (is_dir($nodeModules)) { $result = $this->process->run( command: Command::fromArray(['rm', '-rf', $nodeModules]), timeout: Duration::fromMinutes(2) ); if (! $result->isSuccess()) { return false; } } // Remove package-lock.json if (file_exists($packageLock)) { $result = $this->process->run( command: Command::fromArray(['rm', $packageLock]), timeout: Duration::fromSeconds(5) ); if (! $result->isSuccess()) { return false; } } return true; } /** * Prüft package.json Validität. */ public function validate(): bool { if ($this->projectRoot === null) { return false; } $packageJson = $this->projectRoot->toString() . '/package.json'; if (! file_exists($packageJson)) { return false; } $content = file_get_contents($packageJson); $data = json_decode($content, true); // Grundlegende Validierung return is_array($data) && isset($data['name']) && isset($data['version']); } /** * Initialisiert neues NPM-Projekt. */ public function init(bool $yes = false): bool { $args = ['npm', 'init']; if ($yes) { $args[] = '--yes'; } $result = $this->process->run( command: Command::fromArray($args), workingDirectory: $this->projectRoot, timeout: Duration::fromSeconds(30) ); return $result->isSuccess(); } /** * Installiert spezifisches Package. */ public function installPackage(string $package, bool $dev = false, bool $exact = false): bool { $args = ['npm', 'install', $package]; if ($dev) { $args[] = '--save-dev'; } if ($exact) { $args[] = '--save-exact'; } $result = $this->process->run( command: Command::fromArray($args), workingDirectory: $this->projectRoot, timeout: Duration::fromMinutes(3) ); return $result->isSuccess(); } /** * Deinstalliert spezifisches Package. */ public function uninstallPackage(string $package): bool { $result = $this->process->run( command: Command::fromArray(['npm', 'uninstall', $package]), workingDirectory: $this->projectRoot, timeout: Duration::fromMinutes(2) ); return $result->isSuccess(); } /** * Zeigt Package-Informationen. */ public function showPackageInfo(string $package): ?array { $result = $this->process->run( command: Command::fromArray(['npm', 'show', $package, '--json']), timeout: Duration::fromSeconds(30) ); if (! $result->isSuccess()) { return null; } $data = json_decode($result->stdout, true); return is_array($data) ? $data : null; } }