Global Node Modules without sudo

in #node7 years ago (edited)

Short post about how to better manage Node Modules Installation for your NodeJS Projects.

It will not only let you install Node Modules without using root privileges but it will also ensure the following benefits of a well managed NodeJS Project:

  • Specific Node versions for specific projects
  • Specific Node Modules versions for specific projects
  • Self-sufficient package.json, no prerequisite needed before npm install

Background issue


When installing Node Modules globally with npm, you will encounter EACCESS npm errors:

$ npm install -g whatever
[...]
npm WARN checkPermissions Missing write access to /usr/lib/node_modules/whatever/whatever
[...]
npm ERR! path /usr/lib/node_modules/whatever
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! syscall access
npm ERR! Error: EACCES: permission denied, access '/usr/lib/node_modules/whatever'
npm ERR!  { Error: EACCES: permission denied, access '/usr/lib/node_modules/whatever'
npm ERR!   stack: 'Error: EACCES: permission denied, access \'/usr/lib/node_modules/whatever\'',
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/usr/lib/node_modules/@angular/cli/node_modules/whatever' }
npm ERR! 
npm ERR! Please try running this command again as root/Administrator.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/user/.npm/_logs/2015-10-21T16_29_42_409Z-debug.log


Below are commonly used solutions to fix this problem.

<spoiler-alert> We use the last one. </spoiler-alert>

Solution 1 :
I, For One, Welcome Our New SuperUser Overlord!


The easiest solution would probably be to surrender and run the install script as root/Administrator, as suggested:

$ sudo npm install -g whatever


This will install your global Node Modules under /usr/lib/node_modules.

It works... But do you really want to use the root privileges for every global Node Modules you will install?

No, no, no...

Solution 2:
Not So Global Node Modules...


Instead of using root privileges, you may instead move the node_modules inside your current user home, in a ~/.nvm directory:

$ mkdir ~/.nvm
$ npm config set prefix '~/.nvm'


Then, at the end of your .profile, make sure you update your PATH:

PATH="$HOME/.nvm/bin:$PATH"


Relaunch your terminal or update your system variables:

$ source ~/.profile


And continue installing global Node Modules under your current user.

Solution 3:
Using Node Version Manager


nvm, Node Version Manager, is a simple bash script to manage multiple active node.js versions.

The beautiful side effect of using nvm is that the node_modules of each versions will be stored inside the current users's home directory as ~/.nvm/versions/node/vX.Y.Z/lib/node_modules/. We don't need root privileges to install global Node Modules anymore and we can switch from one Node Version to another, depending on the project.

Installing (or updating) nvm is as easy as: (check on nvm for the latest nvm version)

wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash


Now, after you restart your terminal, install NodeJS (by default, the latest version):

$ nvm install node
Downloading and installing node v9.6.1...
Downloading https://nodejs.org/dist/v9.6.1/node-v9.6.1-linux-x64.tar.xz...
############################################################################################################################################## 100,0%
Computing checksum with sha256sum
Checksums matched!
Now using node v9.6.1 (npm v5.6.0)
Creating default alias: default -> node (-> v9.6.1)


Your global Node Modules will be installed inside ~/.nvm/versions/node/vX.Y.Z/lib/node_modules/.

You will find more information on how to install specific versions of NodeJS and switching from one to the other here.

Solution 4:
Let the Executor do his job


This is the solution we use!

npx, the Node Package Executor, executes command either from a local node_modules/.bin, or from a central cache, installing any packages needed in order for command to run.

Yes, it will even install the packages you need to run a command in a central cache if needed!

Example: Using Angular CLI ng command to create a new Angular Application

You do not need to install Angular CLI globally, you can use npx to run ng new before installing the @angular/cli package:

$ npx -p @angular/cli ng new my-app
  create my-app/README.md (1021 bytes)
  create my-app/.angular-cli.json (1241 bytes)
  [...]
  create my-app/src/app/app.component.spec.ts (986 bytes)
  create my-app/src/app/app.component.ts (207 bytes)
npm WARN deprecated nodemailer@2.7.2: All versions below 4.0.1 of Nodemailer are deprecated. See https://nodemailer.com/status/
npm WARN deprecated node-uuid@1.4.8: Use uuid module instead
> uws@9.14.0 install /home/user/Projects/TEMP/my-app/node_modules/uws
> node-gyp rebuild > build_log.txt 2>&1 || exit 0
> node-sass@4.7.2 install /home/user/Projects/TEMP/my-app/node_modules/node-sass
> node scripts/install.js
Cached binary found at /home/user/.npm/node-sass/4.7.2/linux-x64-59_binding.node
> uglifyjs-webpack-plugin@0.4.6 postinstall /home/user/Projects/TEMP/my-app/node_modules/webpack/node_modules/uglifyjs-webpack-plugin
> node lib/post_install.js
> node-sass@4.7.2 postinstall /home/user/Projects/TEMP/my-app/node_modules/node-sass
> node scripts/build.js
Binary found at /home/user/Projects/TEMP/my-app/node_modules/node-sass/vendor/linux-x64-59/binding.node
Testing binary
Binary is fine
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.1.3 (node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.1.3: wanted {"os":"darwin","arch":"any"} (current: {"os":"linux","arch":"x64"})
added 1273 packages in 36.111s


That's it! Our Angular application is created and all needed Node Modules have been installed locally. Let's serve our newly created Angular WebApp!

$ ng serve
The program 'ng' is currently not installed. You can install it by typing:
sudo apt install ng-common


Ooooops... But don't run to install ng-common globally yet!!!

You just have to use npx to run locally installed npm binaries.

$ npx ng serve


Better, you could configure the Shell Auto Fallback inside your .bashrc:

$ npx --shell-auto-fallback bash
command_not_found_handle() {
  # Do not run within a pipe
  if test ! -t 1; then
    >&2 echo "command not found: $1"
    return 127
  fi
  if which npx > /dev/null; then
    echo "$1 not found. Trying with npx..." >&2
  else
    return 127
  fi
  if ! [[ $1 =~ @ ]]; then
    npx --no-install "$@"
  else
    npx "$@"
  fi
  return $?
}
$ npx --shell-auto-fallback bash >> ~/.bashrc
$ source ~/.bashrc


Now, if the command is not found, it will try to find it within the installed npm binaries.

$ ng serve
ng not found. Trying with npx...
not found: ng
$ cd my-app
$ ng serve
ng not found. Trying with npx...
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Date: 2018-03-01T08:48:23.771Z
Hash: ac1c4647111e56cc4df3
Time: 7485ms
chunk {inline} inline.bundle.js (inline) 3.85 kB [entry] [rendered]
chunk {main} main.bundle.js (main) 20.8 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 549 kB [initial] [rendered]
chunk {styles} styles.bundle.js (styles) 42.2 kB [initial] [rendered]
chunk {vendor} vendor.bundle.js (vendor) 8.45 MB [initial] [rendered]

webpack: Compiled successfully.


Your Angular WebApp is now running on http://localhost:4200

The npx Fallback can also work with Node Modules that have not been installed, if you specify the version:

$ cowsay "Steemit is bad"
cowsay not found. Trying with npx...
not found: cowsay
$ cowsay@latest "Steemit is cool"
cowsay@latest not found. Trying with npx...
npx: installed 9 in 1.23s
 _________________
< Steemit is cool >
 -----------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||


And, if you check inside your current global node_modules, it only contains npm:

$ ls /home/user/.nvm/versions/node/v9.6.1/lib/node_modules/
npm


Advantages of using nvm and npx

Here are the main advantages of using nvm (Node Version Manager) and npx (Node Package Executor):

  • Specific Node versions for specific projects
  • Specific Node Modules versions for specific projects
  • Self-sufficient package.json, no prerequisite needed before npm install

References

  • nvm - the Node Version Manager
  • npx - the Node Package Executor
  • Angular CLI - Angular Command Line Interface
Sort:  

Congratulations @thierrydd! You have completed some achievement on Steemit and have been rewarded with new badge(s) :

You published your First Post
You made your First Comment
You got a First Vote

Click on any badge to view your own Board of Honor on SteemitBoard.
For more information about SteemitBoard, click here

If you no longer want to receive notifications, reply to this comment with the word STOP

Upvote this notification to help all Steemit users. Learn why here!

Thank you Thierrydd for sharing your knowledge on this.

Thank you!!!