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
npmdefaults to running the following commands for common features like
npm install, which can be overridden. For example, the
node-sasspackage overrides this to run a script instead: