Short answer: Unfortunately, npm does not provide any built-in feature(s) to meet your requirement.
Lifecycle hooks/scripts such as postinstall
are invoked only when running the generic npm install
command, and not when someone runs npm install --save <pkg_name>
during the projects development phase.
Workaround: Consider customizing the logic of the npm install --save
compound command by essentially overriding the npm
command at the shell level.
The following solution, albeit a Bash one, describes how this custom logic can be actualized for a specific project(s). However, this solution is dependent on the following conditions:
- Developers working on your project must have their the shell set to Bash when running the
npm install --save
compound command.
- Developers working on your project will need to customize their Bash startup files, namely
~/.bashrc
and possibly ~/.bash_profile
.
- The project directory, i.e. the project directory for which you want the custom logic to be effective, must contain a custom
.bashrc
file.
Bash Solution:
The following three steps are necessary to configure your project, and operating system(s), so that when a developer runs npm install --save <pkg_name>
(or variations of it) the npx snowpack
command is subsequently invoked.
Note: Points two and three (below) are the tasks developers need to carry out (once) to customize their Bash startup files.
The project specific .bashrc
file:
Firstly create the following "project specific" .bashrc
file in the root of your project directory, i.e. save it at the same level as where your projects package.json
file resides:
/some/path/to/my-project/.bashrc
npm() {
local name_badge="\x1b[37;40mpostinstall\x1b[0m"
array_includes() {
local word=$1
shift
for el in "$@"; do [[ "$el" == "$word" ]] && return 0; done
}
log_warn_message() {
local cmd_name=$1 warn_badge warn_mssg
warn_badge="\x1b[30;43mWARN!\x1b[0m"
warn_mssg="${cmd_name} command not found. Cannot run npx snowpack."
echo -e "\n${name_badge} ${warn_badge} ${warn_mssg}" >&2
}
log_run_message() {
echo -e "\n${name_badge} Running pseudo postinstall hook."
}
if [[ $* == "install "* || $* == "i "* ]] && array_includes --save "$@"; then
# 1. Run the given `npm install --save ...` command.
command npm "$@"
# 2. Check whether the `npx` command exists globally.
command -v npx >/dev/null 2>&1 || {
log_warn_message npx
return 1
}
log_run_message
# 3. Run the pseudo "postinstall" command.
command npx snowpack
else
# Run all other `npm` commands as per normal.
command npm "$@"
fi
}
Note: For a better understanding of what this file does refer to the "Explanation" section below.
The ~/.bashrc
file:
To make the custom logic, i.e. the npm
function in the aforementioned .bashrc
file, effective, it's necessary to configure Bash to read the aforementioned "project specific" .bashrc
file. To configure this, add the following line of code to ~/.bashrc
:
PROMPT_COMMAND='if [[ "$bashrc" != "$PWD" && "$PWD" != "$HOME" && -e .bashrc ]]; then bashrc="$PWD"; . .bashrc; fi'
Note: For a better understanding of what this line of code does refer to the "Explanation" section below.
The ~/.bash_profile
file:
Typically your ~/.bash_profile
contains the following line of code to load the ~/.bashrc
file (or some variation of it):
if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
If this is not present, then it must be added to ~/.bash_profile
.
Additional info.
Setup/Configuration helpers:
Consider your developers utilizing the following two commands to aid configuration of their Bash startup files, as per the aforementioned steps two and three.
For step two, run the following command:
echo $'\n'"PROMPT_COMMAND='if [[ \"\$bashrc\" != \"\$PWD\" && \"\$PWD\" != \"\$HOME\" && -e .bashrc ]]; then bashrc=\"\$PWD\"; . .bashrc; fi'" >> ~/.bashrc
This will add the PROMPT_COMMAND=...
line of code to the existing ~/.bashrc
file, or create a new one if it doesn't already exist:
For step three, run the following command to append the line of code necessary in the ~/.bash_profile
for loading the ~/.bashrc
file:
echo $'\n'"if [ -f ~/.bashrc ]; then . ~/.bashrc; fi" >> ~/.bash_profile
Is my shell configured to Bash?
To check whether the shell is configured to Bash you can create a new session, i.e. create a new Terminal window and run:
echo $0
If it prints -bash
then it's using Bash.
How do I configured my shell to Bash?
If echo $0
doesn't print -bash
then you'll need to change the shell. To change it to Bash run:
chsh -s /bin/bash
Note: You'll need to create a new session for this change to become effective.
Explanation
The project specific .bashrc
file:
This .bashrc
file contains a shell function named npm
. The body of this function contains the logic necessary to override the default npm install|i --save
command.
The conditions specified in the if
statement, i.e, the part that reads;
if [[ $* == "install "* || $* == "i "* ]] && array_includes --save "$@"; then
...
fi
essentially reads the $*
special parameter to check whether the argument(s) passed to the npm
function begin with either; install
, or it's shorthand equivalent i
, and whether the --save
option/argument has been passed too.
To check for the existence of the --save
argument we pass the $@
special parameter to the array_includes
function. We handle this argument differently because the position of the --save
option may differ in the compound command. For instance, a user may install a package by running this;
# Example showing `--save` option at the end
npm install <pkg_name> --save
or this (or some other variation):
# Example showing `--save` option in the middle
npm i --save <pkg_name>
When the conditions specified in the if
statement are met, i.e. they're true
, we perform the following tasks in its body:
Run the given npm install --save ...
command as-is via the line that reads:
command npm "$@"
Check whether the npx
command exists globally via the part that reads:
command -v npx >/dev/null 2>&1 || {
log_warn_message npx
return 1
}
If the npx
command is not available (globally) we warn the user that the npx snowpack
command cannot be run, and return
from the function early with an exit status of 1
.
Note: My logic in this check assumes that you'll be installing npx
globally. However if you're installing npm
locally within your project then you'll need to change this logic. Perhaps by checking whether ./node_modules/.bin/npx
exists instead. Or, you may be confident that npx
command will always exists, therefore conclude that this check is unnecessary.
If the npx
command exists globally we then run the pseudo "postinstall" command, i.e.
command npx snowpack
When the conditions specified in the if
statement are NOT met, i.e. they're false
, the user is essentially running any other npm command that is not npm install --save <pkg_name>
. Therefore in the else
branch we run the command as-is:
command npm "$@"
The ~/.bashrc file:
In section 5.2 Bash Variables of the "Bash Reference Manual" the PROMPT_COMMAND
variable is described as follows:
PROMPT_COMMAND
If set, the value is interpreted as a command to execute before the printing of each primary prompt ($PS1
).
So, this line of code (here it is again):
PROMPT_COMMAND='if [[ "$bashrc" != "$PWD" && "$PWD" != "$HOME" && -e .bashrc ]]; then bashrc="$PWD"; . .bashrc; fi'
loads the "project specific" .bashrc
(if one exists), which in turn overrides the npm
command with the npm
function. This is what essentially provides a mechanism for overriding the npm install --save
compound command for a specific project(s).
See this answer by @Cyrus
for further explanation.