Skip to content

Commit c205ed7

Browse files
committed
feat(create): add --package-manager flag and bun catalog support
Add `--package-manager` option to `vp create` for specifying the package manager without interactive prompts. Priority: workspace detection > CLI flag > interactive prompt/default. Fix bun catalog support in monorepo migration: - Enable `catalog:` protocol for bun (was incorrectly excluded) - Add `rewriteBunCatalog()` to write catalog entries to root package.json (bun stores catalogs in package.json, not pnpm-workspace.yaml) - Bun overrides use raw versions (catalog: not supported in overrides) Add `new-vite-monorepo-bun` snap test verifying: - Root package.json has catalog, workspaces, and overrides fields - Sub-packages use `catalog:` references for dependencies - No pnpm-workspace.yaml or .yarnrc.yml files created
1 parent c2741b2 commit c205ed7

4 files changed

Lines changed: 148 additions & 5 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
> vp create vite:monorepo --no-interactive --package-manager bun # create monorepo with bun
2+
> ls vite-plus-monorepo | LC_ALL=C sort # check files created
3+
AGENTS.md
4+
README.md
5+
apps
6+
package.json
7+
packages
8+
tsconfig.json
9+
vite.config.ts
10+
11+
> cat vite-plus-monorepo/package.json # check package.json with catalog
12+
{
13+
"name": "vite-plus-monorepo",
14+
"version": "0.0.0",
15+
"private": true,
16+
"workspaces": [
17+
"packages/*",
18+
"apps/*",
19+
"tools/*"
20+
],
21+
"type": "module",
22+
"scripts": {
23+
"ready": "vp fmt && vp lint && vp run test -r && vp run build -r",
24+
"dev": "vp run website#dev",
25+
"prepare": "vp config"
26+
},
27+
"devDependencies": {
28+
"vite-plus": "catalog:"
29+
},
30+
"overrides": {
31+
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
32+
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
33+
},
34+
"engines": {
35+
"node": ">=22.12.0"
36+
},
37+
"packageManager": "bun@<semver>",
38+
"catalog": {
39+
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
40+
"vitest": "npm:@voidzero-dev/vite-plus-test@latest",
41+
"vite-plus": "latest"
42+
}
43+
}
44+
45+
> test ! -f vite-plus-monorepo/pnpm-workspace.yaml && echo 'No pnpm-workspace.yaml' || echo 'ERROR: pnpm-workspace.yaml exists' # verify no pnpm config
46+
No pnpm-workspace.yaml
47+
48+
> test ! -f vite-plus-monorepo/.yarnrc.yml && echo 'No .yarnrc.yml' || echo 'ERROR: .yarnrc.yml exists' # verify no yarn config
49+
No .yarnrc.yml
50+
51+
> test -d vite-plus-monorepo/.git && echo 'Git initialized' || echo 'No git' # check git init
52+
Git initialized
53+
54+
> ls vite-plus-monorepo/apps # check apps directory
55+
website
56+
57+
> cat vite-plus-monorepo/apps/website/package.json # check website uses catalog:
58+
{
59+
"name": "website",
60+
"version": "0.0.0",
61+
"private": true,
62+
"type": "module",
63+
"scripts": {
64+
"dev": "vp dev",
65+
"build": "tsc && vp build",
66+
"preview": "vp preview"
67+
},
68+
"devDependencies": {
69+
"typescript": "~5.9.3",
70+
"vite": "catalog:",
71+
"vite-plus": "catalog:"
72+
}
73+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"ignoredPlatforms": ["win32"],
3+
"commands": [
4+
{
5+
"command": "vp create vite:monorepo --no-interactive --package-manager bun # create monorepo with bun",
6+
"ignoreOutput": true
7+
},
8+
"ls vite-plus-monorepo | LC_ALL=C sort # check files created",
9+
"cat vite-plus-monorepo/package.json # check package.json with catalog",
10+
"test ! -f vite-plus-monorepo/pnpm-workspace.yaml && echo 'No pnpm-workspace.yaml' || echo 'ERROR: pnpm-workspace.yaml exists' # verify no pnpm config",
11+
"test ! -f vite-plus-monorepo/.yarnrc.yml && echo 'No .yarnrc.yml' || echo 'ERROR: .yarnrc.yml exists' # verify no yarn config",
12+
"test -d vite-plus-monorepo/.git && echo 'Git initialized' || echo 'No git' # check git init",
13+
"ls vite-plus-monorepo/apps # check apps directory",
14+
"cat vite-plus-monorepo/apps/website/package.json # check website uses catalog:"
15+
]
16+
}

packages/cli/src/create/bin.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
rewriteMonorepoProject,
1212
rewriteStandaloneProject,
1313
} from '../migration/migrator.js';
14-
import { DependencyType, type WorkspaceInfo } from '../types/index.js';
14+
import { DependencyType, type PackageManager, type WorkspaceInfo } from '../types/index.js';
1515
import {
1616
detectExistingAgentTargetPaths,
1717
selectAgentTargetPaths,
@@ -91,6 +91,10 @@ const helpMessage = renderCliDoc({
9191
description: 'Set up pre-commit hooks (default in non-interactive mode)',
9292
},
9393
{ label: '--no-hooks', description: 'Skip pre-commit hooks setup' },
94+
{
95+
label: '--package-manager NAME',
96+
description: 'Use specified package manager (pnpm, npm, yarn, bun)',
97+
},
9498
{ label: '--verbose', description: 'Show detailed scaffolding output' },
9599
{ label: '--no-interactive', description: 'Run in non-interactive mode' },
96100
{ label: '--list', description: 'List all available templates' },
@@ -185,6 +189,7 @@ export interface Options {
185189
agent?: string | string[] | false;
186190
editor?: string;
187191
hooks?: boolean;
192+
packageManager?: string;
188193
}
189194

190195
// Parse CLI arguments: split on '--' separator
@@ -207,10 +212,11 @@ function parseArgs() {
207212
agent?: string | string[] | false;
208213
editor?: string;
209214
hooks?: boolean;
215+
'package-manager'?: string;
210216
}>(viteArgs, {
211217
alias: { h: 'help' },
212218
boolean: ['help', 'list', 'all', 'interactive', 'hooks', 'verbose'],
213-
string: ['directory', 'agent', 'editor'],
219+
string: ['directory', 'agent', 'editor', 'package-manager'],
214220
default: { interactive: defaultInteractive() },
215221
});
216222

@@ -227,6 +233,7 @@ function parseArgs() {
227233
agent: parsed.agent,
228234
editor: parsed.editor,
229235
hooks: parsed.hooks,
236+
packageManager: parsed['package-manager'],
230237
} as Options,
231238
templateArgs,
232239
};
@@ -624,9 +631,10 @@ Use \`vp create --list\` to list all available templates, or run \`vp create --h
624631
}
625632
}
626633

627-
// Prompt for package manager or use default
634+
// Resolve package manager: workspace detection > CLI flag > interactive prompt/default
628635
const packageManager =
629636
workspaceInfoOptional.packageManager ??
637+
(options.packageManager as PackageManager | undefined) ??
630638
(await selectPackageManager(options.interactive, compactOutput));
631639
const shouldSilencePackageManagerInstallLog =
632640
compactOutput || (isMonorepo && workspaceInfoOptional.packageManager !== undefined);

packages/cli/src/migration/migrator.ts

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,8 @@ export function rewriteMonorepo(
748748
rewritePnpmWorkspaceYaml(workspaceInfo.rootDir);
749749
} else if (workspaceInfo.packageManager === PackageManager.yarn) {
750750
rewriteYarnrcYml(workspaceInfo.rootDir);
751+
} else if (workspaceInfo.packageManager === PackageManager.bun) {
752+
rewriteBunCatalog(workspaceInfo.rootDir);
751753
}
752754
rewriteRootWorkspacePackageJson(
753755
workspaceInfo.rootDir,
@@ -948,6 +950,51 @@ function rewriteCatalog(doc: YamlDocument): void {
948950
// TODO: rewrite `catalogs` when OVERRIDE_PACKAGES exists in catalog
949951
}
950952

953+
/**
954+
* Write catalog entries to root package.json for bun.
955+
* Bun stores catalogs in package.json under the `catalog` key,
956+
* unlike pnpm which uses pnpm-workspace.yaml.
957+
* @see https://bun.sh/docs/pm/catalogs
958+
*/
959+
function rewriteBunCatalog(projectPath: string): void {
960+
const packageJsonPath = path.join(projectPath, 'package.json');
961+
if (!fs.existsSync(packageJsonPath)) {
962+
return;
963+
}
964+
965+
editJsonFile<{
966+
catalog?: Record<string, string>;
967+
overrides?: Record<string, string>;
968+
}>(packageJsonPath, (pkg) => {
969+
const catalog: Record<string, string> = { ...pkg.catalog };
970+
971+
// Add vite-plus managed packages to catalog
972+
for (const [key, value] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) {
973+
if (!value.startsWith('file:')) {
974+
catalog[key] = value;
975+
}
976+
}
977+
if (!VITE_PLUS_VERSION.startsWith('file:')) {
978+
catalog[VITE_PLUS_NAME] = VITE_PLUS_VERSION;
979+
}
980+
981+
// Remove replaced packages from catalog
982+
for (const name of REMOVE_PACKAGES) {
983+
delete catalog[name];
984+
}
985+
986+
pkg.catalog = catalog;
987+
988+
// bun uses overrides in package.json (catalog: references are NOT supported in overrides)
989+
pkg.overrides = {
990+
...pkg.overrides,
991+
...VITE_PLUS_OVERRIDE_PACKAGES,
992+
};
993+
994+
return pkg;
995+
});
996+
}
997+
951998
/**
952999
* Rewrite root workspace package.json to add vite-plus dependencies
9531000
* @param projectPath - The path to the project
@@ -1093,8 +1140,7 @@ export function rewritePackageJson(
10931140
const updated = rewriteScripts(JSON.stringify(config), readRulesYaml());
10941141
extractedStagedConfig = updated ? JSON.parse(updated) : config;
10951142
}
1096-
const supportCatalog =
1097-
isMonorepo && packageManager !== PackageManager.npm && packageManager !== PackageManager.bun;
1143+
const supportCatalog = isMonorepo && packageManager !== PackageManager.npm;
10981144
let needVitePlus = false;
10991145
for (const [key, version] of Object.entries(VITE_PLUS_OVERRIDE_PACKAGES)) {
11001146
const value = supportCatalog && !version.startsWith('file:') ? 'catalog:' : version;

0 commit comments

Comments
 (0)