Reach "npm run x" without writing "scripts"?

To run the node command in the "context" of your installed node_modules , you can make an entry in the scripts field of package.json . For instance:

 ... "scripts": { "test": "mocha --recursive test/**/*.js --compilers js:babel-register" } ... 

and then I can enter npm run test in my project root directory and the mocha tests will be executed (by calling the mocha binary file installed in node_modules/mocha/bin ).

Is there a way to achieve exactly the same behavior, but without creating script entries? For example, for a one-time "script"?

I present something like the following, as the equivalent of npm run test :

 npm cmd mocha --recursive test/**/*.js --compilers js:babel-register 

Is there any way to achieve this?

NOTE. I must clarify that I am looking for true equivalence. That is, my team should have access to other script commands, etc. I know that you can always call binary files using node and the path to the binary in node_modules, but this is not an adequate solution.

+6
source share
3 answers

Note. This answer relates to a specific use case for OP: calling CLI dependent packages in the context of this project; it's not about making the CLI globally accessible - see the bottom for a discussion.

TL; DR:

On Unix-like platforms , add npm run env -- to your command ; eg:.

 npm run env -- mocha --recursive test/**/*.js --compilers js:babel-register 

This not only allows you to invoke dependent CLIs with a simple name, but completely replicates the environment in which npm installs behind the scenes using npm test or npm run-script <script-defined-in-package.json> .

Unfortunately, this approach does not work on Windows.

Windows solutions use convenience aliases (including half-session environment configuration commands) and help information.


There are two (not mutually exclusive) approaches for creating CLI dependencies of npm project dependencies on a simple name from the shell :

  • (a) Use a command for each call that you pass to commands .
  • (b) Run the command once per session, which (temporarily) changes your environment .

The helpful answer Frxstrem provides an incomplete solution for (a) on Unix-like platforms; it may, however, be sufficient, depending on your specific needs.
It is incomplete in that it simply adds a directory containing (symbolic links) dependent CLIs to $PATH , without performing all other environment modifications that occur when calling npm test or npm run-script <script-defined-in-package.json .


Unix Convenience and Windows Solutions

Please note that all of the solutions below are based on npm run env , which ensures that all the necessary environment variables are set in the same way as when your startup scripts are predefined in the package.json project file with npm test or npm run-script <script> .
These environmental changes include:

  • Prepending $(npm prefix -g)/node_modules/npm/bin/node-gyp-bin and a subdirectory of the project directory ./node_modules/.bin , which contains symbolic links to dependency ./node_modules/.bin (temporarily) with the $PATH environment variable.
  • Defining numerous npm_* environment variables that reflect project parameters, such as npm_package_version , as well as npm / node environment.

Convenient solutions for Unix-like platforms:

Both solutions are below with an alias , which in case (a) is an easier alternative to using a script, and in case (b) there is a prerequisite that allows you to change the current shell environment (although you can use the shell function).

For convenience, add these aliases to your profile / shell initialization file .

(a) Auxiliary Assistant:

Definition

 alias nx='npm run-script env --' 

allows you to call your ad-hoc commands simply by adding nx ; eg:.

 nx mocha --recursive test/**/*.js --compilers js:babel-register 

(b) Post-session configuration command:

 alias npmenv='npm run env -- $SHELL' 

Run npmenv to enter a child shell with a set of npm environments, allowing direct (by name only) invocation of the dependent CLIs in that child shell.
In other words, use it as follows:

 cd ~/some-npm-project npmenv # after this, you can run dependent CLIs by name alone; eg, `mocha ...` # ... run your project-specific commands exit # exit the child shell before you switch to a different project 

Windows Solutions:

(a) and (b) . Please note that Windows (unlike shells similar to POSIX on Unix-like platforms) does not support (directly) the transfer of environment variables that are bound to only one command, therefore the following commands, even when specific commands are executed for execution (case (a) ) invariably also change the session environment (case (b)).

PowerShell (also works on Unix versions):

Add the following function to your $PROFILE (user profile script):

 function npmenv($commandIfAny) { npm run env -- | ? { $_ -and $_ -notmatch '^>' -and $_ -match '^[a-z_][a-z0-9_]+=' } | % { $name, $val = $_ -split '='; set-item -path "env:$name" -value $val } if ($?) { if ($commandIfAny) { & $commandIfAny $Args } } } 

cmd.exe (regular command line, often mistakenly called the "DOS prompt"):

Create a batch file named npmenv.cmd , put it in a folder in %PATH% and define it as follows:

 @echo off :: Set all environment variables that `npm run env` reports. for /f "delims==; tokens=1,*" %%i in ('npm run env ^| findstr /v "^>"') do set "%%i=%%j" :: Invoke a specified command, if any. %* 

Usage (both cmd.exe and PowerShell):

In the case of using (b), just call npmenv with no arguments; after that you can call the dependent CLIs with a simple name ( mocha ... ).

In the case of using (a), preend npmenv to your team; eg:.

  npmenv mocha --recursive test/**/*.js --compilers js:babel-register 

Warning. As noted, the first npmenv — with or without arguments — invariably changes the %PATH% / $env:PATH variable for the remainder of the session.

If you switch to another project in the same session, be sure to run npmenv (at least once) again, but note that this adds additional directories to %PATH% , so you can still accidentally run the previous executable project, if it is not an established dependence on the current project.

From PowerShell, you could actually combine the two solutions to get excellent (a) and (b) functionality: define the *.cmd file above as a separate command (using a different name, for example nx.cmd ), which you only use with arguments (a) and override the PowerShell function to serve as a complement to the complement without changing the arguments (b).
This works because PowerShell invariably runs *.cmd files in a child process that cannot affect the current PowerShell session environment.


Notes on the scope of the question and answer:

The OP question is to call the CLI of already installed dependent packages in the context of this ad-hoc project, by the simple name of the executable file - just like npm allows you to execute commands added to scripts in the package.json file.

The issue is not to make CLIs available worldwide (by installing them using npm install -g ).

In fact, if you want to create modular, stand-alone packages, they do not depend on packages installed globally. Instead, make all dependent packages part of your project: use npm install --save (for run-time dependencies) and npm install --save-dev (for development-time dependencies) - see https://docs.npmjs.com/ cli / install

In particular, if the specified CLI is already installed as a dependency, installing it globally (potentially a different version) is not only redundant, but it also confuses which version is executed when.

+5
source

I saved this little script as ~/bin/npm-cmd on my computer:

 #!/bin/bash PATH="$(npm bin):$PATH" " $@ " 

Then running npm-cmd PROGRAMARGS should first search for PROGRAM in ./node_modules/.bin before returning to the normal search.

+3
source

Note. This answer works on all platforms.

npm i -g mocha

mocha --recursive test/**/*.js --compilers js:babel-register

Mocha CLI documentation (go to the usage section).


If you want to run the npm package as an executable from the CLI, the correct way to do this is through package.json bin . It allows you to display files in your project as executable files. The first line of files, such as regular scripts, should start with shebang , #!/usr/bin/env node , if it is a node script.

By matching these files in your package.json package, you can call them from the script section of other packages or call them from cli when your package is installed globally using npm install -g <package> . A node bunker can be an object that maps script names to script files in your project if you want to add several scripts to your path as executable files or a line with one file that you would like to act as an executable for your package.

This is actually the same mechanism used by wet in your example:

mocha bin

Without this, mocha will never be in your way when you install it worldwide and will not be available through your scripting section.

0
source

All Articles