diff --git a/packages/cli/.gitignore b/packages/cli/.gitignore index 1ff8aa7bb3..5e1e780c15 100644 --- a/packages/cli/.gitignore +++ b/packages/cli/.gitignore @@ -2,3 +2,4 @@ /artifacts /LICENSE /skills/vite-plus/docs +/test diff --git a/packages/cli/build.ts b/packages/cli/build.ts index c595cadc7d..e5ad56c40f 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -21,7 +21,7 @@ import { execSync } from 'node:child_process'; import { existsSync, globSync, readFileSync, readdirSync, statSync } from 'node:fs'; import { copyFile, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; +import { basename, dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseArgs } from 'node:util'; @@ -391,6 +391,11 @@ async function syncTestPackageExports() { const testPkgPath = join(projectDir, '../test/package.json'); const cliPkgPath = join(projectDir, 'package.json'); const testDistDir = join(projectDir, 'dist/test'); + // Proxy files for TypeScript compilerOptions.types resolution. + // TypeScript resolves "vite-plus/test/globals" via the filesystem directly + // (without consulting package.json exports), so we need actual files at + // test/globals.d.ts that TypeScript can find for type-only exports. + const testTypesDir = join(projectDir, 'test'); // Read test package.json const testPkg = JSON.parse(await readFile(testPkgPath, 'utf-8')); @@ -399,6 +404,8 @@ async function syncTestPackageExports() { // Clean up previous build await rm(testDistDir, { recursive: true, force: true }); await mkdir(testDistDir, { recursive: true }); + await rm(testTypesDir, { recursive: true, force: true }); + await mkdir(testTypesDir, { recursive: true }); const generatedExports: Record = {}; @@ -416,6 +423,24 @@ async function syncTestPackageExports() { if (shimExport) { generatedExports[cliExportPath] = shimExport; console.log(` Created ${cliExportPath}`); + + // For type-only exports, also create a proxy file at test/{name}.d.ts so + // TypeScript can resolve "vite-plus/test/globals" via compilerOptions.types. + // compilerOptions.types uses filesystem resolution (not exports field), so + // it looks for node_modules/vite-plus/test/globals.d.ts directly. + if (isTypeOnlyExport(shimExport)) { + const testImportSpecifier = + exportPath === '.' ? TEST_PACKAGE_NAME : `${TEST_PACKAGE_NAME}${exportPath.slice(1)}`; + const shimBaseName = exportPath === '.' ? 'index' : exportPath.slice(2); + const proxyRelDir = dirname(shimBaseName); + const proxyDir = proxyRelDir === '.' ? testTypesDir : join(testTypesDir, proxyRelDir); + await mkdir(proxyDir, { recursive: true }); + const baseFileName = basename(shimBaseName); + await writeFile( + join(proxyDir, `${baseFileName}.d.ts`), + `/// \n`, + ); + } } } @@ -425,6 +450,14 @@ async function syncTestPackageExports() { console.log(`\nSynced ${Object.keys(generatedExports).length} exports from test package`); } +function isTypeOnlyExport(exportValue: ExportValue): boolean { + if (typeof exportValue === 'string') { + return false; + } + const keys = Object.keys(exportValue); + return keys.length === 1 && keys[0] === 'types'; +} + /** * Copy markdown doc files from the monorepo docs/ directory into skills/vite-plus/docs/, * preserving the relative directory structure. This keeps stable file paths for @@ -713,10 +746,13 @@ async function updateCliPackageJson(pkgPath: string, generatedExports: Record