From 393579c0e1ac8c1db509968455699eca0c6e12d2 Mon Sep 17 00:00:00 2001 From: KTrain <69028025+KTrain5169@users.noreply.github.com> Date: Thu, 29 Oct 2026 13:49:12 +0000 Subject: [PATCH 1/5] feat(migrate): add warnings if certain packages and config files are found --- .../src/migration/__tests__/detector.spec.ts | 60 ++++++++++++++++ packages/cli/src/migration/bin.ts | 7 ++ packages/cli/src/migration/detector.ts | 70 +++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 packages/cli/src/migration/__tests__/detector.spec.ts diff --git a/packages/cli/src/migration/__tests__/detector.spec.ts b/packages/cli/src/migration/__tests__/detector.spec.ts new file mode 100644 index 0000000000..e2b85abeb0 --- /dev/null +++ b/packages/cli/src/migration/__tests__/detector.spec.ts @@ -0,0 +1,60 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; + +import { detectMigrationWarnings } from '../detector.ts'; + +function writeProject(projectPath: string, packageJson: object, configFiles: string[] = []): void { + fs.mkdirSync(projectPath, { recursive: true }); + fs.writeFileSync(path.join(projectPath, 'package.json'), JSON.stringify(packageJson)); + for (const configFile of configFiles) { + fs.writeFileSync(path.join(projectPath, configFile), ''); + } +} + +describe('detectMigrationWarnings', () => { + let tmpDir: string; + + beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-migration-detector-')); + }); + + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + it('detects matching packages and config files', () => { + writeProject( + tmpDir, + { + devDependencies: { rollup: '^4.0.0', jest: '^30.0.0' }, + dependencies: { rolldown: '^1.0.0' }, + }, + ['rollup.config.ts', 'rolldown.config.ts', 'jest.config.ts'], + ); + + expect(detectMigrationWarnings(tmpDir)).toEqual([ + 'Rollup configuration detected. Rollup is API-compatible with Rolldown and can be migrated first before migrating further to tsdown or Vite. Once migrated to tsdown or Vite, re-run vp migrate.', + 'Rolldown configuration detected. Rolldown can be migrated to tsdown or Vite, and it is recommended to do so. Once migrated, re-run vp migrate.', + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + ]); + }); + + it('does not warn when the package and config are not both present', () => { + writeProject(tmpDir, { devDependencies: { rollup: '^4.0.0' } }, ['jest.config.ts']); + + expect(detectMigrationWarnings(tmpDir)).toEqual([]); + }); + + it('detects matching packages and configs in workspace packages', () => { + writeProject(path.join(tmpDir, 'packages/app'), { dependencies: { jest: '^30.0.0' } }, [ + 'jest.config.ts', + ]); + + expect(detectMigrationWarnings(tmpDir, [{ name: 'app', path: 'packages/app' }])).toEqual([ + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + ]); + }); +}); diff --git a/packages/cli/src/migration/bin.ts b/packages/cli/src/migration/bin.ts index a16a10a6e5..ec31b3f9c0 100644 --- a/packages/cli/src/migration/bin.ts +++ b/packages/cli/src/migration/bin.ts @@ -40,6 +40,7 @@ import { import type { PackageDependencies } from '../utils/types.ts'; import { detectWorkspace } from '../utils/workspace.ts'; import { checkRolldownCompatibility } from './compat/runner.ts'; +import { detectMigrationWarnings } from './detector.ts'; import { canFormatWithOxfmt, collectChangedFormatPaths, formatMigratedProject } from './format.ts'; import { addFrameworkShim, @@ -740,6 +741,9 @@ async function executeMigrationPlan( report: MigrationReport; }> { const report = createMigrationReport(); + report.warnings.push( + ...detectMigrationWarnings(workspaceInfoOptional.rootDir, workspaceInfoOptional.packages), + ); const migrationProgress = interactive ? prompts.spinner({ indicator: 'timer' }) : undefined; let migrationProgressStarted = false; const updateMigrationProgress = (message: string) => { @@ -1078,6 +1082,9 @@ async function main() { let finalInstallOk = true; let canFormatMigratedProject = !process.env.VP_SKIP_INSTALL; const report = createMigrationReport(); + report.warnings.push( + ...detectMigrationWarnings(workspaceInfoOptional.rootDir, workspaceInfoOptional.packages), + ); // Report reviews at the end with updated locations. Keep them out of the // report until then so review-only items do not force an up-to-date project // through dependency reconciliation on every run. diff --git a/packages/cli/src/migration/detector.ts b/packages/cli/src/migration/detector.ts index aa8bcf4513..3f313e1b0b 100644 --- a/packages/cli/src/migration/detector.ts +++ b/packages/cli/src/migration/detector.ts @@ -1,6 +1,7 @@ import fs from 'node:fs'; import path from 'node:path'; +import type { WorkspacePackage } from '../types/index.ts'; import { VITE_CONFIG_FILES } from '../utils/constants.ts'; export interface ConfigFiles { @@ -59,6 +60,75 @@ export const PRETTIER_CONFIG_FILES = [ 'prettier.config.mts', ] as const; +const MIGRATION_WARNING_DETECTIONS = [ + { + packageName: 'rollup', + configFile: 'rollup.config.ts', + warning: + 'Rollup configuration detected. Rollup is API-compatible with Rolldown and can be migrated first before migrating further to tsdown or Vite. Once migrated to tsdown or Vite, re-run vp migrate.', + }, + { + packageName: 'rolldown', + configFile: 'rolldown.config.ts', + warning: 'Rolldown configuration detected. Rolldown can be migrated to tsdown or Vite, and it is recommended to do so. Once migrated, re-run vp migrate.', + }, + { + packageName: 'jest', + configFile: 'jest.config.ts', + warning: + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + }, +] as const; + +function hasDependency(packageJson: Record, packageName: string): boolean { + return ['dependencies', 'devDependencies', 'optionalDependencies'].some((field) => { + const dependencies = packageJson[field]; + return typeof dependencies === 'object' && dependencies !== null && packageName in dependencies; + }); +} + +/** + * Detect tools that have a direct migration path worth showing before the + * regular Vite+ migration steps run. + */ +export function detectMigrationWarnings( + projectPath: string, + packages: readonly WorkspacePackage[] = [], +): string[] { + const projectPaths = [ + projectPath, + ...packages.map((workspacePackage) => path.join(projectPath, workspacePackage.path)), + ]; + const detected = new Set(); + + for (const currentPath of projectPaths) { + const packageJsonPath = path.join(currentPath, 'package.json'); + if (!fs.existsSync(packageJsonPath)) { + continue; + } + + let packageJson: Record; + try { + packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as Record; + } catch { + continue; + } + + for (const detection of MIGRATION_WARNING_DETECTIONS) { + if ( + hasDependency(packageJson, detection.packageName) && + fs.existsSync(path.join(currentPath, detection.configFile)) + ) { + detected.add(detection.warning); + } + } + } + + return MIGRATION_WARNING_DETECTIONS.map(({ warning }) => warning).filter((warning) => + detected.has(warning), + ); +} + export function detectConfigs(projectPath: string): ConfigFiles { const configs: ConfigFiles = {}; From ae3f39f39f0c587dde70d5572745de7c77fae442 Mon Sep 17 00:00:00 2001 From: KTrain <69028025+KTrain5169@users.noreply.github.com> Date: Thu, 29 Oct 2026 23:08:18 +0000 Subject: [PATCH 2/5] feat: add detection for all config variants instead of just one --- .../src/migration/__tests__/detector.spec.ts | 15 +++++++++++-- packages/cli/src/migration/detector.ts | 21 ++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/migration/__tests__/detector.spec.ts b/packages/cli/src/migration/__tests__/detector.spec.ts index e2b85abeb0..b48b926865 100644 --- a/packages/cli/src/migration/__tests__/detector.spec.ts +++ b/packages/cli/src/migration/__tests__/detector.spec.ts @@ -25,14 +25,14 @@ describe('detectMigrationWarnings', () => { fs.rmSync(tmpDir, { recursive: true, force: true }); }); - it('detects matching packages and config files', () => { + it('detects matching packages and config file variants', () => { writeProject( tmpDir, { devDependencies: { rollup: '^4.0.0', jest: '^30.0.0' }, dependencies: { rolldown: '^1.0.0' }, }, - ['rollup.config.ts', 'rolldown.config.ts', 'jest.config.ts'], + ['rollup.config.mjs', 'rolldown.config.cjs', 'jest.config.json'], ); expect(detectMigrationWarnings(tmpDir)).toEqual([ @@ -48,6 +48,17 @@ describe('detectMigrationWarnings', () => { expect(detectMigrationWarnings(tmpDir)).toEqual([]); }); + it('detects Jest configuration in package.json', () => { + writeProject(tmpDir, { + devDependencies: { jest: '^30.0.0' }, + jest: { testEnvironment: 'node' }, + }); + + expect(detectMigrationWarnings(tmpDir)).toEqual([ + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + ]); + }); + it('detects matching packages and configs in workspace packages', () => { writeProject(path.join(tmpDir, 'packages/app'), { dependencies: { jest: '^30.0.0' } }, [ 'jest.config.ts', diff --git a/packages/cli/src/migration/detector.ts b/packages/cli/src/migration/detector.ts index 3f313e1b0b..c4e13b28e2 100644 --- a/packages/cli/src/migration/detector.ts +++ b/packages/cli/src/migration/detector.ts @@ -60,21 +60,28 @@ export const PRETTIER_CONFIG_FILES = [ 'prettier.config.mts', ] as const; +const CONFIG_SCRIPT_EXTENSIONS = ['js', 'mjs', 'cjs', 'ts', 'mts', 'cts'] as const; + const MIGRATION_WARNING_DETECTIONS = [ { packageName: 'rollup', - configFile: 'rollup.config.ts', + configFiles: CONFIG_SCRIPT_EXTENSIONS.map((extension) => `rollup.config.${extension}`), warning: 'Rollup configuration detected. Rollup is API-compatible with Rolldown and can be migrated first before migrating further to tsdown or Vite. Once migrated to tsdown or Vite, re-run vp migrate.', }, { packageName: 'rolldown', - configFile: 'rolldown.config.ts', - warning: 'Rolldown configuration detected. Rolldown can be migrated to tsdown or Vite, and it is recommended to do so. Once migrated, re-run vp migrate.', + configFiles: CONFIG_SCRIPT_EXTENSIONS.map((extension) => `rolldown.config.${extension}`), + warning: + 'Rolldown configuration detected. Rolldown can be migrated to tsdown or Vite, and it is recommended to do so. Once migrated, re-run vp migrate.', }, { packageName: 'jest', - configFile: 'jest.config.ts', + configFiles: [ + ...CONFIG_SCRIPT_EXTENSIONS.map((extension) => `jest.config.${extension}`), + 'jest.config.json', + ], + packageJsonField: 'jest', warning: 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', }, @@ -117,7 +124,11 @@ export function detectMigrationWarnings( for (const detection of MIGRATION_WARNING_DETECTIONS) { if ( hasDependency(packageJson, detection.packageName) && - fs.existsSync(path.join(currentPath, detection.configFile)) + (detection.configFiles.some((configFile) => + fs.existsSync(path.join(currentPath, configFile)), + ) || + ('packageJsonField' in detection && + Object.hasOwn(packageJson, detection.packageJsonField))) ) { detected.add(detection.warning); } From 20bf6ac8cb9879345c4d313b1a115ab7e6ece476 Mon Sep 17 00:00:00 2001 From: KTrain <69028025+KTrain5169@users.noreply.github.com> Date: Fri, 30 Oct 2026 05:45:58 +0000 Subject: [PATCH 3/5] fix: also detect dependencies declared at project root & preserve early exit when only advisory warnings exist --- .../src/migration/__tests__/detector.spec.ts | 9 +++++++ packages/cli/src/migration/bin.ts | 24 ++++++++++++------- packages/cli/src/migration/detector.ts | 9 ++++++- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/migration/__tests__/detector.spec.ts b/packages/cli/src/migration/__tests__/detector.spec.ts index b48b926865..ffd7be651b 100644 --- a/packages/cli/src/migration/__tests__/detector.spec.ts +++ b/packages/cli/src/migration/__tests__/detector.spec.ts @@ -68,4 +68,13 @@ describe('detectMigrationWarnings', () => { 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', ]); }); + + it('detects workspace configs with dependencies declared at the root', () => { + writeProject(tmpDir, { devDependencies: { jest: '^30.0.0' } }); + writeProject(path.join(tmpDir, 'packages/app'), {}, ['jest.config.js']); + + expect(detectMigrationWarnings(tmpDir, [{ name: 'app', path: 'packages/app' }])).toEqual([ + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + ]); + }); }); diff --git a/packages/cli/src/migration/bin.ts b/packages/cli/src/migration/bin.ts index 807998a070..3c3fd208b0 100644 --- a/packages/cli/src/migration/bin.ts +++ b/packages/cli/src/migration/bin.ts @@ -615,12 +615,7 @@ function showMigrationSummary(options: { if (suggestFullMigration) { log(FULL_MIGRATION_HINT); } - if (report.warnings.length > 0) { - log(`${styleText('yellow', '!')} Warnings:`); - for (const warning of report.warnings) { - log(` - ${warning}`); - } - } + showMigrationWarnings(report.warnings); if (report.manualSteps.length > 0) { log(`${styleText('blue', '→')} Manual follow-up:`); for (const step of report.manualSteps) { @@ -629,6 +624,16 @@ function showMigrationSummary(options: { } } +function showMigrationWarnings(warnings: readonly string[]): void { + if (warnings.length === 0) { + return; + } + log(`${styleText('yellow', '!')} Warnings:`); + for (const warning of warnings) { + log(` - ${warning}`); + } +} + async function downloadSupportedPackageManager(options: { rootDir: string; packageManager: PackageManager; @@ -1066,8 +1071,9 @@ async function main() { let finalInstallOk = true; let canFormatMigratedProject = !process.env.VP_SKIP_INSTALL; const report = createMigrationReport(); - report.warnings.push( - ...detectMigrationWarnings(workspaceInfoOptional.rootDir, workspaceInfoOptional.packages), + const migrationWarnings = detectMigrationWarnings( + workspaceInfoOptional.rootDir, + workspaceInfoOptional.packages, ); // Report reviews at the end with updated locations. Keep them out of the // report until then so review-only items do not force an up-to-date project @@ -1234,6 +1240,7 @@ async function main() { ? hasExistingVitePlusMigrationCandidates(workspaceInfoOptional, options) : hasExplicitExistingVitePlusSetupRequest(options)) ) { + showMigrationWarnings(migrationWarnings); if (vitestV5Preflight) { prompts.log.warn(vitestV5Preflight); } @@ -1246,6 +1253,7 @@ async function main() { prompts.outro(`This project is already using Vite+! ${accent('Happy coding!')}`); return; } + report.warnings.push(...migrationWarnings); for (const warning of coreMigrationResult.taskCacheWarnings) { addMigrationWarning(report, warning); } diff --git a/packages/cli/src/migration/detector.ts b/packages/cli/src/migration/detector.ts index c4e13b28e2..086ee8e1da 100644 --- a/packages/cli/src/migration/detector.ts +++ b/packages/cli/src/migration/detector.ts @@ -107,6 +107,7 @@ export function detectMigrationWarnings( ...packages.map((workspacePackage) => path.join(projectPath, workspacePackage.path)), ]; const detected = new Set(); + let rootPackageJson: Record | undefined; for (const currentPath of projectPaths) { const packageJsonPath = path.join(currentPath, 'package.json'); @@ -120,10 +121,16 @@ export function detectMigrationWarnings( } catch { continue; } + if (currentPath === projectPath) { + rootPackageJson = packageJson; + } for (const detection of MIGRATION_WARNING_DETECTIONS) { if ( - hasDependency(packageJson, detection.packageName) && + (hasDependency(packageJson, detection.packageName) || + (currentPath !== projectPath && + rootPackageJson !== undefined && + hasDependency(rootPackageJson, detection.packageName))) && (detection.configFiles.some((configFile) => fs.existsSync(path.join(currentPath, configFile)), ) || From 7f3ed34b096b59eebf8634019b1c72ee203cabcd Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 30 Oct 2026 15:38:14 +0900 Subject: [PATCH 4/5] fix(migrate): update Jest migration guide URL --- packages/cli/src/migration/__tests__/detector.spec.ts | 8 ++++---- packages/cli/src/migration/detector.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/migration/__tests__/detector.spec.ts b/packages/cli/src/migration/__tests__/detector.spec.ts index ffd7be651b..8f642f7154 100644 --- a/packages/cli/src/migration/__tests__/detector.spec.ts +++ b/packages/cli/src/migration/__tests__/detector.spec.ts @@ -38,7 +38,7 @@ describe('detectMigrationWarnings', () => { expect(detectMigrationWarnings(tmpDir)).toEqual([ 'Rollup configuration detected. Rollup is API-compatible with Rolldown and can be migrated first before migrating further to tsdown or Vite. Once migrated to tsdown or Vite, re-run vp migrate.', 'Rolldown configuration detected. Rolldown can be migrated to tsdown or Vite, and it is recommended to do so. Once migrated, re-run vp migrate.', - 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration/jest, then re-run vp migrate.', ]); }); @@ -55,7 +55,7 @@ describe('detectMigrationWarnings', () => { }); expect(detectMigrationWarnings(tmpDir)).toEqual([ - 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration/jest, then re-run vp migrate.', ]); }); @@ -65,7 +65,7 @@ describe('detectMigrationWarnings', () => { ]); expect(detectMigrationWarnings(tmpDir, [{ name: 'app', path: 'packages/app' }])).toEqual([ - 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration/jest, then re-run vp migrate.', ]); }); @@ -74,7 +74,7 @@ describe('detectMigrationWarnings', () => { writeProject(path.join(tmpDir, 'packages/app'), {}, ['jest.config.js']); expect(detectMigrationWarnings(tmpDir, [{ name: 'app', path: 'packages/app' }])).toEqual([ - 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration/jest, then re-run vp migrate.', ]); }); }); diff --git a/packages/cli/src/migration/detector.ts b/packages/cli/src/migration/detector.ts index 086ee8e1da..cdf40a7934 100644 --- a/packages/cli/src/migration/detector.ts +++ b/packages/cli/src/migration/detector.ts @@ -83,7 +83,7 @@ const MIGRATION_WARNING_DETECTIONS = [ ], packageJsonField: 'jest', warning: - 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration.html#jest, then re-run vp migrate.', + 'Jest configuration detected. Vitest is highly compatible with Jest, and so we recommend migrating. See the Vitest Jest migration guide: https://vitest.dev/guide/migration/jest, then re-run vp migrate.', }, ] as const; From f9370a6934f98a63288ca31c4d8dcdf8d5f15e1f Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 30 Oct 2026 15:45:17 +0900 Subject: [PATCH 5/5] fix(migrate): require --full for existing project tool warnings --- packages/cli/src/migration/bin.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/migration/bin.ts b/packages/cli/src/migration/bin.ts index 3c3fd208b0..105068d213 100644 --- a/packages/cli/src/migration/bin.ts +++ b/packages/cli/src/migration/bin.ts @@ -1071,10 +1071,10 @@ async function main() { let finalInstallOk = true; let canFormatMigratedProject = !process.env.VP_SKIP_INSTALL; const report = createMigrationReport(); - const migrationWarnings = detectMigrationWarnings( - workspaceInfoOptional.rootDir, - workspaceInfoOptional.packages, - ); + // Only --full opts existing projects into tool migration advice. + const migrationWarnings = options.full + ? detectMigrationWarnings(workspaceInfoOptional.rootDir, workspaceInfoOptional.packages) + : []; // Report reviews at the end with updated locations. Keep them out of the // report until then so review-only items do not force an up-to-date project // through dependency reconciliation on every run.