Skip to content

Commit 56e5f12

Browse files
authored
fix(create): support system Yarn Classic template execution
Use the resolved Yarn version to select the existing npx fallback for Classic, which has no dlx command. Preserve Yarn 2+ template execution and cover runner selection, external scaffolding, and the built-in template version pin.
1 parent 9170fb0 commit 56e5f12

8 files changed

Lines changed: 89 additions & 5 deletions

File tree

crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_system_package_manager/snapshots.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,15 @@ name = "create_missing_system_pnpm"
4040
vp = "global"
4141
skip-platforms = ["windows"]
4242
steps = [["node", "verify.cjs", "pnpm", "missing"]]
43+
44+
[[case]]
45+
name = "create_system_yarn_classic_external"
46+
vp = ["local", "global"]
47+
skip-platforms = ["windows"]
48+
steps = [{ argv = ["node", "verify.cjs", "yarn", "system", "external"], timeout = 180000 }]
49+
50+
[[case]]
51+
name = "create_system_yarn_classic"
52+
vp = ["local", "global"]
53+
skip-platforms = ["windows"]
54+
steps = [["node", "verify.cjs", "yarn", "system", "classic"]]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# create_system_yarn_classic
2+
3+
## `node verify.cjs yarn system classic`
4+
5+
```
6+
yarn: system version pinned
7+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# create_system_yarn_classic
2+
3+
## `node verify.cjs yarn system classic`
4+
5+
```
6+
yarn: system version pinned
7+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# create_system_yarn_classic_external
2+
3+
## `node verify.cjs yarn system external`
4+
5+
```
6+
yarn: external template generated with npx
7+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# create_system_yarn_classic_external
2+
3+
## `node verify.cjs yarn system external`
4+
5+
```
6+
yarn: external template generated with npx
7+
```

crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_system_package_manager/verify.cjs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ const fs = require('node:fs');
33
const path = require('node:path');
44
const { spawnSync } = require('node:child_process');
55

6-
const [manager, mode] = process.argv.slice(2);
7-
const systemVersion = { npm: '10.9.3', pnpm: '10.18.0', yarn: '4.9.2', bun: '1.2.3' }[manager];
6+
const [manager, mode, template] = process.argv.slice(2);
7+
const systemVersion = manager === 'yarn' && (template === 'external' || template === 'classic') ? '1.22.22' : { npm: '10.9.3', pnpm: '10.18.0', yarn: '4.9.2', bun: '1.2.3' }[manager];
88
const managedVersion = manager === 'npm' ? '11.6.0' : '10.18.0';
99
const env = { ...process.env };
1010
const entryVp = env.PATH.split(path.delimiter).map(dir => path.join(dir, 'vp')).find(file => fs.existsSync(file));
@@ -35,8 +35,19 @@ run(['env', mode === 'managed' ? 'on' : 'off', manager]);
3535
const current = JSON.parse(run(['env', 'current', manager, '--json'])).package_manager;
3636
if (mode === 'system') assert.equal(current.source, 'system PATH');
3737
else assert.notEqual(current.source, 'system PATH');
38-
run(['create', 'vite:application', '--directory', 'app', '--package-manager', manager,
39-
'--no-interactive', '--no-agent', '--no-editor', '--no-hooks', '--no-git']);
38+
const templateArgs = template === 'external' ? ['vite'] : ['vite:application', '--directory', 'app'];
39+
const output = run(['create', ...templateArgs, '--package-manager', manager,
40+
'--no-interactive', '--no-agent', '--no-editor', '--no-hooks', '--no-git',
41+
...(template === 'external' ? ['--', 'app', '--template', 'vanilla'] : [])]);
42+
if (template === 'external') {
43+
assert.match(output, /Running: npx --yes create-vite/);
44+
const pkg = JSON.parse(fs.readFileSync('app/package.json', 'utf8'));
45+
assert.equal(pkg.name, 'app');
46+
assert.ok(fs.existsSync('app/index.html'));
47+
assert.equal(current.version, systemVersion);
48+
console.log(`${manager}: external template generated with npx`);
49+
process.exit(0);
50+
}
4051
const pin = JSON.parse(fs.readFileSync('app/package.json', 'utf8')).devEngines.packageManager;
4152
assert.equal(pin.name, manager);
4253
assert.equal(pin.version, mode === 'system' ? systemVersion : managedVersion);

packages/cli/src/create/__tests__/command.spec.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import path from 'node:path';
44

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

7+
import { PackageManager, type WorkspaceInfo } from '../../types/index.ts';
8+
79
const { mockRunCommand } = vi.hoisted(() => ({
810
mockRunCommand: vi.fn(),
911
}));
@@ -12,7 +14,7 @@ vi.mock('../../../binding/index.js', () => ({
1214
runCommand: mockRunCommand,
1315
}));
1416

15-
const { runCommandAndDetectProjectDir } = await import('../command.js');
17+
const { formatDlxCommand, runCommandAndDetectProjectDir } = await import('../command.js');
1618

1719
const tempDirs: string[] = [];
1820

@@ -98,3 +100,30 @@ describe('runCommandAndDetectProjectDir', () => {
98100
});
99101
});
100102
});
103+
104+
describe('formatDlxCommand', () => {
105+
it.each([
106+
['1.22.22', 'npx', ['--yes']],
107+
['2.4.3', 'yarn', ['dlx']],
108+
['4.9.2', 'yarn', ['dlx']],
109+
])('runs templates with Yarn %s', (version, command, runnerArgs) => {
110+
const workspace: WorkspaceInfo = {
111+
rootDir: '/project',
112+
isMonorepo: false,
113+
monorepoScope: '',
114+
workspacePatterns: [],
115+
parentDirs: [],
116+
packageManager: PackageManager.yarn,
117+
// Ambient metadata can differ from the selected system version.
118+
packageManagerVersion: '4.9.2',
119+
downloadPackageManager: { name: 'yarn', version, binPrefix: '/system/bin' },
120+
packages: [],
121+
};
122+
expect(
123+
formatDlxCommand('@example/create-app@1.0.0', ['app', '--template', 'vanilla'], workspace),
124+
).toEqual({
125+
command,
126+
args: [...runnerArgs, '@example/create-app@1.0.0', 'app', '--template', 'vanilla'],
127+
});
128+
});
129+
});

packages/cli/src/create/command.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ export function getPackageRunner(workspaceInfo: WorkspaceInfo) {
9696
args: ['dlx'],
9797
};
9898
case 'yarn':
99+
// Match `vp dlx`: Yarn Classic has no dlx command.
100+
if (workspaceInfo.downloadPackageManager.version.startsWith('1.')) {
101+
return { command: 'npx', args: ['--yes'] };
102+
}
99103
return {
100104
command: 'yarn',
101105
args: ['dlx'],

0 commit comments

Comments
 (0)