22

I am maintaining the following directory structure:

/home/user/Desktop/
                 |-- app/
                 |      |-- package.json
                 |      `-- server.js
                 |-- node/
                 |      |-- bin/
                 |      |      |-- node
                 |      |      `-- npm
                 |      |-- include/
                 |      |-- lib/
                 |      `-- share/
                 |
                 `-- npm.sh

I want all my locally installed node modules reside in the directory node. That is, if I run npm install inside the directory app, initially it'll install the modules inside the current directory (app) and then move the node_modules folder to the external directory called node. For this purpose I've written a script npm.sh and placed the mv (move) command inside the postinstall script of package.json.

These are the files npm.sh and package.json.

content of npm.sh:

#/bin/bash

export PATH=/home/user/Desktop/node/bin:$PATH
export NODE_PATH=/home/user/Desktop/node/node_modules
export NODE_MODULE_ROOT=/home/user/Desktop/node
/bin/bash

content of app/package.json:

{
  "name": "app",
  "version": "1.0.0",
  "scripts": {
    "postinstall": "mv node_modules $NODE_MODULE_ROOT",
    "start": "node server.js"
  },
  "dependencies": {
    "jwt-simple": "^0.5.1"
  }
}

But the problem is: when I do ./npm.sh && cd app && npm install, everything works as intended. But when I do npm install jwt-simple, the postinstall script is not getting executed.

Is there a way to make it work for individual npm install <package> ? Or is there any better way to accomplish this ?

dibyendu
  • 515
  • 1
  • 5
  • 16

3 Answers3

10

You can use npm hook scripts to do something after package is installed.

Create node_modules/.hooks/postinstall executable and it will be run also after npm install <package>.

NOTE: I have noticed problems with npm hook scripts between npm version 5.1.0 until 6.0.1. So if you have problems with hooks, check your npm version and upgrade if necessary.

Karl Horky
  • 4,410
  • 2
  • 31
  • 35
Niko Ruotsalainen
  • 49,514
  • 4
  • 24
  • 31
  • The script will be run with the working directory set to the package location. Use $INIT_CWD in your script to get the path of the directory from where the `npm install ` was run. – Tom Howard Dec 15 '19 at 22:09
  • This doesn't seem to work on Windows (I just tried it), because the hook-script filename has to exactly be "postinstall", which makes Windows unable to recognize it as an executable (it just errors with `Exit status 1`, and doesn't run the file, regardless of if I use a (previously) `.exe`, `.js`, `.bat`, or `.cmd` file). Anyone know of a way to get this approach to work on Windows? – Venryx Mar 26 '21 at 16:38
2

For anyone else stumbling here npm doesn't run pre/postinstall in the package.json when installing a specific package. You can check here for reference, https://npm.community/t/preinstall-npm-hook-doesnt-execute-when-installing-a-specific-package/2505. Not sure if there is a way around it but I've been looking too.

2

I can't write a comment yet, since I'm a new user, but I wanted to elaborate on Niko's answer.

It seems the Hook Scripts functionality has been removed starting with npm v7.X.

So, in order to use a node_modules/.hooks/postinstall hook, running npm v6.X would be the best bet.

Plus, as pointed out in the comments, there's a catch: Hook Scripts won't work out of the box on Windows, because it won't be able to recognize the file as being executable since it lacks a file extension.

A not so pretty workaround is to create, for instance, a postinstall.cmd and soft (or hard /H) linking it with mklink postinstall postinstall.cmd

This will ensure that Windows recognizes the file as a .cmd executable to correctly run it.

Penguin
  • 21
  • 2