ChatGPT解决这个技术问题 Extra ChatGPT

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

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

npm install coffee-script

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

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

我读过的很多说明都说npm install niftycommand,然后是niftycommand。但这永远不会奏效,除非你的路径中有 ./node_modules/.bin ,不是吗?
这里有一篇很好的文章:firstdoit.com/… — 基本上它建议您将 coffee 命令放入 npm scripts 部分,例如之后从终端中的 "build": "coffee -co target/directory source/directoy", so you can run npm run build`。
@BennyNeugebauer 确实,这就是我最近一直在做的事情,而不是弄乱 PATH
使用 npm 5.2.0 medium.com/@maybekatz/… 附带的 npx

r
regular

更新:正如 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

您甚至可以更进一步alias coffee="npm-exec coffee"
当您 cd 进入另一个项目时,输出会发生变化。当您在项目中 cd 时,它不会改变。 npm bin 在 cwd 的“祖先目录”链中搜索 node_modules 目录。如果您特别想使用项目的 package.json 中列出的模块的二进制文件,这正是所需的行为。
哦,天哪!为了让我的本地模块工作,我真的必须做这样的事情吗?向团队解释是非常不切实际的!没有什么比这更简单的了?
您始终可以使用 npm scripts,因为它们总是首先搜索本地二进制文件。您可以在那里为每个二进制文件设置别名,或者只使用“build”等通用名称。
@philosodad,实际上不,你没有。 PATH 将恢复到命令调用之前的状态。在运行命令之前在同一行中设置环境变量只会影响该命令的环境。
S
Seyeong Jeong

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

我不喜欢安装 3rd 方全局 npm 包,而 npmpackage.json 提供几乎相同的功能。
如果出现“Path must be a string. Received undefined”消息,这里有一个修复:github.com/zkat/npx/issues/144#issuecomment-391031816
这个答案很好。但我只想说 npx 很蹩脚。它应该是 npm runnpm exec 之类的。
@WilliamEntriken 出于某些原因,npm run [my-local-package] 无法在我的 Ubuntu 上运行,尽管它似乎可以在 Windows 设备上运行。
此答案还应包括如何在 package.json 文件中为发布者注册二进制文件,docs.npmjs.com/cli/v7/configuring-npm/package-json#bin
j
jassa

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

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

例如

$ $(npm bin)/bower install

我喜欢这个简单而通用的解决方案。使别名看起来没有必要。
似乎是下一个最佳解决方案,它比必须做的更优雅、更安全export PATH="./node_modules/.bin:$PATH"
@inf3rno 命令是 $(npm bin)/jasmine,而不是 node $(npm bin)/jasmine(您可能已经弄清楚了,但要向其他人澄清)。
不错的解决方案,但它不能在带有 $ 的标准 Windows 命令行上运行。我觉得把它放在 package.json 脚本部分是一种更好的方法,因为它更兼容。
v
vsync

使用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 朋友仍然喜欢你。 :)
添加别名以将 $(npm bin) 放在您的路径上很聪明,但是这对没有本地配置的人有效这一事实赢得了我的心
这需要更多的支持!在 -- 之后将 args 传递给您的脚本,例如:npm run learnyounode -- --normal-switches --watch -d *.js
我也发现这是最好的解决方案。这里有深入的解释:lostechies.com/derickbailey/2012/04/24/…
这是我通常追求的,但由于某些原因,在 Ubuntu 设备上,npm run ts-node 不适合我。我只需要求助于 npx。
C
Community

更新:如果您使用的是最近的 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


这在传递超过 1 个参数时不起作用。例如 npm-exec gulp
@ OK9999 我确信一些小的修改将允许传递参数(因为当你在这里传递它时,它会在“”中引用);我建议将 gulp 文件从 bin 复制粘贴到项目根目录(需要对文件进行一些修改,但无需编写新代码等即可工作)
是的,我最终这样做了。 node_modules 文件夹必须位于 gulpfile 所在的文件夹中
L
Linus Thiel

更新:我不再推荐这种方法,既是出于上述安全原因,也不是因为较新的 npm bin 命令。原答案如下:

如您所见,任何本地安装的二进制文件都在 ./node_modules/.bin 中。为了始终在此目录中运行二进制文件而不是全局可用的二进制文件(如果存在),我建议您将 ./node_modules/.bin 首先放在路径中:

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

如果您将它放在 ~/.profile 中,则 coffee 将始终为 ./node_modules/.bin/coffee(如果可用),否则为 /usr/local/bin/coffee(或您安装节点模块时使用的任何前缀)。


这可能是最好的解决方案。我还在我的项目中创建了一个名为“watch”的 bash 脚本:./node_modules/.bin/coffee --output lib/ --compile --bare --watch src
危险,威尔罗宾逊!在 $PATH 中使用相对路径会打开一个行星大小的安全漏洞,特别是如果您将它们作为第一项放在前面。如果您所在的目录对每个人都是可写的(比如在 /tmp 中的某处),那么任何进程或用户都可以通过在其中放置普通命令(如 lscp 等)的恶意版本来劫持您的会话。这些可能会产生“不可见”的子外壳来捕获您的密码等。
只能在根目录下工作,其他地方不行。 alias npm-exec='PATH=$(npm bin):$PATH' 更光滑。
如果您不将其作为 PATH 中的第一件事,而是将其放在最后(使用 $(npm bin) 表单),这有多糟糕?因此它们无法覆盖您现有的内容,并且您已经信任 npm bin 目录中的可执行文件,而不管 PATH 变量如何;威胁模型是否会是 a) 恶意用户访问您的文件系统,b) 他们添加名称与这些系统工具接近的可执行文件,以及 c) 您输入错误?考虑到您在使用 npm 安装的程序时已经信任外部可执行文件,试图了解造成这种情况的情况。
您可以使用别名执行 shell 技巧,并且可以手动路径,这“有效”,但它不是很理想。
m
mightyiam

使用 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

不再,请参阅上面引用的 npx...stackoverflow.com/a/45164863/3246805
k
k0pernikus

如果你想保留 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

B
Bob9630

PATH 解决方案的问题是,如果将 $(npm bin) 放在您的 .profile/.bashrc/etc 中,它会被评估一次,并且永远设置为路径首先被评估的目录。如果改为修改当前路径,则每次运行脚本时,您的路径都会增长。

为了解决这些问题,我创建了一个函数并使用了它。它不会修改您的环境并且易于使用:

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

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

npm-exec r.js <args>

我喜欢这个!我只是将我的函数命名为 n
这很棒!感谢分享。我在下面添加了一个鱼壳版本。
s
ssc-hrep3

TL;DR:将 npm execnpm@>=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


g
guneysus

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

package.jsonscripts 部分添加一个简单的行,您可以运行本地 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"
  }
}

n
namuol

如果您希望 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 在终端中启用和禁用它。


J
Jair Reina

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

但是,最近几个月我一直在使用 npx,我很喜欢它。

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

ng serve

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

npx ng serve

Here's an article 我写过关于 NPX 的文章,并且对此进行了更深入的探讨。


N
Nathan

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

P
Pioneer Skies

相同的 @regular 接受的解决方案,但鱼壳风味

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

E
Erem

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

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

T
Tsutomu Kawamura

将此脚本添加到您的 .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 命令一起使用。我已经恢复了代码并为其添加了注释。
a
aLx13

对于 Windows,请使用:

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

C
Community

我遇到了同样的问题,我不是特别喜欢使用别名(如 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>'
}

package.json 脚本不需要这个 setenv 脚本。在执行 npm run {scripts} 时,npm 已经将本地 node_modules/.bin 目录添加到路径中。
o
osdiab

我很想知道这是否是一个不安全/坏主意,但经过一番思考后,我认为这里没有问题:

修改 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 的末尾可能不会执行用户期望的操作:基本上是另一个可执行文件,但更可能是具有另一个版本的全局安装包!
A
Aakash

我是 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

祝你好运。


L
LeOn - Han Li

如果您正在使用 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 中不可用。


B
Bruno Gasnier

我提出了我开发的新解决方案 (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) 行尾,它不起作用。
a
almypal

在 package.json 中包含 coffee-script 以及每个项目所需的特定版本,通常如下所示:

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

然后运行 npm install 在每个项目中安装依赖项。这将安装指定版本的咖啡脚本,每个项目都可以在本地访问它。


是的,正如我在问题中所说的那样。除了./node_modules/.bin/coffee,我如何在我的项目中专门调用它?
如果您在项目的主文件夹中使用 package.json 运行 npm install,则此文件夹中应该有一个 ./node_modules/.bin/coffee 文件夹。使用 ./node_modules/coffee-script/bin/coffee 将运行本地版本的咖啡,而仅运行咖啡将运行全局安装。如果您在此项目文件夹的另一个路径中安装了另一个版本的咖啡,您可以使用 ./path/to/this/installation/coffee 访问它。
这对我不起作用。我正在尝试使用“svgo”,它仅在全局安装时才有效。我已经用 package.json 尝试了 npm install svgonpm install。两种方法都“成功”安装,但“svgo”命令仍然不可用。
Grunt 巧妙地使用了这一点,恕我直言,其他软件包也应如此。首先您在全局安装 grunt-cli 包,然后在您的项目目录中安装 grunt 包的任何(修改)版本,然后当您运行 grunt 时,它将使用此本地版本。