diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/assert-version.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/assert-version.mjs new file mode 100644 index 0000000000..63cbd7ca27 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/assert-version.mjs @@ -0,0 +1,9 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; + +const read = (file) => JSON.parse(fs.readFileSync(file, 'utf8')); +const pkg = read('library/package.json'); +const version = pkg.overrides.vite.replace('npm:@voidzero-dev/vite-plus-core@', ''); +assert.equal(pkg.devDependencies['vite-plus'], version); +assert.equal(read('library/node_modules/vite-plus/package.json').version, version); +console.log('Library dependency and installed Vite+ match the toolchain override.'); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/snapshots.toml new file mode 100644 index 0000000000..6b83380e03 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/snapshots.toml @@ -0,0 +1,10 @@ +[[case]] +name = "create_library_npm" +vp = "local" +local-registry = true +unset-env = ["VP_SKIP_INSTALL", "CI"] +comment = "An npm library uses the creating CLI's version for both vite-plus and the Vite override." +steps = [ + { argv = ["vp", "create", "vite:library", "--directory", "library", "--package-manager", "npm", "--no-interactive", "--no-git", "--no-hooks", "--no-agent", "--no-editor"], snapshot = false, timeout = 120000 }, + { argv = ["node", "assert-version.mjs"] }, +] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/snapshots/create_library_npm.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/snapshots/create_library_npm.md new file mode 100644 index 0000000000..aacf62e9bc --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/create_library_npm/snapshots/create_library_npm.md @@ -0,0 +1,12 @@ +# create_library_npm + +An npm library uses the creating CLI's version for both vite-plus and the Vite override. + +## `vp create vite:library --directory library --package-manager npm --no-interactive --no-git --no-hooks --no-agent --no-editor` + + +## `node assert-version.mjs` + +``` +Library dependency and installed Vite+ match the toolchain override. +``` diff --git a/packages/cli/src/create/__tests__/builtin.spec.ts b/packages/cli/src/create/__tests__/builtin.spec.ts index d63370f272..3d29867546 100644 --- a/packages/cli/src/create/__tests__/builtin.spec.ts +++ b/packages/cli/src/create/__tests__/builtin.spec.ts @@ -1,5 +1,11 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; + import { describe, expect, it, vi } from 'vitest'; +import { VITE_PLUS_VERSION } from '../../utils/constants.ts'; +import { readJsonFile } from '../../utils/json.ts'; import { executeBuiltinTemplate } from '../templates/builtin.js'; const { mockLogError } = vi.hoisted(() => ({ mockLogError: vi.fn() })); @@ -64,3 +70,50 @@ describe('executeBuiltinTemplate', () => { expect(mockLogError).not.toHaveBeenCalled(); }); }); + +describe('builtin library toolchain version', () => { + it.each([false, true])( + 'aligns the downloaded template with the CLI (monorepo: %s)', + async (isMonorepo) => { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vp-library-template-')); + try { + const { runRemoteTemplateCommand } = await import('../templates/remote.js'); + vi.mocked(runRemoteTemplateCommand).mockImplementationOnce(async () => { + const dir = path.join(rootDir, baseTemplateInfo.targetDir); + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync( + path.join(dir, 'package.json'), + JSON.stringify({ + name: 'template-name', + devDependencies: { 'vite-plus': '^0.2.4', typescript: '^7.0.2' }, + peerDependencies: { react: '^19' }, + }), + ); + return { exitCode: 0 }; + }); + const result = await executeBuiltinTemplate( + { + ...workspaceInfo, + rootDir, + isMonorepo, + parentDirs: [], + packages: [], + downloadPackageManager: { binPrefix: '' }, + }, + { ...baseTemplateInfo, command: 'vite:library' }, + { silent: true }, + ); + expect(result.exitCode).toBe(0); + expect( + readJsonFile(path.join(rootDir, baseTemplateInfo.targetDir, 'package.json')), + ).toEqual({ + name: baseTemplateInfo.packageName, + devDependencies: { 'vite-plus': VITE_PLUS_VERSION, typescript: '^7.0.2' }, + peerDependencies: { react: '^19' }, + }); + } finally { + fs.rmSync(rootDir, { recursive: true, force: true }); + } + }, + ); +}); diff --git a/packages/cli/src/create/templates/builtin.ts b/packages/cli/src/create/templates/builtin.ts index 3766f65bc3..caed4fc064 100644 --- a/packages/cli/src/create/templates/builtin.ts +++ b/packages/cli/src/create/templates/builtin.ts @@ -5,6 +5,8 @@ import * as prompts from '@voidzero-dev/vite-plus-prompts'; import colors from 'picocolors'; import type { WorkspaceInfo } from '../../types/index.ts'; +import { VITE_PLUS_NAME, VITE_PLUS_VERSION } from '../../utils/constants.ts'; +import { editJsonFile } from '../../utils/json.ts'; import type { ExecutionWithProjectDir } from '../command.ts'; import { discoverTemplate } from '../discovery.ts'; import { setPackageName } from '../utils.ts'; @@ -49,6 +51,16 @@ export async function executeBuiltinTemplate( } const fullPath = path.join(workspaceInfo.rootDir, templateInfo.targetDir); setPackageName(fullPath, templateInfo.packageName); + // The remote template can lag behind the CLI. Align a newly scaffolded + // library before project setup injects this CLI's toolchain overrides. + editJsonFile<{ devDependencies?: Record }>( + path.join(fullPath, 'package.json'), + (pkg) => { + pkg.devDependencies ??= {}; + pkg.devDependencies[VITE_PLUS_NAME] = VITE_PLUS_VERSION; + return pkg; + }, + ); return { ...result, projectDir: templateInfo.targetDir }; }