Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/guide/migrate-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ scripts while preserving their arguments:
| `lint-staged` | `vp staged` |
| `eslint` | `vp lint`, when its optional migration runs |
| `prettier` | `vp fmt`, when its optional migration runs |
| `tsup` | `vp pack`, when its optional migration runs |

For commands launched through `bunx`, migration preserves `bunx` and its
`--bun` flag (keeping the user's chosen runtime) and rewrites only the managed
Expand Down
4 changes: 2 additions & 2 deletions docs/guide/migrate.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Overview

This command is the starting point for consolidating separate Vite, Vitest, Oxlint, Oxfmt, ESLint, and Prettier setups into Vite+.
This command is the starting point for consolidating separate Vite, Vitest, Oxlint, Oxfmt, ESLint, Prettier, and tsup setups into Vite+.

Use it when you want to take an existing project and move it onto the Vite+ defaults instead of wiring each tool by hand.

Expand Down Expand Up @@ -70,7 +70,7 @@ After running the migration:
- Run `vp install`
- Run `vp check`
- Run `vp test`
- Run `vp build`
- Run `vp build` (or `vp pack` if you are building a library)

## Manual Installation & Migration

Expand Down
9 changes: 9 additions & 0 deletions packages/cli/rules/vite-tools.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,12 @@ rule:
kind: command_name
regex: '^tsdown$'
fix: vp pack

# tsup => vp pack
---
id: replace-tsup
language: bash
rule:
kind: command_name
regex: '^tsup$'
fix: vp pack
9 changes: 7 additions & 2 deletions packages/cli/src/create/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ import {
detectEslintProject,
detectFramework,
detectPrettierProject,
detectTsupProject,
hasFrameworkShim,
injectCreateDefaultTemplate,
installGitHooks,
promptEslintMigration,
promptPrettierMigration,
promptTsupMigration,
rewriteMonorepo,
rewriteMonorepoProject,
rewriteStandaloneProject,
Expand Down Expand Up @@ -1285,7 +1287,9 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
// and relies on `rewrite*Project` to add tarball overrides BEFORE the
// first install, so install-first would break CI's local-tarball resolve.
const shouldMigrateLintFmtTools =
detectEslintProject(fullPath).hasDependency || detectPrettierProject(fullPath).hasDependency;
detectEslintProject(fullPath).hasDependency ||
detectPrettierProject(fullPath).hasDependency ||
detectTsupProject(fullPath).hasDependency;

let installSummary: CommandRunSummary | undefined;

Expand Down Expand Up @@ -1325,10 +1329,11 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
if (installSummary.pendingBuilds && installSummary.pendingBuilds.length > 0) {
migratePendingBuilds = installSummary.pendingBuilds;
}
updateCreateProgress('Migrating lint and format tools');
updateCreateProgress('Migrating lint, format & pack tools');
pauseCreateProgress();
await promptEslintMigration(fullPath, /* interactive */ false);
await promptPrettierMigration(fullPath, /* interactive */ false);
await promptTsupMigration(fullPath, /* interactive */ false, packageManager);
resumeCreateProgress();
};

Expand Down
82 changes: 80 additions & 2 deletions packages/cli/src/migration/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import {
detectNodeVersionManagerFile,
detectPendingCoreMigration,
detectPrettierProject,
detectTsupProject,
detectVitePlusBootstrapPending,
detectYarnPnpMode,
ensureVitePlusBootstrap,
Expand All @@ -55,10 +56,12 @@ import {
detectLegacyGitHooksMigrationCandidate,
injectLintTypeCheckDefaults,
installGitHooks,
mergeTsdownConfigFile,
mergeViteConfigFiles,
migrateEslintToOxlint,
migrateNodeVersionManagerFile,
migratePrettierToOxfmt,
migrateTsupToTsdown,
configureYarnNodeModulesMode,
rewriteMonorepo,
rewriteStandaloneProject,
Expand All @@ -70,7 +73,11 @@ import {
import { prepareNpmViteAliasReinstall } from './npm-reinstall.ts';
import type { MigrationOptions } from './options.ts';
import { addMigrationWarning, createMigrationReport, type MigrationReport } from './report.ts';
import { collectMigrationSetupPlan, type MigrationSetupPlan } from './setup-plan.ts';
import {
collectMigrationSetupPlan,
collectTsupMigrationDecision,
type MigrationSetupPlan,
} from './setup-plan.ts';

async function confirmNodeVersionFileMigration(
interactive: boolean,
Expand Down Expand Up @@ -379,6 +386,8 @@ interface MigrationPlan extends MigrationSetupPlan {
migratePrettier: boolean;
hasPrettierDependency: boolean;
prettierConfigFile?: string;
migrateTsup: boolean;
tsupConfigFile?: string;
fixBaseUrl: boolean;
migrateNodeVersionFile: boolean;
nodeVersionDetection?: NodeVersionManagerDetection;
Expand Down Expand Up @@ -467,12 +476,14 @@ function hasExistingVitePlusMigrationCandidates(
): boolean {
const eslintProject = detectEslintProject(workspaceInfo.rootDir, workspaceInfo.packages);
const prettierProject = detectPrettierProject(workspaceInfo.rootDir, workspaceInfo.packages);
const tsupProject = detectTsupProject(workspaceInfo.rootDir, workspaceInfo.packages);
return (
hasExplicitExistingVitePlusSetupRequest(options) ||
detectLegacyGitHooksMigrationCandidate(workspaceInfo.rootDir) ||
hasBaseUrlInWorkspace(workspaceInfo) ||
eslintProject.hasDependency ||
prettierProject.hasDependency ||
tsupProject.hasDependency ||
detectNodeVersionManagerFile(workspaceInfo.rootDir) !== undefined ||
getFrameworkShimCandidates(workspaceInfo.rootDir, workspaceInfo.packages).length > 0
);
Expand Down Expand Up @@ -525,6 +536,13 @@ async function collectMigrationPlan(
warnPackageLevelPrettier();
}

// 3b. tsup detection + prompt (after Prettier so Prettier -> Oxfmt is checked first)
const { migrateTsup, tsupConfigFile } = await collectTsupMigrationDecision(
rootDir,
options,
packages,
);

// 9. tsconfig baseUrl prompt
const fixBaseUrl = hasBaseUrlInWorkspace({ rootDir, packages })
? await confirmBaseUrlFix(options.interactive)
Expand All @@ -550,6 +568,8 @@ async function collectMigrationPlan(
migratePrettier,
hasPrettierDependency: prettierProject.hasDependency,
prettierConfigFile: prettierProject.configFile,
migrateTsup,
tsupConfigFile,
fixBaseUrl,
migrateNodeVersionFile,
nodeVersionDetection,
Expand Down Expand Up @@ -691,6 +711,9 @@ function showMigrationSummary(options: {
if (report.prettierMigrated) {
log(`${styleText('gray', '•')} Prettier migrated to Oxfmt`);
}
if (report.tsupMigrated) {
log(`${styleText('gray', '•')} tsup config migrated to tsdown (\`vp pack\`)`);
}
if (report.nodeVersionFileMigrated) {
log(`${styleText('gray', '•')} Node version manager file migrated to .node-version`);
}
Expand Down Expand Up @@ -905,6 +928,23 @@ async function executeMigrationPlan(
}
}

// 6b. tsup → tsdown migration (before main rewrite so tsdown.config.* gets picked up)
if (plan.migrateTsup) {
updateMigrationProgress('Migrating tsup');
const tsupOk = await migrateTsupToTsdown(
workspaceInfo.rootDir,
interactive,
plan.packageManager,
plan.tsupConfigFile,
workspaceInfo.packages,
{ silent: true, report },
);
if (!tsupOk) {
failMigrationProgress('Migration failed');
cancelAndExit('tsup migration failed. Fix the issue and re-run `vp migrate`.', 1);
}
}

// Preserve lint-staged whenever hook setup is disabled/unsafe or existing
// project-owned hooks remain authoritative.
const skipStagedMigration = shouldSkipStagedMigrationForHooks(
Expand Down Expand Up @@ -1345,6 +1385,37 @@ async function main() {
}
}

let tsupMigrated = false;
if (fullSetup) {
// Interactive only: stop any active spinner (e.g. "Migrating Prettier") so
// it does not animate beneath the confirm prompt.
if (options.interactive) {
clearMigrationProgress();
}
const { migrateTsup, tsupConfigFile } = await collectTsupMigrationDecision(
workspaceInfoOptional.rootDir,
setupOptions,
workspaceInfoOptional.packages,
);
if (migrateTsup) {
await ensureExistingPackageManager();
updateMigrationProgress('Migrating tsup');
const tsupOk = await migrateTsupToTsdown(
workspaceInfoOptional.rootDir,
options.interactive,
packageManager!, // is it safe to do this?
tsupConfigFile,
workspaceInfoOptional.packages,
{ silent: true, report },
);
if (!tsupOk) {
clearMigrationProgress();
cancelAndExit('tsup migration failed. Fix the issue and re-run `vp migrate`.', 1);
}
tsupMigrated = true;
}
}

// Check if node version manager file migration is needed (full setup only)
if (fullSetup) {
const nodeVersionDetection = detectNodeVersionManagerFile(workspaceInfoOptional.rootDir);
Expand Down Expand Up @@ -1391,18 +1462,25 @@ async function main() {
}

// Merge configs and reinstall once if any tool or bootstrap migration happened
if (eslintMigrated || prettierMigrated) {
if (eslintMigrated || prettierMigrated || tsupMigrated) {
updateMigrationProgress('Rewriting configs');
mergeViteConfigFiles(
workspaceInfoOptional.rootDir,
true,
report,
workspaceInfoOptional.packages,
);
if (tsupMigrated) {
mergeTsdownConfigFile(workspaceInfoOptional.rootDir, true, report);
for (const pkg of workspaceInfoOptional.packages ?? []) {
mergeTsdownConfigFile(path.join(workspaceInfoOptional.rootDir, pkg.path), true, report);
}
}
needsInstall = true;
didMigrate = true;
report.eslintMigrated = eslintMigrated;
report.prettierMigrated = prettierMigrated;
report.tsupMigrated = tsupMigrated;
}

if (plan.shouldSetupHooks) {
Expand Down
31 changes: 31 additions & 0 deletions packages/cli/src/migration/detector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface ConfigFiles {
viteConfig?: string;
vitestConfig?: string;
tsdownConfig?: string;
tsupConfig?: string;
oxlintConfig?: string;
oxfmtConfig?: string;
eslintConfig?: string;
Expand All @@ -20,6 +21,21 @@ export interface ConfigFiles {
// Sentinel value indicating Prettier config lives inside package.json "prettier" key.
export const PRETTIER_PACKAGE_JSON_CONFIG = 'package.json#prettier';

// Sentinel value indicating tsup config lives inside package.json "tsup" key.
export const TSUP_PACKAGE_JSON_CONFIG = 'package.json#tsup';

// All known tsup config file names (standalone files only).
// https://tsup.egoist.dev/#using-a-config-file
export const TSUP_CONFIG_FILES = [
'tsup.config.ts',
'tsup.config.mts',
'tsup.config.cts',
'tsup.config.js',
'tsup.config.mjs',
'tsup.config.cjs',
'tsup.config.json',
] as const;

// All known Prettier config file names (standalone files only).
// https://prettier.io/docs/configuration
export const PRETTIER_CONFIG_FILES = [
Expand Down Expand Up @@ -90,6 +106,17 @@ export function detectConfigs(projectPath: string): ConfigFiles {
}
}

// Check for tsup.config.* (still detected even though tsdown supersedes it —
// `promptTsupMigration` / `detectTsupProject` use this to offer the tsup →
// tsdown migration).
// https://tsup.egoist.dev/#using-a-config-file
for (const config of TSUP_CONFIG_FILES) {
if (fs.existsSync(path.join(projectPath, config))) {
configs.tsupConfig = config;
break;
}
}

// Check for oxlint configs
// https://oxc.rs/docs/guide/usage/linter/config.html#configuration-file-format
const oxlintConfigs = ['.oxlintrc.json', '.oxlintrc.jsonc'];
Expand Down Expand Up @@ -172,6 +199,10 @@ export function detectConfigs(projectPath: string): ConfigFiles {
configs.prettierConfig = PRETTIER_PACKAGE_JSON_CONFIG;
}

if (!configs.tsupConfig && pkg.tsup) {
configs.tsupConfig = TSUP_PACKAGE_JSON_CONFIG;
}

const voltaNode = pkg.volta?.node;
if (typeof voltaNode === 'string') {
configs.voltaNode = voltaNode;
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/migration/migrator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './migrator/shared.ts';
export * from './migrator/eslint.ts';
export * from './migrator/prettier.ts';
export * from './migrator/tsup.ts';
export * from './migrator/tsconfig.ts';
export * from './migrator/framework-shim.ts';
export * from './migrator/vitest-ecosystem.ts';
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/migration/migrator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Pick the file by what a function _does_, not by where it happens to be called.
| `vite-config.ts` | `vite.config.ts` merging, default-config injection, staged-config merge, lazy-plugin wrapping, import rewriting (`rewriteAllImports`), migrated-oxlint-config sanitization, lint-staged removal. |
| `eslint.ts` | ESLint → Oxlint migration, oxlint JS-plugin namespace handling, ESLint prompts/warnings. |
| `prettier.ts` | Prettier → Oxfmt migration and its prompts/warnings. |
| `tsup.ts` | tsup → tsdown (`vp pack`) migration via `tsdown-migrate` and its prompts/warnings. |
| `tsconfig.ts` | `tsconfig.json` cleanup and `types` rewriting. |
| `framework-shim.ts` | Framework (Vue/Astro) shim detection and injection. |
| `git-hooks.ts` | Vite+ hook defaults, project-owned hook preservation, and conservative detection of existing hook tools. |
Expand Down
Loading
Loading