diff --git a/src/commands/migrate.meta.ts b/src/commands/migrate.meta.ts index c21b769..6efcb7c 100644 --- a/src/commands/migrate.meta.ts +++ b/src/commands/migrate.meta.ts @@ -2,6 +2,12 @@ export const meta = { name: 'migrate', description: 'Migrate from a package to a more performant alternative.', args: { + all: { + type: 'boolean', + default: false, + description: + 'Migrate all fixable replacements that exist in project dependencies.' + }, 'dry-run': { type: 'boolean', default: false, diff --git a/src/commands/migrate.ts b/src/commands/migrate.ts index 783a803..6c62c61 100644 --- a/src/commands/migrate.ts +++ b/src/commands/migrate.ts @@ -11,6 +11,7 @@ import {getPackageJson} from '../utils/package-json.js'; export async function run(ctx: CommandContext) { const [_commandName, ...targetModules] = ctx.positionals; + const all = ctx.values.all === true; const dryRun = ctx.values['dry-run'] === true; const interactive = ctx.values.interactive === true; const include = ctx.values.include; @@ -37,7 +38,7 @@ export async function run(ctx: CommandContext) { .map((rep) => rep.from) ); - if (interactive) { + if (interactive && !all) { const additionalTargets = await prompts.autocompleteMultiselect({ message: 'Select packages to migrate', maxItems: 10, @@ -60,34 +61,50 @@ export async function run(ctx: CommandContext) { } } - if (targetModules.length === 0) { - prompts.cancel( - 'Error: Please specify a package to migrate. For example, `migrate chalk`' - ); - return; - } + let selectedReplacements: Replacement[]; - const selectedReplacements: Replacement[] = []; - - for (const targetModule of targetModules) { - const replacement = fixableReplacements.find( - (rep) => rep.from === targetModule + if (all) { + selectedReplacements = fixableReplacements.filter((rep) => + fixableReplacementsTargets.has(rep.from) ); - if (!replacement) { + } else { + if (targetModules.length === 0) { prompts.cancel( - `Error: Target package has no available migrations (${targetModule})` + 'Error: Please specify a package to migrate. For example, `migrate chalk`' ); return; } - selectedReplacements.push(replacement); + selectedReplacements = []; + + for (const targetModule of targetModules) { + if (!fixableReplacementsTargets.has(targetModule)) { + prompts.cancel( + `Error: Target package is not in project dependencies (${targetModule})` + ); + return; + } + + const replacement = fixableReplacements.find( + (rep) => rep.from === targetModule + ); + if (!replacement) { + prompts.cancel( + `Error: Target package has no available migrations (${targetModule})` + ); + return; + } + + selectedReplacements.push(replacement); + } } if (!interactive) { + const targetNames = selectedReplacements.map((r) => r.from); const targetModuleSummary = - targetModules.length > 6 - ? `${targetModules.slice(0, 6).join(', ')} and ${targetModules.length - 6} more` - : targetModules.join(', '); + targetNames.length > 6 + ? `${targetNames.slice(0, 6).join(', ')} and ${targetNames.length - 6} more` + : targetNames.join(', '); prompts.log.message(`Targets: ${styleText('dim', targetModuleSummary)}`); } @@ -106,6 +123,8 @@ export async function run(ctx: CommandContext) { return; } + let filesMigratedCount = 0; + for (const filename of files) { const log = prompts.taskLog({ title: `${filename}...`, @@ -140,10 +159,13 @@ export async function run(ctx: CommandContext) { } totalMigrations++; } + if (totalMigrations > 0) { + filesMigratedCount++; + } log.success( `${filename} ${styleText('dim', `(${totalMigrations} migrated)`)}` ); } - prompts.outro('Migration complete.'); + prompts.outro(`Migration complete - ${filesMigratedCount} files migrated.`); } diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index 10faafa..e1e930a 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -93,3 +93,33 @@ describe('CLI', () => { expect(normalizeStderr(stderr)).toMatchSnapshot(); }); }); + +const basicChalkFixture = path.join( + __dirname, + '../../test/fixtures/basic-chalk' +); + +describe('migrate --all', () => { + it('should migrate all fixable replacements with --all --dry-run when project has fixable deps', async () => { + const {stdout, stderr, code} = await runCliProcess( + ['migrate', '--all', '--dry-run'], + basicChalkFixture + ); + expect(code).toBe(0); + const output = stdout + stderr; + expect(output).toContain('Migration complete'); + expect(output).toContain('files migrated'); + expect(output).toContain('chalk'); + }); + + it('should run to completion and show Migration complete when --all has no fixable replacements', async () => { + const {stdout, stderr, code} = await runCliProcess( + ['migrate', '--all'], + tempDir + ); + const output = stdout + stderr; + expect(code).toBe(0); + expect(output).toContain('Migration complete'); + expect(output).toContain('0 files migrated'); + }); +});