@@ -50,13 +50,62 @@ packages/cli/
5050│ ├── create.js
5151│ ├── migrate.js
5252│ └── version.js
53- ├── install.sh / install.ps1 # Global install scripts (from global)
54- ├── templates/ # Project templates (from global)
55- ├── rules/ # Oxlint rules (from global)
53+ ├── install.sh / install.ps1 # Global install scripts
54+ ├── templates/ # Project templates
55+ ├── rules/ # Oxlint rules
5656├── snap-tests/ # Local CLI snap tests
57- └── snap-tests-global/ # Global CLI snap tests (from global)
57+ └── snap-tests-global/ # Global CLI snap tests
5858```
5959
60+ ### Global Install Directory (` ~/.vite-plus/ ` )
61+
62+ The global install directory uses a wrapper package pattern. Each version directory
63+ declares ` vite-plus ` as an npm dependency instead of extracting its internals directly.
64+ This decouples the ` vp ` binary from vite-plus's internal file layout.
65+
66+ ```
67+ ~/.vite-plus/
68+ ├── bin/
69+ │ └── vp # Symlink to current/bin/vp
70+ ├── current -> <version>/ # Symlink to active version
71+ ├── <version>/
72+ │ ├── bin/
73+ │ │ └── vp # Rust binary (from platform package)
74+ │ ├── package.json # Wrapper: { "dependencies": { "vite-plus": "<version>" } }
75+ │ └── node_modules/
76+ │ ├── vite-plus/ # Installed as npm dependency
77+ │ │ ├── dist/bin.js # JS entry point (found by Rust binary)
78+ │ │ ├── dist/global/ # Bundled global commands
79+ │ │ ├── binding/ # NAPI loader
80+ │ │ ├── templates/ # Project templates
81+ │ │ ├── rules/ # Oxlint rules
82+ │ │ └── package.json # Real vite-plus package.json
83+ │ ├── @voidzero-dev/ # Platform package (via optionalDeps)
84+ │ │ └── vite-plus-<platform>/ # Contains .node NAPI binary
85+ │ └── [other transitive deps]
86+ ├── env, env.fish, env.ps1 # Shell PATH configuration
87+ └── packages/ # Globally installed packages (vp install -g)
88+ ```
89+
90+ ** Install flows:**
91+
92+ - ** Production** (` curl -fsSL https://viteplus.dev/install.sh | bash ` ):
93+ Downloads platform tarball (extracts only ` vp ` binary), generates wrapper ` package.json ` ,
94+ runs ` vp install --silent ` which installs ` vite-plus ` + all transitive deps via npm.
95+
96+ - ** Upgrade** (` vp upgrade ` ):
97+ Downloads platform tarball (binary only), generates wrapper ` package.json ` ,
98+ runs ` vp install --silent ` . No main tarball download needed.
99+
100+ - ** Local dev** (` pnpm bootstrap-cli ` ):
101+ Copies ` vp ` binary, generates wrapper ` package.json ` , symlinks
102+ ` node_modules/vite-plus ` to ` packages/cli/ ` source with transitive deps
103+ symlinked from ` packages/cli/node_modules/ ` .
104+
105+ - ** CI** (` pnpm bootstrap-cli:ci --tgz <path> ` ):
106+ Copies ` vp ` binary, generates wrapper ` package.json ` with ` file: ` protocol
107+ refs to tgz files, runs ` npm install ` .
108+
60109### Command Routing
61110
62111The Rust ` vp ` binary (` crates/vite_global_cli/ ` ) routes commands in two categories:
@@ -107,6 +156,19 @@ The Rust `vp` binary (`crates/vite_global_cli/`) routes commands in two categori
107156- ** Category A (Package Manager)** : ` install ` , ` add ` , ` remove ` , ` update ` , etc. — Handled directly in Rust
108157- ** Category B (JavaScript)** : All other commands (` build ` , ` test ` , ` lint ` , ` create ` , ` migrate ` , ` --version ` , etc.) — Rust uses ` oxc_resolver ` to find the project's local ` vite-plus/dist/bin.js ` and runs it. Falls back to the global installation's ` dist/bin.js ` if no local installation exists. The unified ` bin.ts ` entry point then routes to either NAPI bindings (task commands) or rolldown-bundled modules in ` dist/global/ ` (create, migrate, version).
109158
159+ ### Global scripts_dir Resolution (Rust)
160+
161+ The ` vp ` binary auto-detects the JS scripts directory from its own location:
162+
163+ ``` rust
164+ // Auto-detect from binary location
165+ // ~/.vite-plus/<version>/bin/vp -> ~/.vite-plus/<version>/node_modules/vite-plus/dist/
166+ let exe_path = std :: env :: current_exe ()? ;
167+ let bin_dir = exe_path . parent ()? ; // ~/.vite-plus/<version>/bin/
168+ let version_dir = bin_dir . parent ()? ; // ~/.vite-plus/<version>/
169+ let scripts_dir = version_dir . join (" node_modules" ). join (" vite-plus" ). join (" dist" );
170+ ```
171+
110172### Local vite-plus Resolution (Rust)
111173
112174``` rust
@@ -160,33 +222,43 @@ if (command === 'create') {
160222 - Removed JS shim layer — no more ` dist/index.js ` intermediary
161223 - Updated all command entry points from ` index.js ` to ` bin.js `
162224 - Changed ` MAIN_PACKAGE_NAME ` from ` vite-plus-cli ` to ` vite-plus `
163- - Added ` binding/ ` to install entries for upgrade command
225+ - Scripts dir resolution: ` version_dir/node_modules/vite-plus/dist/ `
226+
227+ 4 . ** Restructured global install directory** (` ~/.vite-plus/<version>/ ` ):
228+ - Wrapper ` package.json ` declares ` vite-plus ` as a dependency
229+ - ` vite-plus ` installed into ` node_modules/ ` by npm (not extracted from tarball)
230+ - ` .node ` NAPI binaries installed via npm optionalDependencies (not manually copied)
231+ - Removed ` extract_main_package() ` , ` strip_dev_dependencies() ` , ` MAIN_PACKAGE_ENTRIES `
232+ - Added ` generate_wrapper_package_json() ` for upgrade command
233+ - Simplified install scripts: only extract ` vp ` binary + generate wrapper
234+ - Simplified ` install-global-cli.ts ` : symlink-based local dev, wrapper-based CI
164235
165- 4 . ** Updated build system** :
236+ 5 . ** Updated build system** :
166237 - Added ` rolldown.config.ts ` to bundle global CLI modules into ` dist/global/ `
167238 - ` treeshake: false ` required for dynamic imports
168239 - Plugin to fix binding import paths in rolldown output
169240 - Simplified root ` package.json ` build scripts (removed global package steps)
170241
171- 5 . ** Updated CI/CD** :
242+ 6 . ** Updated CI/CD** :
172243 - Simplified ` build-upstream ` action (removed global package build steps)
173244 - Simplified ` release.yml ` (removed global package publish, now 3 packages instead of 4)
174- - Bumped cache key from ` v2 ` to ` v3 `
245+ - ` get_cli_version() ` reads from ` node_modules/vite-plus/package.json `
175246
176- 6 . ** Removed ` vite ` bin alias** — Only ` vp ` binary entry remains
247+ 7 . ** Removed ` vite ` bin alias** — Only ` vp ` binary entry remains
177248
178- 7 . ** Updated package.json** :
249+ 8 . ** Updated package.json** :
179250 - Added runtime deps: ` cross-spawn ` , ` picocolors `
180251 - Added devDeps from global: ` semver ` , ` yaml ` , ` glob ` , ` minimatch ` , ` mri ` , etc.
181252 - Added ` snap-test-global ` script
182253 - Added ` files ` entries: ` AGENTS.md ` , ` rules ` , ` templates `
183254
184- 8 . ** Updated documentation** : ` CLAUDE.md ` , ` CONTRIBUTING.md `
255+ 9 . ** Updated documentation** : ` CLAUDE.md ` , ` CONTRIBUTING.md `
185256
186257## Verification
187258
188259- ` cargo test -p vite_global_cli ` — Rust unit tests pass
189260- ` pnpm -F vite-plus snap-test-local ` — Local CLI snap tests pass
190261- ` pnpm -F vite-plus snap-test-global ` — Global CLI snap tests pass
191262- ` pnpm bootstrap-cli ` — Full build and global install succeeds
263+ - ` VITE_PLUS_VERSION=test bash packages/cli/install.sh ` — Production install from npm works
192264- Manual testing: ` vp create ` , ` vp migrate ` , ` vp --version ` , ` vp build ` , ` vp test ` all work
0 commit comments