From e7b2cba3ae38467b005b1a60d88e06f5ce9ba88a Mon Sep 17 00:00:00 2001 From: LongYinan Date: Thu, 25 Dec 2025 23:13:14 +0800 Subject: [PATCH 1/9] feat(cli): align vite exports with upstream vite package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing exports to vite-plus CLI package to match vite's export map: - ./client (types only, ambient declarations) - ./module-runner - ./internal - ./dist/client/* - ./types/* - ./types/internal/* (blocked) This enables imports like: ```typescript import type { ImportGlobFunction } from '@voidzero-dev/vite-plus/types/importGlob.d.ts'; ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/cli/build.ts | 110 ++++++++++++++++++++++++++++++++++++++ packages/cli/package.json | 16 ++++++ 2 files changed, 126 insertions(+) diff --git a/packages/cli/build.ts b/packages/cli/build.ts index e4d72b895d..4d025c9375 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -16,9 +16,11 @@ import { const projectDir = dirname(fileURLToPath(import.meta.url)); const TEST_PACKAGE_NAME = '@voidzero-dev/vite-plus-test'; +const CORE_PACKAGE_NAME = '@voidzero-dev/vite-plus-core'; await buildCli(); await buildNapiBinding(); +await syncCorePackageExports(); await syncTestPackageExports(); async function buildNapiBinding() { @@ -73,6 +75,114 @@ async function buildCli() { } } +/** + * Sync Vite core exports from @voidzero-dev/vite-plus-core to @voidzero-dev/vite-plus + * + * This creates shim files for: + * - ./client (types only) + * - ./module-runner + * - ./internal + * - ./dist/client/* (wildcard) + * - ./types/* (wildcard, types only) + */ +async function syncCorePackageExports() { + console.log('\nSyncing core package exports...'); + + const distDir = join(projectDir, 'dist'); + const clientDir = join(distDir, 'client'); + const typesDir = join(distDir, 'types'); + + // Clean up previous build + await rm(clientDir, { recursive: true, force: true }); + await rm(typesDir, { recursive: true, force: true }); + await mkdir(clientDir, { recursive: true }); + await mkdir(typesDir, { recursive: true }); + + // Create ./client shim (types only) - uses triple-slash reference since client.d.ts is ambient + console.log(' Creating ./client'); + await writeFile( + join(distDir, 'client.d.ts'), + `/// \n`, + ); + + // Create ./module-runner shim + console.log(' Creating ./module-runner'); + await writeFile( + join(distDir, 'module-runner.js'), + `export * from '${CORE_PACKAGE_NAME}/module-runner';\n`, + ); + await writeFile( + join(distDir, 'module-runner.d.ts'), + `export * from '${CORE_PACKAGE_NAME}/module-runner';\n`, + ); + + // Create ./internal shim + console.log(' Creating ./internal'); + await writeFile(join(distDir, 'internal.js'), `export * from '${CORE_PACKAGE_NAME}/internal';\n`); + await writeFile( + join(distDir, 'internal.d.ts'), + `export * from '${CORE_PACKAGE_NAME}/internal';\n`, + ); + + // Create ./dist/client/* shims by reading core's dist/vite/client files + console.log(' Creating ./dist/client/*'); + const coreClientDir = join(projectDir, '../core/dist/vite/client'); + if (existsSync(coreClientDir)) { + const { readdirSync } = await import('node:fs'); + for (const file of readdirSync(coreClientDir)) { + const shimPath = join(clientDir, file); + if (file.endsWith('.js')) { + await writeFile(shimPath, `export * from '${CORE_PACKAGE_NAME}/dist/client/${file}';\n`); + } else if (file.endsWith('.d.ts')) { + await writeFile( + shimPath, + `export * from '${CORE_PACKAGE_NAME}/dist/client/${file.replace('.d.ts', '')}';\n`, + ); + } else { + // Copy non-JS/TS files directly (e.g., CSS, source maps) + const { copyFileSync } = await import('node:fs'); + copyFileSync(join(coreClientDir, file), shimPath); + } + } + } + + // Create ./types/* shims by reading core's dist/vite/types files + console.log(' Creating ./types/*'); + const coreTypesDir = join(projectDir, '../core/dist/vite/types'); + if (existsSync(coreTypesDir)) { + const { readdirSync, statSync } = await import('node:fs'); + await syncTypesDir(coreTypesDir, typesDir, ''); + } + + console.log('\nSynced core package exports'); +} + +async function syncTypesDir(srcDir: string, destDir: string, relativePath: string) { + const { readdirSync, statSync } = await import('node:fs'); + const entries = readdirSync(srcDir); + + for (const entry of entries) { + const srcPath = join(srcDir, entry); + const destPath = join(destDir, entry); + const entryRelPath = relativePath ? `${relativePath}/${entry}` : entry; + + if (statSync(srcPath).isDirectory()) { + // Skip internal directory - it's blocked by exports + if (entry === 'internal') continue; + + await mkdir(destPath, { recursive: true }); + await syncTypesDir(srcPath, destPath, entryRelPath); + } else if (entry.endsWith('.d.ts')) { + // Create shim that re-exports from core - must include .d.ts extension for wildcard exports + // Use 'export type *' since we're re-exporting from a .d.ts file + await writeFile( + destPath, + `export type * from '${CORE_PACKAGE_NAME}/types/${entryRelPath}';\n`, + ); + } + } +} + /** * Sync exports from @voidzero-dev/vite-plus-test to @voidzero-dev/vite-plus * diff --git a/packages/cli/package.json b/packages/cli/package.json index f78fdc9823..13936fc7d2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -58,6 +58,22 @@ "require": "./dist/index.cjs", "types": "./dist/index.d.ts" }, + "./client": { + "types": "./dist/client.d.ts" + }, + "./module-runner": { + "types": "./dist/module-runner.d.ts", + "default": "./dist/module-runner.js" + }, + "./internal": { + "types": "./dist/internal.d.ts", + "default": "./dist/internal.js" + }, + "./dist/client/*": "./dist/client/*", + "./types/*": { + "types": "./dist/types/*" + }, + "./types/internal/*": null, "./bin": { "import": "./dist/bin.js" }, From 3153e9266d5552e0a4edfd89d3d2af370ad18f18 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Dec 2025 10:27:22 +0800 Subject: [PATCH 2/9] fix(cli): address PR review comments for exports sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use static imports instead of dynamic imports for consistency - Add error handling when core package isn't built (throw instead of silent skip) - Fix .mjs/.cjs file handling to create re-export shims instead of copying - Remove unused dynamic import statement 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/cli/build.ts | 44 ++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/cli/build.ts b/packages/cli/build.ts index 4d025c9375..c09416308b 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -1,4 +1,4 @@ -import { existsSync, mkdirSync, writeFileSync } from 'node:fs'; +import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs'; import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -127,38 +127,40 @@ async function syncCorePackageExports() { // Create ./dist/client/* shims by reading core's dist/vite/client files console.log(' Creating ./dist/client/*'); const coreClientDir = join(projectDir, '../core/dist/vite/client'); - if (existsSync(coreClientDir)) { - const { readdirSync } = await import('node:fs'); - for (const file of readdirSync(coreClientDir)) { - const shimPath = join(clientDir, file); - if (file.endsWith('.js')) { - await writeFile(shimPath, `export * from '${CORE_PACKAGE_NAME}/dist/client/${file}';\n`); - } else if (file.endsWith('.d.ts')) { - await writeFile( - shimPath, - `export * from '${CORE_PACKAGE_NAME}/dist/client/${file.replace('.d.ts', '')}';\n`, - ); - } else { - // Copy non-JS/TS files directly (e.g., CSS, source maps) - const { copyFileSync } = await import('node:fs'); - copyFileSync(join(coreClientDir, file), shimPath); - } + if (!existsSync(coreClientDir)) { + throw new Error( + `Core client artifacts not found at "${coreClientDir}". ` + + `Make sure ${CORE_PACKAGE_NAME} is built before building the CLI.`, + ); + } + for (const file of readdirSync(coreClientDir)) { + const shimPath = join(clientDir, file); + if (file.endsWith('.js') || file.endsWith('.mjs') || file.endsWith('.cjs')) { + await writeFile(shimPath, `export * from '${CORE_PACKAGE_NAME}/dist/client/${file}';\n`); + } else if (file.endsWith('.d.ts') || file.endsWith('.d.mts') || file.endsWith('.d.cts')) { + const baseFile = file.replace(/\.d\.[mc]?ts$/, ''); + await writeFile(shimPath, `export * from '${CORE_PACKAGE_NAME}/dist/client/${baseFile}';\n`); + } else { + // Copy non-JS/TS files directly (e.g., CSS, source maps) + copyFileSync(join(coreClientDir, file), shimPath); } } // Create ./types/* shims by reading core's dist/vite/types files console.log(' Creating ./types/*'); const coreTypesDir = join(projectDir, '../core/dist/vite/types'); - if (existsSync(coreTypesDir)) { - const { readdirSync, statSync } = await import('node:fs'); - await syncTypesDir(coreTypesDir, typesDir, ''); + if (!existsSync(coreTypesDir)) { + throw new Error( + `Core type definitions not found at "${coreTypesDir}". ` + + `Make sure ${CORE_PACKAGE_NAME} is built before building the CLI.`, + ); } + await syncTypesDir(coreTypesDir, typesDir, ''); console.log('\nSynced core package exports'); } async function syncTypesDir(srcDir: string, destDir: string, relativePath: string) { - const { readdirSync, statSync } = await import('node:fs'); const entries = readdirSync(srcDir); for (const entry of entries) { From ec4349caf18870ddff979da351d628726e4e9ded Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Dec 2025 11:03:07 +0800 Subject: [PATCH 3/9] fix(cli): handle .d.mts/.d.cts and fix internal skip scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add .d.mts/.d.cts handling in syncTypesDir for consistency with client - Only skip 'internal' directory at top level (matches ./types/internal/* export block) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/cli/build.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/build.ts b/packages/cli/build.ts index c09416308b..c57de610c5 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -169,13 +169,13 @@ async function syncTypesDir(srcDir: string, destDir: string, relativePath: strin const entryRelPath = relativePath ? `${relativePath}/${entry}` : entry; if (statSync(srcPath).isDirectory()) { - // Skip internal directory - it's blocked by exports - if (entry === 'internal') continue; + // Skip top-level internal directory - it's blocked by ./types/internal/* export + if (entry === 'internal' && relativePath === '') continue; await mkdir(destPath, { recursive: true }); await syncTypesDir(srcPath, destPath, entryRelPath); - } else if (entry.endsWith('.d.ts')) { - // Create shim that re-exports from core - must include .d.ts extension for wildcard exports + } else if (/\.d\.[mc]?ts$/.test(entry)) { + // Create shim that re-exports from core - must include extension for wildcard exports // Use 'export type *' since we're re-exporting from a .d.ts file await writeFile( destPath, From 20b8719b29d6b095212bc5e14f5baa5930d36574 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Dec 2025 11:12:24 +0800 Subject: [PATCH 4/9] fix(cli): fix export order and add directory check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move ./types/internal/* before ./types/* for correct precedence - Add directory check in client loop to prevent EISDIR crash - Use async copyFile for consistency with other file operations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/cli/build.ts | 9 ++++++--- packages/cli/package.json | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/cli/build.ts b/packages/cli/build.ts index c57de610c5..a5180ae0b1 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -1,5 +1,5 @@ -import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs'; -import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs'; +import { copyFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -134,7 +134,10 @@ async function syncCorePackageExports() { ); } for (const file of readdirSync(coreClientDir)) { + const srcPath = join(coreClientDir, file); const shimPath = join(clientDir, file); + // Skip directories + if (statSync(srcPath).isDirectory()) continue; if (file.endsWith('.js') || file.endsWith('.mjs') || file.endsWith('.cjs')) { await writeFile(shimPath, `export * from '${CORE_PACKAGE_NAME}/dist/client/${file}';\n`); } else if (file.endsWith('.d.ts') || file.endsWith('.d.mts') || file.endsWith('.d.cts')) { @@ -142,7 +145,7 @@ async function syncCorePackageExports() { await writeFile(shimPath, `export * from '${CORE_PACKAGE_NAME}/dist/client/${baseFile}';\n`); } else { // Copy non-JS/TS files directly (e.g., CSS, source maps) - copyFileSync(join(coreClientDir, file), shimPath); + await copyFile(srcPath, shimPath); } } diff --git a/packages/cli/package.json b/packages/cli/package.json index 13936fc7d2..db09889720 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -70,10 +70,10 @@ "default": "./dist/internal.js" }, "./dist/client/*": "./dist/client/*", + "./types/internal/*": null, "./types/*": { "types": "./dist/types/*" }, - "./types/internal/*": null, "./bin": { "import": "./dist/bin.js" }, From 8adffe91ba95bf5c7706b5ed441caee66cbb17c2 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Dec 2025 11:17:41 +0800 Subject: [PATCH 5/9] docs(cli): improve build.ts documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add file header explaining the build process overview - Expand syncCorePackageExports JSDoc with usage examples and export order note - Add comprehensive JSDoc for syncTypesDir function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/cli/build.ts | 52 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/packages/cli/build.ts b/packages/cli/build.ts index a5180ae0b1..bfe35af78e 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -1,3 +1,19 @@ +/** + * Build script for @voidzero-dev/vite-plus CLI package + * + * This script performs three main tasks: + * 1. buildCli() - Compiles TypeScript sources + * 2. buildNapiBinding() - Builds the native Rust binding via NAPI + * 3. syncCorePackageExports() - Creates shim files to re-export from @voidzero-dev/vite-plus-core + * 4. syncTestPackageExports() - Creates shim files to re-export from @voidzero-dev/vite-plus-test + * + * The sync functions allow this package to be a drop-in replacement for 'vite' by + * re-exporting all the same subpaths (./client, ./types/*, etc.) while delegating + * to the core package for actual implementation. + * + * IMPORTANT: The core package must be built before running this script. + */ + import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs'; import { copyFile, mkdir, readFile, rm, writeFile } from 'node:fs/promises'; import { dirname, join } from 'node:path'; @@ -78,12 +94,21 @@ async function buildCli() { /** * Sync Vite core exports from @voidzero-dev/vite-plus-core to @voidzero-dev/vite-plus * - * This creates shim files for: - * - ./client (types only) - * - ./module-runner - * - ./internal - * - ./dist/client/* (wildcard) - * - ./types/* (wildcard, types only) + * Creates shim files that re-export from the core package, enabling imports like: + * - `import type { ... } from '@voidzero-dev/vite-plus/types/importGlob.d.ts'` + * - `import { ... } from '@voidzero-dev/vite-plus/module-runner'` + * + * Export paths created: + * - ./client - Triple-slash reference (ambient type declarations for CSS, assets, etc.) + * - ./module-runner - Re-exports both JS and types + * - ./internal - Re-exports both JS and types + * - ./dist/client/* - Re-exports client runtime files (.mjs, .cjs) + * - ./types/* - Type-only re-exports using `export type *` + * + * Note: In package.json exports, ./types/internal/* must come BEFORE ./types/* + * for correct precedence (more specific patterns must precede wildcards). + * + * @throws Error if core package is not built (missing dist directories) */ async function syncCorePackageExports() { console.log('\nSyncing core package exports...'); @@ -163,6 +188,21 @@ async function syncCorePackageExports() { console.log('\nSynced core package exports'); } +/** + * Recursively sync type definition files from core to CLI package + * + * Creates shim .d.ts files that re-export types from the core package. + * Uses `export type * from` syntax which is valid in TypeScript 5.0+. + * + * @param srcDir - Source directory containing .d.ts files + * @param destDir - Destination directory for shim files + * @param relativePath - Current path relative to types root (empty string at top level) + * + * Special handling: + * - Skips top-level 'internal' directory (blocked by ./types/internal/* export) + * - Supports .d.ts, .d.mts, and .d.cts extensions + * - Preserves directory structure recursively + */ async function syncTypesDir(srcDir: string, destDir: string, relativePath: string) { const entries = readdirSync(srcDir); From 5aa8ed0ebccf94582d45a63d9407a2ec203e327e Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Dec 2025 11:29:22 +0800 Subject: [PATCH 6/9] docs(cli): update BUNDLING.md for core package export sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the new 4-step build process that includes core package export synchronization, enabling @voidzero-dev/vite-plus to serve as a drop-in replacement for upstream vite. Key additions: - Step 3: syncCorePackageExports() documentation - Export mapping tables for ./client, ./module-runner, ./internal, ./dist/client/*, and ./types/* - Technical details on type-only exports, internal types blocking, and triple-slash references for ambient client types - Updated output structure and build flow diagrams 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/cli/BUNDLING.md | 173 +++++++++++++++++++++++++++++++++------ 1 file changed, 148 insertions(+), 25 deletions(-) diff --git a/packages/cli/BUNDLING.md b/packages/cli/BUNDLING.md index 39bc28e288..d89dcc5c0c 100644 --- a/packages/cli/BUNDLING.md +++ b/packages/cli/BUNDLING.md @@ -1,16 +1,17 @@ # CLI Package Build Architecture -This document explains how `@voidzero-dev/vite-plus` is built and how it re-exports the test package. +This document explains how `@voidzero-dev/vite-plus` is built and how it re-exports from both the core and test packages to serve as a drop-in replacement for `vite`. ## Overview -The CLI package uses a **3-step build process**: +The CLI package uses a **4-step build process**: 1. **TypeScript Compilation** - Compile TypeScript source to JavaScript 2. **NAPI Binding Build** - Compile Rust code to native Node.js bindings -3. **Test Package Export Sync** - Re-export `@voidzero-dev/vite-plus-test` under `./test/*` +3. **Core Package Export Sync** - Re-export `@voidzero-dev/vite-plus-core` under `./client`, `./types/*`, etc. +4. **Test Package Export Sync** - Re-export `@voidzero-dev/vite-plus-test` under `./test/*` -This allows users to import everything from a single package (`@voidzero-dev/vite-plus`) instead of needing to know about the separate test package. +This architecture allows users to import everything from a single package (`@voidzero-dev/vite-plus`) as a drop-in replacement for `vite`, without needing to know about the separate core and test packages. ## Build Steps @@ -50,7 +51,39 @@ await cli.build({ The build generates platform-specific native binaries and formats the generated JavaScript wrapper with `oxfmt`. -### Step 3: Test Package Export Sync (`syncTestPackageExports`) +### Step 3: Core Package Export Sync (`syncCorePackageExports`) + +Creates shim files that re-export from `@voidzero-dev/vite-plus-core`, enabling this package to be a drop-in replacement for upstream `vite`. This is critical for compatibility with existing Vite plugins and configurations. + +**Prerequisites**: The core package must be built first (its `dist/vite/` directory must exist). + +**Export paths created**: + +| Export Path | Type | Description | +| -------------------- | ---------- | --------------------------------------------------------------------------------------- | +| `./client` | Types only | Triple-slash reference for ambient type declarations (CSS modules, asset imports, etc.) | +| `./module-runner` | JS + Types | Re-exports the Vite module runner for SSR/environments | +| `./internal` | JS + Types | Re-exports internal Vite APIs | +| `./dist/client/*` | JS | Client runtime files (`.mjs`, `.cjs`) | +| `./types/*` | Types only | Type-only re-exports using `export type *` | +| `./types/internal/*` | Blocked | Set to `null` to prevent access to internal types | + +**Shim file examples**: + +```typescript +// dist/client.d.ts (triple-slash reference for ambient types) +/// + +// dist/module-runner.js +export * from '@voidzero-dev/vite-plus-core/module-runner'; + +// dist/types/importMeta.d.ts (type-only export) +export type * from '@voidzero-dev/vite-plus-core/types/importMeta.d.ts'; +``` + +**Note on export ordering**: In `package.json`, the `./types/internal/*` export (set to `null`) must appear before `./types/*` for correct precedence. More specific patterns must precede wildcards. + +### Step 4: Test Package Export Sync (`syncTestPackageExports`) Reads the test package's exports and creates shim files that re-export everything under `./test/*`: @@ -74,6 +107,21 @@ packages/cli/ │ ├── index.cjs # Main entry (CJS) │ ├── index.d.ts # Type declarations │ ├── bin.js # CLI entry point +│ ├── client.d.ts # ./client types (triple-slash ref) +│ ├── module-runner.js # ./module-runner shim +│ ├── module-runner.d.ts +│ ├── internal.js # ./internal shim +│ ├── internal.d.ts +│ ├── client/ # Synced client runtime files +│ │ ├── client.mjs # ESM client shim +│ │ ├── client.d.ts +│ │ ├── env.mjs +│ │ └── ... +│ ├── types/ # Synced type definitions +│ │ ├── importMeta.d.ts # Type shims (export type *) +│ │ ├── importGlob.d.ts +│ │ ├── customEvent.d.ts +│ │ └── ... │ └── test/ # Synced test exports │ ├── index.js # Re-exports @voidzero-dev/vite-plus-test │ ├── index.cjs @@ -113,6 +161,62 @@ These targets are defined in `package.json` under the `napi.targets` field. --- +## Core Package Export Sync Details + +### Why Shim Files? + +The CLI package creates thin shim files that re-export from `@voidzero-dev/vite-plus-core` rather than bundling the actual code. This approach: + +1. **Enables drop-in replacement** - Users can replace `vite` with `@voidzero-dev/vite-plus` without changing imports +2. **Keeps packages in sync** - No need to rebuild CLI when core package changes +3. **Reduces duplication** - No file copying, just re-exports +4. **Preserves module resolution** - Node.js resolves to the actual core package + +### Export Mapping (Core) + +| Upstream Vite Export | CLI Package Export | Description | +| -------------------- | --------------------------------------- | ------------------------------------------ | +| `vite/client` | `@voidzero-dev/vite-plus/client` | Ambient types for HMR, CSS modules, assets | +| `vite/module-runner` | `@voidzero-dev/vite-plus/module-runner` | SSR/Environment module runner | +| `vite/internal` | `@voidzero-dev/vite-plus/internal` | Internal APIs | +| `vite/dist/client/*` | `@voidzero-dev/vite-plus/dist/client/*` | Client runtime files | +| `vite/types/*` | `@voidzero-dev/vite-plus/types/*` | Type definitions | + +### Type-Only Exports + +For `./types/*` exports, shim files use `export type *` syntax (TypeScript 5.0+) to ensure only type information is re-exported: + +```typescript +// dist/types/importMeta.d.ts +export type * from '@voidzero-dev/vite-plus-core/types/importMeta.d.ts'; +``` + +This is important because `./types/*` only exposes `.d.ts` files and should never include runtime code. + +### Internal Types Blocking + +The `./types/internal/*` export is set to `null` in package.json to block access to internal type definitions: + +```json +"./types/internal/*": null, +"./types/*": { "types": "./dist/types/*" } +``` + +The `syncTypesDir()` helper skips the top-level `internal` directory when creating shims, since access is blocked at the exports level. + +### Client Types (Triple-Slash Reference) + +The `./client` export uses a triple-slash reference instead of a regular export because Vite's `client.d.ts` contains ambient type declarations (for CSS modules, assets, etc.) that should be globally available: + +```typescript +// dist/client.d.ts +/// +``` + +This allows TypeScript to pick up types like `import.meta.hot`, CSS module types, and asset imports without explicit imports. + +--- + ## Test Package Export Sync Details ### Why Shim Files? @@ -123,7 +227,7 @@ Instead of copying the actual dist files from the test package, we create thin s 2. **Reduces duplication** - No file copying, just re-exports 3. **Preserves module resolution** - Node.js resolves to the actual test package -### Export Mapping +### Export Mapping (Test) All test package exports are mapped under `./test/*`: @@ -186,9 +290,12 @@ module.exports = require('@voidzero-dev/vite-plus-test'); **Type shim** (`dist/test/browser-playwright.d.ts`): ```typescript +import '@voidzero-dev/vite-plus-test/browser-playwright'; export * from '@voidzero-dev/vite-plus-test/browser-playwright'; ``` +Note: Type shims include a side-effect import to preserve module augmentations (e.g., `toMatchSnapshot` on the `Assertion` interface). + --- ## Build Dependencies @@ -216,10 +323,10 @@ This sets `release: false` in the NAPI build options, producing larger but faste ## Build Commands ```bash -# Build the CLI package +# Build the CLI package (requires core package to be built first) pnpm -C packages/cli build -# Build from monorepo root +# Build from monorepo root (builds all dependencies first) pnpm build --filter @voidzero-dev/vite-plus # Debug build @@ -232,16 +339,21 @@ VITE_PLUS_CLI_DEBUG=1 pnpm -C packages/cli build After building, the CLI package exports: -| Export Path | Description | -| --------------------------- | ------------------------------- | -| `.` | Main entry (CLI utilities) | -| `./bin` | CLI binary entry point | -| `./binding` | NAPI native binding | -| `./test` | Test package main entry | -| `./test/browser` | Browser testing utilities | -| `./test/browser-playwright` | Playwright integration | -| `./test/plugins/*` | Plugin shims for pnpm overrides | -| `./package.json` | Package metadata | +| Export Path | Description | +| --------------------------- | ----------------------------------- | +| `.` | Main entry (CLI utilities) | +| `./client` | Client types (ambient declarations) | +| `./module-runner` | Vite module runner for SSR | +| `./internal` | Internal Vite APIs | +| `./dist/client/*` | Client runtime files | +| `./types/*` | Type definitions | +| `./bin` | CLI binary entry point | +| `./binding` | NAPI native binding | +| `./test` | Test package main entry | +| `./test/browser` | Browser testing utilities | +| `./test/browser-playwright` | Playwright integration | +| `./test/plugins/*` | Plugin shims for pnpm overrides | +| `./package.json` | Package metadata | See `package.json` for the complete list of exports. @@ -252,17 +364,26 @@ See `package.json` for the complete list of exports. ### Build Flow ``` -1. buildCli() TypeScript compilation -> dist/*.js -2. buildNapiBinding() Rust -> binding/*.node (per platform) -3. syncTestPackageExports() Read test pkg exports -> dist/test/* - ├── createShimForExport() Generate shim files - ├── createConditionalShim() Handle import/require conditions - └── updateCliPackageJson() Update exports in package.json +1. buildCli() TypeScript compilation -> dist/*.js +2. buildNapiBinding() Rust -> binding/*.node (per platform) +3. syncCorePackageExports() Read core pkg dist -> dist/client/, dist/types/ + ├── createClientShim() Triple-slash reference for ./client + ├── createModuleRunnerShim() JS + types for ./module-runner + ├── createInternalShim() JS + types for ./internal + ├── syncClientDir() Shims for ./dist/client/* + └── syncTypesDir() Type-only shims for ./types/* +4. syncTestPackageExports() Read test pkg exports -> dist/test/* + ├── createShimForExport() Generate shim files + ├── createConditionalShim() Handle import/require conditions + └── updateCliPackageJson() Update exports in package.json ``` ### Key Constants ```typescript +// Core package name for Vite compatibility exports +const CORE_PACKAGE_NAME = '@voidzero-dev/vite-plus-core'; + // Test package name for re-exports const TEST_PACKAGE_NAME = '@voidzero-dev/vite-plus-test'; ``` @@ -275,4 +396,6 @@ The build script automatically updates `package.json`: 2. Adds new exports from test package 3. Ensures `dist/test` is in the `files` array -This keeps the CLI package exports in sync with the test package without manual maintenance. +Core package exports (`./client`, `./module-runner`, `./internal`, `./dist/client/*`, `./types/*`) are defined statically in `package.json` and not auto-generated, since they match upstream Vite's exports structure. + +This keeps the CLI package exports in sync with both upstream Vite and the test package without manual maintenance. From 8d95d40dedb429002f8d69c241c02d1d749b3d4d Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Dec 2025 11:35:46 +0800 Subject: [PATCH 7/9] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: LongYinan --- packages/cli/build.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/build.ts b/packages/cli/build.ts index bfe35af78e..5053d7b61e 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -1,7 +1,7 @@ /** * Build script for @voidzero-dev/vite-plus CLI package * - * This script performs three main tasks: + * This script performs four main tasks: * 1. buildCli() - Compiles TypeScript sources * 2. buildNapiBinding() - Builds the native Rust binding via NAPI * 3. syncCorePackageExports() - Creates shim files to re-export from @voidzero-dev/vite-plus-core From 20889e8ca71ccb06adc76cd4215749defe84c0ff Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Dec 2025 11:48:22 +0800 Subject: [PATCH 8/9] fix(cli): strip extension from types/* import specifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For consistency with ./dist/client/* handling, strip .d.ts/.d.mts/.d.cts extensions from the import specifiers in type shims. TypeScript's module resolution automatically finds .d.ts files without explicit extensions. Before: export type * from 'pkg/types/importMeta.d.ts'; After: export type * from 'pkg/types/importMeta'; 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- packages/cli/BUNDLING.md | 4 ++-- packages/cli/build.ts | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/cli/BUNDLING.md b/packages/cli/BUNDLING.md index d89dcc5c0c..cb60e820f9 100644 --- a/packages/cli/BUNDLING.md +++ b/packages/cli/BUNDLING.md @@ -77,8 +77,8 @@ Creates shim files that re-export from `@voidzero-dev/vite-plus-core`, enabling // dist/module-runner.js export * from '@voidzero-dev/vite-plus-core/module-runner'; -// dist/types/importMeta.d.ts (type-only export) -export type * from '@voidzero-dev/vite-plus-core/types/importMeta.d.ts'; +// dist/types/importMeta.d.ts (type-only export, extension stripped) +export type * from '@voidzero-dev/vite-plus-core/types/importMeta'; ``` **Note on export ordering**: In `package.json`, the `./types/internal/*` export (set to `null`) must appear before `./types/*` for correct precedence. More specific patterns must precede wildcards. diff --git a/packages/cli/build.ts b/packages/cli/build.ts index 5053d7b61e..9e9d378a44 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -95,7 +95,7 @@ async function buildCli() { * Sync Vite core exports from @voidzero-dev/vite-plus-core to @voidzero-dev/vite-plus * * Creates shim files that re-export from the core package, enabling imports like: - * - `import type { ... } from '@voidzero-dev/vite-plus/types/importGlob.d.ts'` + * - `import type { ... } from '@voidzero-dev/vite-plus/types/importGlob'` * - `import { ... } from '@voidzero-dev/vite-plus/module-runner'` * * Export paths created: @@ -218,12 +218,11 @@ async function syncTypesDir(srcDir: string, destDir: string, relativePath: strin await mkdir(destPath, { recursive: true }); await syncTypesDir(srcPath, destPath, entryRelPath); } else if (/\.d\.[mc]?ts$/.test(entry)) { - // Create shim that re-exports from core - must include extension for wildcard exports + // Create shim that re-exports from core + // Strip extension for consistency with ./dist/client/* handling - TypeScript resolves .d.ts automatically // Use 'export type *' since we're re-exporting from a .d.ts file - await writeFile( - destPath, - `export type * from '${CORE_PACKAGE_NAME}/types/${entryRelPath}';\n`, - ); + const modulePath = entryRelPath.replace(/\.d\.[mc]?ts$/, ''); + await writeFile(destPath, `export type * from '${CORE_PACKAGE_NAME}/types/${modulePath}';\n`); } } } From 714de68cecbb99cefbb3ff63cb4e4fa98c595e77 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 26 Dec 2025 11:51:41 +0800 Subject: [PATCH 9/9] Revert "fix(cli): strip extension from types/* import specifiers" This reverts commit 20889e8ca71ccb06adc76cd4215749defe84c0ff. --- packages/cli/BUNDLING.md | 4 ++-- packages/cli/build.ts | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/cli/BUNDLING.md b/packages/cli/BUNDLING.md index cb60e820f9..d89dcc5c0c 100644 --- a/packages/cli/BUNDLING.md +++ b/packages/cli/BUNDLING.md @@ -77,8 +77,8 @@ Creates shim files that re-export from `@voidzero-dev/vite-plus-core`, enabling // dist/module-runner.js export * from '@voidzero-dev/vite-plus-core/module-runner'; -// dist/types/importMeta.d.ts (type-only export, extension stripped) -export type * from '@voidzero-dev/vite-plus-core/types/importMeta'; +// dist/types/importMeta.d.ts (type-only export) +export type * from '@voidzero-dev/vite-plus-core/types/importMeta.d.ts'; ``` **Note on export ordering**: In `package.json`, the `./types/internal/*` export (set to `null`) must appear before `./types/*` for correct precedence. More specific patterns must precede wildcards. diff --git a/packages/cli/build.ts b/packages/cli/build.ts index 9e9d378a44..5053d7b61e 100644 --- a/packages/cli/build.ts +++ b/packages/cli/build.ts @@ -95,7 +95,7 @@ async function buildCli() { * Sync Vite core exports from @voidzero-dev/vite-plus-core to @voidzero-dev/vite-plus * * Creates shim files that re-export from the core package, enabling imports like: - * - `import type { ... } from '@voidzero-dev/vite-plus/types/importGlob'` + * - `import type { ... } from '@voidzero-dev/vite-plus/types/importGlob.d.ts'` * - `import { ... } from '@voidzero-dev/vite-plus/module-runner'` * * Export paths created: @@ -218,11 +218,12 @@ async function syncTypesDir(srcDir: string, destDir: string, relativePath: strin await mkdir(destPath, { recursive: true }); await syncTypesDir(srcPath, destPath, entryRelPath); } else if (/\.d\.[mc]?ts$/.test(entry)) { - // Create shim that re-exports from core - // Strip extension for consistency with ./dist/client/* handling - TypeScript resolves .d.ts automatically + // Create shim that re-exports from core - must include extension for wildcard exports // Use 'export type *' since we're re-exporting from a .d.ts file - const modulePath = entryRelPath.replace(/\.d\.[mc]?ts$/, ''); - await writeFile(destPath, `export type * from '${CORE_PACKAGE_NAME}/types/${modulePath}';\n`); + await writeFile( + destPath, + `export type * from '${CORE_PACKAGE_NAME}/types/${entryRelPath}';\n`, + ); } } }