使用发行版 nodejs 包(Ubuntu)将 NPM 安装到 home 目录

我希望使用发行版 Node.js 包(或者 Chris Lea Ppa以获得更新的版本) ,但是要将 NPM 安装到我的主目录中。

这可能看起来有点挑剔,但是对于使用 polyglot/github 的开发者来说,这是一种在 Linux 下设置语言运行时/库环境的相当惯用的方法: 运行时的发行版包,每个用户环境中的第三方库(参见 viralenv,RVM-RVM 也会为你构建 Ruby,如果你愿意的话)。如果有必要,我将在本地构建节点,但它是一个 PITA,因为 Node 正在成为许多项目的附带开发需求。

66123 次浏览

NPM will install local packages into your projects already, but I still like to keep the system away from my operating system's files. Here's how I suggest compartmentalizing Nodejs packages:

Install Nodejs and NPM via the chris-lea PPA. Then I set up a package root in my homedir to hold the Node "global" packages:

 $ NPM_PACKAGES="$HOME/.npm-packages"
$ mkdir -p "$NPM_PACKAGES"

Set NPM to use this directory for its global package installs:

 $ echo "prefix = $NPM_PACKAGES" >> ~/.npmrc

Configure your PATH and MANPATH to see commands in your $NPM_PACKAGES prefix by adding the following to your .zshrc/.bashrc:

# NPM packages in homedir
NPM_PACKAGES="$HOME/.npm-packages"


# Tell our environment about user-installed node tools
PATH="$NPM_PACKAGES/bin:$PATH"
# Unset manpath so we can inherit from /etc/manpath via the `manpath` command
unset MANPATH  # delete if you already modified MANPATH elsewhere in your configuration
MANPATH="$NPM_PACKAGES/share/man:$(manpath)"


# Tell Node about these packages
NODE_PATH="$NPM_PACKAGES/lib/node_modules:$NODE_PATH"

Now when you do an npm install -g, NPM will install the libraries into ~/.npm-packages/lib/node_modules, and link executable tools into ~/.npm-packages/bin, which is in your PATH.

Just use npm install -g as you would normally:

[justjake@marathon:~] $ npm install -g coffee-script
... (npm downloads stuff) ...
/home/justjake/.npm-packages/bin/coffee -> /home/justjake/.npm-packages/lib/node_modules/coffee-script/bin/coffee
/home/justjake/.npm-packages/bin/cake -> /home/justjake/.npm-packages/lib/node_modules/coffee-script/bin/cake
coffee-script@1.3.3 /home/justjake/.npm-packages/lib/node_modules/coffee-script


[justjake@marathon:~] $ which coffee
/home/justjake/.npm-packages/bin/coffee

The solution posted by Just Jake is great. However, due to a bug with npm > 1.4.10, it may not work as expected. (See this and this)

While the bug is solved, you can downgrade to npm 1.4.10 by following this steps:

  1. Comment the prefix line in your $HOME/.npmrc
  2. Run sudo npm install -g npm@1.4.10
  3. Ensure that the right version of npm is installed (npm --version)
  4. Uncomment the prefix line in your $HOME/.npmrc
  5. Proceed to install your global packages in your home folder!.

To expand on the answer provided by Just Jake and user1533401: I am unable to downgrade as I use shared hosting and node is installed in a system directory. This is also why I have change the directory where npm installs global scripts if I want it to do that. For those in the same boat, here is a another temporary fix I found works:

npm install -g --prefix=$(npm config get prefix) <package>

The bug is that npm doesn't read your per-user config file, but specifying it every time you install a global script fixes that. Found here.

Because python does already a great job virtualenv, I use nodeenv. Compared to nvm, you can create multiple environments for the same node version (e.g. two environments for node 0.10 but with different sets of packages).

ENVNAME=dev1


#  create an environment
python -m virtualenv ${ENVNAME}


# switch to the newly created env
source ${ENVNAME}/bin/activate


# install nodeenv
pip install nodeenv


# install system's node into virtualenv
nodeenv --node=system --python-virtualenv

The readme is pretty good: https://github.com/ekalinin/nodeenv

I used @just-jake solution for some time and found that nvm is easier to setup. Also it's much powerful solution that allows to install and use different versions of nodejs.

On Ubuntu 14.04 or 16.04:

  1. Install prerequisite packages for building nodejs:

    sudo apt-get update
    sudo apt-get install build-essential libssl-dev
    
  2. Install nvm:

    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash
    

    In case newer version of nvm will be available you can find actual installation command on nvm site.

  3. nvm installer will add bootstrap script to ~/.bashrc, so you need either to reopen terminal to run it, or to do:

    source ~/.bashrc
    
  4. Now you can install any nodejs version you like, switch between them etc.

    Use nvm ls-remote to list available nodejs versions.

    To install, for example, nodejs v4.2.4 do:

    # install v4.2.4
    nvm install v4.2.4
    # use nodejs v4.2.4 in the current terminal session
    nvm use v4.2.4
    # use v4.2.4 by default in new terminal session
    nvm alias default v4.2.4
    

As stated already here and here

npm config set prefix ~
echo export PATH=\$PATH:\~/bin >> ~/.bashrc
. ~/.bashrc

Jake's answer was posted in 2012 and while useful it references Chris Lea's Node.js PPAs who are no longer updated since march 2015.

Here's the steps I use to install Node.js and npm in my home directory:

Install Node.js with nvm (no sudo required):

curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh | bash
source ~/.bashrc
nvm install 7
npm install -g npm  # update npm

Now you can install -g without sudo and everything goes into ~/.nvm/

Or install Node.js without nvm (official instructions):

Install Node.js

  • Node.js v6 (current LTS as of May 2017):

    curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash -
    sudo apt-get install -y nodejs
    
  • Node.js v7:

    curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -
    sudo apt-get install -y nodejs
    

Change npm's default directory to a local one:

mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
export PATH="$HOME/.npm-global/bin:$PATH"  # ← put this line in .bashrc
source ~/.bashrc  # if you only updated .bashrc

Alternatively replace .npm-global by the directory of your choice.

Update npm and check it is installed in your $HOME directory:

$ npm install npm -g
/home/<username>/.npm-global/bin/npm -> /home/<username>/.npm-global/lib/node_modules/npm/bin/npm-cli.js
/home/<username>/.npm-global/lib
└─┬ npm@3.10.6
├─┬ glob@7.0.5
│ └── minimatch@3.0.2
├── npm-user-validate@0.1.5
└── rimraf@2.5.3

Now you can install -g without sudo and without messing with your system files.

Other answers have outdated solutions: 2020's solution is using NPM_CONFIG_PREFIX environment variable. (See details)

For example,

$ NPM_CONFIG_PREFIX="$HOME/.npm-packages" npm install -g ios-sim
/Users/<name>/.npm-packages/bin/ios-sim -> /Users/<name>/.npm-packages/lib/node_modules/ios-sim/bin/ios-sim
+ ios-sim@9.0.0
added 108 packages from 68 contributors in 3.094s

I have a slightly different solution to a similar problem, which was due to my installing npm globally so I can use it in the Terminal of my macOS system. I simply initialised it locally at the root directory of my repository with the command:

npm init --yes

This did the trick for enabling me to install node packages in the local root directory at /node_modules with the "package.json" and "package-lock.json" files instead of at the user's home directory.

At least on Ubuntu the default config for system wide npm is that npm install --global tries to install packages to /usr/lib/node_modules. To set different default for your own user account run following once:

mkdir -p ~/.npm/lib/bin
npm config set prefix "~/.npm/lib"

in addition you want following fragment in .profile:

# set PATH so it includes user's private .npm/lib/bin if it exists
if [ -d "$HOME/.npm/lib/bin" ] ; then
PATH="$HOME/.npm/lib/bin:$PATH"
fi

If you now install something with npm install --global packagename it will end up in correct location and can be found in your PATH (you may need to logout and re-login for .profile changes to take effect).

Of course, you could select some other directory instead. For example ~/.config/npm could make sense for modern systems.

You can use npm-user to automatically set up npm to install packages into your user's directories instead of the system's. No root privileges needed.

Here's a link to the script, instructions on how to use it and information about its options. It works on macOS, Linux, *BSD and Windows.

You can run it like so:

$ curl -s "https://raw.githubusercontent.com/alexdelorenzo/npm-user/main/npm-user.sh" | bash

After you run it, using npm install -g <package> will install packages to your user's directories without needing to use sudo.

Here's the code if you want to copy and paste it into your console:

#!/usr/bin/env bash
# Copyright 2022 Alex DeLorenzo <alexdelorenzo.dev>. Licensed under the GPLv3.
export ROOT="${1:-$HOME}"


export NPM_DIR=".npm-packages"
export NPM_ROOT="$ROOT/$NPM_DIR"
export NPM_BIN="$NPM_ROOT/bin"
export NPM_MAN="$NPM_ROOT/share/man"


export BASH_RC="$HOME/.bashrc"
export ZSH_RC="$HOME/.zshrc"
export DEFAULT_RC="$BASH_RC"


export RC_ERR=1
export INDENT=2


set -e


shopt -s expand_aliases


alias indent="paste /dev/null - | expand -$INDENT"




quiet() {
"$@" &> /dev/null
}




expand-tilde() {
local path="$1"
echo "${path/#\~/$HOME}"
}




create-paths() {
local bin="${1:-$NPM_BIN}"
local man="${2:-$NPM_MAN}"


mkdir --parents --verbose "$bin" "$man"
}




set-prefix() {
npm config set prefix "$NPM_ROOT"
}




get-vars() {
local bin="${1:-$NPM_BIN}"
local man="${2:-$NPM_MAN}"


cat <<EOF
export PATH="\$PATH:$bin"
export MANPATH="\${MANPATH:-\$(manpath)}:$man"
export NPM_PACKAGES="$NPM_ROOT"
EOF
}




already-added() {
local rc="${1:-$DEFAULT_RC}"
local bin="${2:-$NPM_BIN}"
local man="${2:-$NPM_MAN}"


local vars="$(get-vars "$bin" "$man")"
quiet grep "$vars" "$rc"
}




main() {
local rc="$(expand-tilde "${1:-$DEFAULT_RC}")"
local bin="$(expand-tilde "${2:-$NPM_BIN}")"
local man="$(expand-tilde "${3:-$NPM_MAN}")"


printf "Creating %s and %s\n" "$bin" "$man"
create-paths "$bin" "$man" || {
printf "Couldn't create paths: %s and %s.\n" "$bin" "$man"
return $RC_ERR
}
  

printf "Setting npm prefix.\n"
set-prefix || {
printf "Couldn't set prefix.\n"
return $RC_ERR
}


if ! already-added "$rc" "$bin" "$man"; then
printf "Writing to %s.\n" "$rc"
get-vars "$bin" "$man" >> "$rc"
 

fi || {
printf "Unable to write to %s.\n" "$rc"
printf "Add the following to your shell's configuration file:\n\n"


get-vars "$bin" "$man" | indent
return $RC_ERR
}


printf "Done.\n\n"
printf "To load the changes in this shell, run:\n"
printf "\tsource %s\n" "$rc"
}




main "$2" "$3" "$4"