Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

`~/.vite-plus/env` costs 85-315 ms per shell start on long PATHs (super-linear `${var#*pattern}` in the dedupe loop)

Open Beginner friendly
#2,898 0 comments 3 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@jonatankruszewski is already working on this.

Since Oct 6, 2026.

  • #2905 by @jonatankruszewski — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
82/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
bash, rust, shell, zsh

Research direction

The POSIX env template is ENV_TEMPLATE_POSIX in crates/vp_global_cli/src/commands/env/setup.rs. Replace the ${__vp_tmp#*":${__vp_dir}:"} dedupe loop with the IFS-split rebuild given in the issue so PATH stays linear on zsh, bash, and dash. Slot the rewrite into the existing snapshot runner from #2494 (sh, bash, zsh) and confirm the emitted ~/.vite-plus/env still produces the same PATH.

Written by the indexing model from the issue text.

Description

contribution welcome

Summary

The PATH dedupe loop in the POSIX env script (ENV_TEMPLATE_POSIX in crates/vp_global_cli/src/commands/env/setup.rs, introduced by #2494, shipped as ~/.vite-plus/env) runs

__vp_after="${__vp_tmp#*":${__vp_dir}:"}"

once per occurrence of each vp dir in PATH. ${var#*X} (shortest-prefix removal with a leading *) is super-linear in the offset of X on every POSIX shell I tested. With a PATH of ~8 KB (147 entries, which is what mise activate produces for a user with ~110 globally installed tools) each expansion takes 50-260 ms depending on the shell, and the loop runs twice (bin and fallback-bin).

Because the installer puts . "$HOME/.vite-plus/env" into ~/.zshenv, this is paid by every zsh process on the machine, interactive or not: zsh -c true goes from 0 ms to 180 ms. Every #!/usr/bin/env zsh script, every zsh -c from a tool, every IDE env probe pays it.

Environment: vp v1.0.0, macOS 26 arm64, zsh 5.9 (/bin/zsh) and 5.9.2 (brew), bash 5.3.15 and 3.2, dash.

Repro (zsh, 3 lines)

PATH="$(printf '/Users/me/.local/share/mise/installs/tool-%03d/latest/bin:' {1..140})/x/bin"; tmp=":${PATH}:"; d=/x/bin
typeset -F3 SECONDS=0; a="${tmp#*":${d}:"}";  print "#*  : ${SECONDS}s"
SECONDS=0;              b="${tmp%%":${d}:"*}"; print "%%* : ${SECONDS}s"
#*  : 0.109s
%%* : 0.000s

Measurements

Single ${tmp#*":${dir}:"} on an 8.7 KB PATH, target dir last:

shell #* (shipped) %%
zsh 5.9 147 ms 5 ms
zsh 5.9.2 146 ms 5 ms
bash 5.3.15 63 ms 14 ms
bash 3.2 53 ms 13 ms
dash 264 ms 4 ms

Cost of sourcing the whole env file in zsh -f as PATH grows (vp dirs present, so the loop runs):

PATH entries chars time
20 1353 4 ms
40 2653 13 ms
80 5253 51 ms
147 9656 172 ms
300 19754 720 ms

Whole env file from the real 147-entry PATH, median of 5:

file zsh 5.9 zsh 5.9.2 bash 5.3 bash 3.2 dash
shipped ~/.vite-plus/env 177 178 85 83 315
literal-prefix one-liner (see below) 6 6 128 133 147
IFS-split loop (proposed) 6 6 9 13 5

Proposed fix

A literal-prefix strip (__vp_after="${__vp_tmp#"${__vp_before}:${__vp_dir}:"}") fixes zsh but makes bash and dash slower (table above). Splitting PATH on : once and rebuilding it is linear on every shell and produced an identical PATH to the current script in /bin/sh, dash, bash 3.2, bash 5.3 and zsh for a PATH containing both vp dirs, one of them twice:

__vp_bin="$HOME/.vite-plus/bin"
__vp_fallback="$HOME/.vite-plus/fallback-bin"
__vp_new=
__vp_ifs=$IFS
IFS=:
if [ -n "${ZSH_VERSION-}" ]; then setopt localoptions shwordsplit noglob; else set -f; fi
for __vp_p in $PATH; do
  case "$__vp_p" in "$__vp_bin"|"$__vp_fallback") ;; *) __vp_new="${__vp_new:+${__vp_new}:}${__vp_p}" ;; esac
done
IFS=$__vp_ifs
[ -n "${ZSH_VERSION-}" ] || set +f
PATH="${__vp_bin}${__vp_new:+:${__vp_new}}:${__vp_fallback}"
export PATH
unset __vp_bin __vp_fallback __vp_new __vp_ifs __vp_p

The snapshot runner from #2494 already exercises the file in sh, bash and zsh, so the rewrite can slot into the same tests.

Who hits this

Anyone whose PATH is long when a nested shell starts: mise/asdf/nix users with many tools, monorepo devs with per-package bin dirs, CI images. A fresh login shell with a 30-entry PATH pays ~4 ms, which is why it is easy to miss.

Dominant language
Rust
Stars
6k
Forks
271
Avg merge
1d 15h
Merged PRs (30d)
172

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from voidzero-dev/vite-plus

All issues in voidzero-dev/vite-plus

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.