When debugging an issue with the node-sass package recently, I ran into a feature of npm install
that I used a lot of times, but never thought twice about how it worked. The node-sass
package allows you to pass extra command-line arguments like the path to a compiled sass binary path. I never wondered how the args get passed; and more importantly, whether that argument get passed to all the modules that’re getting installed when you run npm install
without any package name. Well, until now.
The answer is yes. The argument gets passed to all the modules if naked npm install
is run, or just the module if a name is specified. Any module can read the same node-sass
-specific command-line flag and work with it if needed.
The way this works internally is after npm
cli has processed the common arguments that it requires (think --save-dev
etc.), rest of the arguments are then added to the process.env
global, each prefixed by npm_config_
. So in the following scenario:
When the install life-cycle event of the module gets run1, a variable process.env.npm_config_dest_dir
can be used to get at the value src
. Incidentally, this is the same object that gets populated if you have a project/global config in npm config
too! So you can do this, instead:
The third alternative that works in the same way is when you use an environmental variable (case-insensitive!) that has NPM_CONFIG
as the prefix. So the above two variants are the same as the following:
Notice that in these examples, the hyphenated arguments are auto-converted to underscore versions when they get passed via the env.
The code flow
This entire processing happens at two different stages: first, the arguments and configs are processed, and the second part is where this information is re-injected into the proccess.env.npm_config_
prefixed list.
The first part is achieved by npm using the config-chain module. The location for local and global npmrc
, and the command line flags that were passed to the npm
command itself among others are passed to this module to create a config object. This is the place where any env var that has the NPM_CONFIG
prefix gets addded to the config list as a parameter, with the prefix stripped out.
The second part happens, from the looks of it, only inside the npm life-cycle events. These events are invoked by calling into a separate module that was extracted out from npm/cli, and handles all the necessary work that needs to be done when running the life-cycle events, and this is where the config data built in the previous step gets added to process.env.<prefix>
.
Footnotes
-
npm
defaults to running the following commands for common features likenpm install
, which can be overridden. For example, thenode-sass
package overrides this to run a script instead:scripts/install.js
↩