Как использовать исполняемые файлы из пакета, установленного локально в node_modules?

493

Как использовать локальную версию модуля в node.js . Например, в моем приложении я установил coffee-script:

npm install coffee-script

Это устанавливает его, ./node_modulesи команда кофе находится в./node_modules/.bin/coffee . Есть ли способ выполнить эту команду, когда я нахожусь в главной папке моего проекта? Я думаю, что я ищу что-то похожее на bundle execв комплекте. По сути, я бы хотел указать версию кофе-скрипта, которую должны использовать все участники проекта.

Я знаю, что могу добавить -gфлаг, чтобы установить его глобально, чтобы кофе работал в любом месте, но что, если я хотел бы иметь разные версии кофе на проект?

typeoneerror
источник
9
Много инструкций, которые я читаю, говорят о таких вещах, как npm install niftycommandи тогда niftycommand. Но это никогда не сработает, если у вас нет пути ./node_modules/.bin, не так ли?
Беннет МакЭлви,
2
Здесь очень хорошая запись: firstdoit.com/… - По сути, он рекомендует вам поместить свою coffeeкоманду в npm scriptsраздел, например, после "build": "coffee -co target/directory source/directoy", so you can run запуска npm run build` из терминала.
Бенни Нойгебауэр,
@BennyNeugebauer действительно, это то, что я делал в последнее время вместо того, чтобы возиться с PATH
typeoneerror
12
Использование , npxкоторое поставляется с npm 5.2.0 medium.com/@maybekatz/...
onmyway133

Ответы:

568

ОБНОВЛЕНИЕ : Как 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
регулярный
источник
17
Вы даже можете сделать еще один шаг впередalias coffee="npm-exec coffee"
регулярно
6
Выходные данные изменятся, когда вы перейдете в другой проект. Он не меняется, когда вы находитесь в проекте. npm binищет цепочку «каталогов предков» в cwd для поиска каталога node_modules. Это именно то поведение, которое вам нужно, если вы хотите использовать двоичные модули модулей, перечисленные в package.json проекта.
регулярная
11
о Боже! мне действительно нужно сделать что-то подобное, чтобы мои локальные модули работали? это практически невозможно объяснить команде! нет ничего более простого?
Алексиан
17
Вы всегда можете использовать сценарии npm, так как они всегда сначала ищут локальные двоичные файлы. Вы можете настроить псевдонимы для каждого из ваших двоичных файлов или просто использовать общие имена, такие как «build».
Джо Зим
6
@philosodad, на самом деле нет, ты не. PATHБудет вернуться к тому , что это было до вызова команды. Установка переменной среды в той же строке перед выполнением команды влияет только на среду этой команды.
обычное
410

Хороший пример

Вам не нужно $PATHбольше манипулировать !

Начиная с npm@5.2.0 , npm поставляется с npxпакетом, который позволяет запускать команды из локальногоnode_modules/.bin или из центрального кэша.

Просто запустите:

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

По умолчанию npxпроверяет, <command>существует $PATHли он в локальных или локальных двоичных файлах проекта, и выполняет его.

Вызов, npx <command>когда его <command>еще нет $PATH, автоматически установит для вас пакет с этим именем из реестра NPM и вызовет его. Когда это будет сделано, установленный пакет не будет нигде в ваших глобальных, так что вам не придется беспокоиться о загрязнении в долгосрочной перспективе. Вы можете предотвратить это, предоставив --no-installопцию.

Для npm < 5.2.0, вы можете установить npxпакет вручную, выполнив следующую команду:

$ npm install -g npx
Сён Чжон
источник
1
Я не люблю устанавливать сторонние глобальные пакеты npm npmи package.jsonпредоставляет почти такую же функциональность.
guneysus
Если появляется сообщение «Путь должен быть строкой. Получено не определено», вот исправление: github.com/zkat/npx/issues/144#issuecomment-391031816
Валерий Катков
1
Этот ответ хорош. Но я просто хочу сказать, npxчто хромает. Это должно было npm runили npm execили что - то.
Уильям Энтрикен
@WilliamEntriken По некоторым причинам npm run [my-local-package]не работает на моем Ubuntu, хотя, похоже, он работает на устройстве Windows.
Заводной
97

Используйте npm binкоманду, чтобы получить каталог node modules / bin вашего проекта

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

например

$ $(npm bin)/bower install
Jassa
источник
4
Мне нравится это простое и общее решение. Псевдоним кажется ненужным.
Мэтт Монтег
Кажется, что это следующее лучшее решение, которое элегантнее и безопаснее, чем нужноexport PATH="./node_modules/.bin:$PATH"
jontsai
1
@ inf3rno команда $(npm bin)/jasmineне, node $(npm bin)/jasmine(вы, наверное, разобрались, но проясняете для других).
Ясса
5
Неплохое решение, но оно не запускается в стандартной командной строке Windows с $. Я считаю, что лучше поместить его в раздел скриптов package.json, так как он более совместим.
Тимоти Гонсалес
77

использование 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 работал бы без добавления в блок scripts.

JLA
источник
5
Я обнаружил, что этот подход является более единообразным при работе с несколькими разработчиками в проекте - он устраняет необходимость настраивать что-либо локально ... вы просто npm installполучаете доступ к своим зависимостям dev. Единственный минус - вам нужно npm run eslint(или что-то еще). Вы можете создать скрипт с именем «start», который запускает gulp, поэтому вам нужно всего лишь набрать текст, npm startчтобы запустить свой dev-сервер. Довольно классная штука и ничего хорошего, так что твои друзья из Windows по-прежнему любят тебя. :)
Jpoveda
1
добавление псевдонима для добавления $ (npm bin) на ваш путь - это разумно, но тот факт, что это будет работать для людей без локальной конфигурации, завоевывает мое сердце
Conrad.Dean
12
это требует больше голосов! Передавайте аргументы в ваши сценарии после --:npm run learnyounode -- --normal-switches --watch -d *.js
ptim
Я также считаю, что это лучшее решение. Здесь есть подробное объяснение: lostechies.com/derickbailey/2012/04/24/…
adampasz
1
Это то, к чему я обычно обращаюсь, но по некоторым причинам на устройстве Ubuntu npm run ts-nodeу меня не работает. Я просто должен сделать репортаж в npx.
Заводной
42

Использование npm-run.

Из readme:

NPM-бег

Найти и запустить локальные исполняемые файлы из 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
mightyiam
источник
8
Больше не смотрите npx, на который есть ссылки выше ... stackoverflow.com/a/45164863/3246805
tj
41

Обновление: я больше не рекомендую этот метод, как по упомянутым причинам безопасности, так и, что немаловажно, более новая npm binкоманда. Оригинальный ответ ниже:

Как вы узнали, все локально установленные двоичные файлы находятся в ./node_modules/.bin. Для того, чтобы всегда запускать двоичные файлы в этом каталоге, а не глобально доступные двоичные файлы, если они есть, я предлагаю вам поставить ./node_modules/.binпервым на своем пути:

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

Если вы поместите это в свой ~/.profile, coffeeвсегда будет, ./node_modules/.bin/coffeeесли доступно, в противном случае /usr/local/bin/coffee(или любой другой префикс, под которым вы устанавливаете модули узлов).

Линус Густав Ларссон Тиль
источник
1
это, наверное, лучшее решение. Я также создал bash-скрипт под названием «watch» в моем проекте:./node_modules/.bin/coffee --output lib/ --compile --bare --watch src
typeoneerror
72
Опасность, Уилл Робинсон! Использование относительных путей в вашем $ PATH открывает дыру в безопасности размером с планету, особенно если вы поместите их прямо в качестве первого элемента. Если каталог, в котором вы находитесь, доступен для записи всем (скажем, где-нибудь в /tmp), любой процесс или пользователь может захватить ваш сеанс, поместив туда вредоносные версии обычных команд (например ls, cpи т. Д.). Они могут порождать «невидимые» вложенные оболочки, собирающие ваши пароли, и так далее.
извед
будет работать только в корне и других местах нет. alias npm-exec='PATH=$(npm bin):$PATH'является дождевика.
oligofren
1
Насколько это плохо, если вы не ставите это как первое PATH, а последнее (используя $(npm bin)форму)? поэтому они не могут перезаписать ваш существующий материал, и вы уже доверяли бы исполняемым файлам в npm binкаталоге независимо от PATHvar; Будет ли модель угрозы такой: а) кто-то злоумышленник получит доступ к вашей файловой системе, б) добавит исполняемые файлы с именами, близкими к этим системным инструментам, и в) вы ошиблись в типе? Попытка понять сценарии, которые делают это плохо, учитывая, что вы уже доверяете сторонним исполняемым npmфайлам при использовании установленных программ.
Осдиаб
Вы можете делать трюки с псевдонимом, и вы можете указать путь вручную, и это «работает», но это не совсем идеально.
killscreen
22

Решение PATH имеет проблему, заключающуюся в том, что если $ (npm bin) помещается в ваш файл .profile / .bashrc / etc, он оценивается один раз и всегда устанавливается в любой каталог, в котором сначала был оценен путь. Если вместо этого вы измените текущий путь, то каждый раз, когда вы запускаете скрипт, ваш путь будет расти.

Чтобы обойти эти проблемы, я создал функцию и использовал ее. Он не изменяет вашу среду и прост в использовании:

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

Это может быть использовано следующим образом без каких-либо изменений в вашей среде:

npm-exec r.js <args>
Bob9630
источник
2
Мне это нравится! Я просто назвал свою функциюn
jontsai
Это замечательно! Спасибо, что поделился. Я добавил версию рыбных раковин ниже.
Леон - Хан Ли
22

Если вы хотите сохранить npm, то npx должен делать то, что вам нужно.


Если вам подходит переход на пряжу (замена npm на facebook), вы можете позвонить:

 yarn yourCmd

скрипты в package.json будут иметь приоритет, если ничего не найдено, они будут смотреть внутрь ./node_modules/.bin/ папки.

Это также выводит то, что это бежало:

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

Так что вам не нужно настраивать скрипты для каждой команды в вашем package.json .


Если у вас есть сценарий, определенный .scriptsвнутри вашего package.json:

"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
k0pernikus
источник
14

обновление: если вы на недавнем npm (версия> 5.2)

Ты можешь использовать:

npx <command>

npxищет команду в .binкаталоге вашегоnode_modules

старый ответ:

Для 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> ...

Например

Чтобы выполнить wdioустановленный в локальном каталоге node_modules, выполните:

npm-exec wdio wdio.conf.js

т.е. он будет работать .\node_modules\.bin\wdio wdio.conf.js

Дирадж Бхаскар
источник
Это не работает при передаче более 1 аргумента. Например, npm-exec gulp <some_task>
OK999
@ OK9999 Я уверен, что некоторые незначительные изменения позволят передавать аргументы (потому что, если вы передадите его здесь, оно будет заключено в кавычки в ""); Я предлагаю скопировать и вставить файл gulp из bin в корневой каталог вашего проекта (для этого нужно внести некоторые изменения, но он будет работать без написания нового кода и т. Д.)
Dheeraj Bhaskar,
Да, я так и сделал. Папка node_modules должна находиться в папке, где существует
файл
7

Я предпочитаю не полагаться на псевдонимы оболочки или другой пакет.

Добавив простую строку в scriptsваш раздел package.json, вы можете запустить локальные команды 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"
  }
}
guneysus
источник
5

Если вы хотите, чтобы ваша переменная 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, соответственно.

namuol
источник
4

Я всегда использовал тот же подход, что и @guneysus, чтобы решить эту проблему: создать скрипт в файле package.json и использовать его, запустив npm run script-name.

Тем не менее, в последние месяцы я использую npx, и мне это нравится.

Например, я скачал проект Angular и не хотел устанавливать Angular CLI глобально. Итак, с установленным npx, вместо использования глобальной угловой команды cli (если я ее установил), вот так:

ng serve

Я могу сделать это из консоли:

npx ng serve

Вот статья, которую я написал о NPX, и это углубляется в это.

Jair Reina
источник
2

zxc похож на "bundle exec" для nodejs. Это похоже на использование PATH=$(npm bin):$PATH:

$ npm install -g zxc
$ npm install gulp
$ zxc which gulp
/home/nathan/code/project1/node_modules/.bin/gulp
Натан
источник
2

То же решение @regular, но вкус рыбных раковин

if not contains (npm bin) $PATH
    set PATH (npm bin) $PATH
end
Пионерское небо
источник
1

Вы также можете использовать direnv и изменять переменную $ PATH только в вашей рабочей папке.

$ cat .envrc
> export PATH=$(npm bin):$PATH
Erem
источник
1

Добавьте этот скрипт в свой .bashrc. Тогда вы можете позвонить coffeeили что-нибудь на месте. Это удобно для вашего ноутбука, но не используйте его на своем сервере.

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.

примечание 2 : вы можете изменить третью строку на NODE_MODULES=$(npm bin);. Но это сделало бы cdкоманду слишком медленной.

Цутому Кавамура
источник
1
Используйте $(npm bin)вместо жесткого кодирования ./node_modules/.bin.
bfontaine
Хм, $(npm bin)кажется, слишком медленно, чтобы использовать с каждой cdкомандой. Я восстановил код и добавил примечание для него.
Цутому Кавамура
1

Для Windows используйте это:

/* cmd into "node_modules" folder */
"%CD%\.bin\grunt" --version
b3wii
источник
0

Я столкнулся с той же проблемой, и мне не особо нравятся псевдонимы (как это обычно ), и если они вам тоже не нравятся, вот еще один обходной путь, который я использую, сначала вы должны создать крошечный исполняемый скрипт 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

Если вы используете scriptsв package.json, то:

...,
scripts: {
    'start': './setenv.sh <command>'
}
NKH
источник
2
этот сценарий setenv не требуется для сценариев package.json. npm уже добавляет в путь локальный каталог node_modules / .bin для вас при выполнении npm run {scripts}.
Jasonkarns
0

Я хотел бы знать, является ли это небезопасной / плохой идеей, но, подумав немного, я не вижу здесь проблемы:

Модифицируя небезопасное решение Линуса, чтобы добавить его в конец, используя npm binдля поиска каталог, и заставляя скрипт вызывать только npm binкогда package.jsonприсутствует родительский элемент (для скорости), вот для чего я придумал 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"
osdiab
источник
Добавление npm binв конец $PATHможет не выполнить то, что ожидает пользователь: в основном другой исполняемый файл, но, скорее всего, глобально установленный пакет с другой версией!
LoganMzz
0

Я 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

Удачи.

Акаша
источник
0

Если вы используете 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.

Леон - Хан Ли
источник
-9

Включите coffee-script в package.json с конкретной версией, необходимой для каждого проекта, как правило, так:

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

Затем запустите npm install, чтобы установить зависимости в каждом проекте. Это установит указанную версию coffee-script, которая будет доступна локально для каждого проекта.

almypal
источник
да, я так далеко, как я сказал в своем вопросе. как мне конкретно назвать один в моем проекте, кроме ./node_modules/.bin/coffee?
typeoneerror
Если вы запустили npm install с пакетом package.json в основной папке вашего проекта, в этой папке должна быть папка ./node_modules/.bin/coffee. Использование ./node_modules/coffee-script/bin/coffee запускает локальную версию кофе, в то время как только запуск кофе запускает глобальную установку. Если у вас установлена ​​другая версия кофе по другому пути в этой папке проекта, вы можете получить к ней доступ, используя ./path/to/this/installation/coffee.
almypal
Это не сработало для меня. Я пытаюсь использовать «svgo», и он работает только при глобальной установке. Я пробовал npm install svgoтак же, как и npm installс package.json. Оба метода установлены «успешно», но команда «svgo» по-прежнему недоступна.
Райан Уил
1
Grunt использует это хитроумно, и ИМХО, так же как и другие пакеты. Сначала вы устанавливаете grunt-cliпакет глобально, затем в каталоге вашего проекта установите любую (измененную) версию gruntпакета, затем при запуске gruntон будет использовать эту локальную версию.
извед