/* eslint-disable max-depth */ import fs from 'fs'; import path from 'path'; import { execSync } from 'child_process'; import { glob } from 'glob'; import chalk from 'chalk'; import { showHelpAndExit } from '../utils/cli'; showHelpAndExit( chalk.bold.cyan(` Usage: plyaz-check-deps [options] Options: --rewrite Rewrite all dependency versions to the highest found --report= Path to write the markdown report --help Show this help message `) ); const root = process.cwd(); const args = process.argv.slice(2); const shouldRewrite = args.includes('--rewrite'); const reportArg = args.find(arg => arg.startsWith('--report=')); const reportPath = reportArg ? path.resolve(reportArg.split('=')[1]) : null; type PkgJson = { name: string; dependencies?: Record; devDependencies?: Record; peerDependencies?: Record; }; type VersionUsage = Record< string, { versions: Record; // version => [pkgNames] latestVersion: string; } >; function findHighestSemver(versions: string[]): string { const cleaned = versions.map(v => v.replace(/^[~^]/, '')); return cleaned.sort((a, b) => (a === b ? 0 : a > b ? -1 : 1))[0]; } function loadPackageJson(filePath: string): PkgJson | null { try { return JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { return null; } } function writePackageJson(filePath: string, data: PkgJson): void { fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n'); } // Find all workspace package.json files const workspacePkgFiles = glob.sync('{packages/*/package.json,apps/*/package.json}', { cwd: root, absolute: true, }); if (workspacePkgFiles.length === 0) { console.warn( 'āš ļø Skipped: Not a monorepo (no /packages or /apps folder found). This script is intended for monorepos.' ); console.warn('āž”ļø Exiting without error.\n'); // eslint-disable-next-line n/no-process-exit process.exit(0); } // Collect dependency versions across workspaces const versionMap: VersionUsage = {}; const pkgLocations: Record = {}; for (const pkgFile of workspacePkgFiles) { const pkg = loadPackageJson(pkgFile); if (!pkg?.name) continue; pkgLocations[pkg.name] = pkgFile; const sections: (keyof Pick)[] = ['dependencies', 'devDependencies', 'peerDependencies']; for (const section of sections) { const deps = pkg[section]; if (!deps) continue; for (const [dep, version] of Object.entries(deps)) { if (!versionMap[dep]) { versionMap[dep] = { versions: {}, latestVersion: '' }; } if (!versionMap[dep].versions[version]) { versionMap[dep].versions[version] = []; } versionMap[dep].versions[version].push(pkg.name); } } } // Resolve latest version per dependency for (const dep of Object.keys(versionMap)) { const allVersions = Object.keys(versionMap[dep].versions); versionMap[dep].latestVersion = `^${findHighestSemver(allVersions)}`; } // CLI Summary const PAD_END = 12; let markdownOutput = `# šŸ“¦ Dependency Version Report\n\n`; console.log('\nšŸ“¦ Dependency Version Report:\n'); for (const dep of Object.keys(versionMap)) { const versions = versionMap[dep].versions; const latest = versionMap[dep].latestVersion; if (Object.keys(versions).length <= 1) continue; console.log(`šŸ”§ ${dep}`); markdownOutput += `### šŸ”§ ${dep}\n`; for (const [ver, pkgs] of Object.entries(versions)) { const line = ` • ${ver.padEnd(PAD_END)} → used by: ${pkgs.join(', ')}`; console.log(line); markdownOutput += `- \`${ver}\` → used by: ${pkgs.join(', ')}\n`; } console.log(` āœ… Suggested version: ${latest}\n`); markdownOutput += `- āœ… Suggested version: \`${latest}\`\n\n`; } if (reportPath) { try { fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, markdownOutput, 'utf8'); console.log(`šŸ“„ Report saved to: ${reportPath}`); } catch (err) { console.error(`āŒ Failed to write report: ${err}`); } } // Rewrite if needed if (shouldRewrite) { console.log('\nāœļø Rewriting versions to latest...\n'); for (const pkgFile of workspacePkgFiles) { const pkg = loadPackageJson(pkgFile); if (!pkg?.name) continue; let changed = false; for (const section of ['dependencies', 'devDependencies', 'peerDependencies'] as const) { const deps = pkg[section]; if (!deps) continue; for (const dep of Object.keys(deps)) { const latestVersion = versionMap[dep]?.latestVersion; if (latestVersion && deps[dep] !== latestVersion) { deps[dep] = latestVersion; changed = true; } } } if (changed) { writePackageJson(pkgFile, pkg); console.log(`āœ… Updated: ${pkg.name}`); } } console.log('\nšŸ“¦ Installing updated packages...'); try { execSync('pnpm install', { stdio: 'inherit' }); console.log('āœ… Done.'); } catch { console.warn('\nāš ļø Failed to run pnpm install automatically.'); console.warn('āž”ļø Please run it manually.\n'); } }