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
24 changes: 19 additions & 5 deletions .github/workflows/test-vp-create.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,13 @@ jobs:
# Issue 2: apps/website is scaffolded by create-vite which ships
# `vite` (and sometimes `vitest`) in devDependencies. After
# migration the scripts are rewritten to `vp ...` and `vite-plus`
# brings the runtime in transitively, so those keys must be gone.
# brings the runtime in transitively. For npm/yarn/bun those keys
# are dropped (the root overrides/resolutions redirect the
# transitive/peer vite to @voidzero-dev/vite-plus-core regardless).
# For pnpm the aliased `vite` is kept on purpose: pnpm only surfaces
# the pnpm-workspace.yaml `overrides.vite: catalog:` entry through a
# package that directly depends on `vite`, so dropping it would make
# `vp why vite` report upstream vite and the override look ineffective.
node -e "
const fs = require('fs');
const pm = process.env.PACKAGE_MANAGER;
Expand All @@ -303,13 +309,21 @@ jobs:
const appDev = app.devDependencies || {};
const utilsDev = utils.devDependencies || {};

for (const name of ['vite', 'vitest']) {
if (appDev[name]) {
console.error('✗ apps/website devDependencies still has ' + name + ': ' + appDev[name]);
if (pm === 'pnpm') {
if (!appDev['vite']) {
console.error('✗ pnpm apps/website should keep aliased vite so the workspace override stays effective');
process.exit(1);
}
console.log('✓ pnpm apps/website keeps aliased vite');
} else {
for (const name of ['vite', 'vitest']) {
if (appDev[name]) {
console.error('✗ apps/website devDependencies still has ' + name + ': ' + appDev[name]);
process.exit(1);
}
}
console.log('✓ apps/website devDependencies has no vite/vitest');
}
console.log('✓ apps/website devDependencies has no vite/vitest');

if (!appDev['vite-plus']) {
console.error('✗ apps/website missing vite-plus devDependency');
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/migrate.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ After the migration:

- Confirm `vite` imports were rewritten to `vite-plus` where needed
- Confirm `vitest` imports were rewritten to `vite-plus/test` where needed
- Remove old `vite` and `vitest` dependencies only after those rewrites are confirmed
- On pnpm, keep the `vite` / `vitest` entries that `vp migrate` aliased to the Vite+ packages so the workspace override stays effective; with other package managers you can remove them once those rewrites are confirmed
- Move remaining tool-specific config into the appropriate blocks in `vite.config.ts`

Command mapping to keep in mind:
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/snap-tests-global/migration-check/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ Migration Prompt:
After the migration:
- Confirm `vite` imports were rewritten to `vite-plus` where needed
- Confirm `vitest` imports were rewritten to `vite-plus/test` where needed
- Remove old `vite` and `vitest` dependencies only after those rewrites
are confirmed
- On pnpm, keep the `vite` / `vitest` entries that `vp migrate` aliased to
the Vite+ packages so the workspace override stays effective; with other
package managers you can remove them once those rewrites are confirmed
- Move remaining tool-specific config into the appropriate blocks in
`vite.config.ts`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ Migration Prompt:
After the migration:
- Confirm `vite` imports were rewritten to `vite-plus` where needed
- Confirm `vitest` imports were rewritten to `vite-plus/test` where needed
- Remove old `vite` and `vitest` dependencies only after those rewrites
are confirmed
- On pnpm, keep the `vite` / `vitest` entries that `vp migrate` aliased to
the Vite+ packages so the workspace override stays effective; with other
package managers you can remove them once those rewrites are confirmed
- Move remaining tool-specific config into the appropriate blocks in
`vite.config.ts`

Expand Down
3 changes: 2 additions & 1 deletion packages/cli/snap-tests-global/new-vite-monorepo/snap.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Git initialized
> ls vite-plus-monorepo/apps # check apps directory created
website

> cat vite-plus-monorepo/apps/website/package.json # check website strips aliased vite/vitest
> cat vite-plus-monorepo/apps/website/package.json # check website keeps aliased vite for pnpm (workspace override stays effective)
{
"name": "website",
"version": "0.0.0",
Expand All @@ -109,6 +109,7 @@ website
},
"devDependencies": {
"typescript": "~6.0.2",
"vite": "catalog:",
"vite-plus": "catalog:"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"node -e \"const { spawnSync } = require('node:child_process'); const file = '.vscode/settings.json'; const result = spawnSync('git', ['-C', 'vite-plus-monorepo', 'check-ignore', '--no-index', file], { stdio: 'ignore' }); const status = result.status; if (status === 1) { console.log(file + ' trackable'); } else { console.log(status === 0 ? 'ERROR: ' + file + ' ignored' : 'ERROR: git check-ignore failed with status ' + status); process.exit(status || 1); }\" # check VS Code settings are trackable",
"node -e \"const { spawnSync } = require('node:child_process'); const file = '.vscode/extensions.json'; const result = spawnSync('git', ['-C', 'vite-plus-monorepo', 'check-ignore', '--no-index', file], { stdio: 'ignore' }); const status = result.status; if (status === 1) { console.log(file + ' trackable'); } else { console.log(status === 0 ? 'ERROR: ' + file + ' ignored' : 'ERROR: git check-ignore failed with status ' + status); process.exit(status || 1); }\" # check VS Code extensions are trackable",
"ls vite-plus-monorepo/apps # check apps directory created",
"cat vite-plus-monorepo/apps/website/package.json # check website strips aliased vite/vitest",
"cat vite-plus-monorepo/apps/website/package.json # check website keeps aliased vite for pnpm (workspace override stays effective)",
"cat vite-plus-monorepo/packages/utils/package.json # check utils normalizes vite-plus to catalog:",
{
"command": "cd vite-plus-monorepo && vp create --no-interactive vite:application # create application in non-interactive mode",
Expand Down
77 changes: 77 additions & 0 deletions packages/cli/src/create/__tests__/monorepo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';

import { afterEach, beforeEach, describe, expect, it } from 'vitest';

import { PackageManager } from '../../types/index.js';
import { dropAliasedRuntimeDevDeps } from '../templates/monorepo.js';

describe('dropAliasedRuntimeDevDeps', () => {
let tmpDir: string;

beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-monorepo-strip-'));
});

afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});

function writeWebsitePackageJson(devDependencies: Record<string, string>): void {
fs.writeFileSync(
path.join(tmpDir, 'package.json'),
JSON.stringify({ name: 'website', private: true, devDependencies }, null, 2),
);
}

function readDevDependencies(): Record<string, string> {
const pkg = JSON.parse(fs.readFileSync(path.join(tmpDir, 'package.json'), 'utf8')) as {
devDependencies?: Record<string, string>;
};
return pkg.devDependencies ?? {};
}

// Regression test for "vp why vite reports the override as ineffective" in a
// freshly created pnpm monorepo: pnpm only surfaces the pnpm-workspace.yaml
// `overrides` through a package that directly depends on `vite`/`vitest`, so
// the aliased (catalog:) devDeps must survive for the override to be
// observable. Dropping them leaves `vite` resolving to upstream vite instead
// of @voidzero-dev/vite-plus-core.
it('keeps aliased vite/vitest for pnpm so the workspace override stays effective', () => {
writeWebsitePackageJson({
vite: 'catalog:',
vitest: 'catalog:',
'vite-plus': 'catalog:',
typescript: '~6.0.2',
});

dropAliasedRuntimeDevDeps(tmpDir, PackageManager.pnpm);

const devDependencies = readDevDependencies();
expect(devDependencies.vite).toBe('catalog:');
expect(devDependencies.vitest).toBe('catalog:');
expect(devDependencies['vite-plus']).toBe('catalog:');
});

// npm/yarn/bun redirect the transitive/peer `vite` to
// @voidzero-dev/vite-plus-core via root overrides/resolutions regardless of a
// direct dependency, so the aliased keys are dead weight and stay dropped.
for (const packageManager of [PackageManager.npm, PackageManager.yarn, PackageManager.bun]) {
it(`drops aliased vite/vitest for ${packageManager}`, () => {
writeWebsitePackageJson({
vite: 'npm:@voidzero-dev/vite-plus-core@latest',
vitest: 'npm:@voidzero-dev/vite-plus-test@latest',
'vite-plus': 'latest',
typescript: '~6.0.2',
});

dropAliasedRuntimeDevDeps(tmpDir, packageManager);

const devDependencies = readDevDependencies();
expect(devDependencies.vite).toBeUndefined();
expect(devDependencies.vitest).toBeUndefined();
expect(devDependencies['vite-plus']).toBe('latest');
});
}
});
56 changes: 40 additions & 16 deletions packages/cli/src/create/templates/monorepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,22 +123,9 @@ export async function executeMonorepoTemplate(
undefined,
options?.silent ?? false,
);
// Drop the aliased vite/vitest devDeps left by the migrator: the
// create-vite scripts were rewritten to `vp ...`, and the fresh
// template has no user `import 'vite'`, so vite-plus brings them in.
editJsonFile<{ devDependencies?: Record<string, string> }>(
path.join(appProjectPath, 'package.json'),
(pkg) => {
let changed = false;
for (const name of ['vite', 'vitest']) {
if (pkg.devDependencies?.[name]) {
delete pkg.devDependencies[name];
changed = true;
}
}
return changed ? pkg : undefined;
},
);
// Drop the migrator's aliased vite/vitest devDeps for npm/yarn/bun (pnpm
// keeps them so its workspace override stays effective; see the helper).
dropAliasedRuntimeDevDeps(appProjectPath, workspaceInfo.packageManager);

// Automatically create a default library in packages/utils
if (!options?.silent) {
Expand Down Expand Up @@ -174,6 +161,43 @@ export async function executeMonorepoTemplate(
return { exitCode: 0, projectDir: templateInfo.targetDir };
}

/**
* Drop the aliased `vite` / `vitest` devDeps that `create-vite` leaves on a
* scaffolded sub-package. After migration its scripts already use `vp ...` and
* nothing imports `'vite'` directly, so `vite-plus` provides them transitively.
*
* pnpm is the exception and keeps them: pnpm only surfaces the
* pnpm-workspace.yaml `overrides.vite: catalog:` entry through a package that
* directly depends on `vite`, so keeping the aliased devDep lets `vp why vite`
* reflect the override (resolving to @voidzero-dev/vite-plus-core). npm, yarn,
* and bun redirect the transitive/peer vite via their root
* overrides/resolutions regardless of a direct dep, so the aliased keys are
* dead weight and are dropped.
*/
export function dropAliasedRuntimeDevDeps(
appProjectPath: string,
packageManager: PackageManager,
): void {
// pnpm keeps the aliased vite/vitest so the pnpm-workspace.yaml override has
// a direct consumer to redirect; see the doc comment above.
if (packageManager === PackageManager.pnpm) {
return;
}
editJsonFile<{ devDependencies?: Record<string, string> }>(
path.join(appProjectPath, 'package.json'),
(pkg) => {
let changed = false;
for (const name of ['vite', 'vitest']) {
if (pkg.devDependencies?.[name]) {
delete pkg.devDependencies[name];
changed = true;
}
}
return changed ? pkg : undefined;
},
);
}

function getScopeFromPackageName(packageName: string) {
if (packageName.startsWith('@')) {
return packageName.split('/')[0];
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/migration/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,9 @@ const helpMessage = renderCliDoc({
' After the migration:',
' - Confirm `vite` imports were rewritten to `vite-plus` where needed',
' - Confirm `vitest` imports were rewritten to `vite-plus/test` where needed',
' - Remove old `vite` and `vitest` dependencies only after those rewrites',
' are confirmed',
' - On pnpm, keep the `vite` / `vitest` entries that `vp migrate` aliased to',
' the Vite+ packages so the workspace override stays effective; with other',
' package managers you can remove them once those rewrites are confirmed',
' - Move remaining tool-specific config into the appropriate blocks in',
' `vite.config.ts`',
'',
Expand Down
Loading