UNPKG

ng-upgrade-orchestrator

Version:

Enterprise-grade Angular Multi-Version Upgrade Orchestrator with automatic npm installation, comprehensive dependency management, and seamless integration of all 9 official Angular migrations. Safely migrate Angular applications across multiple major vers

311 lines 13.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DependencyInstaller = void 0; const child_process_1 = require("child_process"); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const chalk_1 = __importDefault(require("chalk")); const ora_1 = __importDefault(require("ora")); class DependencyInstaller { projectPath; spinner; constructor(projectPath) { this.projectPath = projectPath; this.spinner = (0, ora_1.default)(); } /** * Install or update multiple dependencies automatically */ async installDependencies(dependencies, message) { try { this.spinner.start(message || 'Installing dependencies...'); // First, update package.json with new dependencies const packageJsonUpdated = await this.updatePackageJsonSafely(dependencies); if (!packageJsonUpdated) { this.spinner.fail(chalk_1.default.red('✗ Failed to update package.json')); return false; } // Then run npm install to install all dependencies this.spinner.text = 'Running npm install to install all dependencies...'; const npmInstallSuccess = await this.runNpmInstallWithRetry(); if (npmInstallSuccess) { this.spinner.succeed(chalk_1.default.green('✓ Dependencies installed successfully')); return true; } else { this.spinner.fail(chalk_1.default.red('✗ npm install failed')); // Try individual package installation as fallback console.log(chalk_1.default.yellow('Attempting individual package installation...')); return await this.installIndividualPackages(dependencies); } } catch (error) { this.spinner.fail(chalk_1.default.red('✗ Failed to install dependencies')); console.error(chalk_1.default.yellow('Error details:'), error instanceof Error ? error.message : String(error)); // Final fallback: just update package.json const fallbackSuccess = await this.fallbackUpdatePackageJson(dependencies); if (fallbackSuccess) { console.log(chalk_1.default.yellow('\n⚠ Dependencies were added to package.json.')); console.log(chalk_1.default.yellow('Please run "npm install" manually to complete the installation.\n')); } return fallbackSuccess; } } /** * Update Angular core packages to specific version */ async updateAngularPackages(version) { const angularPackages = [ // Core Angular packages { name: '@angular/animations', version: `^${version}.0.0`, type: 'dependencies' }, { name: '@angular/common', version: `^${version}.0.0`, type: 'dependencies' }, { name: '@angular/compiler', version: `^${version}.0.0`, type: 'dependencies' }, { name: '@angular/core', version: `^${version}.0.0`, type: 'dependencies' }, { name: '@angular/forms', version: `^${version}.0.0`, type: 'dependencies' }, { name: '@angular/platform-browser', version: `^${version}.0.0`, type: 'dependencies' }, { name: '@angular/platform-browser-dynamic', version: `^${version}.0.0`, type: 'dependencies' }, { name: '@angular/router', version: `^${version}.0.0`, type: 'dependencies' }, // Dev dependencies { name: '@angular/cli', version: `^${version}.0.0`, type: 'devDependencies' }, { name: '@angular/compiler-cli', version: `^${version}.0.0`, type: 'devDependencies' }, { name: '@angular-devkit/build-angular', version: `^${version}.0.0`, type: 'devDependencies' } ]; // Filter to only include packages that exist in the project const packageJsonPath = path.join(this.projectPath, 'package.json'); const packageJson = await fs.readJson(packageJsonPath); const existingPackages = angularPackages.filter(pkg => packageJson.dependencies?.[pkg.name] || packageJson.devDependencies?.[pkg.name]); return await this.installDependencies(existingPackages, `Updating Angular packages to version ${version}...`); } /** * Update TypeScript to compatible version */ async updateTypeScript(version) { return await this.installDependencies([{ name: 'typescript', version, type: 'devDependencies' }], 'Updating TypeScript...'); } /** * Install additional required packages */ async installRequiredPackages(packages) { return await this.installDependencies(packages, 'Installing required packages...'); } /** * Check if a package is installed */ async isPackageInstalled(packageName) { try { const packageJsonPath = path.join(this.projectPath, 'package.json'); const packageJson = await fs.readJson(packageJsonPath); return !!(packageJson.dependencies?.[packageName] || packageJson.devDependencies?.[packageName]); } catch { return false; } } /** * Get installed version of a package */ async getInstalledVersion(packageName) { try { const packageJsonPath = path.join(this.projectPath, 'package.json'); const packageJson = await fs.readJson(packageJsonPath); return packageJson.dependencies?.[packageName] || packageJson.devDependencies?.[packageName] || null; } catch { return null; } } /** * Fallback: Update package.json directly if npm install fails */ async fallbackUpdatePackageJson(dependencies) { try { const packageJsonPath = path.join(this.projectPath, 'package.json'); const packageJson = await fs.readJson(packageJsonPath); for (const dep of dependencies) { if (dep.type === 'dependencies') { if (!packageJson.dependencies) packageJson.dependencies = {}; packageJson.dependencies[dep.name] = dep.version; } else { if (!packageJson.devDependencies) packageJson.devDependencies = {}; packageJson.devDependencies[dep.name] = dep.version; } } await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); return true; } catch (error) { console.error(chalk_1.default.red('Failed to update package.json:'), error instanceof Error ? error.message : String(error)); return false; } } /** * Run npm install to ensure all dependencies are installed */ async runNpmInstall() { try { this.spinner.start('Running npm install...'); (0, child_process_1.execSync)('npm install', { cwd: this.projectPath, stdio: 'pipe', encoding: 'utf-8' }); this.spinner.succeed(chalk_1.default.green('✓ npm install completed successfully')); return true; } catch (error) { this.spinner.fail(chalk_1.default.red('✗ npm install failed')); console.error(chalk_1.default.yellow('You may need to run "npm install" manually')); return false; } } /** * Run npm install with retry mechanism */ async runNpmInstallWithRetry(maxRetries = 2) { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const command = attempt === 1 ? 'npm install' : 'npm install --force'; this.spinner.text = `Running npm install (attempt ${attempt}/${maxRetries})...`; (0, child_process_1.execSync)(command, { cwd: this.projectPath, stdio: 'pipe', encoding: 'utf-8', timeout: 300000 // 5 minutes timeout }); return true; } catch (error) { if (attempt === maxRetries) { console.error(chalk_1.default.red(`npm install failed after ${maxRetries} attempts:`)); console.error(chalk_1.default.yellow(error instanceof Error ? error.message : String(error))); return false; } // Wait a bit before retrying await new Promise(resolve => setTimeout(resolve, 2000)); } } return false; } /** * Install individual packages as fallback */ async installIndividualPackages(dependencies) { let successCount = 0; for (const dep of dependencies) { try { const saveFlag = dep.type === 'dependencies' ? '--save' : '--save-dev'; const command = `npm install ${dep.name}@${dep.version} ${saveFlag}`; this.spinner.text = `Installing ${dep.name}@${dep.version}...`; (0, child_process_1.execSync)(command, { cwd: this.projectPath, stdio: 'pipe', encoding: 'utf-8', timeout: 120000 // 2 minutes per package }); successCount++; } catch (error) { console.log(chalk_1.default.yellow(`⚠ Failed to install ${dep.name}: ${error instanceof Error ? error.message : String(error)}`)); } } const success = successCount > 0; if (success) { console.log(chalk_1.default.green(`✓ Successfully installed ${successCount}/${dependencies.length} packages`)); } return success; } /** * Safely update package.json with new dependencies */ async updatePackageJsonSafely(dependencies) { try { const packageJsonPath = path.join(this.projectPath, 'package.json'); // Create backup const backupPath = `${packageJsonPath}.backup.${Date.now()}`; await fs.copy(packageJsonPath, backupPath); const packageJson = await fs.readJson(packageJsonPath); for (const dep of dependencies) { if (dep.type === 'dependencies') { if (!packageJson.dependencies) packageJson.dependencies = {}; packageJson.dependencies[dep.name] = dep.version; } else { if (!packageJson.devDependencies) packageJson.devDependencies = {}; packageJson.devDependencies[dep.name] = dep.version; } } await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); // Clean up backup on success await fs.remove(backupPath); return true; } catch (error) { console.error(chalk_1.default.red('Failed to update package.json safely:'), error instanceof Error ? error.message : String(error)); return false; } } /** * Verify that dependencies are actually installed */ async verifyDependenciesInstalled(dependencies) { try { for (const dep of dependencies) { const depPath = path.join(this.projectPath, 'node_modules', dep.name); if (!await fs.pathExists(depPath)) { console.log(chalk_1.default.yellow(`⚠ Package ${dep.name} not found in node_modules`)); return false; } } return true; } catch { return false; } } } exports.DependencyInstaller = DependencyInstaller; //# sourceMappingURL=DependencyInstaller.js.map