From 4e8d7e5ad6999c9339c03ee84385807279617d7b Mon Sep 17 00:00:00 2001 From: Sakana <15715093608@163.com> Date: Wed, 16 Sep 2026 13:28:29 +0800 Subject: [PATCH] fix(create): resolve generator catalogs for unsupported managers Inline the bundled monorepo dependency ranges when scaffolding a generator with a package manager that cannot resolve catalog references. Preserve catalog references for compatible workspace managers. --- .../fixtures/create_generator_npm/.gitignore | 1 + .../create_generator_npm/package.json | 7 ++ .../create_generator_npm/snapshots.toml | 12 +++ .../snapshots/create_generator_npm.md | 43 +++++++++++ .../create_generator_npm/vite.config.ts | 1 + .../__tests__/generator-scaffold.spec.ts | 77 +++++++++++++++++++ .../cli/src/create/templates/generator.ts | 21 +++++ 7 files changed, 162 insertions(+) create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/.gitignore create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/package.json create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/snapshots.toml create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/snapshots/create_generator_npm.md create mode 100644 crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/vite.config.ts create mode 100644 packages/cli/src/create/__tests__/generator-scaffold.spec.ts diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/.gitignore b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/.gitignore new file mode 100644 index 0000000000..c2658d7d1b --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/package.json new file mode 100644 index 0000000000..d7786e6440 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/package.json @@ -0,0 +1,7 @@ +{ + "name": "generator-npm-fixture", + "private": true, + "type": "module", + "packageManager": "npm@12.0.2", + "workspaces": ["tools/*"] +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/snapshots.toml new file mode 100644 index 0000000000..e6d299da5a --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/snapshots.toml @@ -0,0 +1,12 @@ +[[case]] +name = "create_generator_npm" +vp = "local" +local-registry = true +unset-env = ["VP_SKIP_INSTALL", "CI"] +comment = "A generator added to an npm workspace uses installable dependency ranges instead of catalog references." +steps = [ + { argv = ["vp", "create", "vite:generator", "--no-interactive", "--directory", "tools/my-generator"], snapshot = false }, + { argv = ["vpt", "print-file", "tools/my-generator/package.json"] }, + { argv = ["vp", "install"], snapshot = false }, + { argv = ["node", "tools/my-generator/bin/index.ts", "--help"], snapshot = false }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/snapshots/create_generator_npm.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/snapshots/create_generator_npm.md new file mode 100644 index 0000000000..468d2518ee --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/snapshots/create_generator_npm.md @@ -0,0 +1,43 @@ +# create_generator_npm + +A generator added to an npm workspace uses installable dependency ranges instead of catalog references. + +## `vp create vite:generator --no-interactive --directory tools/my-generator` + + +## `vpt print-file tools/my-generator/package.json` + +``` +{ + "name": "my-generator", + "version": "0.0.0", + "private": true, + "description": "A starter for creating a Vite+ code generator.", + "keywords": [ + "vite-plus-generator" + ], + "bin": "./bin/index.ts", + "type": "module", + "scripts": { + "test": "vp test", + "dev": "node bin/index.ts" + }, + "dependencies": { + "bingo": "^0.9.3", + "zod": "^3.25.76" + }, + "devDependencies": { + "@types/node": "^24", + "typescript": "^7.0.0" + }, + "engines": { + "node": ">=22.18.0" + } +} +``` + +## `vp install` + + +## `node tools/my-generator/bin/index.ts --help` + diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/vite.config.ts new file mode 100644 index 0000000000..ff8b4c5632 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_generator_npm/vite.config.ts @@ -0,0 +1 @@ +export default {}; diff --git a/packages/cli/src/create/__tests__/generator-scaffold.spec.ts b/packages/cli/src/create/__tests__/generator-scaffold.spec.ts new file mode 100644 index 0000000000..d1122a84d9 --- /dev/null +++ b/packages/cli/src/create/__tests__/generator-scaffold.spec.ts @@ -0,0 +1,77 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + +import { afterEach, describe, expect, it } from 'vitest'; + +import { PackageManager, type WorkspaceInfo } from '../../types/index.ts'; +import { readJsonFile } from '../../utils/json.ts'; +import { templatesDir } from '../../utils/path.ts'; +import { readYamlFile } from '../../utils/yaml.ts'; +import { executeGeneratorScaffold } from '../templates/generator.ts'; +import { BuiltinTemplate, TemplateType } from '../templates/types.ts'; + +let rootDir: string; +afterEach(() => { + if (rootDir) { + fs.rmSync(rootDir, { recursive: true, force: true }); + } +}); + +async function scaffold(packageManager: PackageManager, version: string) { + rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-generator-scaffold-')); + const workspace: WorkspaceInfo = { + rootDir, + isMonorepo: true, + monorepoScope: '', + workspacePatterns: ['tools/*'], + parentDirs: ['tools'], + packageManager, + packageManagerVersion: version, + downloadPackageManager: { + name: packageManager, + packageName: packageManager, + version, + installDir: '', + binPrefix: '', + }, + packages: [], + }; + await executeGeneratorScaffold( + workspace, + { + command: BuiltinTemplate.generator, + type: TemplateType.builtin, + packageName: 'my-generator', + targetDir: 'tools/my-generator', + interactive: false, + args: [], + envs: {}, + }, + { silent: true }, + ); + return readJsonFile(path.join(rootDir, 'tools/my-generator/package.json')); +} + +describe('generator scaffold dependencies', () => { + it.each([ + [PackageManager.npm, '12.0.2'], + [PackageManager.yarn, '4.9.0'], + [PackageManager.pnpm, '9.4.0'], + ])('uses installable versions for %s %s without catalog support', async (manager, version) => { + const pkg = await scaffold(manager, version); + const { catalog } = readYamlFile(path.join(templatesDir, 'monorepo/pnpm-workspace.yaml')); + expect(pkg.devDependencies).toEqual(catalog); + expect(pkg.name).toBe('my-generator'); + expect(pkg.dependencies).toEqual({ bingo: '^0.9.3', zod: '^3.25.76' }); + }); + + it.each([ + [PackageManager.pnpm, '10.0.0'], + [PackageManager.yarn, '4.10.0'], + [PackageManager.bun, '1.3.0'], + ])('preserves workspace catalogs for %s %s', async (manager, version) => { + const pkg = await scaffold(manager, version); + expect(pkg.devDependencies).toEqual({ '@types/node': 'catalog:', typescript: 'catalog:' }); + }); +}); diff --git a/packages/cli/src/create/templates/generator.ts b/packages/cli/src/create/templates/generator.ts index a87d651db6..64ef108df6 100644 --- a/packages/cli/src/create/templates/generator.ts +++ b/packages/cli/src/create/templates/generator.ts @@ -3,9 +3,11 @@ import path from 'node:path'; import * as prompts from '@voidzero-dev/vite-plus-prompts'; +import { supportsCatalog } from '../../migration/migrator.ts'; import type { WorkspaceInfo } from '../../types/index.ts'; import { editJsonFile } from '../../utils/json.ts'; import { templatesDir } from '../../utils/path.ts'; +import { readYamlFile } from '../../utils/yaml.ts'; import type { ExecutionWithProjectDir } from '../command.ts'; import { copyDir } from '../utils.ts'; import type { BuiltinTemplateInfo } from './types.ts'; @@ -40,6 +42,25 @@ export async function executeGeneratorScaffold( fs.chmodSync(path.join(fullPath, 'bin/index.ts'), '755'); editJsonFile(path.join(fullPath, 'package.json'), (pkg) => { pkg.name = templateInfo.packageName; + if ( + !supportsCatalog( + workspaceInfo.packageManager, + workspaceInfo.downloadPackageManager.version, + workspaceInfo.isMonorepo, + ) + ) { + // The bundled generator uses the monorepo template's catalog. Managers + // without catalog support need the same dependency ranges inlined. + const { catalog } = readYamlFile(path.join(templatesDir, 'monorepo/pnpm-workspace.yaml')) as { + catalog: Record; + }; + const devDependencies = pkg.devDependencies as Record; + for (const [name, spec] of Object.entries(devDependencies)) { + if (spec === 'catalog:' && catalog[name]) { + devDependencies[name] = catalog[name]; + } + } + } if (description) { pkg.description = description; }