Skip to content

Commit 7fc5047

Browse files
authored
build(vite-plus): sync exports in test package (#349)
1 parent 04960d9 commit 7fc5047

3 files changed

Lines changed: 734 additions & 2 deletions

File tree

packages/cli/BUNDLING.md

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
# CLI Package Build Architecture
2+
3+
This document explains how `@voidzero-dev/vite-plus` is built and how it re-exports the test package.
4+
5+
## Overview
6+
7+
The CLI package uses a **3-step build process**:
8+
9+
1. **TypeScript Compilation** - Compile TypeScript source to JavaScript
10+
2. **NAPI Binding Build** - Compile Rust code to native Node.js bindings
11+
3. **Test Package Export Sync** - Re-export `@voidzero-dev/vite-plus-test` under `./test/*`
12+
13+
This allows users to import everything from a single package (`@voidzero-dev/vite-plus`) instead of needing to know about the separate test package.
14+
15+
## Build Steps
16+
17+
### Step 1: TypeScript Compilation (`buildCli`)
18+
19+
Compiles TypeScript source files using the TypeScript compiler API:
20+
21+
```typescript
22+
const program = createProgram({
23+
rootNames: fileNames,
24+
options,
25+
host,
26+
});
27+
program.emit();
28+
```
29+
30+
**Input**: `src/*.ts` files
31+
**Output**: `dist/*.js`, `dist/*.d.ts`
32+
33+
### Step 2: NAPI Binding Build (`buildNapiBinding`)
34+
35+
Builds native Rust bindings using `@napi-rs/cli`:
36+
37+
```typescript
38+
const cli = new NapiCli();
39+
await cli.build({
40+
packageJsonPath: '../package.json',
41+
cwd: 'binding',
42+
platform: true,
43+
release: process.env.VITE_PLUS_CLI_DEBUG !== '1',
44+
esm: true,
45+
});
46+
```
47+
48+
**Input**: `binding/*.rs` (Rust source)
49+
**Output**: `binding/*.node` (platform-specific binaries)
50+
51+
The build generates platform-specific native binaries and formats the generated JavaScript wrapper with `oxfmt`.
52+
53+
### Step 3: Test Package Export Sync (`syncTestPackageExports`)
54+
55+
Reads the test package's exports and creates shim files that re-export everything under `./test/*`:
56+
57+
```typescript
58+
// For each test package export like "./browser-playwright"
59+
// Creates a shim file: dist/test/browser-playwright.js
60+
export * from '@voidzero-dev/vite-plus-test/browser-playwright';
61+
```
62+
63+
**Input**: `../test/package.json` exports
64+
**Output**: `dist/test/*.js`, `dist/test/*.d.ts`, updated `package.json` exports
65+
66+
---
67+
68+
## Output Structure
69+
70+
```
71+
packages/cli/
72+
├── dist/
73+
│ ├── index.js # Main entry (ESM)
74+
│ ├── index.cjs # Main entry (CJS)
75+
│ ├── index.d.ts # Type declarations
76+
│ ├── bin.js # CLI entry point
77+
│ └── test/ # Synced test exports
78+
│ ├── index.js # Re-exports @voidzero-dev/vite-plus-test
79+
│ ├── index.cjs
80+
│ ├── index.d.ts
81+
│ ├── browser-playwright.js
82+
│ ├── browser-playwright.d.ts
83+
│ ├── plugins/
84+
│ │ ├── runner.js
85+
│ │ ├── utils.js
86+
│ │ ├── spy.js
87+
│ │ └── ... (33+ plugin shims)
88+
│ └── ...
89+
├── binding/
90+
│ ├── index.js # NAPI binding JS wrapper
91+
│ ├── index.d.ts # NAPI type declarations
92+
│ └── *.node # Platform-specific binaries
93+
└── bin/
94+
└── vite # Shell entry point
95+
```
96+
97+
---
98+
99+
## NAPI Targets
100+
101+
The CLI builds native bindings for the following platform targets:
102+
103+
| Target | Platform | Architecture | Output File |
104+
| --------------------------- | -------- | ------------ | --------------------------------- |
105+
| `aarch64-apple-darwin` | macOS | ARM64 | `vite-plus.darwin-arm64.node` |
106+
| `x86_64-apple-darwin` | macOS | x64 | `vite-plus.darwin-x64.node` |
107+
| `aarch64-unknown-linux-gnu` | Linux | ARM64 | `vite-plus.linux-arm64-gnu.node` |
108+
| `x86_64-unknown-linux-gnu` | Linux | x64 | `vite-plus.linux-x64-gnu.node` |
109+
| `aarch64-pc-windows-msvc` | Windows | ARM64 | `vite-plus.win32-arm64-msvc.node` |
110+
| `x86_64-pc-windows-msvc` | Windows | x64 | `vite-plus.win32-x64-msvc.node` |
111+
112+
These targets are defined in `package.json` under the `napi.targets` field.
113+
114+
---
115+
116+
## Test Package Export Sync Details
117+
118+
### Why Shim Files?
119+
120+
Instead of copying the actual dist files from the test package, we create thin shim files that re-export from `@voidzero-dev/vite-plus-test`. This approach:
121+
122+
1. **Keeps packages in sync** - No need to rebuild CLI when test package changes
123+
2. **Reduces duplication** - No file copying, just re-exports
124+
3. **Preserves module resolution** - Node.js resolves to the actual test package
125+
126+
### Export Mapping
127+
128+
All test package exports are mapped under `./test/*`:
129+
130+
| Test Package Export | CLI Package Export |
131+
| ------------------------------------------------- | ------------------------------------------------- |
132+
| `@voidzero-dev/vite-plus-test` | `@voidzero-dev/vite-plus/test` |
133+
| `@voidzero-dev/vite-plus-test/browser` | `@voidzero-dev/vite-plus/test/browser` |
134+
| `@voidzero-dev/vite-plus-test/browser-playwright` | `@voidzero-dev/vite-plus/test/browser-playwright` |
135+
| `@voidzero-dev/vite-plus-test/plugins/runner` | `@voidzero-dev/vite-plus/test/plugins/runner` |
136+
137+
### Conditional Export Handling
138+
139+
The sync handles complex conditional exports with `import`/`require`/`node`/`types` conditions.
140+
141+
**Test package's main export** (`"."`):
142+
143+
```json
144+
".": {
145+
"import": { "types": "...", "node": "...", "default": "..." },
146+
"require": { "types": "...", "default": "..." }
147+
}
148+
```
149+
150+
**Becomes CLI package export** (`"./test"`):
151+
152+
```json
153+
"./test": {
154+
"import": {
155+
"types": "./dist/test/index.d.ts",
156+
"node": "./dist/test/index.js",
157+
"default": "./dist/test/index.js"
158+
},
159+
"require": {
160+
"types": "./dist/test/index.d.cts",
161+
"default": "./dist/test/index.cjs"
162+
}
163+
}
164+
```
165+
166+
For each condition, appropriate shim files are created:
167+
168+
- `.js` for ESM imports
169+
- `.cjs` for CommonJS requires
170+
- `.d.ts` / `.d.cts` for type declarations
171+
172+
### Shim File Contents
173+
174+
**ESM shim** (`dist/test/browser-playwright.js`):
175+
176+
```javascript
177+
export * from '@voidzero-dev/vite-plus-test/browser-playwright';
178+
```
179+
180+
**CJS shim** (`dist/test/index.cjs`):
181+
182+
```javascript
183+
module.exports = require('@voidzero-dev/vite-plus-test');
184+
```
185+
186+
**Type shim** (`dist/test/browser-playwright.d.ts`):
187+
188+
```typescript
189+
export * from '@voidzero-dev/vite-plus-test/browser-playwright';
190+
```
191+
192+
---
193+
194+
## Build Dependencies
195+
196+
| Package | Purpose |
197+
| -------------- | -------------------------------- |
198+
| `@napi-rs/cli` | NAPI build toolchain for Rust |
199+
| `oxfmt` | Code formatting for generated JS |
200+
| `typescript` | TypeScript compilation |
201+
202+
---
203+
204+
## Debug Mode
205+
206+
To build with debug (unoptimized) Rust bindings:
207+
208+
```bash
209+
VITE_PLUS_CLI_DEBUG=1 pnpm build
210+
```
211+
212+
This sets `release: false` in the NAPI build options, producing larger but faster-to-compile debug binaries.
213+
214+
---
215+
216+
## Build Commands
217+
218+
```bash
219+
# Build the CLI package
220+
pnpm -C packages/cli build
221+
222+
# Build from monorepo root
223+
pnpm build --filter @voidzero-dev/vite-plus
224+
225+
# Debug build
226+
VITE_PLUS_CLI_DEBUG=1 pnpm -C packages/cli build
227+
```
228+
229+
---
230+
231+
## Package Exports
232+
233+
After building, the CLI package exports:
234+
235+
| Export Path | Description |
236+
| --------------------------- | ------------------------------- |
237+
| `.` | Main entry (CLI utilities) |
238+
| `./bin` | CLI binary entry point |
239+
| `./binding` | NAPI native binding |
240+
| `./test` | Test package main entry |
241+
| `./test/browser` | Browser testing utilities |
242+
| `./test/browser-playwright` | Playwright integration |
243+
| `./test/plugins/*` | Plugin shims for pnpm overrides |
244+
| `./package.json` | Package metadata |
245+
246+
See `package.json` for the complete list of exports.
247+
248+
---
249+
250+
## Technical Reference
251+
252+
### Build Flow
253+
254+
```
255+
1. buildCli() TypeScript compilation -> dist/*.js
256+
2. buildNapiBinding() Rust -> binding/*.node (per platform)
257+
3. syncTestPackageExports() Read test pkg exports -> dist/test/*
258+
├── createShimForExport() Generate shim files
259+
├── createConditionalShim() Handle import/require conditions
260+
└── updateCliPackageJson() Update exports in package.json
261+
```
262+
263+
### Key Constants
264+
265+
```typescript
266+
// Test package name for re-exports
267+
const TEST_PACKAGE_NAME = '@voidzero-dev/vite-plus-test';
268+
```
269+
270+
### Package.json Auto-Update
271+
272+
The build script automatically updates `package.json`:
273+
274+
1. Removes old `./test/*` exports
275+
2. Adds new exports from test package
276+
3. Ensures `dist/test` is in the `files` array
277+
278+
This keeps the CLI package exports in sync with the test package without manual maintenance.

0 commit comments

Comments
 (0)