NPM philosophy, for better or worse, is to have all necessary dependencies for a project to be locally installed, in ./node_modules
, and for package.json to reflect all necessary deps.
I have a library, that would typically benefit from both global installation and local installation, similar to Gulp, Mocha, etc.
One of the most insiduous problems with the above, is that the global and local versions might differ, causing compatibility issues for a given project.
It occurred to me that we could avoid this problem, by creating an alias in .bash_profile
, something like this:
# bash psuedocode
* alias gulp='using current working directory, find the locally installed gulp and run that'
My library is not Gulp, but you get the idea. I know a lot of devs don't like the idea of 3rd parties writing to their bash_profiles, but I think that is a bit anal-retentive, considering doing something like this has zero outside effects (TMK).
So I have three questions:
(1) Is this a good idea?
(2) Are there any projects that do something similar and how?
(3) I an not great with bash scripting, how do you write a bash script
that can recursively look for node_modules/.bin/gulp if you the dev
runs the gulp command from a directory that is within the project
but not in the root?
My thinking was that as a postinstall script to
npm install --save-dev gulp
we would run a script that
- added the above line* to the .bash_profile
- in that line, we would point to a script stored under /Users/you/.gulp that would be responsible for locating the locally installed gulp.
This way we save the need for global installations of modules, when they are only used for command line convenience, as well as alleviating the problem of differing package versions.
I'll go through your 3 questions one by one:
(1) Is this a good idea?
No. Changing user's private files automatically is never a good idea and I see several things that can go horribly wrong if you do that - from a pure annoyance of users having to clean after your programs, to breaking user's system config, to serious security problems.
(2) Are there any projects that do something similar and how?
I hope not. I would never run any program that messes with my .bash_profile
- even though I don't have it. I have .profile
and .bashrc
. And that's another problem for you - are you sure you even know what to edit? What if I use dash
which I use sometimes, or zsh
or some other shell?
(3) I an not great with bash scripting, how do you write a bash script that can recursively look for node_modules/.bin/gulp if you the dev runs the gulp command from a directory that is within the project but not in the root?
If you are not great with bash scripting then your definitely should not edit user's private files automatically.
What you can do is to provide a certain alias or bash function in a separate file for user's convenience and explain how users can source your file inside of their .bashrc
if they want to.
But never make that decision for them. If anything goes wrong this can even have legal consequences for you.
Examples of functions
Answering your questions posted in the comments, I'll add few words on how functions work in Bash.
In my collection of script on GitHub I have few functions with instructions on how to install them and how to use them. Their source code is available here.
An example function in that collection is ok
- this is its source code:
ok() {
# prints OK or ERROR and exit status of previous command
s=$?
if [[ $s = 0 ]]; then
echo OK
else
echo ERROR: $s
fi
}
You can download it (with some other related functions) from:
- https://raw.githubusercontent.com/rsp/scripts/master/ok-functions
If you save it as ~/ok-functions
then you can either run:
source ~/ok-functions
to have them available in that session, or you can put that line in .profile
or .bashrc
to have it available after every login. You can use it as if it was a normal command or program like this:
ls /bin; ok
ls /binn; ok
Working example of preventing global install
I wrote a Bash function that does what you are trying to do - it stops global installation of a certain module - called modname
in this example. Save it to some file - e.g. to ~/no-global-restricted-module
- of course you can change the restricted-module
name in n
and the warning message in w
to suit your needs:
npm() {
n=restricted-module
w="Please don't install $n globally - see: http://example.com/"
i=0
g=0
m=0
for a in "$@"; do
case $a in
i|install)
i=1;;
-g|--global)
g=1;;
$n)
m=1;;
esac
done
if (( $i == 1 && $g == 1 && $m == 1 )); then
echo $w >&2
return 1
else
`which npm` "$@"
fi
}
And then add this line to your ~/.profile
or ~/.bashrc
:
source ~/no-global-restricted-module
Now when you log in again, or when you open a new terminal window, you can try running this in your command line:
npm install --global restricted-module
You should see a warning and no installation will be started. If you enter some other module name or you omit the --global
switch then the installation should proceed as usual. This also works with the shortcuts of i
instead of install
and -g
instead of --global
.
This is pretty much what you are trying to do. You can include that function in your documentation with instructions on how to use it. I am hereby releasing it under the terms of the MIT license so you can use it freely. I published it on GitHub with some more options for configuration and information on how to use it, see:
- https://github.com/rsp/npm-no-global-install
See also
See also this answer for a much better explanation of writing, installing and using Bash functions on the example of playing various sounds in the terminal.