Let npm scripts change environment of the current shell (to run scripts in a current shell, instead of a subshell)

(Jerry Green) #1

My exact concern/problem is described on stackoverflow

Long story short: I want let npm to change environment of my shell with npm scripts.

I.e., I don’t want my npm start or npm run prestart or whatever to be executed in a subshell.

Maybe we can have some config value for .npmrc, like create-subshell. Which is default to false but can be changed to true (per project or globally), so then running scripts like npm start will be equal to source ... analogue of sh ... (source command executes bash script in a current shell, sh command executes script in a subshell)

(Isaac Z. Schlueter) #2

Unfortunately, there is no facility in Node.js to run a “foreground” script. (Ie, exec without fork.)

Furthermore, since npm itself is in a subshell, if we could exec a started script in the foreground process, it would only be foregrounded in the npm process, not in your shell process itself. The only way that I can think of to do that would be to write npm itself in bash.

Lastly, if you run your test (or whatever) in the foreground process, then it would close your shell when the test completes.

All that being said, I am never going to shy away from an opportunity to do stupid things with bash, so here’s a little bash program you could run that will do more or less what you’re asking for :slight_smile:

#!/bin/bash

# run with a script name as first arg, defaults to "test"
script=${1:test}

# npm i -g json
cmd=$(json "scripts.${script}" < package.json)

# set the path like npm run does
# note that all the OTHER npm envs are not set, so
# that's left as an exercise for the reader.
export PATH=$PWD/node_modules/.bin:$PATH
exec $cmd

If you run this script as bash runcmd.sh or ./runcmd.sh then it’ll foreground the npm script over itself. If you . runcmd.sh then it’ll foreground in your current shell process, and close the shell when the command closes.

1 Like
(Jerry Green) #3

Wow, that’s a good script and idea overall

With eval "$cmd" instead of exec the shell won’t be terminated

Also, I got rid of the json dependency:

#!/bin/bash

script=${1:-install}
cmd=$(node -p -e "require(\"./package\").scripts.${script}")

export PATH=$PWD/node_modules/.bin:$PATH
eval "$cmd"

I’m thinking to make sort of a wrapper for npm which is a bash script itself but executes npm. Not sure if I really going to do that but I like the idea

(Isaac Z. Schlueter) #4

If you do it this way, it’ll work for script names that aren’t valid JS identifiers:

cmd=$(node -p -e 'require("./package").scripts[process.argv[1]]' "$script")