如何使用从本地安装在node_modules包的可执行文件?

如何在node.js中使用模块的本地版本。例如,在我的应用程序中,我安装了咖啡脚本:

npm install coffee-script

这会将其安装在./node_modules中,咖啡命令位于./node_modules/.bin/coffee中。当我在项目的主文件夹中时,有办法运行此命令吗?我想我正在捆绑器中寻找类似于bundle exec的东西。基本上,我想指定一个参与项目的每个人都应该使用的咖啡脚本版本。

我知道我可以添加-g标志来全局安装它,所以咖啡在任何地方都可以正常工作,但是如果我想每个项目都有不同版本的咖啡呢?

397019 次浏览

在package.json中包含每个项目所需的特定版本的咖啡脚本,通常如下所示:

"dependencies":{
"coffee-script": ">= 1.2.0"

然后运行npm install在每个项目中安装依赖项。这将安装每个项目可以在本地访问的指定版本的caco-cript。

更新时间:我不再推荐这种方法,出于上述安全原因,尤其是更新的npm bin命令。下面的原始答案:

正如你所发现的,任何本地安装的二进制文件都在./node_modules/.bin中。为了始终在此目录中运行二进制文件,而不是全局可用的二进制文件,如果存在,我建议你将./node_modules/.bin放在路径的第一位:

export PATH="./node_modules/.bin:$PATH"

如果你把它放在你的~/.profile中,coffee将永远是./node_modules/.bin/coffee(如果可用),否则/usr/local/bin/coffee(或者你安装节点模块的任何前缀)。

更新:正如Seyeong Jeong在下面的回答中指出的那样,从npm 5.2.0开始,您可以使用npx [command],这更方便。

5.2.0之前版本的旧答案

问题是把

./node_modules/.bin

只有当您当前的工作目录是项目目录结构的根目录(即node_modules的位置)时,它才有效

无论您的工作目录是什么,您都可以通过以下方式获取本地安装的二进制文件的路径

npm bin

要执行本地安装的coffee二进制文件,无论您在项目目录层次结构中的位置如何,您都可以使用此bash构造

PATH=$(npm bin):$PATH coffee

我把这个化名给npm-exec了

alias npm-exec='PATH=$(npm bin):$PATH'

所以现在我可以了

npm-exec coffee

无论我身处何地都能找到正确的咖啡

$ pwd
/Users/regular/project1


$ npm-exec which coffee
/Users/regular/project1/node_modules/.bin/coffee


$ cd lib/
$ npm-exec which coffee
/Users/regular/project1/node_modules/.bin/coffee


$ cd ~/project2
$ npm-exec which coffee
/Users/regular/project2/node_modules/.bin/coffee

如果您希望PATH变量根据当前工作目录正确更新,请将其添加到.bashrc等效文件的末尾(或定义PATH的任何内容之后):

__OLD_PATH=$PATH
function updatePATHForNPM() {
export PATH=$(npm bin):$__OLD_PATH
}


function node-mode() {
PROMPT_COMMAND=updatePATHForNPM
}


function node-mode-off() {
unset PROMPT_COMMAND
PATH=$__OLD_PATH
}


# Uncomment to enable node-mode by default:
# node-mode

这可能会在每次呈现bash提示符时增加短暂的延迟(很可能取决于项目的大小),因此默认情况下它是禁用的。

您可以通过分别运行node-modenode-mode-off在终端中启用和禁用它。

使用npm bin命令获取项目的节点模块 /bin目录

$ $(npm bin)/<binary-name> [args]

e. g.

$ $(npm bin)/bower install

我遇到了同样的问题,我不喜欢使用别名(正如定期所建议的那样),如果你也不喜欢它们,那么我使用的另一个解决方法是,你首先必须创建一个微小的可执行bash脚本,比如setenv.sh

#!/bin/sh


# Add your local node_modules bin to the path
export PATH="$(npm bin):$PATH"


# execute the rest of the command
exec "$@"

然后您可以使用以下命令使用本地/bin中的任何可执行文件:

./setenv.sh <command>
./setenv.sh 6to5-node server.js
./setenv.sh grunt

如果您在package.json中使用scripts

...,
scripts: {
'start': './setenv.sh <command>'
}

使用npm run[-script] <script name>

使用npm将bin包安装到本地./node_modules目录后,修改package.json添加<script name>,如下所示:

$ npm install --save learnyounode
$ edit packages.json
>>> in packages.json
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"learnyounode": "learnyounode"
},
...
$ npm run learnyounode

如果npm install有一个--add-cript选项或其他东西,或者如果npm run可以在不添加脚本块的情况下工作,那就太好了。

PATH解决方案存在这样的问题:如果$(npm bin)放置在您的. file/. bashrc/etc中,它会评估一次并永远设置为路径首次评估的任何目录。如果你修改当前路径,那么每次运行脚本时,你的路径都会增长。

为了解决这些问题,我创建了一个函数并使用了它。它不会修改你的环境,使用起来很简单:

function npm-exec {
$(npm bin)/$@
}

然后可以这样使用,而无需对您的环境进行任何更改:

npm-exec r.js <args>

相同@常规的公认解决方案,但鱼壳味

if not contains (npm bin) $PATH
set PATH (npm bin) $PATH
end

zxc就像nodejs的“bundle exec”。它类似于使用PATH=$(npm bin):$PATH

$ npm install -g zxc
$ npm install gulp
$ zxc which gulp
/home/nathan/code/project1/node_modules/.bin/gulp

我很想知道这是否是一个不安全/坏主意,但经过思考,我在这里看不到问题:

修改Linus的不安全解决方案以将其添加到末尾,使用npm bin查找目录,并使脚本仅在父级中存在package.json时调用npm bin(为了速度),这是我为zsh想出的:

find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}


precmd() {
if [ "$(find-up package.json)" != "" ]; then
new_bin=$(npm bin)
if [ "$NODE_MODULES_PATH" != "$new_bin" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}:$new_bin
export NODE_MODULES_PATH=$new_bin
fi
else
if [ "$NODE_MODULES_PATH" != "" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}
export NODE_MODULES_PATH=""
fi
fi
}

对于bash,而不是使用precmd钩子,您可以使用$PROMPT_COMMAND变量(我还没有测试过,但您明白了):

__add-node-to-path() {
if [ "$(find-up package.json)" != "" ]; then
new_bin=$(npm bin)
if [ "$NODE_MODULES_PATH" != "$new_bin" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}:$new_bin
export NODE_MODULES_PATH=$new_bin
fi
else
if [ "$NODE_MODULES_PATH" != "" ]; then
export PATH=${PATH%:$NODE_MODULES_PATH}
export NODE_MODULES_PATH=""
fi
fi
}


export PROMPT_COMMAND="__add-node-to-path"

更新:如果您使用的是最近的npm(版本>5.2)

您可以使用:

npx <command>

npxnode_modules.bin目录中查找命令

老答案:

对于windows

将以下内容存储在名为npm-exec.bat的文件中并将其添加到您的%PATH%

@echo off
set cmd="npm bin"
FOR /F "tokens=*" %%i IN (' %cmd% ') DO SET modules=%%i
"%modules%"\%*

用法

然后你可以像这样使用它 npm-exec <command> <arg0> <arg1> ...

例如

要执行安装在本地node_modules目录中的wdio,请执行:

npm-exec wdio wdio.conf.js

即它将运行.\node_modules\.bin\wdio wdio.conf.js

使用npm-run

自述文件:

npm-run项目名称

从node_modules查找和运行本地可执行文件

任何可用于npm生命周期脚本的可执行文件都可用于npm-run

用法

$ npm install mocha # mocha installed in ./node_modules
$ npm-run mocha test/* # uses locally installed mocha executable

安装

$ npm install -g npm-run

您还可以使用direnv并仅在您的工作文件夹中更改$PATH变量。

$ cat .envrc
> export PATH=$(npm bin):$PATH

将此脚本添加到您的.bashrc。然后您可以在本地调用coffee或anyhting。这对您的笔记本电脑很方便,但不要在您的服务器上使用它。

DEFAULT_PATH=$PATH;


add_local_node_modules_to_path(){
NODE_MODULES='./node_modules/.bin';
if [ -d $NODE_MODULES ]; then
PATH=$DEFAULT_PATH:$NODE_MODULES;
else
PATH=$DEFAULT_PATH;
fi
}


cd () {
builtin cd "$@";
add_local_node_modules_to_path;
}


add_local_node_modules_to_path;

注意:此脚本为cd命令创建别名,并在每次调用cd后检查node_modules/.bin并将其添加到您的$PATH

note 2:您可以将第三行更改为NODE_MODULES=$(npm bin);。但这会使cd命令太慢。

我是Windows用户,这对我很有用:

// First set some variable - i.e. replace is with "xo"
D:\project\root> set xo="./node_modules/.bin/"


// Next, work with it
D:\project\root> %xo%/bower install

祝你好运。

对于Windows使用此:

/* cmd into "node_modules" folder */
"%CD%\.bin\grunt" --version

如果您使用的是fish shell并且出于安全原因不想添加到$path。我们可以添加以下函数来运行本地节点可执行文件。

### run executables in node_module/.bin directory
function n
set -l npmbin (npm bin)
set -l argvCount (count $argv)
switch $argvCount
case 0
echo please specify the local node executable as 1st argument
case 1
# for one argument, we can eval directly
eval $npmbin/$argv
case '*'
set --local executable $argv[1]
# for 2 or more arguments we cannot append directly after the $npmbin/ since the fish will apply each array element after the the start string: $npmbin/arg1 $npmbin/arg2...
# This is just how fish interoperate array.
set --erase argv[1]
eval $npmbin/$executable $argv
end
end

现在你可以运行这样的东西:

n coffee

或更多参数,如:

n browser-sync --version

请注意,如果您是bash用户,那么@Bob9630是通过利用bash的$@来回答的,这在fishshell中不可用。

不错的例子

你再也不用操纵$PATH了!

npm@5.2.0开始,npm附带npx包,可让您从本地node_modules/.bin或中央缓存运行命令。

只需运行:

$ npx [options] <command>[@version] [command-arg]...

默认情况下,npx将检查<command>是否存在于$PATH或本地项目二进制文件中,并执行它。

<command>尚未在您的$PATH中时调用npx <command>将自动为您从NPM注册表中安装具有该名称的包并调用它。完成后,安装的包将不在您的全局变量中的任何位置,因此您不必担心长期污染。您可以通过提供--no-install选项来防止这种行为。

对于npm < 5.2.0,您可以通过运行以下命令手动安装npx包:

$ npm install -g npx

如果你想保留npm,那么npx应该做你需要的。


如果切换到YARN(Facebook的npm替代品)是您的选择,那么您可以致电:

 yarn yourCmd

package.json中的脚本将优先,如果没有找到,它将查看./node_modules/.bin/文件夹。

它还输出它运行的内容:

$ yarn tsc
yarn tsc v0.27.5
$ "/home/philipp/rate-pipeline/node_modules/.bin/tsc"

因此,您不必为package.json中的每个命令设置脚本。


如果您在package.json中的.scripts定义了一个脚本:

"tsc": "tsc" // each command defined in the scripts will be executed from `./node_modules/.bin/` first

yarn tsc等价于yarn run tscnpm run tsc

 yarn tsc
yarn tsc v0.27.5
$ tsc

我宁愿不依赖shell别名或其他包。

package.jsonscripts部分添加一个简单的行,您可以运行本地npm命令,例如

npm run webpack

package.json

{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"webpack": "webpack"
},
"devDependencies": {
"webpack": "^4.1.1",
"webpack-cli": "^2.0.11"
}
}

我一直使用与@gun eysus相同的方法来解决这个问题,即在package.json文件中创建一个脚本并使用它运行npm run script-name。

然而,最近几个月我一直在使用npx,我喜欢它。

例如,我下载了一个Angular项目,我不想全局安装Angular CLI。所以,安装了npx,而不是像这样使用全局角度cli命令(如果我安装了它):

ng serve

我可以从控制台执行此操作:

npx ng serve

这有篇文章我写了关于NPX的文章,并且深入其中。

太长别读:使用npm execnpm@>=7


其他答案中提到的npx命令已在npm@7中完全重写,默认情况下随node@15一起提供,并且可以安装在node@>=10上。该实现现在等于新引入的npm exec命令,该命令类似于但不等于之前的npx命令实现。

一个区别是,例如,它总是交互式地询问是否应该在尚未安装依赖项时下载依赖项(也可以用参数--yes--no覆盖)。

这是npm exec的示例。双破折号(--)将npm exec参数与实际命令参数分开:

npm exec --no -- jest --coverage

请参阅官方留档至npm exec

我提出我开发的新解决方案(05/2021)

您可以使用lpxhttps://www.npmjs.com/package/lpx

  • 运行在本地node_modules/. bin文件夹中找到的二进制文件
  • 从工作区的任何位置运行在工作区根的node_modules/. bin中找到的二进制文件

如果本地找不到二进制文件,lpx不会下载任何包(即不像npx)

示例: lpx tsc -b -w将使用本地打字脚本包运行tsc -b -w