Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pnpm lint-staged

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "migration-husky-latest-dist-tag-v9-installed",
"scripts": {
"prepare": "husky"
},
"devDependencies": {
"husky": "latest",
"lint-staged": "^16.2.6",
"vite": "^7.0.0"
},
"lint-staged": {
"*.js": "oxlint --fix"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
> git init
> vp migrate --no-interactive # should resolve husky v9 from node_modules, no warning
VITE+ - The Unified Toolchain for the Web

◇ Migrated . to Vite+<repeat>
• Node <semver> pnpm <semver>
• 2 config updates applied
• Git hooks configured

> cat package.json # husky and lint-staged should be removed
{
"name": "migration-husky-latest-dist-tag-v9-installed",
"scripts": {
"prepare": "vp config"
},
"devDependencies": {
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
"vite-plus": "latest"
},
"pnpm": {
"overrides": {
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
}
},
"packageManager": "pnpm@<semver>"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"commands": [
{ "command": "git init", "ignoreOutput": true },
"vp migrate --no-interactive # should resolve husky v9 from node_modules, no warning",
"cat package.json # husky and lint-staged should be removed"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pnpm lint-staged
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "migration-husky-latest-dist-tag",
"scripts": {
"prepare": "husky"
},
"devDependencies": {
"husky": "latest",
"vite": "^7.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
> git init
> vp migrate --no-interactive # should warn about uncoercible husky version
VITE+ - The Unified Toolchain for the Web


⚠ Could not determine husky version from "latest" — please specify a semver-compatible version (e.g., "^9.0.0") and re-run migration.
◇ Migrated . to Vite+<repeat>
• Node <semver> pnpm <semver>
• 1 config update applied

> cat package.json # husky should still be in devDeps
{
"name": "migration-husky-latest-dist-tag",
"scripts": {
"prepare": "husky"
},
"devDependencies": {
"husky": "latest",
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
"vite-plus": "latest"
},
"pnpm": {
"overrides": {
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
}
},
"packageManager": "pnpm@<semver>"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"commands": [
{ "command": "git init", "ignoreOutput": true },
"vp migrate --no-interactive # should warn about uncoercible husky version",
"cat package.json # husky should still be in devDeps"
]
}
31 changes: 24 additions & 7 deletions packages/cli/src/migration/migrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1510,18 +1510,34 @@ function rewriteAllImports(projectPath: string, silent = false, report?: Migrati
/**
* Check if the project has an unsupported husky version (<9.0.0).
* Uses `semver.coerce` to handle ranges like `^8.0.0` → `8.0.0`.
* Accepts pre-loaded deps to avoid re-reading package.json when called
* from contexts that already parsed it.
* When the specifier is not coercible (e.g. `"latest"`), falls back to
* the installed version in node_modules via `detectPackageMetadata`.
* Returns a reason string if hooks migration should be skipped, or null
* if husky is absent or compatible.
*/
function checkUnsupportedHuskyVersion(
projectPath: string,
deps: Record<string, string> | undefined,
prodDeps: Record<string, string> | undefined,
): boolean {
): string | null {
const huskyVersion = deps?.husky ?? prodDeps?.husky;
if (!huskyVersion) {
return false;
return null;
}
let coerced = semver.coerce(huskyVersion);
if (coerced == null) {
const installed = detectPackageMetadata(projectPath, 'husky');
if (installed) {
coerced = semver.coerce(installed.version);
}
if (coerced == null) {
return `Could not determine husky version from "${huskyVersion}" — please specify a semver-compatible version (e.g., "^9.0.0") and re-run migration.`;
}
}
if (semver.satisfies(coerced, '<9.0.0')) {
return 'Detected husky <9.0.0 — please upgrade to husky v9+ first, then re-run migration.';
}
return semver.satisfies(semver.coerce(huskyVersion) ?? '0.0.0', '<9.0.0');
return null;
}

const OTHER_HOOK_TOOLS = ['simple-git-hooks', 'lefthook', 'yorkie'] as const;
Expand Down Expand Up @@ -1635,8 +1651,9 @@ export function preflightGitHooksSetup(projectPath: string): string | null {
return `Detected ${tool} — skipping git hooks setup. Please configure git hooks manually.`;
}
}
if (checkUnsupportedHuskyVersion(deps, prodDeps)) {
return 'Detected husky <9.0.0 — please upgrade to husky v9+ first, then re-run migration.';
const huskyReason = checkUnsupportedHuskyVersion(projectPath, deps, prodDeps);
if (huskyReason) {
return huskyReason;
}
if (hasUnsupportedLintStagedConfig(projectPath)) {
return 'Unsupported lint-staged config format — skipping git hooks setup. Please configure git hooks manually.';
Expand Down
Loading