如何在 node.js
中使用本地版本的模块。例如,在我的应用程序中,我安装了咖啡脚本:
npm install coffee-script
这会将其安装在 ./node_modules
中,并且 coffee 命令在 ./node_modules/.bin/coffee
中。当我在项目的主文件夹中时,有没有办法运行这个命令?我想我在 bundler 中寻找类似于 bundle exec
的东西。基本上,我想指定一个涉及项目的每个人都应该使用的咖啡脚本版本。
我知道我可以添加 -g
标志来全局安装它,这样咖啡在任何地方都可以正常工作,但是如果我想每个项目有不同版本的咖啡怎么办?
npm install niftycommand
,然后是niftycommand
。但这永远不会奏效,除非你的路径中有 ./node_modules/.bin ,不是吗?
coffee
命令放入 npm scripts
部分,例如之后从终端中的 "build": "coffee -co target/directory source/directoy", so you can run
npm run build`。
更新:正如 Seyeong Jeong 在下面的回答中指出的那样,从 npm 5.2.0 开始,您可以使用 npx [command]
,这更方便。
5.2.0 之前版本的旧答案:
放的问题
./node_modules/.bin
进入您的 PATH 是它仅在您当前的工作目录是项目目录结构的根目录时才有效(即 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
https://i.stack.imgur.com/zyFua.gif
您不必再操纵 $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
和 package.json
提供几乎相同的功能。
npx
很蹩脚。它应该是 npm run
或 npm exec
之类的。
npm run [my-local-package]
无法在我的 Ubuntu 上运行,尽管它似乎可以在 Windows 设备上运行。
使用 npm bin
命令获取项目的节点模块 /bin 目录
$ $(npm bin)/<binary-name> [args]
例如
$ $(npm bin)/bower install
export PATH="./node_modules/.bin:$PATH"
$(npm bin)/jasmine
,而不是 node $(npm bin)/jasmine
(您可能已经弄清楚了,但要向其他人澄清)。
使用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-script 选项或其他东西,或者如果 npm run 可以在不添加到脚本块的情况下工作,那就太好了。
npm install
然后您就可以访问您的开发依赖项。唯一的小缺点是您需要 npm run eslint
(或其他)。您可以创建一个名为“start”的脚本来运行 gulp,这样您只需键入 npm start
即可启动您的开发服务器。很酷的东西,没有 bash 的好处,所以你的 windows 朋友仍然喜欢你。 :)
--
之后将 args 传递给您的脚本,例如:npm run learnyounode -- --normal-switches --watch -d *.js
npm run ts-node
不适合我。我只需要求助于 npx。
更新:如果您使用的是最近的 npm(版本 >5.2)
您可以使用:
npx <command>
npx
在您的 node_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 bin
命令。原答案如下:
如您所见,任何本地安装的二进制文件都在 ./node_modules/.bin
中。为了始终在此目录中运行二进制文件而不是全局可用的二进制文件(如果存在),我建议您将 ./node_modules/.bin
首先放在路径中:
export PATH="./node_modules/.bin:$PATH"
如果您将它放在 ~/.profile
中,则 coffee
将始终为 ./node_modules/.bin/coffee
(如果可用),否则为 /usr/local/bin/coffee
(或您安装节点模块时使用的任何前缀)。
./node_modules/.bin/coffee --output lib/ --compile --bare --watch src
/tmp
中的某处),那么任何进程或用户都可以通过在其中放置普通命令(如 ls
、cp
等)的恶意版本来劫持您的会话。这些可能会产生“不可见”的子外壳来捕获您的密码等。
alias npm-exec='PATH=$(npm bin):$PATH'
更光滑。
PATH
中的第一件事,而是将其放在最后(使用 $(npm bin)
表单),这有多糟糕?因此它们无法覆盖您现有的内容,并且您已经信任 npm bin
目录中的可执行文件,而不管 PATH
变量如何;威胁模型是否会是 a) 恶意用户访问您的文件系统,b) 他们添加名称与这些系统工具接近的可执行文件,以及 c) 您输入错误?考虑到您在使用 npm
安装的程序时已经信任外部可执行文件,试图了解造成这种情况的情况。
使用 npm-run
。
从自述文件:
npm-运行
从 node_modules 查找并运行本地可执行文件
npm-run
可以使用任何可用于 npm 生命周期脚本的可执行文件。
用法
$ npm install mocha # mocha installed in ./node_modules
$ npm-run mocha test/* # uses locally installed mocha executable
安装
$ npm install -g npm-run
如果你想保留 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 tsc
或 npm run tsc
:
yarn tsc
yarn tsc v0.27.5
$ tsc
PATH 解决方案的问题是,如果将 $(npm bin) 放在您的 .profile/.bashrc/etc 中,它会被评估一次,并且永远设置为路径首先被评估的目录。如果改为修改当前路径,则每次运行脚本时,您的路径都会增长。
为了解决这些问题,我创建了一个函数并使用了它。它不会修改您的环境并且易于使用:
function npm-exec {
$(npm bin)/$@
}
然后可以像这样使用它,而无需对您的环境进行任何更改:
npm-exec r.js <args>
n
TL;DR:将 npm exec
与 npm@>=7
一起使用。
其他答案中提到的 npx
命令已在 npm@7
中完全重写,默认情况下随 node@15
一起提供,可以安装在 node@>=10
上。该实现现在等同于新引入的 npm exec
命令,它与之前的 npx
命令实现类似但不等同。
一个区别是它总是交互式地询问是否应该在尚未安装依赖项时下载它(也可以用参数 --yes
或 --no
覆盖)。
这是 npm exec
的示例。双破折号 (--
) 将 npm exec
参数与实际命令参数分开:
npm exec --no -- jest --coverage
另请参阅 updated, official documentation to npm exec
。
我宁愿不依赖 shell 别名或其他包。
在 package.json
的 scripts
部分添加一个简单的行,您可以运行本地 npm 命令,例如
npm run webpack
包.json
{
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"webpack": "webpack"
},
"devDependencies": {
"webpack": "^4.1.1",
"webpack-cli": "^2.0.11"
}
}
如果您希望 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-mode
和 node-mode-off
在终端中启用和禁用它。
我一直使用与@guneysus 相同的方法来解决这个问题,即在 package.json 文件中创建一个脚本并使用它运行 npm run script-name。
但是,最近几个月我一直在使用 npx,我很喜欢它。
例如,我下载了一个 Angular 项目,但我不想全局安装 Angular CLI。因此,安装了 npx,而不是像这样使用全局 angular cli 命令(如果我安装了它):
ng serve
我可以从控制台执行此操作:
npx ng serve
Here's an article 我写过关于 NPX 的文章,并且对此进行了更深入的探讨。
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
相同的 @regular 接受的解决方案,但鱼壳风味
if not contains (npm bin) $PATH
set PATH (npm bin) $PATH
end
您还可以使用 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
。
note2:您可以将第三行更改为 NODE_MODULES=$(npm bin);
。但这会使 cd
命令太慢。
$(npm bin)
而不是硬编码 ./node_modules/.bin
。
$(npm bin)
似乎太慢了,无法与每个 cd
命令一起使用。我已经恢复了代码并为其添加了注释。
对于 Windows,请使用:
/* cmd into "node_modules" folder */
"%CD%\.bin\grunt" --version
我遇到了同样的问题,我不是特别喜欢使用别名(如 regular 所建议的那样),如果您也不喜欢它们,那么这是我使用的另一种解决方法,您首先必须创建一个小型可执行 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>'
}
我很想知道这是否是一个不安全/坏主意,但经过一番思考后,我认为这里没有问题:
修改 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
,您可以使用 $PROMPT_COMMAND
变量而不是使用 precmd
钩子(我还没有测试过,但您明白了):
__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 bin
添加到 $PATH
的末尾可能不会执行用户期望的操作:基本上是另一个可执行文件,但更可能是具有另一个版本的全局安装包!
我是 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
祝你好运。
如果您正在使用 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
中不可用。
我提出了我开发的新解决方案 (05/2021)
您可以使用 lpx https://www.npmjs.com/package/lpx
运行在本地 node_modules/.bin 文件夹中找到的二进制文件
从工作空间中的任何位置运行在工作空间根目录的 node_modules/.bin 中找到的二进制文件
如果在本地找不到二进制文件,lpx 不会下载任何包(即不像 npx)
示例:lpx tsc -b -w
将使用本地 typescript 包运行 tsc -b -w
lpx
,但由于文件在 shebang 行上使用了错误的 (Windows) 行尾,它不起作用。
在 package.json 中包含 coffee-script 以及每个项目所需的特定版本,通常如下所示:
"dependencies":{
"coffee-script": ">= 1.2.0"
然后运行 npm install 在每个项目中安装依赖项。这将安装指定版本的咖啡脚本,每个项目都可以在本地访问它。
npm install svgo
和 npm install
。两种方法都“成功”安装,但“svgo”命令仍然不可用。
grunt-cli
包,然后在您的项目目录中安装 grunt
包的任何(修改)版本,然后当您运行 grunt
时,它将使用此本地版本。
alias coffee="npm-exec coffee"
npm bin
在 cwd 的“祖先目录”链中搜索 node_modules 目录。如果您特别想使用项目的 package.json 中列出的模块的二进制文件,这正是所需的行为。PATH
将恢复到命令调用之前的状态。在运行命令之前在同一行中设置环境变量只会影响该命令的环境。