diff --git a/README.md b/README.md index 3899b8a..88f71be 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,17 @@ export NVM_AUTO_USE=true antigen bundle lukechilds/zsh-nvm ``` +### Local bins + +If your projects include dependencies with binaries, this ensures that they take precedence on your `$PATH` over globally installed ones. + +For example, if you are using antigen, you would put the following in your `.zshrc`: + +```shell +export NVM_PREFER_LOCAL_BINS=true +antigen bundle lukechilds/zsh-nvm +``` + ## Installation ### Using [Antigen](https://github.com/zsh-users/antigen) diff --git a/tests/options/NVM_PREFER_LOCAL_BINS b/tests/options/NVM_PREFER_LOCAL_BINS new file mode 100755 index 0000000..0a886a7 --- /dev/null +++ b/tests/options/NVM_PREFER_LOCAL_BINS @@ -0,0 +1,50 @@ +#!/bin/sh +source ../common.sh + +# Setup projects in tmp, so we don't have ancestor paths of this repo messing +# with things. +cd $(mktemp -d) +echo "Running NVM_PREFER_LOCAL_BINS in $(pwd)\n" + +mkdir -p package/src/a/b +touch package/package.json +mkdir -p package/vendor +touch package/vendor/package.json + +# Load it. +export NVM_PREFER_LOCAL_BINS=true +load_zsh_nvm + +# Check cding without node installed (shouldn't fail) +cd ./package +cd ./src/a/b +cd ../../../.. + +# Now test with node installed +nvm install 8 +nvm use 8 +# This is our base path, without any local binary paths in it. +base_path=$PATH +# npm bin can resolve to a different path (/private/var instead of /var) on some +# platforms. It also resolves to the current directory if there is no +# package.json up the current path. So, we use it to expand our path. +sandbox=$(dirname $(dirname $(npm bin))) + +# Check cd directly into a package directory +cd ./package +[[ "$PATH" == "$sandbox/package/node_modules/.bin:$base_path" ]] || die "Didn't prepend local bin dir to path when moving into package directory" + +# Check package subdirs +cd ./src +cd ./a +cd ./b +[[ "$PATH" == "$sandbox/package/node_modules/.bin:$base_path" ]] || die "Didn't prepend local bin dir to path when moving into package subdirectories" + +# Check nested packages +cd ../../.. +cd ./vendor +[[ "$PATH" == "$sandbox/package/vendor/node_modules/.bin:$base_path" ]] || die "Didn't prepend local bin dir to path when moving into a nested package" + +# Check leaving +cd ../.. +[[ "$PATH" == "$base_path" ]] || die "Didn't remove local bin dir when leaving a package directory" diff --git a/zsh-nvm.plugin.zsh b/zsh-nvm.plugin.zsh index a672ed7..27da5e2 100644 --- a/zsh-nvm.plugin.zsh +++ b/zsh-nvm.plugin.zsh @@ -195,6 +195,28 @@ _zsh_nvm_install_wrapper() { esac } +_zsh_nvm_add_local_bin_path() { + _zsh_nvm_has npm || return # No-op until we have a copy of node in our env. + local nearest_package=$(nvm_find_up package.json) + # Skip if we're moving within a project (npm bin is slow). + # + # Also notice that we prepend a value in front of the variable so that it + # isn't expanded by %~ prompts. + [[ "z$nearest_package" == "$NVM_CURRENT_PACKAGE_PATH" ]] && return + NVM_CURRENT_PACKAGE_PATH=z$nearest_package + + # Ensure a clean slate; so that we are not prepending paths forever. + if [[ -n "$NVM_PATH_WITHOUT_LOCAL_BINS" ]]; then + PATH=$NVM_PATH_WITHOUT_LOCAL_BINS + unset NVM_PATH_WITHOUT_LOCAL_BINS + fi + + if [[ -n "$nearest_package" ]]; then + NVM_PATH_WITHOUT_LOCAL_BINS=$PATH + PATH=$(npm bin):$PATH + fi +} + # Don't init anything if this is true (debug/testing only) if [[ "$ZSH_NVM_NO_LOAD" != true ]]; then @@ -209,6 +231,9 @@ if [[ "$ZSH_NVM_NO_LOAD" != true ]]; then # Auto use nvm on chpwd [[ "$NVM_AUTO_USE" == true ]] && add-zsh-hook chpwd _zsh_nvm_auto_use && _zsh_nvm_auto_use + + # Ensure that local bins take precedence on $PATH. + [[ "$NVM_PREFER_LOCAL_BINS" == true ]] && add-zsh-hook chpwd _zsh_nvm_add_local_bin_path && _zsh_nvm_add_local_bin_path fi fi