Skip to content

Commit 0132c60

Browse files
committed
fix(migrate): preserve protocol-prefixed vite-plus specs; gate vitest auto-add to migrations
Address review findings on the new vite-plus normalize path: - Preserve protocol-prefixed specs (`catalog:named`, `workspace:*`, `link:`, `file:`, `npm:`, `github:`, `git+`/`git:`, `http(s)://`) so deliberate user pins survive `vp migrate`. Previously the new normalize rule rewrote anything that wasn't literally `catalog:` — silently clobbering named-catalog references, workspace and link protocols, and aliased pins. - Split the vitest peer-dep auto-add back behind `needVitePlus` only. A pure normalize pass (sub-package only needed its pinned `^x.y.z` re-aligned to `catalog:`) must not also inject a `vitest` devDep just because the project happens to have a `vitest-browser-*` peer. - Drop the now-redundant `const version = canonicalVitePlusSpec` alias. Also update `snap-tests-global/new-vite-monorepo-bun/snap.txt` to drop the stripped `"vite": "catalog:"` line in apps/website (matches the cleanup in `executeMonorepoTemplate`), and add an existsSync guard to the workflow's node -e assertion so a missing sub-package package.json surfaces with a targeted message instead of an opaque ENOENT. Test coverage: yarn + bun catalog normalize, protocol-prefix preservation, and "pure normalize must not auto-add vitest". 410 passed.
1 parent f9bfde4 commit 0132c60

4 files changed

Lines changed: 67 additions & 11 deletions

File tree

.github/workflows/test-vp-create.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,12 @@ jobs:
291291
node -e "
292292
const fs = require('fs');
293293
const pm = process.env.PACKAGE_MANAGER;
294+
for (const f of ['apps/website/package.json', 'packages/utils/package.json']) {
295+
if (!fs.existsSync(f)) {
296+
console.error('✗ expected ' + f + ' to exist after vp create vite:monorepo');
297+
process.exit(1);
298+
}
299+
}
294300
const app = JSON.parse(fs.readFileSync('apps/website/package.json', 'utf8'));
295301
const utils = JSON.parse(fs.readFileSync('packages/utils/package.json', 'utf8'));
296302
const appDev = app.devDependencies || {};

packages/cli/snap-tests-global/new-vite-monorepo-bun/snap.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ website
6767
},
6868
"devDependencies": {
6969
"typescript": "~6.0.2",
70-
"vite": "catalog:",
7170
"vite-plus": "catalog:"
7271
}
7372
}

packages/cli/src/migration/__tests__/migrator.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,42 @@ describe('rewritePackageJson', () => {
183183
expect(pkg.devDependencies['vite-plus']).toBe('^0.1.20');
184184
});
185185

186+
it('normalizes a pre-existing pinned vite-plus on yarn/bun monorepo projects', async () => {
187+
for (const pm of [PackageManager.yarn, PackageManager.bun]) {
188+
const pkg = { devDependencies: { 'vite-plus': '^0.1.20' } };
189+
rewritePackageJson(pkg, pm, true);
190+
expect(pkg.devDependencies['vite-plus']).toBe('catalog:');
191+
}
192+
});
193+
194+
it('preserves protocol-prefixed vite-plus specs (catalog:named, workspace:, link:, github:) in catalog-supporting monorepos', async () => {
195+
for (const existing of [
196+
'catalog:next',
197+
'workspace:*',
198+
'link:../vite-plus',
199+
'github:fork/vite-plus',
200+
'npm:@scope/vite-plus@^1.0.0',
201+
]) {
202+
const pkg = { devDependencies: { 'vite-plus': existing } };
203+
rewritePackageJson(pkg, PackageManager.pnpm, true);
204+
expect(pkg.devDependencies['vite-plus']).toBe(existing);
205+
}
206+
});
207+
208+
it('does not auto-add vitest on a pure normalize pass (only on actual vite/vitest/REMOVE migrations)', async () => {
209+
const pkg = {
210+
devDependencies: {
211+
'vite-plus': '^0.1.20',
212+
'vitest-browser-svelte': '^1.0.0',
213+
},
214+
};
215+
216+
rewritePackageJson(pkg, PackageManager.pnpm, true);
217+
218+
expect(pkg.devDependencies['vite-plus']).toBe('catalog:');
219+
expect((pkg.devDependencies as Record<string, string>).vitest).toBeUndefined();
220+
});
221+
186222
it('uses default catalog specs for non-catalog dependency specs in monorepo projects', async () => {
187223
const pkg = {
188224
devDependencies: {

packages/cli/src/migration/migrator.ts

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1985,24 +1985,31 @@ export function rewritePackageJson(
19851985
}
19861986
// Normalize a pre-existing pinned vite-plus so sub-packages don't drift
19871987
// from siblings: in catalog-supporting monorepos that's `catalog:`, under
1988-
// force-override (file:) it's the tgz path. Scoped to catalog-supporting
1989-
// PMs so npm/standalone keep their pinned spec under `vp migrate`.
1988+
// force-override (file:) it's the tgz path. Preserve protocol-prefixed
1989+
// specs (catalog:named, workspace:*, link:, file:, npm:, github:, git+/git:,
1990+
// http(s)://) so deliberate user pins survive; only vanilla version ranges
1991+
// (e.g. `^0.1.20`, `latest`) are rewritten.
19901992
const canonicalVitePlusSpec =
19911993
supportCatalog && !VITE_PLUS_VERSION.startsWith('file:') ? 'catalog:' : VITE_PLUS_VERSION;
19921994
const existingVitePlus = pkg.devDependencies?.[VITE_PLUS_NAME];
19931995
const shouldNormalizeExistingVitePlus =
1994-
!!existingVitePlus && supportCatalog && existingVitePlus !== canonicalVitePlusSpec;
1996+
!!existingVitePlus &&
1997+
supportCatalog &&
1998+
existingVitePlus !== canonicalVitePlusSpec &&
1999+
!isProtocolPinnedSpec(existingVitePlus);
19952000
if (needVitePlus || shouldNormalizeExistingVitePlus) {
1996-
// add vite-plus to devDependencies
1997-
const version = canonicalVitePlusSpec;
19982001
pkg.devDependencies = {
19992002
...pkg.devDependencies,
2000-
[VITE_PLUS_NAME]: version,
2003+
[VITE_PLUS_NAME]: canonicalVitePlusSpec,
20012004
};
2002-
// Add vitest to devDependencies when a remaining dependency likely peer-depends
2003-
// on vitest (e.g., vitest-browser-svelte). Without this, pnpm resolves the real
2004-
// vitest for peer deps instead of @voidzero-dev/vite-plus-test, causing
2005-
// third-party type augmentations to target the wrong module.
2005+
}
2006+
// Add vitest to devDependencies when a remaining dependency likely peer-depends
2007+
// on vitest (e.g., vitest-browser-svelte). Without this, pnpm resolves the real
2008+
// vitest for peer deps instead of @voidzero-dev/vite-plus-test, causing
2009+
// third-party type augmentations to target the wrong module. Gated by
2010+
// needVitePlus (something actually changed) — a pure normalize pass must not
2011+
// mutate the project beyond the vite-plus spec.
2012+
if (needVitePlus) {
20062013
const installableDeps = {
20072014
...pkg.dependencies,
20082015
...pkg.devDependencies,
@@ -2013,12 +2020,20 @@ export function rewritePackageJson(
20132020
Object.keys(installableDeps).some((name) => name.includes('vitest'))
20142021
) {
20152022
const ver = VITE_PLUS_OVERRIDE_PACKAGES.vitest;
2023+
pkg.devDependencies ??= {};
20162024
pkg.devDependencies.vitest = getCatalogDependencySpec(undefined, ver, supportCatalog);
20172025
}
20182026
}
20192027
return extractedStagedConfig;
20202028
}
20212029

2030+
// Returns true if the spec uses a known protocol prefix (catalog:, workspace:,
2031+
// link:, file:, npm:, github:, git+/git:, http(s)://) and so represents a
2032+
// deliberate user choice that should not be silently rewritten.
2033+
function isProtocolPinnedSpec(spec: string): boolean {
2034+
return /^(catalog:|workspace:|link:|file:|npm:|github:|git[+:]|https?:\/\/)/.test(spec);
2035+
}
2036+
20222037
// Remove the "lint-staged" key from package.json after config has been
20232038
// successfully merged into vite.config.ts.
20242039
function removeLintStagedFromPackageJson(packageJsonPath: string): void {

0 commit comments

Comments
 (0)