From a55e003487d711c765cc977231dc8c6de2a86943 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Tue, 3 Feb 2026 01:57:02 -0600 Subject: [PATCH 1/7] feat: add --all flag to migrate command --- src/commands/migrate.meta.ts | 6 ++++ src/commands/migrate.ts | 59 +++++++++++++++++++++++++----------- src/test/cli.test.ts | 39 ++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 18 deletions(-) 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 ccc58a7..32fde00 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,56 @@ 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) { + if (selectedReplacements.length === 0) { + prompts.cancel( + 'No fixable replacements found in project dependencies.' + ); + return; + } + } 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: ${colors.dim(targetModuleSummary)}`); } diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index 10faafa..c96d4b9 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -93,3 +93,42 @@ describe('CLI', () => { expect(normalizeStderr(stderr)).toMatchSnapshot(); }); }); + +describe('migrate --all', () => { + it('should migrate all fixable replacements with --all --dry-run when project has fixable deps', async () => { + const chalkDir = await createTempDir(); + await createTestPackage(chalkDir, { + name: 'chalk-test', + version: '1.0.0', + type: 'module', + main: 'index.js', + dependencies: {chalk: '^4.0.0'} + }); + await fs.writeFile( + path.join(chalkDir, 'index.js'), + "import chalk from 'chalk';\nconsole.log(chalk.cyan('hello'));" + ); + try { + const {stdout, stderr, code} = await runCliProcess( + ['migrate', '--all', '--dry-run'], + chalkDir + ); + expect(code).toBe(0); + const output = stdout + stderr; + expect(output).toContain('Migration complete'); + expect(output).toContain('chalk'); + } finally { + await cleanupTempDir(chalkDir); + } + }); + + it('should show message when --all is used but no fixable replacements exist in dependencies', async () => { + const {stdout, stderr, code} = await runCliProcess( + ['migrate', '--all'], + tempDir + ); + const output = stdout + stderr; + expect(output).toContain('No fixable replacements found in project dependencies'); + expect(code).toBe(0); + }); +}); From 42a7cdef00918837e5ee0caeb2abd2eccc71a5f5 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Tue, 3 Feb 2026 01:57:58 -0600 Subject: [PATCH 2/7] format --- src/commands/migrate.ts | 4 +--- src/test/cli.test.ts | 4 +++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/migrate.ts b/src/commands/migrate.ts index 32fde00..ebdb3e6 100644 --- a/src/commands/migrate.ts +++ b/src/commands/migrate.ts @@ -68,9 +68,7 @@ export async function run(ctx: CommandContext) { fixableReplacementsTargets.has(rep.from) ); if (selectedReplacements.length === 0) { - prompts.cancel( - 'No fixable replacements found in project dependencies.' - ); + prompts.cancel('No fixable replacements found in project dependencies.'); return; } } else { diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index c96d4b9..04fca82 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -128,7 +128,9 @@ describe('migrate --all', () => { tempDir ); const output = stdout + stderr; - expect(output).toContain('No fixable replacements found in project dependencies'); + expect(output).toContain( + 'No fixable replacements found in project dependencies' + ); expect(code).toBe(0); }); }); From 5e109398d8af1f915ebb576a8e803062383ea849 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:11:33 -0600 Subject: [PATCH 3/7] refactor: update migration command output messages --- src/commands/migrate.ts | 14 +++++++++++--- src/test/cli.test.ts | 7 +++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/commands/migrate.ts b/src/commands/migrate.ts index ebdb3e6..fe93e47 100644 --- a/src/commands/migrate.ts +++ b/src/commands/migrate.ts @@ -68,8 +68,9 @@ export async function run(ctx: CommandContext) { fixableReplacementsTargets.has(rep.from) ); if (selectedReplacements.length === 0) { - prompts.cancel('No fixable replacements found in project dependencies.'); - return; + prompts.log.message( + 'No fixable replacements found in project dependencies.' + ); } } else { if (targetModules.length === 0) { @@ -127,6 +128,8 @@ export async function run(ctx: CommandContext) { return; } + let filesMigratedCount = 0; + for (const filename of files) { const log = prompts.taskLog({ title: `${filename}...`, @@ -161,8 +164,13 @@ export async function run(ctx: CommandContext) { } totalMigrations++; } + if (totalMigrations > 0) { + filesMigratedCount++; + } log.success(`${filename} ${colors.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 04fca82..0dfd8e1 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -116,21 +116,24 @@ describe('migrate --all', () => { expect(code).toBe(0); const output = stdout + stderr; expect(output).toContain('Migration complete'); + expect(output).toContain('files migrated'); expect(output).toContain('chalk'); } finally { await cleanupTempDir(chalkDir); } }); - it('should show message when --all is used but no fixable replacements exist in dependencies', async () => { + 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'); expect(output).toContain( 'No fixable replacements found in project dependencies' ); - expect(code).toBe(0); }); }); From e1d9d232c45aead91c150a1e9e0914046731f12b Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Tue, 3 Feb 2026 12:12:28 -0600 Subject: [PATCH 4/7] format --- src/commands/migrate.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/commands/migrate.ts b/src/commands/migrate.ts index fe93e47..158257b 100644 --- a/src/commands/migrate.ts +++ b/src/commands/migrate.ts @@ -170,7 +170,5 @@ export async function run(ctx: CommandContext) { log.success(`${filename} ${colors.dim(`(${totalMigrations} migrated)`)}`); } - prompts.outro( - `Migration complete - ${filesMigratedCount} files migrated.` - ); + prompts.outro(`Migration complete - ${filesMigratedCount} files migrated.`); } From c9a05a0bba6a748e9a71d81795735dead047a372 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 4 Feb 2026 07:58:04 -0600 Subject: [PATCH 5/7] refactor: remove log message for no fixable replacements in migrate command --- src/commands/migrate.ts | 5 ----- src/test/cli.test.ts | 3 --- 2 files changed, 8 deletions(-) diff --git a/src/commands/migrate.ts b/src/commands/migrate.ts index 158257b..6c1ad95 100644 --- a/src/commands/migrate.ts +++ b/src/commands/migrate.ts @@ -67,11 +67,6 @@ export async function run(ctx: CommandContext) { selectedReplacements = fixableReplacements.filter((rep) => fixableReplacementsTargets.has(rep.from) ); - if (selectedReplacements.length === 0) { - prompts.log.message( - 'No fixable replacements found in project dependencies.' - ); - } } else { if (targetModules.length === 0) { prompts.cancel( diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index 0dfd8e1..9ff9577 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -132,8 +132,5 @@ describe('migrate --all', () => { expect(code).toBe(0); expect(output).toContain('Migration complete'); expect(output).toContain('0 files migrated'); - expect(output).toContain( - 'No fixable replacements found in project dependencies' - ); }); }); From 373b9a23ea5b5ae80b8723323e1660358019e159 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:17:24 -0600 Subject: [PATCH 6/7] refactor: simplify test for migrate --all command by using a fixture --- src/test/cli.test.ts | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index 9ff9577..155f2fa 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -94,33 +94,19 @@ describe('CLI', () => { }); }); +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 chalkDir = await createTempDir(); - await createTestPackage(chalkDir, { - name: 'chalk-test', - version: '1.0.0', - type: 'module', - main: 'index.js', - dependencies: {chalk: '^4.0.0'} - }); - await fs.writeFile( - path.join(chalkDir, 'index.js'), - "import chalk from 'chalk';\nconsole.log(chalk.cyan('hello'));" + const {stdout, stderr, code} = await runCliProcess( + ['migrate', '--all', '--dry-run'], + basicChalkFixture ); - try { - const {stdout, stderr, code} = await runCliProcess( - ['migrate', '--all', '--dry-run'], - chalkDir - ); - expect(code).toBe(0); - const output = stdout + stderr; - expect(output).toContain('Migration complete'); - expect(output).toContain('files migrated'); - expect(output).toContain('chalk'); - } finally { - await cleanupTempDir(chalkDir); - } + 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 () => { From 2ce982d7cceb7d836fa5e9937fa80d2f682776b4 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:18:08 -0600 Subject: [PATCH 7/7] format --- src/test/cli.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/cli.test.ts b/src/test/cli.test.ts index 155f2fa..e1e930a 100644 --- a/src/test/cli.test.ts +++ b/src/test/cli.test.ts @@ -94,7 +94,10 @@ describe('CLI', () => { }); }); -const basicChalkFixture = path.join(__dirname, '../../test/fixtures/basic-chalk'); +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 () => {