From df75cfc4a2e303a6822fd0c64833cc4867521936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A2=85=EA=B2=BD?= Date: Tue, 24 Mar 2026 18:28:44 +0900 Subject: [PATCH 1/3] fix(migrate): warn on uncoercible husky version instead of false positive --- .../.husky/pre-commit | 1 + .../package.json | 10 +++++++ .../migration-husky-latest-dist-tag/snap.txt | 29 +++++++++++++++++++ .../steps.json | 7 +++++ packages/cli/src/migration/migrator.ts | 22 +++++++++----- 5 files changed, 62 insertions(+), 7 deletions(-) create mode 100755 packages/cli/snap-tests-global/migration-husky-latest-dist-tag/.husky/pre-commit create mode 100644 packages/cli/snap-tests-global/migration-husky-latest-dist-tag/package.json create mode 100644 packages/cli/snap-tests-global/migration-husky-latest-dist-tag/snap.txt create mode 100644 packages/cli/snap-tests-global/migration-husky-latest-dist-tag/steps.json diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/.husky/pre-commit b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/.husky/pre-commit new file mode 100755 index 0000000000..cb2c84d5c3 --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/.husky/pre-commit @@ -0,0 +1 @@ +pnpm lint-staged diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/package.json b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/package.json new file mode 100644 index 0000000000..33842ad2e1 --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/package.json @@ -0,0 +1,10 @@ +{ + "name": "migration-husky-latest-dist-tag", + "scripts": { + "prepare": "husky" + }, + "devDependencies": { + "husky": "latest", + "vite": "^7.0.0" + } +} diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/snap.txt b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/snap.txt new file mode 100644 index 0000000000..116f8a51c1 --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/snap.txt @@ -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+ +• Node pnpm +• 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@" +} diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/steps.json b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/steps.json new file mode 100644 index 0000000000..33fe726efc --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag/steps.json @@ -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" + ] +} diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index 0932320f27..6d85f16d43 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -1510,18 +1510,25 @@ 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. + * Returns a reason string if hooks migration should be skipped, or null + * if husky is absent or compatible. */ function checkUnsupportedHuskyVersion( deps: Record | undefined, prodDeps: Record | undefined, -): boolean { +): string | null { const huskyVersion = deps?.husky ?? prodDeps?.husky; if (!huskyVersion) { - return false; + return null; } - return semver.satisfies(semver.coerce(huskyVersion) ?? '0.0.0', '<9.0.0'); + const coerced = semver.coerce(huskyVersion); + 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 null; } const OTHER_HOOK_TOOLS = ['simple-git-hooks', 'lefthook', 'yorkie'] as const; @@ -1635,8 +1642,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(deps, prodDeps); + if (huskyReason) { + return huskyReason; } if (hasUnsupportedLintStagedConfig(projectPath)) { return 'Unsupported lint-staged config format — skipping git hooks setup. Please configure git hooks manually.'; From 730c45a19f926247ce6d96db9562d030d9978e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A2=85=EA=B2=BD?= Date: Tue, 24 Mar 2026 21:34:26 +0900 Subject: [PATCH 2/3] fix(migrate): fall back to installed husky version when specifier is uncoercible --- .../.husky/pre-commit | 1 + .../package.json | 14 ++++++++++ .../snap.txt | 27 +++++++++++++++++++ .../steps.json | 7 +++++ packages/cli/src/migration/migrator.ts | 15 ++++++++--- 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/.husky/pre-commit create mode 100644 packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/package.json create mode 100644 packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/snap.txt create mode 100644 packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/steps.json diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/.husky/pre-commit b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/.husky/pre-commit new file mode 100644 index 0000000000..cb2c84d5c3 --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/.husky/pre-commit @@ -0,0 +1 @@ +pnpm lint-staged diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/package.json b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/package.json new file mode 100644 index 0000000000..0b66c6d1d5 --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/package.json @@ -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" + } +} diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/snap.txt b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/snap.txt new file mode 100644 index 0000000000..3ca53f7df1 --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/snap.txt @@ -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+ +• Node pnpm +• 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@" +} diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/steps.json b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/steps.json new file mode 100644 index 0000000000..1d17f7cb11 --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/steps.json @@ -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" + ] +} diff --git a/packages/cli/src/migration/migrator.ts b/packages/cli/src/migration/migrator.ts index 6d85f16d43..7d55e73501 100644 --- a/packages/cli/src/migration/migrator.ts +++ b/packages/cli/src/migration/migrator.ts @@ -1510,10 +1510,13 @@ 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`. + * 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 | undefined, prodDeps: Record | undefined, ): string | null { @@ -1521,9 +1524,15 @@ function checkUnsupportedHuskyVersion( if (!huskyVersion) { return null; } - const coerced = semver.coerce(huskyVersion); + let coerced = semver.coerce(huskyVersion); 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.`; + 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.'; @@ -1642,7 +1651,7 @@ export function preflightGitHooksSetup(projectPath: string): string | null { return `Detected ${tool} — skipping git hooks setup. Please configure git hooks manually.`; } } - const huskyReason = checkUnsupportedHuskyVersion(deps, prodDeps); + const huskyReason = checkUnsupportedHuskyVersion(projectPath, deps, prodDeps); if (huskyReason) { return huskyReason; } From 7072892caf96477edfb34896d90063d24a35f9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A2=85=EA=B2=BD?= Date: Tue, 24 Mar 2026 22:49:11 +0900 Subject: [PATCH 3/3] fix(snap-test): track fixture node_modules/husky for dist-tag fallback test --- .../node_modules/husky/package.json | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/node_modules/husky/package.json diff --git a/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/node_modules/husky/package.json b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/node_modules/husky/package.json new file mode 100644 index 0000000000..6f2ba0e24a --- /dev/null +++ b/packages/cli/snap-tests-global/migration-husky-latest-dist-tag-v9-installed/node_modules/husky/package.json @@ -0,0 +1,4 @@ +{ + "name": "husky", + "version": "9.1.7" +}