How to write a .nvmrc file which automatically change node version - node.js

Hi I have two projects one in angularjs 4.4.7 and another in angular 6 version. I need to switch between node version for this. I tried using NVM which is working manually. How to handle the version change inside the angularjs program to change the node version when automatically the latest angular page gets loaded. Is there a possible way like that. I went through the #avn also but how to create the .node-version file. Can someone help with any link or correct sample steps

As #Aditya-M-P has already mentioned you can run the following command inside your projects root directory to generate the .nvmrc to set a desired NodeJS version for you project to work properly:
node -v > .nvmrc
It will generate something like this inside your .nvmrc file:
v10.16.2
Also using 10.16.2 without the v letter will work just fine.
However, in the official documentation in the .nvmrc section it never mentions that once you get this file created, the specified node version will be loaded automatically.
So that's not enough, you need to run the command below so that nvm can look for the .nvmrc file to load the specified version:
nvm use
Here it is a gif for demoing purpose:
To autoload the specified node version:
You need to add something else to your shell configuration depending on what you use bash or zsh
To get the exact configuration for each of them, please follow the instructions in the corresponding shell config section.
In my case I'm using zsh so I do need to add this at the end of my .zshrc file and here is the image that confirms it works like a charm:
# place this after nvm initialization!
autoload -U add-zsh-hook
load-nvmrc() {
local node_version="$(nvm version)"
local nvmrc_path="$(nvm_find_nvmrc)"
if [ -n "$nvmrc_path" ]; then
local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")
if [ "$nvmrc_node_version" = "N/A" ]; then
nvm install
elif [ "$nvmrc_node_version" != "$node_version" ]; then
nvm use
fi
elif [ "$node_version" != "$(nvm version default)" ]; then
echo "Reverting to nvm default version"
nvm use default
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc
I hope it could be useful for anyone else facing the same question! 😎

Check the README from nvm's repo on GitHub. Solutions have been given there.
Shell Integraton
For bash, put the following at the end of your $HOME/.bashrc, the shell will change the node version according to the .nvmrc file under the dir.
find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
cdnvm(){
cd "$#";
nvm_path=$(find-up .nvmrc | tr -d '[:space:]')
# If there are no .nvmrc file, use the default nvm version
if [[ ! $nvm_path = *[^[:space:]]* ]]; then
declare default_version;
default_version=$(nvm version default);
# If there is no default version, set it to `node`
# This will use the latest version on your machine
if [[ $default_version == "N/A" ]]; then
nvm alias default node;
default_version=$(nvm version default);
fi
# If the current version is not the default version, set it to use the default version
if [[ $(nvm current) != "$default_version" ]]; then
nvm use default;
fi
elif [[ -s $nvm_path/.nvmrc && -r $nvm_path/.nvmrc ]]; then
declare nvm_version
nvm_version=$(<"$nvm_path"/.nvmrc)
declare locally_resolved_nvm_version
# `nvm ls` will check all locally-available versions
# If there are multiple matching versions, take the latest one
# Remove the `->` and `*` characters and spaces
# `locally_resolved_nvm_version` will be `N/A` if no local versions are found
locally_resolved_nvm_version=$(nvm ls --no-colors "$nvm_version" | tail -1 | tr -d '\->*' | tr -d '[:space:]')
# If it is not already installed, install it
# `nvm install` will implicitly use the newly-installed version
if [[ "$locally_resolved_nvm_version" == "N/A" ]]; then
nvm install "$nvm_version";
elif [[ $(nvm current) != "$locally_resolved_nvm_version" ]]; then
nvm use "$nvm_version";
fi
fi
}
alias cd='cdnvm'
Cause there's no hook support in Bash, the solution above is ugly.
For zsh, put this into your $HOME/.zshrc
# place this after nvm initialization!
autoload -U add-zsh-hook
load-nvmrc() {
local node_version="$(nvm version)"
local nvmrc_path="$(nvm_find_nvmrc)"
if [ -n "$nvmrc_path" ]; then
local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")
if [ "$nvmrc_node_version" = "N/A" ]; then
nvm install
elif [ "$nvmrc_node_version" != "$node_version" ]; then
nvm use
fi
elif [ "$node_version" != "$(nvm version default)" ]; then
echo "Reverting to nvm default version"
nvm use default
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc
A Better Solution
A better solution is to use nodenv. I'm not kidding, nodenv is very different from nvm, and n.
nodenv is a member of the rbenv family. These version managers have big advantages over the others.
It changes node version without modifying environment variable PATH time by time, because it uses shim executables. This makes it having a builtin support to switch node version automatically.
Auto version switch in nodenv doesn't have to be hooked on chpwd to do periodical check for directory change. The version selection is delayed to when node command is executed.
The commands in nodenv are implemented in scripts. While, commands from nvm are implemented in functions, which means all the 4000+ line of code have to be parsed on shell startup and increases the shell init time dramatically. nodenv initializes much faster.
References
nodenv/nodenv

As pointed out in the GitHub issue thread related to this on the nvm repository, you may run the following command in each of your Angular project folders:
$ node -v > .nvmrc
Note that you need to first switch to the right version of node in each of your projects, before running the command above.
What's happening in the command:
node -v will out the current version of node to stdout.
The > symbol will then redirecting the output to a file called .nvmrc (it will overwrite if something already exists with the same file name).
Read more bash redirections under the REDIRECTION section under the bash man page: https://linux.die.net/man/1/bash
When you cd into your target directories, nvm will now first read the file, and auto-switch to the correct version.

After you create the .nvmrc file at the root of your project with the node version you need in that project, something like
v12.20.0
You should be able to cd into the project folder and run nvm use. This will print something like this:
Found '/Users/you/myproject/.nvmrc' with version <v12.20.0>
Now using node v12.20.0 (npm v6.14.8)
There is no automated way AFAIK, provided out of the box by NVM except by creating a bash script that does this for you which the NVM documentation covers in detail here

Related

Node doesn't start without source ~/.bash_profile command

I have to use source ~/.bash_profile for node command to work on my terminal. All the node commands work just fine after using source ~/.bash_profile. I used nvm to install Node LTS(v14.18.0).
My .bash_profile
export NVM_DIR=~/.nvm
source $(brew --prefix nvm)/nvm.sh
Is there any way to fix my node installation?
MacOS uses zsh as the default shell and not bash. You will need to create a ~/.zshrc file and then run the install script of nvm again.
You can probably skip the rerunning the install script by manually inserting in .zshrc file the following:
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
"Since macOS 10.15, the default shell is zsh and nvm will look for .zshrc to update, none is installed by default. Create one with touch ~/.zshrc and run the install script again." - from nvm docs on github.
https://github.com/nvm-sh/nvm#troubleshooting-on-macos
I was facing the same issue and the above solution didn't work for me.
Finally the solution that did work for me was adding these lines to ~/.zshrc file.
source ~/.nvm/nvm.sh

How to solve $PATH not resolving in VSCode

After recent upgrade of Fedora 30 -> Fedora 31 I'm observing that when I launch a terminal from within VSCode for the project that I'm working on, I see this message:
bash: sed: command not found
~/data/Programming/JS/React vgorcinschi $
It is complaining about sed because I have a script in ~/.bashrc that tries to include in PS1 information about git branches:
# somewhere in ~/.bashrc
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] \u $ "
If, within the same terminal, I do man sed it will complain in the same manner about man. If I open a system terminal and navigate to the same location my path would look like this:
~ vgorcinschi $ cd ~/data/Programming/JS/React
~/data/Programming/JS/React (ch14) vgorcinschi $ echo $PATH
./node_modules/.bin:/home/vgorcinschi/.nvm/versions/node/v10.6.0/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/vgorcinschi/.local/bin:/home/vgorcinschi/bin:/home/vgorcinschi/.local/bin:/home/vgorcinschi/bin:/usr/local/java/jdk1.8.0_231/bin:/opt/maven/bin:/snap/bin
NB Note how sed works from within the same directory.
Repeating echo $PATH from VS Code terminal yields just this: /home/vgorcinschi/data/Programming/JS/React/node_modules/.bin
I think this question may come-up so these are the contents of my ~/.config/Code/User/settings.json:
{
"git.enableSmartCommit": true,
"metals.javaHome": "/usr/local/java/jdk1.8.0_231",
"window.zoomLevel": 0,
"terminal.integrated.shell.linux": "/usr/bin/bash",
"editor.tabSize": 2,
"editor.detectIndentation": false,
"terminal.external.linuxExec": "bash"
}
The following could also be very important. The directory above is on a partition which is on HDD (/dev/sdb1) , my ~/.bashrc and ~/.bash-profile are on a SSD (/dev/mapper/fedora-home).
If I open an empty directory with VS Code on the main partition (on SSD) I don't have this issue with the Code's terminal. But I didn't have it on the other partition either before the upgrade - this was just two days ago.
So I am not sure what is the issue really due to, but hopefully someone could help me with it.
Update [2020.10.30]
.node_modules appear on the path inside VSCode terminal because of these lines in ~/.bashrc:
if [ -d "$PWD/node_modules/.bin" ]; then
PATH="$PWD/node_modules/.bin"
fi
VS Code version
Version: 1.50.1
Commit: d2e414d9e4239a252d1ab117bd7067f125afd80a
Date: 2020-10-13T14:44:48.716Z
Electron: 9.2.1
Chrome: 83.0.4103.122
Node.js: 12.14.1
V8: 8.3.110.13-electron.0
OS: Linux x64 5.8.15-101.fc31.x86_64
Well that's embarrassing. As it becomes obvious from my latest description update I was re-setting the PATH once ~/.node_modules were in a sub-directory. So A solution was to add as a suffix the existing PATH in the if block:
if [ -d "$PWD/node_modules/.bin" ]; then
PATH="$PWD/node_modules/.bin:$PATH"
fi
Since I haven't played with ~/.bashrc in a while, I have no idea how did it use to work before.

Why does binary npm file have two checks

Every binary inside node_modules/.bin have the following code:
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
# check if there is node executable in the same directory as this binary
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../angular-cli/bin/ng" "$#"
ret=$?
else
node "$basedir/../angular-cli/bin/ng" "$#"
ret=$?
fi
exit $ret
I sort of understand what this code is doing (comments are mine), but is there anywhere an explanation why they are here (some use cases)?
Checking for the node binary in the same directory is for the cases when this module was installed globally and has an executable file in the same place as Node. It makes sure that it uses the same node for which it was installed, even if you have some other Node in your PATH.
The else branch uses just node which means the same binary as you get by typing:
which node
which is the first node in one of the directories in the PATH environment variable.
The problem with installing Node scripts is that they have to have some shebang line. People usually use #!/usr/bin/env node to run whatever node you have in PATH. But it may be a different node (possibly incompatible) than the one that was actually used to install that script.
Incidentally, that is one of the reasons why I prefer installing Node from sources than from the binary packages, because otherwise my npm script can run the wrong node if I have multiple versions installed. See my tutorial on Node installation for details.
Thye Cygwin test checks if the output of the uname command contains the word CYGWIN and in that case uses a Cygwin-specific path resolution using cygpath -w "$basedir". Cygwin is a collection of GNU and Open Source tools which provide functionality similar to a Linux distribution on Windows - see: https://www.cygwin.com/ - and it does some path translation to make scripts written for Unix and Linux work on Windows. For Linux the output of uname is just Linux. On Cygwin it contains CYGWIN.

How do I access my nvm node version number from .bash_profile to customize my terminal prompt?

I want to customize my terminal prompt so that it displays whatever version of node that I'm using through nvm. So, for example, the terminal prompt my read:
[current directory] : [node version] $
Does anyone know how to modify .bash_profile to display the current nvm node version?
Add this to your ~/.bashrc :
PS1='\w : $(node --version) \$'
or
PS1='\w : $(nvm run node --version) \$'
In bash:
nvm=$([[ $NVM_BIN =~ ([^/]+)/bin$ ]] && echo "${BASH_REMATCH[2]}" || echo "system")
PS1="($nvm) $PS1"
This is much faster than running nvm directly. For timings and zsh, see here.

how to make nvm automatically sourced upon login

I am using nvm to manage the node version.
I like to make nvm sourced upon login, so I dont have to do it manually everytime I login.
in my user home directory, there's a .bashrc file. I appended following two lines to the end of the file. then restart my mac os. after I login, nvm is not sourced. I have to manually run them again. coudln't figure out whats wrong. please help.
. ~/nvm/nvm.sh
nvm use 0.8.20
You need to put into your .bash_profile this line
[[ -s $HOME/.nvm/nvm.sh ]] && . $HOME/.nvm/nvm.sh
Source the file (source ~/.bash_profile) or reopen the shell and then in the shell execute:
$ nvm alias default <version>
That would load node in any new shell you open.
Using .nvmrc file
It turns out that creating a .nvmrc file in your home directory is enough. It is loaded automatically when you open the terminal.
To create the .nvmrc file with the version we want to run (0.12.2) do this:
echo '0.12.2' > ~/.nvmrc
If that for some reason doesn't work, force load the nvm from your .bashrc
But only if you're running in interactive mode.
Add the command to run nvm use 0.12.2 (or any other version you'd like to run) if the shell is launched in interactive mode to the end of .bashrc.
echo '[[ $- == *i* ]] && nvm use 0.12.2' >> ~/.bashrc
Boom, done! :)
For Mac at least, open your .profile file, and add this line in
[[ -s $HOME/.nvm/nvm.sh ]] && source "$HOME/.nvm/nvm.sh"
# Load NVM into a shell session *as a function*
Done, bingo bango, GG.
Please use the official website carefully as I used
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | zsh from https://github.com/nvm-sh/nvm and it automatically does my work from installing to load automatically setup.

Resources