>_ZSH-BETTER-NPM-COMPLETION
Better completion for npm. npm install <TAB> recommends from npm cache. npm uninstall <TAB> recommends from dependencies / devDependencies. npm run <TAB> shows the script body. Falls back to the default npm completion when none of the above applies.
Install
# Zinit
zinit ice lucid nocompile
zinit load MenkeTechnologies/zsh-better-npm-completion
# Oh My Zsh
git clone https://github.com/MenkeTechnologies/zsh-better-npm-completion \
"$HOME/.oh-my-zsh/custom/plugins/zsh-better-npm-completion"
# then: plugins+=(zsh-better-npm-completion)
# Antigen
antigen bundle MenkeTechnologies/zsh-better-npm-completion
# Manual
git clone https://github.com/MenkeTechnologies/zsh-better-npm-completion ~/.zsh-better-npm-completion
source ~/.zsh-better-npm-completion/zsh-better-npm-completion.plugin.zsh
Overview
The plugin is two files. zsh-better-npm-completion.plugin.zsh is a 4-line entry that runs the Zsh Plugin Standard 0= path header, then prepends ${0:h}/src to fpath — nothing else. src/_npm is a 153-line compsys completion file beginning with #compdef npm pnpm, so the same completer is registered for both the npm and pnpm binaries.
The top-level _npm dispatcher inspects $words[2] (the npm subcommand) and routes to one of three custom completers. Anything it does not recognise falls through to npm's own bundled completion, so you never lose default behaviour.
$words[2] | routed to |
i / install | __zbnc_npm_install_completion |
r / uninstall | __zbnc_npm_uninstall_completion |
run | __zbnc_npm_run_completion |
| anything else | __zbnc_default_npm_completion (shells out to npm completion) |
Usage
| install from cache | npm install <TAB> |
| install search (prefix ≥ 2 chars) | npm i ab<TAB> |
| uninstall from package.json | npm uninstall <TAB> |
| run a script (shows body) | npm run <TAB> |
| pnpm (same completer) | pnpm run <TAB> |
| everything else | falls through to default npm completion |
No configuration. Source the plugin, restart zsh (or re-run compinit), hit TAB.
Reference
npm install <TAB> — __zbnc_npm_install_completion
- If the current word starts with
-(you are mid-flag, e.g.npm i --save-d), the completer returns immediately and the default completion handles flags. - When the prefix is 2 or more characters (
(( $#PREFIX >= 2 ))), it runsnpm search --no-progress $PREFIXand offers the matching package names with their descriptions via_describe -t npm-search. Results are memoised under the cache keynpm_${PREFIX}_cacheusing zsh_retrieve_cache/_store_cache, so a repeated TAB on the same prefix does not re-hit the network. - It also always offers the contents of your local
~/.npm/cache directory ("$HOME/".npm/*(/:t)) through_wanted npm-cache … compadd -Q— packages you have installed before, available even offline.
npm uninstall <TAB> — __zbnc_npm_uninstall_completion
- If the third word is
-gor--global, it returns and lets the default completion list global modules. - Otherwise it walks up from
$PWDtoward/looking for the nearestpackage.json(__zbnc_recursively_look_for package.json). If none is found it returns. - It then offers the union of the
dependenciesanddevDependencieskeys from that file via_values— every package you could plausibly remove, regardless of which list it lives in.
npm run <TAB> — __zbnc_npm_run_completion
- Only fires on exactly
npm run ?((( $#words != 3 ))returns early). - Walks up for the nearest
package.json; returns if none is found. - Parses the
scriptsblock and offers each script name with its literal command body as the completion description (_describe), so you see what a script runs before pressing Enter.
Fallback — __zbnc_default_npm_completion
For every other subcommand the dispatcher shells out to npm's own completion engine: npm completion -- "${words[@]}" with COMP_CWORD / COMP_LINE / COMP_POINT set, feeding the result back through compadd. Nothing npm already completes is lost.
Helper functions
__zbnc_npm_command_arg | echoes $words[3] (used to detect -g/--global on uninstall) |
__zbnc_recursively_look_for | walks $PWD up to / looking for a named file |
__zbnc_get_package_json_property_object | extracts a top-level JSON object block via two sed passes, emits key=>value |
__zbnc_get_package_json_property_object_keys | same, then cut -f1 -d= to keep only the keys |
__zbnc_parse_package_json_for_script_suggestions | turns the scripts block into name + body completion candidates |
__zbnc_parse_package_json_for_deps | emits the keys of dependencies + devDependencies |
Configuration
There is no plugin-specific configuration. Two stock zsh mechanisms affect behaviour:
- Completion cache. The
npm searchmemoisation goes through zsh's standard_retrieve_cache/_store_cache, which obeyzstyle ':completion:*' use-cache yesandzstyle ':completion:*' cache-path ~/.zcompcache. Enable caching there to persist results across shell sessions. compinit. The plugin does not callcompinititself — it only addssrc/tofpath. Your.zshrc(or your plugin manager) must runcompinitfor_npmto be picked up.
Examples
# Offer cached packages plus a live search for the "exp" prefix npm install exp<TAB> # express expand-tilde expect ... (from npm search, cached as npm_exp_cache) # + everything already in ~/.npm/ # Remove a dependency without remembering whether it was prod or dev npm uninstall <TAB> # react react-dom typescript @types/node (deps + devDeps, unioned) # See what each script actually runs before invoking it npm run <TAB> # build -- tsc -p . # test -- jest # pnpm benefits from the same completer (#compdef npm pnpm) pnpm run <TAB>
Troubleshooting
- No completions appear. Confirm
compinitruns after the plugin loads and thatsrc/is onfpath(echo $fpath | tr ' ' '\n' | grep zsh-better-npm-completion). - Zinit users: use the
nocompileice. Without it,${0:h}/srccan resolve to the compiled.zwcpath instead of the on-disksrc/directory. npm runshows nothing. Thescriptsblock is parsed withsed; a single-line / collapsedpackage.jsondefeats the line-based extractor. The standard npm-formatted (multi-line) shape works.- Search returns nothing. Search only fires for prefixes of 2+ characters; below that you get only the
~/.npm/cache directory contents.
Sibling plugins
Part of the MenkeTechnologies zsh plugin family — the MenkeTechnologiesMeta umbrella:
- zsh-cargo-completion — live
cargo add/cargo installcompletion - zsh-cpan-completion — live MetaCPAN
cpan/cpanmcompletion - zsh-dotnet-completion — dotnet CLI completion
- zsh-expand — spacebar expansion (11,688 tests)
- zsh-gem-completion — ruby
gem installremote completion - zsh-git-acp — 159 git aliases + add/commit/push keybindings
- zsh-git-repo-cache — index every git repo on disk
- zsh-learn — MySQL-backed learning collection / quiz
- zsh-more-completions — 42,226-file mega completion corpus
- zsh-nginx — nginx commands + service-wrapper aliases
- zsh-pip-description-completion — pip remote completion with descriptions
- zsh-sed-sub — in-place sed substitution on the command line
- zsh-sudo — ESC ESC to prepend sudo
- zsh-xcode-completions — xcodebuild / xcrun / swift completion