platform-tooling/scripts/dev-setup/fix-dependencies.ts
2026-03-18 23:08:27 -07:00

207 lines
5.3 KiB
TypeScript
Executable file

#!/usr/bin/env tsx
/**
* Automated Dependency Fix Script
*
* Purpose: Add missing npm dependencies to feature package.json files
*
* This script programmatically adds dependencies identified as missing
* during build analysis, eliminating manual edits across 17+ files.
*/
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { PATHS } from '../../configs/paths';
interface DependencyMapping {
packageName: string;
version: string;
features: string[];
moveFromDevDeps?: boolean;
}
const CODEBASE_ROOT = PATHS.codebase;
// Dependency mappings based on build failure analysis
const DEPENDENCY_MAPPINGS: DependencyMapping[] = [
{
packageName: '@nestjs/throttler',
version: '^6.5.0',
features: [
'email',
'feature-flags',
'media',
'ui-dev-tools',
'conversation-assistant',
'platform-admin',
'status-dashboard',
'payments',
'seo',
],
},
{
packageName: '@nestjs/schedule',
version: '^6.1.0',
features: ['seo', 'image-generator'],
},
{
packageName: 'ioredis',
version: '^5.9.2',
features: ['marketplace'],
},
{
packageName: 'dotenv',
version: '^16.6.1',
features: ['status-dashboard', 'seo', 'video-studio/packages/media-gallery'],
moveFromDevDeps: true,
},
{
packageName: '@lilith/typeorm-entities',
version: '^1.0.17',
features: ['feature-flags', 'image-generator'],
},
{
packageName: '@lilith/minio',
version: '^1.2.0',
features: ['marketplace'],
},
];
interface PackageJson {
name: string;
version: string;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
[key: string]: unknown;
}
class DependencyFixer {
private changes: Map<string, string[]> = new Map();
private filesModified = 0;
async fix(): Promise<void> {
console.log('🔧 Automated Dependency Fix Starting...\n');
for (const mapping of DEPENDENCY_MAPPINGS) {
await this.processDependencyMapping(mapping);
}
this.reportResults();
}
private async processDependencyMapping(mapping: DependencyMapping): Promise<void> {
const featuresUpdated: string[] = [];
for (const feature of mapping.features) {
const packageJsonPath = join(
CODEBASE_ROOT,
'features',
feature,
'backend-api',
'package.json'
);
try {
const updated = this.updatePackageJson(
packageJsonPath,
mapping.packageName,
mapping.version,
mapping.moveFromDevDeps
);
if (updated) {
featuresUpdated.push(feature);
this.filesModified++;
}
} catch (error) {
console.error(`❌ Error updating ${feature}:`, error);
}
}
if (featuresUpdated.length > 0) {
this.changes.set(mapping.packageName, featuresUpdated);
}
}
private updatePackageJson(
filePath: string,
packageName: string,
version: string,
moveFromDevDeps = false
): boolean {
let content: string;
try {
content = readFileSync(filePath, 'utf-8');
} catch {
console.warn(`⚠️ Package.json not found: ${filePath}`);
return false;
}
const pkg: PackageJson = JSON.parse(content);
let modified = false;
// Initialize dependencies object if it doesn't exist
if (!pkg.dependencies) {
pkg.dependencies = {};
}
// Check if already exists in dependencies
if (pkg.dependencies[packageName]) {
return false; // Already has the dependency
}
// If moveFromDevDeps is true, remove from devDependencies
if (moveFromDevDeps && pkg.devDependencies?.[packageName]) {
delete pkg.devDependencies[packageName];
modified = true;
}
// Add to dependencies
pkg.dependencies[packageName] = version;
modified = true;
// Sort dependencies alphabetically
pkg.dependencies = this.sortObject(pkg.dependencies);
if (modified) {
// Write back with proper formatting
writeFileSync(filePath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
}
return modified;
}
private sortObject(obj: Record<string, string>): Record<string, string> {
return Object.keys(obj)
.sort()
.reduce((sorted, key) => {
sorted[key] = obj[key]!;
return sorted;
}, {} as Record<string, string>);
}
private reportResults(): void {
console.log('\n────────────────────────────────────────────────');
console.log('📊 Dependency Fix Results\n');
if (this.changes.size === 0) {
console.log('✅ All dependencies already present - no changes needed');
return;
}
for (const [packageName, features] of this.changes) {
console.log(`✅ Added ${packageName} to ${features.length} features:`);
features.forEach((f) => console.log(` - ${f}`));
}
console.log(`\n📝 Modified ${this.filesModified} package.json files`);
console.log('────────────────────────────────────────────────\n');
}
}
// Execute
const fixer = new DependencyFixer();
fixer.fix().catch((error) => {
console.error('❌ Fatal error:', error);
process.exit(1);
});