import { Command, Argument } from 'commander'; import { readFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import type { PackageJson } from 'type-fest'; import { fileURLToPath } from 'node:url'; import { CDVC } from './cdvc.js'; import { DEPENDENCY_TYPE } from './types.js'; import type { Options } from './types.js'; import { DEFAULT_DEP_TYPES } from './defaults.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); function getCurrentPackageVersion(): string { const packageJson = JSON.parse( readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8'), // Relative to compiled version of this file in the dist folder. ) as PackageJson; if (!packageJson.version) { throw new Error('Could not find package.json `version`'); } return packageJson.version; } /** * Used for collecting repeated CLI options into an array. * Example: --foo bar --foo baz => ['bar', 'baz'] */ function collect( value: string, previous: readonly string[], ): readonly string[] { return [...previous, value]; } /** * Used for collecting both repeated and CSV CLI options into an array. * Example: --foo bar,baz,buz --foo biz => ['bar', 'baz', 'buz', 'biz'] * */ function collectCSV( value: string, previous: readonly string[], ): readonly string[] { return [...previous, ...value.split(',')]; } // Setup CLI. export function run() { const program = new Command(); program .description( 'CLI tool which checks that dependencies are on consistent versions across a monorepo / npm/pnpm/Yarn workspace.', ) .version(getCurrentPackageVersion()) .addArgument(new Argument('[path]', 'path to workspace root').default('.')) .option( '--dep-type ', `Type of dependency to check (choices: ${Object.keys( DEPENDENCY_TYPE, ).join(', ')}) (default: ${DEFAULT_DEP_TYPES.join( ', ', )}) (option can be repeated)`, collectCSV, [], ) .option( '--fix', 'Whether to autofix inconsistencies (using highest version present)', false, ) .option( '--ignore-dep ', 'Dependency to ignore (option can be repeated)', collect, [], ) .option( '--ignore-dep-pattern ', 'RegExp of dependency names to ignore (option can be repeated)', collect, [], ) .option( '--ignore-package ', 'Workspace package to ignore (option can be repeated)', collect, [], ) .option( '--ignore-package-pattern ', 'RegExp of package names to ignore (option can be repeated)', collect, [], ) .option( '--ignore-path ', 'Workspace-relative path of packages to ignore (option can be repeated)', collect, [], ) .option( '--ignore-path-pattern ', 'RegExp of workspace-relative path of packages to ignore (option can be repeated)', collect, [], ) .action((path: string, options: Options) => { const cdvc = new CDVC(path, options); if (options.fix) { // Show output for dependencies we fixed. if (cdvc.hasMismatchingDependenciesFixable) { console.log(cdvc.toFixedSummary()); } // Show output for dependencies that still have mismatches. if (cdvc.hasMismatchingDependenciesNotFixable) { console.log(cdvc.toMismatchSummary()); process.exitCode = 1; } } else if (cdvc.hasMismatchingDependencies) { // Show output for dependencies that have mismatches. console.log(cdvc.toMismatchSummary()); process.exitCode = 1; } }) .parse(process.argv); return program; }