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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "generator-npm-fixture",
"private": true,
"type": "module",
"packageManager": "npm@12.0.2",
"workspaces": ["tools/*"]
}
Original file line number Diff line number Diff line change
@@ -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 },
]
Original file line number Diff line number Diff line change
@@ -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`

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default {};
77 changes: 77 additions & 0 deletions packages/cli/src/create/__tests__/generator-scaffold.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, 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:' });
});
});
21 changes: 21 additions & 0 deletions packages/cli/src/create/templates/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<string, string>;
};
const devDependencies = pkg.devDependencies as Record<string, string>;
for (const [name, spec] of Object.entries(devDependencies)) {
if (spec === 'catalog:' && catalog[name]) {
devDependencies[name] = catalog[name];
}
}
}
if (description) {
pkg.description = description;
}
Expand Down
Loading