Skip to content

feat: XDG directory layout via VpDirs - #2346

Open
forehalo wants to merge 4 commits into
voidzero-dev:mainfrom
forehalo:feat/dirs-path-resolution
Open

feat: XDG directory layout via VpDirs#2346
forehalo wants to merge 4 commits into
voidzero-dev:mainfrom
forehalo:feat/dirs-path-resolution

Conversation

@forehalo

@forehalo forehalo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Close #827

Summary

Centralize on-disk paths in vp_shared::VpDirs and default fresh installs to the split XDG/platform layout. Existing default installs under ~/.vite-plus remain on the legacy monolithic root for now (grandfathered via existence-gated resolution). Automatic layout migration is out of scope here and tracked as follow-up work.

Resolution (high level)

Ordered sources (per category): deprecated VP_HOME (custom full-root pin) → existing ~/.vite-plus / ./.vite-plus (exist-gated legacy mapping) → VP_BIN_DIR / VP_DATA_DIR / VP_CACHE_DIRXDG_* → platform defaults.

Category Split default (Unix) Legacy (~/.vite-plus)
bin ~/.local/bin <root>/bin
data ~/.local/share/vite-plus <root>
cache ~/.cache/vite-plus <root>/cache
config ~/.config/vite-plus <root>
state ~/.local/state/vite-plus <root>

Windows split uses %LOCALAPPDATA%\vite-plus\{bin,data,cache,state} and %APPDATA%\vite-plus for config. Relative VP_* / XDG_* values are treated as unset.

Commits (review order)

  1. docs(rfc): document split directory layout via VpDirs

    • Adds rfcs/directory-layout.md describing the resolution chain, grandfathering, installer alignment, and follow-ups (VP_HOME cleanup, migrate-on-upgrade).
    • Historical RFCs (e.g. env-command.md) are left unchanged.
  2. feat(shared): introduce VpDirs with strategy-gated path resolution

    • Replaces get_vp_home / home.rs with VpDirs + dirs/resolution.rs strategy chain.
    • Category roots and first-level data subdirs only; deeper paths stay with owning features.
    • Wires EnvConfig / env vars so tests isolate layout via test_guard / for_test_with_home without process-env leaks.
    • Updates AGENTS.md pointer to the central path API.
  3. refactor: migrate call sites from get_vp_home to VpDirs

    • Migrates global CLI, shims, js_runtime, package manager, implode, env setup/doctor, and related helpers onto VpDirs.
    • Under the split layout, injects VP_BIN_DIR / VP_DATA_DIR / VP_CACHE_DIR into JS children when unset; hooks / org-tarball honor those roots.
    • Snapshot runner and fixtures provision a full legacy on-disk shape and pin install roots explicitly (no binary PATH self-location in VpDirs).
    • CI bootstrap seeds ~/.vite-plus before bootstrap-cli so existing snapshot/e2e paths keep finding the global binary until layout cleanup lands.
  4. feat(install): default installers to the split XDG layout

    • install.sh / install.ps1 / vp-setup / trampoline / Dockerfile / install-global-cli share the same resolution chain as the CLI.
    • Fresh install → split; existing ~/.vite-plus or explicit VP_HOME / --install-dir → legacy monolithic root.
    • Installer env docs (VP_HOME deprecated) and standalone-install CI coverage for split + legacy upgrade/implode.

User impact

Install Impact
Existing ~/.vite-plus Unchanged path (grandfathered until migrate follow-up)
Custom VP_HOME Still works (deprecated pin)
Fresh Split layout; typically only ~/.local/bin needs to be on PATH

Follow-up

Test plan

  • cargo check / unit tests for dirs resolution and layout helpers
  • Snapshot fixtures adapted for layout isolation (pin VP_HOME / disable shared seed where fakes are used)
  • Installer unification (single install.sh / install.ps1)
  • Formatting for RFC + install-global-cli
  • Windows unit test: uninstall removes shims under for_test_with_home bin mapping
  • CI: test-standalone-install (released + local split/legacy-upgrade jobs)
  • Manual: fresh install lands on split paths; existing ~/.vite-plus still works without moving

@netlify

netlify Bot commented Aug 5, 2026

Copy link
Copy Markdown

Deploy Preview for viteplus-preview ready!

Name Link
🔨 Latest commit ca9dcca
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a75ccc3647a480008cac815
😎 Deploy Preview https://deploy-preview-2346--viteplus-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@forehalo
forehalo force-pushed the feat/dirs-path-resolution branch 4 times, most recently from b8069a5 to 2f8be03 Compare August 5, 2026 17:13
@forehalo forehalo changed the title feat: introduce Dirs for unified XDG-aware path resolution feat: XDG-compliant directory layout via unified Dirs resolution Aug 6, 2026
@forehalo
forehalo force-pushed the feat/dirs-path-resolution branch 2 times, most recently from 08b7589 to 4e2abdf Compare August 6, 2026 17:25
@forehalo forehalo changed the title feat: XDG-compliant directory layout via unified Dirs resolution feat: XDG directory layout via VpDirs Aug 6, 2026
@fengmk2

fengmk2 commented Aug 7, 2026

Copy link
Copy Markdown
Member

@liangmiQwQ Your refactor needs to wait until this merge is completed first, as the changes are expected to be quite significant.

@socket-security

socket-security Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​temp-env@​0.3.68710093100100

View full report

@forehalo
forehalo force-pushed the feat/dirs-path-resolution branch 2 times, most recently from 402f6db to 6b26e69 Compare August 7, 2026 10:49
Capture the resolution design, grandfathering of existing ~/.vite-plus
installs, installer alignment, and follow-ups for VP_HOME cleanup and
migrate-on-upgrade.
Replace get_vp_home / the monolithic home module with VpDirs: category
roots (bin, data, cache, config, state) come from an ordered resolution
chain in dirs/resolution.rs (legacy Exist-gated grandfathering, VP_* and
XDG Set overrides, then platform defaults), and first-level data subdirs
(current, js_runtime, package_manager, packages, bins) are pure joins.
Files and deeper trees stay with their features.

Wire EnvConfig so test_guard/for_test_with_home isolate install roots
without process-env leaks. Groundwork for voidzero-dev#827.
Switch global CLI, shims, js_runtime, package_manager, and related helpers
off the old home path helpers onto VpDirs. Inject VP_BIN_DIR/VP_DATA_DIR/
VP_CACHE_DIR into JS children under the split layout; teach hooks and
org-tarball to honor those roots. implode, env setup/doctor, and snapshot
fixtures understand split vs legacy layouts without binary self-location
(pin VP_HOME / isolated roots explicitly in tests).
Fresh installs land versions under the data dir and shims under the bin
dir (XDG/platform defaults via VpDirs). install.sh, install.ps1, vp-setup,
Dockerfile, trampoline, and install-global-cli follow the same resolution
chain as the CLI; an existing ~/.vite-plus or explicit VP_HOME/--install-dir
keeps the legacy monolithic root.

Document the installer env surface (VP_HOME deprecated) and cover split
plus legacy upgrade/implode paths in standalone install CI.
@forehalo
forehalo force-pushed the feat/dirs-path-resolution branch from 6b26e69 to ca9dcca Compare August 7, 2026 12:17
@fengmk2 fengmk2 self-assigned this Aug 7, 2026
@fengmk2

fengmk2 commented Aug 7, 2026

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca9dcca689

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +34 to +35
let split = base.join("data").join("current").join("bin").join("vp.exe");
split.is_file().then_some((split, false))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve custom Windows data roots in trampolines

When VP_DATA_DIR or VP_BIN_DIR places the split data and bin directories under different parents, the Windows trampoline only searches for vp.exe at the fixed sibling path <parent-of-bin>\data\current\bin\vp.exe. The installer supports these overrides independently, so every generated vp.exe, node.exe, and other trampoline exits before spawning the real CLI for such an installation, even if the overrides remain set. The trampoline needs to locate the configured data directory rather than assuming the default sibling layout.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Comment thread packages/cli/install.ps1
Comment on lines +39 to +40
if ($env:VP_DATA_DIR) {
$InstallDir = $env:VP_DATA_DIR

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore relative Windows directory overrides

When a Windows user sets a relative VP_DATA_DIR, this installer accepts it and installs the payload relative to its working directory, while VpDirs deliberately treats relative VP_*_DIR values as unset. Consequently the installed CLI resolves its data from the platform default instead of the location populated by the installer; VP_BIN_DIR has the same mismatch below. Validate these overrides as rooted paths and fall back to the defaults when they are relative.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

// (legacy layout) or `<base>\data\current\bin\vp.exe` (split layout).
#[cfg(windows)]
const VP_USE_CMD_CONTENT: &str = "@echo off\r\nset VP_ENV_USE_EVAL_ENABLE=1\r\nset VP_HOME=%~dp0..\r\nfor /f \"delims=\" %%i in ('%~dp0..\\current\\bin\\vp.exe env use %*') do %%i\r\nset VP_ENV_USE_EVAL_ENABLE=\r\n";
const VP_USE_CMD_CONTENT: &str = "@echo off\r\nset VP_ENV_USE_EVAL_ENABLE=1\r\nset \"VP_EXE=%~dp0..\\current\\bin\\vp.exe\"\r\nif not exist \"%VP_EXE%\" set \"VP_EXE=%~dp0..\\data\\current\\bin\\vp.exe\"\r\nfor /f \"delims=\" %%i in ('%VP_EXE% env use %*') do %%i\r\nset VP_ENV_USE_EVAL_ENABLE=\r\n";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Generate vp-use.cmd from the resolved data directory

For a Windows split install using independent VP_BIN_DIR and VP_DATA_DIR values, vp-use.cmd searches only the legacy sibling and default <base>\data layouts, so the documented vp-use 20 flow invokes a nonexistent executable even though the main installation is otherwise valid. Render the resolved VpDirs::current_dir() location into this wrapper, or pass enough configuration for it to find the custom data root.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Comment on lines +64 to +65
elif [ -n "\${HOME-}" ]; then
__vp_bin="$HOME/.local/bin"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the Windows split bin directory to hook PATH

On a default Windows split installation, generated Git hooks reach this branch with neither VP_BIN_DIR nor VP_HOME set and append $HOME/.local/bin, but Windows installs the shims under %LOCALAPPDATA%\vite-plus\bin. Hooks launched by an IDE or other process that has not inherited the installer-updated User PATH therefore cannot find vp, managed Node, or other Vite+ shims. The fallback needs to use the Windows platform bin location rather than the Unix split default.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Comment on lines +96 to +97
if let Some(home) = EnvConfig::get().user_home {
return Some(home);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prefer USERPROFILE when grandfathering Windows installs

On Windows when HOME is set to a different directory from USERPROFILE, this returns the HOME value cached by EnvConfig, so the resolution chain never checks %USERPROFILE%\.vite-plus. In particular, vp-setup.exe can miss an existing legacy installation and create a second split installation even though the PowerShell installer correctly grandfathers %USERPROFILE%\.vite-plus. Select the platform-specific home variable on Windows, or use BaseDirs, before applying the legacy existence check.

AGENTS.md reference: AGENTS.md:L47-L47

Useful? React with 👍 / 👎.

Comment on lines +143 to +148
let mut failed = false;
for dir in &self.dirs {
if !dir.as_path().exists() {
continue;
}
if remove_vite_plus_dir(dir).is_err() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Refuse to recursively delete unsafe category overrides

When a split-layout user points VP_DATA_DIR or VP_CACHE_DIR at a shared or critical directory—for example VP_DATA_DIR=$HOME or VP_CACHE_DIR=/tmpvp implode --yes places that override directly in self.dirs and recursively deletes the entire directory. Unlike the shared bin cleanup, there is no ownership or safe-path check before remove_dir_all, so a valid absolute override can cause extensive unrelated data loss. Reject unsafe roots/home directories or remove only Vite+-owned contents for overridden category paths.

Useful? React with 👍 / 👎.

Comment on lines +270 to +276
dir.as_path().exists()
}
}
}
}

macro_rules! resolutions {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require legacy candidates to be directories

If $HOME/.vite-plus or ./.vite-plus exists as a regular file or dangling-install artifact rather than a directory, this existence gate still selects the legacy layout. All resolved paths then become children of that file, so configuration, runtime downloads, and shim setup fail, while both installers correctly use directory-only checks and would have selected the split layout. Use is_dir() for legacy-root gates so the CLI and installers resolve the same layout.

Useful? React with 👍 / 👎.

@liangmiQwQ

liangmiQwQ commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

IMO, I would be a little bit curious about the benefit for this move. The current VP_HOME layout has been working well, the similar single-root approaches are also used by tools like Cargo, Bun, and Volta. Moving to a new layout is not a low-cost change: it affects existing installations, upgrade paths, user workflows, and debugging. For a released tool, I think we should carefully evaluate the trade-offs.

For example, I help maintain the Vite+'s global package installations in several, I'm concerned that using ~/.local/bin may introduce heavier binary conflicts and overlapping issues. I'm not sure if ~/.local/bin, a public bin location, should be managed (even partially) by Vite+.

I'm not against this direction, I just want to understand the motivation and the problem it solves. If it does help Vite+ improve, then now is really a good time to implement it (before RC).

Comment thread rfcs/directory-layout.md
| ------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`VP_HOME`** (deprecated) | Set | When set, pins the **legacy monolithic mapping** under that root for all categories. |
| **`~/.vite-plus`** | Exist | When that directory exists, use the legacy mapping under it. |
| **`./.vite-plus`** | Exist | When present in the process cwd, same legacy mapping (project-local / tests). |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This compatibility is limited to the voidzero-dev/vite-plus repo, right? Normal projects shouldn't be compatible with this logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

use xdg config

3 participants