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
8 changes: 8 additions & 0 deletions packages/cli/src/create/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,8 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
updateCreateProgress('Installing dependencies');
const installSummary = await runViteInstall(fullPath, options.interactive, installArgs, {
silent: compactOutput,
packageManager: workspaceInfo.packageManager,
packageManagerVersion: workspaceInfo.downloadPackageManager.version,
});
updateCreateProgress('Formatting code');
await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput });
Expand Down Expand Up @@ -1038,6 +1040,8 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
updateCreateProgress('Installing dependencies');
installSummary = await runViteInstall(installCwd, options.interactive, installArgs, {
silent: compactOutput,
packageManager: workspaceInfo.packageManager,
packageManagerVersion: workspaceInfo.downloadPackageManager.version,
});
if (installSummary.status !== 'installed') {
return;
Expand Down Expand Up @@ -1126,6 +1130,8 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
updateCreateProgress('Installing dependencies');
installSummary = await runViteInstall(workspaceInfo.rootDir, options.interactive, installArgs, {
silent: compactOutput,
packageManager: workspaceInfo.packageManager,
packageManagerVersion: workspaceInfo.downloadPackageManager.version,
});
updateCreateProgress('Formatting code');
await runViteFmt(workspaceInfo.rootDir, options.interactive, [projectDir], {
Expand All @@ -1148,6 +1154,8 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
updateCreateProgress('Installing dependencies');
installSummary = await runViteInstall(fullPath, options.interactive, installArgs, {
silent: compactOutput,
packageManager: workspaceInfo.packageManager,
packageManagerVersion: workspaceInfo.downloadPackageManager.version,
});
updateCreateProgress('Formatting code');
await runViteFmt(fullPath, options.interactive, undefined, { silent: compactOutput });
Expand Down
26 changes: 25 additions & 1 deletion packages/cli/src/migration/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,8 @@ async function executeMigrationPlan(
undefined,
{
silent: true,
packageManager: workspaceInfo.packageManager,
packageManagerVersion: workspaceInfo.downloadPackageManager.version,
},
);

Expand Down Expand Up @@ -778,7 +780,11 @@ async function executeMigrationPlan(
workspaceInfo.rootDir,
interactive,
installArgs,
{ silent: true },
{
silent: true,
packageManager: workspaceInfo.packageManager,
packageManagerVersion: workspaceInfo.downloadPackageManager.version,
},
);

clearMigrationProgress();
Expand Down Expand Up @@ -868,12 +874,30 @@ async function main() {
updateMigrationProgress('Rewriting configs');
mergeViteConfigFiles(workspaceInfoOptional.rootDir, true, report);
updateMigrationProgress('Installing dependencies');
// Resolve the actual pnpm version that `vp install` will use so the
// auto-install can opt into `--ignore-scripts` on pnpm v11 (which fails
// unapproved build scripts with `ERR_PNPM_IGNORED_BUILDS`).
let resolvedVersion = workspaceInfoOptional.packageManagerVersion;
if (
workspaceInfoOptional.packageManager &&
!semver.valid(semver.coerce(resolvedVersion) ?? '')
) {
const resolved = await downloadPackageManager(
workspaceInfoOptional.packageManager,
resolvedVersion,
options.interactive,
true,
);
resolvedVersion = resolved.version;
}
const installSummary = await runViteInstall(
workspaceInfoOptional.rootDir,
options.interactive,
undefined,
{
silent: true,
packageManager: workspaceInfoOptional.packageManager,
packageManagerVersion: resolvedVersion,
},
);
installDurationMs += installSummary.durationMs;
Expand Down
39 changes: 39 additions & 0 deletions packages/cli/src/utils/__tests__/prompts.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, expect, it } from 'vitest';

import { PackageManager } from '../../types/index.ts';
import { shouldIgnoreScriptsForAutoInstall } from '../prompts.ts';

describe('shouldIgnoreScriptsForAutoInstall', () => {
it('returns true for pnpm >= 11.0.0', () => {
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, '11.0.0')).toBe(true);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, '11.0.8')).toBe(true);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, '12.0.0')).toBe(true);
});

it('returns false for pnpm < 11.0.0', () => {
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, '10.33.2')).toBe(false);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, '9.5.0')).toBe(false);
});

it('returns false for non-pnpm package managers', () => {
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.npm, '11.0.0')).toBe(false);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.yarn, '11.0.0')).toBe(false);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.bun, '11.0.0')).toBe(false);
});

it('returns false when version is unknown or unparsable', () => {
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, undefined)).toBe(false);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, 'latest')).toBe(false);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, '')).toBe(false);
});

it('returns false when packageManager is undefined', () => {
expect(shouldIgnoreScriptsForAutoInstall(undefined, '11.0.0')).toBe(false);
});

it('coerces non-strict semver pnpm versions', () => {
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, 'v11.0.0')).toBe(true);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, '11')).toBe(true);
expect(shouldIgnoreScriptsForAutoInstall(PackageManager.pnpm, '10')).toBe(false);
});
});
39 changes: 37 additions & 2 deletions packages/cli/src/utils/prompts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as prompts from '@voidzero-dev/vite-plus-prompts';
import semver from 'semver';

import { downloadPackageManager as downloadPackageManagerBinding } from '../../binding/index.js';
import { PackageManager } from '../types/index.ts';
Expand All @@ -11,6 +12,28 @@ export interface CommandRunSummary {
status: 'installed' | 'formatted' | 'failed' | 'skipped';
}

/**
* pnpm v11 promoted `ERR_PNPM_IGNORED_BUILDS` from a warning to a hard
* exit-1. Auto-installs run by `vp migrate` / `vp create` happen before the
* user has a chance to approve build scripts via `pnpm.onlyBuiltDependencies`,
* so transitive deps like `esbuild` would fail the install. Pass
* `--ignore-scripts` in that window so the orchestration succeeds; the user's
* own subsequent `vp install` keeps default pnpm behavior.
*/
export function shouldIgnoreScriptsForAutoInstall(
packageManager: PackageManager | undefined,
packageManagerVersion: string | undefined,
): boolean {
if (packageManager !== PackageManager.pnpm) {
return false;
}
const coerced = packageManagerVersion ? semver.coerce(packageManagerVersion)?.version : undefined;
if (!coerced) {
return false;
}
return semver.gte(coerced, '11.0.0');
}

export function cancelAndExit(message = 'Operation cancelled', exitCode = 0): never {
prompts.cancel(message);
process.exit(exitCode);
Expand Down Expand Up @@ -63,19 +86,31 @@ export async function runViteInstall(
cwd: string,
interactive?: boolean,
extraArgs?: string[],
options?: { silent?: boolean },
options?: {
silent?: boolean;
packageManager?: PackageManager;
packageManagerVersion?: string;
},
) {
// install dependencies on non-CI environment
if (process.env.VP_SKIP_INSTALL) {
return { durationMs: 0, status: 'skipped' } satisfies CommandRunSummary;
}

const installArgs = [...(extraArgs ?? [])];
if (
shouldIgnoreScriptsForAutoInstall(options?.packageManager, options?.packageManagerVersion) &&
!installArgs.includes('--ignore-scripts')
) {
installArgs.push('--ignore-scripts');
}

const spinner = options?.silent ? getSilentSpinner() : getSpinner(interactive);
const startTime = Date.now();
spinner.start(`Installing dependencies...`);
const { exitCode, stderr, stdout } = await runCommandSilently({
command: process.env.VP_CLI_BIN ?? 'vp',
args: ['install', ...(extraArgs ?? [])],
args: ['install', ...installArgs],
cwd,
envs: process.env,
});
Expand Down
Loading