Як використовувати виконувані файли з пакету, встановленого локально в node_modules?


493

Як використовувати локальну версію модуля в node.js. Наприклад, у своєму додатку я встановив сценарій кави:

npm install coffee-script

Це встановлює його в ./node_modulesі команда кава входить ./node_modules/.bin/coffee. Чи є спосіб запустити цю команду, коли я перебуваю в головній папці свого проекту? Я думаю, я шукаю щось схоже на bundle execв пакеті. В основному, я хотів би вказати версію кавового сценарію, яку повинні використовувати всі, хто бере участь у проекті.

Я знаю, що можу додати -gпрапор, щоб встановити його в усьому світі, щоб кава чудово працювала в будь-якому місці, але що робити, якщо я хотів мати різні версії кави на проект?


9
Багато інструкцій, які я читаю, говорять про такі речі, як npm install niftycommandі тоді niftycommand. Але це ніколи не вийде, якщо у вас не буде ./node_modules/.bin на своєму шляху, чи не так?
Bennett McElwee

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

Відповіді:


567

ОНОВЛЕННЯ : Як вказує 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і package.jsonзабезпечує майже такі ж функціональність.
guneysus

Якщо з'являється повідомлення "Шлях повинен бути рядком. Отримане невизначене", ось виправлення: github.com/zkat/npx/isissue/144#issuecomment-391031816
Валерій Катков

1
Ця відповідь хороша. Але я просто хочу сказати npx, що кульгавий. Це повинно було npm runабо npm execчи що - то.
Вільям Ентрікен

@WilliamEntriken З деяких причин npm run [my-local-package]не працює на моєму Ubuntu, хоча, здається, працює на пристрої Windows.
Годинник

97

Використовуйте npm binкоманду, щоб отримати модулі вузлів / каталог бін вашого проекту

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

напр

$ $(npm bin)/bower install

4
Мені подобається це просте і загальне рішення. Псевдонім здається непотрібним.
Метт Монтаг

Здається, наступне найкраще рішення, яке є елегантним та безпечнішим, ніж робитиexport PATH="./node_modules/.bin:$PATH"
Jontsai

1
@ inf3rno команда є $(npm bin)/jasmine, ні node $(npm bin)/jasmine(ви, мабуть, зрозуміли це, але уточнюючи для інших).
jassa

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 буде працювати без додавання до блоку скриптів.


5
Я знайшов цей підхід більш рівномірним у роботі з декількома розробниками над проектом - це дозволяє уникнути необхідності налаштовувати що-небудь локально ... ви просто npm installтоді у вас є доступ до ваших залежностей від розробників. Єдина незначна сторона вниз - вам потрібно npm run eslint(або що завгодно). Ви можете створити сценарій під назвою "start", який запускає gulp, так що вам потрібно лише набрати npm startдля запуску свого сервера розробників. Дуже прикольні речі та без баламутності, тому ваші друзі з 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-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

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'є дощовика.
олігофрен

1
Наскільки це погано, якщо ви не ставите це як перше в своєму PATH, а останнє (використовуючи $(npm bin)форму)? тож вони не можуть перезаписати наявні речі, і ви б довіряли виконуваним файлам у npm binкаталозі вже незалежно від PATHvar; чи може модель загрози полягає в тому, що: а) хтось із шкідливих шкіл отримує доступ до вашої файлової системи; Намагаючись зрозуміти сценарії, які роблять це погано, враховуючи, що ви вже довіряєте іноземним виконуваним npmфайлам під час використання встановлених програм.
osdiab

Ви можете робити трюки з оболонками з псевдонімом, і ви можете прокладати шлях вручну, і це "працює", але це не зовсім ідеально.
вбивчий екран

22

У рішенні PATH виникає проблема, що якщо $ (npm bin) розміщено у вашому .profile / .bashrc / тощо, він оцінюється один раз і назавжди встановлюється в той каталог, в якому вперше був оцінений шлях. Якщо замість цього ви змінюєте поточний шлях, з кожним запуском сценарію ваш шлях буде зростати.

Щоб обійти ці проблеми, я створив функцію і використав її. Це не змінює ваше оточення і просте у використанні:

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

Потім це можна використовувати так, не вносячи жодних змін у ваше оточення:

npm-exec r.js <args>

2
Мені це подобається! Я просто назвав свою функціюn
jontsai

Це чудово! Дякую, що поділились. Я додав версію рибної шкаралупи нижче.
LeOn - Хан Лі

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

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 з кошика в корінь вашого проекту (потрібні деякі модифікації файлу, але це буде просто працювати без написання нового коду тощо)
Dheeraj Bhaskar

Так, я закінчила це. Папка node_modules повинна знаходитися в папці, де існує
gulpfile

6

Я вважаю за краще не покладатися на псевдоніми оболонки чи інший пакет.

Додавши простий рядок до 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"
  }
}

5

Якщо ви хочете, щоб ваша змінна PATH правильно оновлювалася на основі вашої поточної робочої директорії, додайте це до кінця вашого .bashrc-equivalent (або після всього, що визначає 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, відповідно.


4

Я завжди використовував той самий підхід, що і @guneysus, щоб вирішити цю проблему, яка створює скрипт у файлі package.json і використовує його під управлінням npm run-name.

Однак в останні місяці я використовую npx і мені це подобається.

Наприклад, я завантажив проект Angular і не хотів встановлювати Angular CLI в усьому світі. Отже, при встановленому npx замість використання глобальної команди angular cli (якщо я її встановив) так:

ng serve

Я можу це зробити з консолі:

npx ng serve

Ось стаття, яку я написав про NPX, і яка заглиблюється в неї.


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


1

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

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

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

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>'
}

2
цей сценарій setenv не потрібен для script.json-скриптів. npm вже попереджує локальний каталог node_modules / .bin, щоб пройти шлях до вас під час виконання npm run {scriptpts}.
jasonkarns

0

Мені хотілося б дізнатися, чи це небезпечна / погана ідея, але, подумавши над цим, я не бачу тут проблеми:

Змінюючи небезпечне рішення Лінуса, щоб додати його до кінця, використовуючи npm binдля пошуку каталог і здійснюючи виклик сценарію лише npm binтоді, коли a 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"

Додавання 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's $@, який недоступний у fishshell.


-9

Включіть кава-скрипт у package.json з конкретною версією, яка потрібна в кожному проекті, як правило, так:

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

Потім запустіть npm install, щоб встановити залежності в кожному проекті. Це встановить вказану версію кави-скрипту, яка буде доступна локально для кожного проекту.


так, я дістався так, як я сказав у своєму запитанні. як я конкретно називаю ту, що є в моєму проекті, окрім ./node_modules/.bin/coffee?
typeoneerror

Якщо ви запустили npm install з пакетом package.json у головній папці вашого проекту, у цій папці має бути папка ./node_modules/.bin/coffee. Використання ./node_modules/coffee-script/bin/coffee запустить локальну версію кави, тоді як просто запущена кава запустить глобальну установку. Якщо у вас є інша версія кави, встановлена ​​на іншому шляху в цій папці проекту, ви можете отримати доступ до неї за допомогою ./path/to/this/installation/coffee.
альміпал

Це для мене не вийшло. Я намагаюся використовувати "svgo", і він працює лише при встановленні в усьому світі. Я спробував npm install svgoтак само, як і npm installз package.json. Обидва способи встановлено "успішно", але команда "svgo" все ще недоступна.
Райан Уіл

1
Grunt використовує це розумно, а IMHO так само, як і інші пакети. Спочатку ви встановлюєте grunt-cliпакунок у глобальному масштабі, потім у своєму каталозі проекту встановіть будь-яку (модифіковану) версію gruntпакета, потім, коли ви запустите grunt, він використовуватиме цю локальну версію.
ак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.