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
344 lines ⢠16.1 kB
JavaScript
;
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.program = void 0;
const commander_1 = require("commander");
const chalk_1 = __importDefault(require("chalk"));
const NgCompatibilityUpdater_1 = require("../utils/NgCompatibilityUpdater");
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const program = new commander_1.Command();
exports.program = program;
program
.name('ng-check-updates')
.description('Check and update dependencies for Angular compatibility (similar to npm-check-updates)')
.version('1.0.0')
.argument('[angular-version]', 'Target Angular version (e.g., 18, 19, 20)', '18')
.option('-u, --upgrade', 'Apply updates to package.json (like ncu -u)')
.option('-d, --dev', 'Include devDependencies')
.option('-a, --angular-only', 'Only check Angular ecosystem packages')
.option('-s, --strategy <type>', 'Update strategy: conservative|aggressive', 'conservative')
.option('-t, --target <version>', 'Specific Angular version to target')
.option('--dry-run', 'Show what would be updated without making changes')
.option('--format <type>', 'Output format: table|json|summary', 'table')
.option('-f, --filter <pattern>', 'Filter packages by pattern (regex)')
.option('--exclude <pattern>', 'Exclude packages by pattern (regex)')
.action(async (angularVersion, options) => {
try {
const targetVersion = options.target || angularVersion;
const projectPath = process.cwd();
console.log(chalk_1.default.cyan(`\nš Checking Angular ${targetVersion} compatibility...\n`));
// Validate Angular version
if (!isValidAngularVersion(targetVersion)) {
console.error(chalk_1.default.red(`ā Invalid Angular version: ${targetVersion}`));
console.error(chalk_1.default.gray('Supported versions: 12, 13, 14, 15, 16, 17, 18, 19, 20'));
process.exit(1);
}
// Check if package.json exists
const packageJsonPath = path.join(projectPath, 'package.json');
if (!await fs.pathExists(packageJsonPath)) {
console.error(chalk_1.default.red('ā No package.json found in current directory'));
process.exit(1);
}
// Create updater instance
const updater = new NgCompatibilityUpdater_1.NgCompatibilityUpdater(targetVersion);
// Configure update options
const updateOptions = {
dryRun: options.dryRun || !options.upgrade,
includeDevDependencies: options.dev,
onlyAngularEcosystem: options.angularOnly,
updateStrategy: options.strategy,
filter: options.filter ? new RegExp(options.filter) : undefined,
exclude: options.exclude ? new RegExp(options.exclude) : undefined
};
console.log(chalk_1.default.gray('Options:'));
console.log(chalk_1.default.gray(` Angular version: ${targetVersion}`));
console.log(chalk_1.default.gray(` Strategy: ${options.strategy}`));
console.log(chalk_1.default.gray(` Include dev deps: ${options.dev ? 'yes' : 'no'}`));
console.log(chalk_1.default.gray(` Angular only: ${options.angularOnly ? 'yes' : 'no'}`));
console.log(chalk_1.default.gray(` Apply updates: ${options.upgrade ? 'yes' : 'no'}`));
console.log('');
// Run compatibility check
const result = await updater.checkAndUpdate(projectPath, updateOptions);
// Display results
displayResults(result, targetVersion, options);
if (result.updates.length > 0 && !options.upgrade && !options.dryRun) {
console.log(chalk_1.default.cyan('\nš” Run with -u to apply updates to package.json'));
console.log(chalk_1.default.gray(' Example: ng-check-updates 18 -u'));
}
if (options.upgrade && result.updates.length > 0) {
console.log(chalk_1.default.green('\nā
Dependencies updated! Run npm install to install new versions.'));
}
}
catch (error) {
console.error(chalk_1.default.red(`ā Error: ${error instanceof Error ? error.message : String(error)}`));
process.exit(1);
}
});
// Add interactive mode
program
.command('interactive')
.alias('i')
.description('Interactive mode to select which updates to apply')
.argument('[angular-version]', 'Target Angular version', '18')
.option('-d, --dev', 'Include devDependencies')
.option('-a, --angular-only', 'Only check Angular ecosystem packages')
.action(async (angularVersion, options) => {
try {
const projectPath = process.cwd();
console.log(chalk_1.default.cyan(`\nš Interactive Angular ${angularVersion} compatibility check...\n`));
const updater = new NgCompatibilityUpdater_1.NgCompatibilityUpdater(angularVersion);
const result = await updater.checkAndUpdate(projectPath, {
dryRun: true,
includeDevDependencies: options.dev,
onlyAngularEcosystem: options.angularOnly,
updateStrategy: 'conservative'
});
if (result.updates.length === 0) {
console.log(chalk_1.default.green('ā
All dependencies are already compatible!'));
return;
}
console.log(chalk_1.default.yellow('š¦ Found updates:'));
// Group and display updates
const { createPromptModule } = await Promise.resolve().then(() => __importStar(require('inquirer')));
const prompt = createPromptModule();
const choices = result.updates.map(update => ({
name: `${update.name}: ${update.currentVersion} ā ${update.compatibleVersion} ${update.notes ? `(${update.notes})` : ''}`,
value: update.name,
checked: update.required
}));
const { selectedUpdates } = await prompt({
type: 'checkbox',
name: 'selectedUpdates',
message: 'Select packages to update:',
choices,
pageSize: 15
});
if (selectedUpdates.length === 0) {
console.log(chalk_1.default.gray('No updates selected.'));
return;
}
// Apply selected updates
const filteredResult = {
...result,
updates: result.updates.filter(u => selectedUpdates.includes(u.name))
};
await applySelectedUpdates(projectPath, filteredResult);
console.log(chalk_1.default.green(`\nā
Updated ${selectedUpdates.length} packages!`));
}
catch (error) {
console.error(chalk_1.default.red(`ā Error: ${error instanceof Error ? error.message : String(error)}`));
process.exit(1);
}
});
// Add doctor command for health check
program
.command('doctor')
.description('Comprehensive Angular dependency health check')
.argument('[angular-version]', 'Target Angular version', '18')
.action(async (angularVersion) => {
try {
const projectPath = process.cwd();
console.log(chalk_1.default.cyan(`\nš„ Angular ${angularVersion} Dependency Health Check\n`));
const updater = new NgCompatibilityUpdater_1.NgCompatibilityUpdater(angularVersion);
const result = await updater.checkAndUpdate(projectPath, {
dryRun: true,
includeDevDependencies: true,
onlyAngularEcosystem: false,
updateStrategy: 'aggressive'
});
// Health score calculation
const totalDeps = await getTotalDependencyCount(projectPath);
const healthScore = calculateHealthScore(result, totalDeps);
console.log(chalk_1.default.bold('šÆ HEALTH SCORE: ') + getHealthScoreDisplay(healthScore));
console.log('');
// Detailed breakdown
displayHealthBreakdown(result, totalDeps);
if (result.deprecated.length > 0) {
console.log(chalk_1.default.red('\nšļø DEPRECATED PACKAGES:'));
result.deprecated.forEach(pkg => {
console.log(chalk_1.default.red(` ā ${pkg}`));
});
}
// Recommendations
console.log(chalk_1.default.cyan('\nš” RECOMMENDATIONS:'));
if (result.criticalUpdates > 0) {
console.log(chalk_1.default.red(` šØ ${result.criticalUpdates} critical updates need immediate attention`));
}
if (result.deprecated.length > 0) {
console.log(chalk_1.default.yellow(` š¦ ${result.deprecated.length} deprecated packages should be replaced`));
}
if (healthScore >= 80) {
console.log(chalk_1.default.green(' ā
Dependencies are in good shape!'));
}
else if (healthScore >= 60) {
console.log(chalk_1.default.yellow(' ā ļø Some maintenance required'));
}
else {
console.log(chalk_1.default.red(' š§ Significant updates needed'));
}
}
catch (error) {
console.error(chalk_1.default.red(`ā Error: ${error instanceof Error ? error.message : String(error)}`));
process.exit(1);
}
});
function displayResults(result, targetVersion, options) {
if (options.format === 'json') {
console.log(JSON.stringify(result, null, 2));
return;
}
if (result.updates.length === 0) {
console.log(chalk_1.default.green(`ā
All dependencies are compatible with Angular ${targetVersion}`));
return;
}
if (options.format === 'summary') {
console.log(chalk_1.default.yellow(`š ${result.totalUpdates} updates available (${result.criticalUpdates} critical)`));
return;
}
// Table format (default)
console.log(chalk_1.default.bold('š¦ DEPENDENCY UPDATES:'));
console.log('');
const maxNameLength = Math.max(...result.updates.map(u => u.name.length), 20);
const maxCurrentLength = Math.max(...result.updates.map(u => u.currentVersion.length), 10);
// Header
console.log(chalk_1.default.gray('Package'.padEnd(maxNameLength) +
'Current'.padEnd(maxCurrentLength + 3) +
'Compatible'.padEnd(15) +
'Type'.padEnd(8) +
'Notes'));
console.log(chalk_1.default.gray('ā'.repeat(maxNameLength + maxCurrentLength + 40)));
// Updates
result.updates.forEach(update => {
const typeColor = getUpdateTypeColor(update.updateType);
const requiredIcon = update.required ? 'š“' : '';
console.log(chalk_1.default.white(update.name.padEnd(maxNameLength)) +
chalk_1.default.gray(update.currentVersion.padEnd(maxCurrentLength + 3)) +
chalk_1.default.green(update.compatibleVersion.padEnd(15)) +
typeColor(update.updateType.padEnd(8)) +
chalk_1.default.gray(update.notes || '') +
requiredIcon);
});
if (result.warnings.length > 0) {
console.log(chalk_1.default.yellow('\nā ļø WARNINGS:'));
result.warnings.forEach(warning => {
console.log(chalk_1.default.yellow(` ${warning}`));
});
}
}
function getUpdateTypeColor(type) {
const colors = {
major: chalk_1.default.red,
minor: chalk_1.default.yellow,
patch: chalk_1.default.green,
compatible: chalk_1.default.blue,
deprecated: chalk_1.default.gray
};
return colors[type] || chalk_1.default.white;
}
function isValidAngularVersion(version) {
const validVersions = ['12', '13', '14', '15', '16', '17', '18', '19', '20'];
return validVersions.includes(version);
}
async function applySelectedUpdates(projectPath, result) {
const packageJsonPath = path.join(projectPath, 'package.json');
const packageJson = await fs.readJson(packageJsonPath);
for (const update of result.updates) {
if (update.updateType === 'deprecated') {
// Remove deprecated packages
if (packageJson.dependencies?.[update.name]) {
delete packageJson.dependencies[update.name];
}
if (packageJson.devDependencies?.[update.name]) {
delete packageJson.devDependencies[update.name];
}
}
else {
// Update to compatible version
if (packageJson.dependencies?.[update.name]) {
packageJson.dependencies[update.name] = update.compatibleVersion;
}
if (packageJson.devDependencies?.[update.name]) {
packageJson.devDependencies[update.name] = update.compatibleVersion;
}
}
}
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
}
async function getTotalDependencyCount(projectPath) {
const packageJsonPath = path.join(projectPath, 'package.json');
const packageJson = await fs.readJson(packageJsonPath);
const depCount = Object.keys(packageJson.dependencies || {}).length;
const devDepCount = Object.keys(packageJson.devDependencies || {}).length;
return depCount + devDepCount;
}
function calculateHealthScore(result, totalDeps) {
if (totalDeps === 0)
return 100;
const upToDateDeps = totalDeps - result.totalUpdates;
const deprecatedPenalty = result.deprecated.length * 15;
const criticalPenalty = result.criticalUpdates * 10;
const minorPenalty = (result.totalUpdates - result.criticalUpdates) * 2;
const baseScore = (upToDateDeps / totalDeps) * 100;
const finalScore = Math.max(0, baseScore - deprecatedPenalty - criticalPenalty - minorPenalty);
return Math.round(finalScore);
}
function getHealthScoreDisplay(score) {
if (score >= 90)
return chalk_1.default.green(`${score}/100 š¢ EXCELLENT`);
if (score >= 80)
return chalk_1.default.green(`${score}/100 š¢ GOOD`);
if (score >= 70)
return chalk_1.default.yellow(`${score}/100 š” FAIR`);
if (score >= 60)
return chalk_1.default.yellow(`${score}/100 š” NEEDS ATTENTION`);
return chalk_1.default.red(`${score}/100 š“ POOR`);
}
function displayHealthBreakdown(result, totalDeps) {
console.log(chalk_1.default.bold('š BREAKDOWN:'));
console.log(` Total dependencies: ${totalDeps}`);
console.log(` Up to date: ${chalk_1.default.green(totalDeps - result.totalUpdates)}`);
console.log(` Need updates: ${chalk_1.default.yellow(result.totalUpdates)}`);
console.log(` Critical updates: ${chalk_1.default.red(result.criticalUpdates)}`);
console.log(` Deprecated: ${chalk_1.default.gray(result.deprecated.length)}`);
}
if (require.main === module) {
program.parse();
}
//# sourceMappingURL=ng-check-updates.js.map