Comment utiliser les exécutables d'un package installé localement dans node_modules?


493

Comment utiliser une version locale d'un module dans node.js. Par exemple, dans mon application, j'ai installé coffee-script:

npm install coffee-script

Cela l'installe dans ./node_moduleset la commande coffee est dans ./node_modules/.bin/coffee. Existe-t-il un moyen d'exécuter cette commande lorsque je suis dans le dossier principal de mon projet? Je suppose que je cherche quelque chose de similaire à bundle execbundler. En gros, je voudrais spécifier une version de coffee-script que toutes les personnes impliquées dans le projet devraient utiliser.

Je sais que je peux ajouter le -gdrapeau pour l'installer à l'échelle mondiale afin que le café fonctionne bien n'importe où, mais que faire si je voulais avoir différentes versions de café par projet?


9
Beaucoup d'instructions que je lis disent des choses comme ça npm install niftycommandet ensuite niftycommand. Mais cela ne fonctionnera jamais à moins que vous ayez ./node_modules/.bin sur votre chemin, n'est-ce pas?
Bennett McElwee

2
Il y a un très bon résumé ici: firstdoit.com/… - Fondamentalement, il vous recommande de mettre votre coffeecommande dans la npm scriptssection, comme "build": "coffee -co target/directory source/directoy", so you can run npm run build` depuis le terminal.
Benny Neugebauer

@BennyNeugebauer en effet, c'est ce que j'ai fait récemment au lieu de jouer avec PATH
typeoneerror

12
Utilisation npxfournie avec npm 5.2.0 medium.com/@maybekatz/…
onmyway133

Réponses:


568

MISE À JOUR : Comme Seyeong Jeong le souligne dans sa réponse ci-dessous, depuis npm 5.2.0, vous pouvez utiliser npx [command], ce qui est plus pratique.

ANCIENNE REPONSE pour les versions antérieures à 5.2.0 :

Le problème avec mettre

./node_modules/.bin

dans votre PATH est qu'il ne fonctionne que lorsque votre répertoire de travail actuel est la racine de la structure de votre répertoire de projet (c'est-à-dire l'emplacement de node_modules)

Indépendamment de votre répertoire de travail, vous pouvez obtenir le chemin des binaires installés localement avec

npm bin

Pour exécuter un coffeebinaire installé localement indépendamment de l'endroit où vous vous trouvez dans la hiérarchie du répertoire du projet, vous pouvez utiliser cette construction bash

PATH=$(npm bin):$PATH coffee

J'ai donné un alias à npm-exec

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

Donc maintenant je peux

npm-exec coffee

pour exécuter la bonne copie de café, peu importe où je suis

$ 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
vous pouvez même aller plus loin etalias coffee="npm-exec coffee"
le

6
La sortie change lorsque vous cd dans un autre projet. Cela ne change pas lorsque vous cd dans un projet. npm binrecherche dans la chaîne des «répertoires ancêtres» vers le cwd un répertoire node_modules. C'est exactement le comportement souhaité si vous souhaitez spécifiquement utiliser les binaires des modules répertoriés dans le package.json du projet.
régulier

11
Oh mince! dois-je vraiment faire quelque chose comme ça pour que mes modules locaux fonctionnent? c'est assez impraticable de l'expliquer à une équipe! il n'y a rien de plus simple?
Alexian

17
Vous pouvez toujours utiliser des scripts npm car ils recherchent toujours les binaires locaux en premier. Vous pouvez y configurer des alias pour chacun de vos binaires ou simplement utiliser des noms génériques comme "build".
Joe Zim

6
@philosodad, en fait non, vous ne le faites pas. Le PATHsera de retour à ce qu'il était avant l'invocation de la commande. La définition d'une variable d'environnement sur la même ligne, avant d'exécuter une commande, affecte uniquement l'environnement de cette commande.
régulier

410

Bel exemple

Plus besoin de manipuler $PATH!

À partir de npm@5.2.0 , npm est fourni avec un npxpackage qui vous permet d'exécuter des commandes à partir node_modules/.bind'un cache local ou central.

Exécutez simplement:

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

Par défaut, npxvérifiera s'il <command>existe dans $PATHou dans les binaires du projet local et l'exécutera.

L' appel npx <command>quand <command>est pas déjà dans votre $PATHinstallera automatiquement un paquet avec ce nom du registre de NPM pour vous, et l' invoquer. Une fois terminé, le package installé ne se trouvera nulle part dans vos pays, vous n'aurez donc pas à vous soucier de la pollution à long terme. Vous pouvez empêcher ce comportement en fournissant une --no-installoption.

Pour npm < 5.2.0, vous pouvez installer le npxpackage manuellement en exécutant la commande suivante:

$ npm install -g npx

1
Je n'aime pas installer des packages npm globaux tiers npmet package.jsonfournit presque les mêmes fonctionnalités.
guneysus

Si le message "Le chemin doit être une chaîne. Reçu non défini" s'affiche, voici un correctif: github.com/zkat/npx/issues/144#issuecomment-391031816
Valeriy Katkov

1
Cette réponse est bonne. Mais je veux juste dire npxest boiteux. Cela aurait dû être npm runou npm execou quelque chose.
William Entriken

@WilliamEntriken Pour certaines raisons, npm run [my-local-package]ne fonctionne pas sur mon Ubuntu, bien qu'il semble fonctionner sur un appareil Windows.
Clockwork

97

Utilisez la npm bincommande pour obtenir le répertoire node modules / bin de votre projet

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

par exemple

$ $(npm bin)/bower install

4
J'aime cette solution simple et générique. Rend un alias inutile.
Matt Montag

Semble être la prochaine meilleure solution qui est élégante et plus sûre que d'avoir à le faireexport PATH="./node_modules/.bin:$PATH"
jontsai

1
@ inf3rno la commande ne l'est $(npm bin)/jasminepas node $(npm bin)/jasmine(vous l'avez probablement compris mais clarifiez pour les autres).
jassa

5
Pas une mauvaise solution, mais elle ne fonctionne pas sur une ligne de commande Windows standard avec $. Le mettre dans la section des scripts package.json est une meilleure approche, je pense, car il est plus compatible.
Timothy Gonzalez

77

Utilisation npm run[-script] <script name>

Après avoir utilisé npm pour installer le paquet bin dans votre ./node_modulesrépertoire local , modifiez package.jsonpour ajouter <script name>comme ceci:

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

Ce serait bien si l'installation de npm avait une option --add-script ou quelque chose ou si l'exécution de npm fonctionnerait sans ajouter au bloc de scripts.


5
J'ai trouvé que cette approche était plus uniforme lorsque vous traitez plusieurs développeurs sur un projet - cela évite d'avoir à configurer quoi que ce soit localement ... vous avez npm installalors accès à vos dépendances de développement. Le seul inconvénient mineur est que vous devez npm run eslint(ou peu importe). Vous pouvez créer un script appelé "start" qui exécute gulp afin que vous n'ayez qu'à taper npm startpour démarrer votre serveur de développement. Des trucs assez cool et pas de bonté, donc vos amis Windows vous aiment toujours. :)
jpoveda

1
ajouter un alias pour mettre $ (npm bin) sur votre chemin est intelligent, mais le fait que cela fonctionnera pour les personnes sans configuration locale gagne mon cœur
Conrad.Dean

12
cela nécessite plus de votes positifs! Passez les arguments à vos scripts après --comme:npm run learnyounode -- --normal-switches --watch -d *.js
ptim

Je trouve également que c'est la meilleure solution. Il y a une explication détaillée ici: lostechies.com/derickbailey/2012/04/24/…
adampasz

1
C'est ce que je fais habituellement, mais pour certaines raisons, sur un appareil Ubuntu, cela npm run ts-nodene fonctionne pas pour moi. Je vais juste devoir passer à npx.
Clockwork

42

Utilisation npm-run .

Du fichier Lisezmoi:

npm-run

Rechercher et exécuter des exécutables locaux à partir de node_modules

Tout exécutable disponible pour un script de cycle de vie npm est disponible pour npm-run .

Usage

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

Installation

$ npm install -g npm-run

8
Plus, voir npx référencé ci-dessus ... stackoverflow.com/a/45164863/3246805
tj

41

Mise à jour: je ne recommande plus cette méthode, à la fois pour les raisons de sécurité mentionnées et non des moindresnpm bin commande . Réponse originale ci-dessous:

Comme vous l'avez découvert, tous les binaires installés localement sont disponibles ./node_modules/.bin. Afin de toujours exécuter les binaires dans ce répertoire plutôt que les binaires disponibles globalement, s'ils sont présents, je vous suggère de mettre en ./node_modules/.binpremier sur votre chemin:

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

Si vous mettez ceci dans votre ~/.profile, coffeesera toujours ./node_modules/.bin/coffeesi disponible, sinon /usr/local/bin/coffee(ou quel que soit le préfixe sous lequel vous installez les modules de noeud).


1
c'est probablement la meilleure solution. J'ai également créé un script bash appelé "watch" dans mon projet:./node_modules/.bin/coffee --output lib/ --compile --bare --watch src
typeoneerror

72
Danger, Will Robinson! L'utilisation de chemins relatifs dans votre $ PATH ouvre un trou de sécurité de la taille d'une planète, surtout si vous les placez tout de suite comme premier élément. Si le répertoire que vous êtes est inscriptible par tout le monde (disons quelque part /tmp), tout processus ou utilisateur peut pirater votre session en mettant les versions malveillantes des commandes ordinaires (comme ls, cp, etc.). Ceux-ci peuvent engendrer des sous-coquilles «invisibles» capturant vos mots de passe, etc.
ack

ne fonctionnera que dans la racine et aucun autre endroit. le alias npm-exec='PATH=$(npm bin):$PATH'est plus lisse.
oligofren

1
À quel point est-ce mauvais si vous ne le mettez pas en premier PATH, mais en dernier (en utilisant le $(npm bin)formulaire)? donc ils ne peuvent pas écraser vos trucs existants, et vous auriez déjà fait confiance aux exécutables du npm binrépertoire indépendamment de la PATHvar; le modèle de menace serait-il que a) une personne malveillante accède à votre système de fichiers, b) elle ajoute des exécutables avec des noms proches de ces outils système, et c) vous vous trompez? Essayer de comprendre les scénarios qui rendent cela mauvais, étant donné que vous faites déjà confiance aux exécutables étrangers lorsque vous utilisez des npmprogrammes installés.
osdiab

Vous pouvez faire des trucs shell avec un alias et vous pouvez manuellement le chemin et cela "fonctionne" mais ce n'est pas tout à fait idéal.
killscreen

22

La solution PATH a le problème que si $ (npm bin) est placé dans votre .profile / .bashrc / etc, il est évalué une fois et est toujours défini dans le répertoire dans lequel le chemin a été évalué pour la première fois. Si vous modifiez plutôt le chemin actuel, chaque fois que vous exécutez le script, votre chemin se développe.

Pour contourner ces problèmes, je crée une fonction et l'utilise. Il ne modifie pas votre environnement et est simple à utiliser:

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

Cela peut ensuite être utilisé comme ceci sans apporter de modifications à votre environnement:

npm-exec r.js <args>

2
J'aime ça! J'ai simplement nommé ma fonctionn
jontsai

C'est bien! Merci d'avoir partagé. J'ai ajouté une version coquille de poisson ci-dessous.
LeOn - Han Li

22

Si vous souhaitez conserver npm, alors npx devrait faire ce dont vous avez besoin.


Si le passage au fil (un remplacement npm par Facebook) est une option pour vous, alors vous pouvez appeler:

 yarn yourCmd

les scripts à l'intérieur du package.json auront priorité, si aucun n'est trouvé, ils regarderont à l'intérieur du ./node_modules/.bin/dossier.

Il affiche également ce qu'il a exécuté:

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

Vous n'avez donc pas à configurer de scripts pour chaque commande de votre package.json.


Si vous aviez un script défini à l' .scriptsintérieur de votre package.json:

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

yarn tscserait équivalent yarn run tscou npm run tsc:

 yarn tsc
 yarn tsc v0.27.5
 $ tsc

14

mise à jour: si vous êtes sur le npm récent (version> 5.2)

Vous pouvez utiliser:

npx <command>

npxrecherche la commande dans le .binrépertoire de votrenode_modules

ancienne réponse:

Pour les fenêtres

Stockez les éléments suivants dans un fichier appelé npm-exec.batet ajoutez-les à votre%PATH%

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

Usage

Ensuite, vous pouvez l'utiliser comme npm-exec <command> <arg0> <arg1> ...

Par exemple

Pour exécuter wdioinstallé dans le répertoire local node_modules, procédez comme suit:

npm-exec wdio wdio.conf.js

c'est-à-dire qu'il fonctionnera .\node_modules\.bin\wdio wdio.conf.js


Cela ne fonctionne pas lors du passage de plus d'un argument. Par exemple, npm-exec gulp <some_task>
OK999

@ OK9999 Je suis sûr qu'une modification mineure permettra de passer des arguments (car lorsque vous le passez ici, il arrive entre guillemets dans ""); Ce que je suggère est de copier-coller le fichier gulp de bin vers la racine de votre projet (quelques modifications nécessaires du fichier, mais cela fonctionnera sans écrire de nouveau code, etc.)
Dheeraj Bhaskar

Oui, j'ai fini par le faire. Le dossier node_modules doit être dans le dossier où existe le gulpfile
OK999

7

Je préfère ne pas compter sur des alias de shell ou un autre package.

En ajoutant une ligne simple à la scriptssection de votre package.json, vous pouvez exécuter des commandes locales npm comme

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

Si vous souhaitez que votre variable PATH se mette à jour correctement en fonction de votre répertoire de travail actuel, ajoutez ceci à la fin de votre .bashrcéquivalent (ou après tout ce qui définit 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

Cela peut ajouter un court délai chaque fois que l'invite bash est rendue (en fonction de la taille de votre projet, probablement), elle est donc désactivée par défaut.

Vous pouvez activer et désactiver au sein de votre terminal en cours d' exécution node-modeet node-mode-off, respectivement.


4

J'ai toujours utilisé la même approche que @guneysus pour résoudre ce problème, qui crée un script dans le fichier package.json et l'utilise en exécutant npm run script-name.

Cependant, ces derniers mois, j'utilise npx et je l'adore.

Par exemple, j'ai téléchargé un projet Angular et je ne voulais pas installer la CLI Angular globalement. Donc, avec npx installé, au lieu d'utiliser la commande global angular cli (si je l'avais installé) comme ceci:

ng serve

Je peux le faire depuis la console:

npx ng serve

Voici un article que j'ai écrit sur NPX et qui l'approfondit.


2

zxc est comme "bundle exec" pour nodejs. Cela revient à utiliser PATH=$(npm bin):$PATH:

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

2

Même solution acceptée par @regular, mais saveur de coquille de poisson

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

1

Vous pouvez également utiliser direnv et modifier la variable $ PATH uniquement dans votre dossier de travail.

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

1

Ajoutez ce script à votre .bashrc. Ensuite, vous pouvez appeler coffeeou n'importe quoi localement. C'est pratique pour votre ordinateur portable, mais ne l'utilisez pas sur votre serveur.

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;

note : ce script fait aliase de cdcommande, et après chaque appel cdil vérifie node_modules/.binet l'ajoute à votre $PATH.

note2 : vous pouvez changer la troisième ligne en NODE_MODULES=$(npm bin);. Mais cela rendrait le cdcommandement trop lent.


1
Utilisez $(npm bin)au lieu du codage en dur ./node_modules/.bin.
bfontaine

Hmm, $(npm bin)semble trop lent à utiliser avec chaque cdcommande. J'ai restauré le code et ajouté une note à ce sujet.
Tsutomu Kawamura

1

Pour Windows, utilisez ceci:

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

0

J'ai rencontré le même problème et je n'aime pas particulièrement utiliser les alias (comme le suggèrent les habitués ), et si vous ne les aimez pas aussi, voici une autre solution de contournement que j'utilise, vous devez d'abord créer un minuscule script bash exécutable, dites 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 "$@"

puis vous pouvez ensuite utiliser tous les exécutables de votre section locale à l' /binaide de cette commande:

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

Si vous utilisez scriptsdans package.json alors:

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

2
ce script setenv n'est pas nécessaire pour les scripts package.json. npm ajoute déjà le répertoire local node_modules / .bin à votre chemin lors de l'exécution de npm run {scripts}.
jasonkarns

0

Je serais ravi de savoir si c'est une idée peu sûre / mauvaise, mais après y avoir réfléchi un peu, je ne vois pas de problème ici:

Modifier la solution non sécurisée de Linus pour l'ajouter à la fin, en utilisant npm binpour trouver le répertoire, et en faisant que le script n'appelle que npm binlorsque a package.jsonest présent dans un parent (pour la vitesse), voici ce que j'ai trouvé pourzsh :

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
}

Pour bash, au lieu d'utiliser le precmdcrochet, vous pouvez utiliser la $PROMPT_COMMANDvariable (je n'ai pas testé cela mais vous avez l'idée):

__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"

L'ajout npm binà la fin de $PATHne peut pas exécuter ce que l'utilisateur attend: fondamentalement un autre exécutable mais plus probablement un package installé mondialement avec une autre version!
LoganMzz

0

Je suis un Windowsutilisateur et c'est ce qui a fonctionné pour moi:

// 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

Bonne chance.


0

Dans le cas où vous utilisez fish shellet ne souhaitez pas ajouter à $pathpour des raisons de sécurité. Nous pouvons ajouter la fonction ci-dessous pour exécuter des exécutables de nœuds locaux.

### 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

Maintenant, vous pouvez exécuter quelque chose comme:

n coffee

ou plusieurs arguments comme:

n browser-sync --version

Notez que si vous êtes bashutilisateur, alors les réponses @ Bob9630 sont la voie à suivre en exploitant bash $@, qui n'est pas disponible dans fishshell.


-9

Incluez coffee-script dans package.json avec la version spécifique requise dans chaque projet, généralement comme ceci:

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

Exécutez ensuite npm install pour installer les dépendances dans chaque projet. Cela installera la version spécifiée de coffee-script qui sera accessible localement à chaque projet.


oui, je suis allé aussi loin que je l'ai dit dans ma question. comment puis-je appeler spécifiquement celui de mon projet en plus de ./node_modules/.bin/coffee?
typeoneerror

Si vous avez exécuté npm install avec le package.json dans le dossier principal de votre projet, vous devriez avoir un dossier ./node_modules/.bin/coffee dans ce dossier. L'utilisation de ./node_modules/coffee-script/bin/coffee exécutera la version locale de coffee tandis que l'exécution de coffee exécutera l'installation globale. Si vous avez une autre version de coffee installée dans un autre chemin dans ce dossier de projet, vous pouvez y accéder en utilisant ./path/to/this/installation/coffee.
almypal

Cela n'a pas fonctionné pour moi. J'essaie d'utiliser "svgo", et cela ne fonctionne que lorsqu'il est installé à l'échelle mondiale. J'ai essayé npm install svgoaussi bien npm installavec package.json. Les deux méthodes ont été installées «avec succès», mais la commande «svgo» n'est toujours pas disponible.
Ryan Wheale

1
Grunt utilise cela d'une manière intelligente, et à mon humble avis, il devrait en être de même pour les autres packages. D'abord, vous installez le grunt-clipackage globalement, puis dans votre répertoire de projet installez n'importe quelle version (modifiée) du gruntpackage, puis lorsque vous l'exécuterez grunt, il utilisera cette version locale.
ack
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.