Come usare gli eseguibili da un pacchetto installato localmente in node_modules?


493

Come posso utilizzare una versione locale di un modulo in node.js. Ad esempio, nella mia app ho installato coffee-script:

npm install coffee-script

Questo lo installa ./node_modulese il comando caffè è attivo ./node_modules/.bin/coffee. C'è un modo per eseguire questo comando quando mi trovo nella cartella principale del mio progetto? Immagino che sto cercando qualcosa di simile a bundle execin bundler. Fondamentalmente, vorrei specificare una versione della sceneggiatura del caffè che tutti gli attori coinvolti nel progetto dovrebbero usare.

So di poter aggiungere il -gflag per installarlo a livello globale in modo che il caffè funzioni bene ovunque, ma se volessi avere versioni diverse di caffè per progetto?


9
Molte istruzioni che leggo dicono cose come npm install niftycommande poi niftycommand. Ma questo non funzionerà mai a meno che tu non abbia ./node_modules/.bin nel tuo percorso, vero?
Bennett McElwee,

2
C'è un ottimo commento qui: firstdoit.com/… - Fondamentalmente ti consiglia di inserire il tuo coffeecomando nella npm scriptssezione, come "build": "coffee -co target/directory source/directoy", so you can run npm run build` dal terminale in seguito.
Benny Neugebauer,

@BennyNeugebauer in effetti, è quello che ho fatto ultimamente invece di fare
casini

12
Uso npxche viene fornito con npm 5.2.0 medium.com/@maybekatz/…
onmyway133

Risposte:


568

AGGIORNAMENTO : Come sottolinea Seyeong Jeong nella loro risposta di seguito, da npm 5.2.0 è possibile utilizzare npx [command], il che è più conveniente.

RISPOSTA VECCHIA per versioni precedenti alla 5.2.0 :

Il problema con il mettere

./node_modules/.bin

nel tuo PERCORSO è che funziona solo quando la tua directory di lavoro corrente è la radice della struttura della directory del tuo progetto (ovvero la posizione di node_modules)

Indipendentemente dalla directory di lavoro, è possibile ottenere il percorso dei file binari installati localmente

npm bin

Per eseguire un coffeebinario installato localmente indipendente da dove ti trovi nella gerarchia della directory di progetto puoi usare questo costrutto bash

PATH=$(npm bin):$PATH coffee

Ho modificato questo al npm-exec

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

Quindi ora posso

npm-exec coffee

per eseguire la copia corretta del caffè, indipendentemente da dove mi trovo

$ 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
puoi anche fare un altro passo ealias coffee="npm-exec coffee"
regolare il

6
L'output cambia quando si esegue il cd in un altro progetto. Non cambia quando si esegue il cd all'interno di un progetto. npm bincerca nella catena di 'directory degli antenati' nel CWD una directory node_modules. Questo è esattamente il comportamento desiderato se si desidera utilizzare specificamente i file binari dei moduli elencati nel package.json del progetto.
regolare

11
Oh mamma! devo davvero fare qualcosa del genere per far funzionare i miei moduli locali? è abbastanza impraticabile spiegarlo a una squadra! non c'è niente di più semplice?
Alexian,

17
Puoi sempre usare gli script npm poiché cercano sempre prima i binari locali. Puoi impostare alias per ciascuno dei tuoi binari lì o semplicemente usare nomi generici come "build".
Joe Zim,

6
@philosodad, in realtà no, non è così. Il PATHsarà di nuovo a quello che era prima del comando invocazione. L'impostazione di una variabile di ambiente nella stessa riga, prima di eseguire un comando, influisce solo sull'ambiente di quel comando.
regolare il

410

Bell'esempio

Non devi $PATHpiù manipolare !

Da npm@5.2.0 , npm viene fornito con un npxpacchetto che consente di eseguire comandi da una node_modules/.bincache locale o centrale.

Esegui semplicemente:

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

Per impostazione predefinita, npxverificherà se <command>esiste $PATHo nei file binari del progetto locale ed eseguirà ciò.

La chiamata npx <command>quando <command>non è già $PATHpresente installerà automaticamente un pacchetto con quel nome dal registro NPM e lo invocherà. Al termine, il pacchetto installato non sarà in nessuna parte dei tuoi globi, quindi non dovrai preoccuparti dell'inquinamento a lungo termine. È possibile impedire questo comportamento fornendo l' --no-installopzione.

Per npm < 5.2.0, è possibile installare il npxpacchetto manualmente eseguendo il comando seguente:

$ npm install -g npx

1
Non mi piace installare pacchetti npm globali di terze parti mentre npme package.jsonfornisce quasi la stessa funzionalità.
guneiso

Se viene visualizzato il messaggio "Percorso deve essere una stringa. Ricevuto non definito", ecco una correzione: github.com/zkat/npx/issues/144#issuecomment-391031816
Valeriy Katkov

1
Questa risposta è buona Ma voglio solo dire che npxè zoppo. Avrebbe dovuto essere npm runo npm execo qualcosa del genere.
William Entriken,

@WilliamEntriken Per alcuni motivi, npm run [my-local-package]non funziona sul mio Ubuntu, anche se sembrava funzionare su un dispositivo Windows.
Orologio

97

Utilizzare il npm bincomando per ottenere la directory dei moduli / bin del nodo del progetto

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

per esempio

$ $(npm bin)/bower install

4
Mi piace questa soluzione semplice e generica. Rende inutile un alias.
Matt Montag,

Sembra essere la prossima migliore soluzione che sia elegante e più sicura che dover fareexport PATH="./node_modules/.bin:$PATH"
jontsai

1
@ inf3rno il comando è $(npm bin)/jasmine, non node $(npm bin)/jasmine(probabilmente l'hai capito ma chiarendo per gli altri).
Jassa,

5
Non è una cattiva soluzione, ma non funziona su una riga di comando di Windows standard con $. Metterlo nella sezione script package.json è un approccio migliore che sento, poiché è più compatibile.
Timothy Gonzalez,

77

Uso npm run[-script] <script name>

Dopo aver usato npm per installare il pacchetto bin nella tua ./node_modulesdirectory locale , modifica package.jsonper aggiungere in <script name>questo modo:

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

Sarebbe bello se npm install avesse un'opzione --add-script o qualcosa del genere o se npm run funzionasse senza aggiungere al blocco degli script.


5
Ho trovato questo approccio più uniforme quando si ha a che fare con più sviluppatori in un progetto - evita la necessità di configurare qualcosa localmente ... proprio in npm installquel momento hai accesso alle dipendenze degli sviluppatori. L'unico lato negativo è che devi npm run eslint(o qualunque cosa). È possibile creare uno script chiamato "start" che esegue gulp in modo che sia sufficiente digitare npm startper avviare il server di sviluppo. Roba piuttosto bella e niente bontà, quindi ai tuoi amici di Windows piaci ancora. :)
jpoveda,

1
aggiungere un alias per mettere $ (npm bin) sul tuo percorso è intelligente, ma il fatto che questo funzionerà per le persone senza configurazione locale mi conquista il cuore
Conrad.Dean

12
questo ha bisogno di più voti! Passa args ai tuoi script dopo --come:npm run learnyounode -- --normal-switches --watch -d *.js
ptim

Trovo anche questa la soluzione migliore. C'è una spiegazione approfondita qui: lostechies.com/derickbailey/2012/04/24/…
adampasz

1
Questo è ciò che di solito cerco, ma per alcuni motivi su un dispositivo Ubuntu npm run ts-nodenon funziona per me. Dovrò solo eseguire il riassortimento in npx.
Orologio

42

Usa npm-run.

Dal readme:

NPM-run

Trova ed esegui eseguibili locali da node_modules

Qualsiasi eseguibile disponibile per uno script del ciclo di vita di npm è disponibile per npm-run.

uso

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

Installazione

$ npm install -g npm-run

8
Non più, vedere NPX di cui sopra ... stackoverflow.com/a/45164863/3246805
tj

41

Aggiornamento: non consiglio più questo metodo, sia per i motivi di sicurezza menzionati, sia per il npm bincomando più recente . Risposta originale di seguito:

Come hai scoperto, sono presenti tutti i file binari installati localmente ./node_modules/.bin. Per eseguire sempre i binari in questa directory piuttosto che i binari disponibili a livello globale, se presente, ti suggerisco di inserire ./node_modules/.binprima il percorso:

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

Se lo metti nel tuo ~/.profile, coffeesarà sempre ./node_modules/.bin/coffeese disponibile, altrimenti /usr/local/bin/coffee(o qualunque prefisso stai installando i moduli del nodo).


1
questa è probabilmente la soluzione migliore. Ho anche creato uno script bash chiamato "watch" nel mio progetto:./node_modules/.bin/coffee --output lib/ --compile --bare --watch src
typeoneerror

72
Pericolo, Will Robinson! L'uso di percorsi relativi nel tuo $ PATH apre una falla di sicurezza delle dimensioni di un pianeta, specialmente se li metti in primo piano come primo oggetto. Se la directory si è in è scrivibile da tutti (dire da qualche parte in /tmp), qualsiasi processo o utente può dirottare la sessione mettendo versioni maligni di comandi ordinari (come ls, cp, etc.) là. Questi possono generare sotto-shell "invisibili" che catturano le tue password, eccetera.
ack

funzionerà solo nella radice e non in altri luoghi. il alias npm-exec='PATH=$(npm bin):$PATH'è lisciante.
oligofren,

1
Quanto è grave se non lo metti come la prima cosa nella tua PATH, ma l'ultima (usando il $(npm bin)modulo)? quindi non possono sovrascrivere le tue cose esistenti e ti saresti fidato degli eseguibili nella npm bindirectory già indipendentemente dalla PATHvar; il modello di minaccia sarebbe che a) qualcuno malintenzionato abbia accesso al tuo file system, b) aggiungano eseguibili con nomi vicini a quegli strumenti di sistema e c) digiti male? Cercare di capire gli scenari che rendono questo male, dato che ti stai già fidando degli eseguibili stranieri quando usi npmprogrammi installati.
osdiab,

Puoi fare trucchi con la shell con un alias e puoi tracciarli manualmente e questo "funziona" ma non è del tutto ideale.
Killscreen,

22

La soluzione PATH ha il problema che se $ (npm bin) viene inserito nel tuo .profile / .bashrc / etc, viene valutato una volta ed è sempre impostato su qualsiasi directory in cui è stato valutato il percorso per la prima volta. Se invece modifichi il percorso corrente, allora ogni volta che esegui lo script il tuo percorso aumenterà.

Per aggirare questi problemi, ho creato una funzione e l'ho usata. Non modifica il tuo ambiente ed è semplice da usare:

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

Questo può quindi essere utilizzato in questo modo senza apportare modifiche al proprio ambiente:

npm-exec r.js <args>

2
Mi piace questo! Ho semplicemente chiamato la mia funzionen
jontsai il

Questo è fantastico! Grazie per la condivisione. Ho aggiunto una versione conchiglia di pesce di seguito.
LeOn - Han Li,

22

Se vuoi mantenere npm, allora npx dovrebbe fare quello che ti serve.


Se passare al filato (una sostituzione npm di Facebook) è un'opzione per te, puoi chiamare:

 yarn yourCmd

gli script all'interno di package.json avranno la precedenza, se non viene trovato nessuno, apparirà all'interno della ./node_modules/.bin/cartella.

Produce anche ciò che ha funzionato:

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

Quindi non devi impostare gli script per ciascun comando nel tuo package.json.


Se .scriptsall'interno del tuo script fosse definito uno script package.json:

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

yarn tscsarebbe equivalente a yarn run tsco npm run tsc:

 yarn tsc
 yarn tsc v0.27.5
 $ tsc

14

aggiornamento: se sei nel npm recente (versione> 5.2)

Puoi usare:

npx <command>

npxcerca il comando nella .bindirectory del tuonode_modules

vecchia risposta:

Per Windows

Memorizza quanto segue in un file chiamato npm-exec.bate aggiungilo al tuo%PATH%

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

uso

Quindi puoi usarlo come npm-exec <command> <arg0> <arg1> ...

Per esempio

Per eseguire wdioinstallato nella directory node_modules locale, eseguire:

npm-exec wdio wdio.conf.js

cioè funzionerà .\node_modules\.bin\wdio wdio.conf.js


Questo non funziona passando più di 1 argomento. Ad esempio npm-exec gulp <some_task>
OK999

@ OK9999 Sono sicuro che alcune modifiche minori consentiranno il passaggio di argomenti (perché quando lo passi qui, viene citato tra ""); Quello che ti suggerisco è copia incolla il file gulp dal cestino alla radice del tuo progetto (sono necessarie alcune modifiche al file, ma funzionerà solo senza scrivere nuovo codice ecc.)
Dheeraj Bhaskar,

Sì, ho finito per farlo. La cartella node_modules deve trovarsi nella cartella in cui esiste il file gulp
OK999,

7

Preferisco non fare affidamento sugli alias di shell o su un altro pacchetto.

Aggiungendo una semplice riga alla scriptssezione del tuo package.json, puoi eseguire comandi npm locali come

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

Se vuoi che la tua variabile PATH si aggiorni correttamente in base alla tua attuale directory di lavoro, aggiungi questo alla fine del tuo .bashrcequivalente (o dopo tutto ciò che definisce 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

Questo può aggiungere un breve ritardo ogni volta che viene visualizzato il prompt di bash (a seconda delle dimensioni del progetto, molto probabilmente), quindi è disabilitato per impostazione predefinita.

È possibile abilitarlo e disabilitarlo all'interno del proprio terminale eseguendo node-modee node-mode-off, rispettivamente.


4

Ho sempre usato lo stesso approccio di @guneysus per risolvere questo problema, che sta creando uno script nel file package.json e lo uso con npm run script-name.

Tuttavia, negli ultimi mesi ho usato npx e lo adoro.

Ad esempio, ho scaricato un progetto Angular e non volevo installare la CLI Angular a livello globale. Quindi, con npx installato, invece di utilizzare il comando cli angolare globale (se lo avessi installato) in questo modo:

ng serve

Posso farlo dalla console:

npx ng serve

Ecco un articolo che ho scritto su NPX e che approfondisce.


2

zxc è come "bundle exec" per nodejs. È simile all'utilizzo di PATH=$(npm bin):$PATH:

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

2

Stessa soluzione accettata da @regular, ma sapore di conchiglia di pesce

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

1

Puoi anche usare direnv e cambiare la variabile $ PATH solo nella tua cartella di lavoro.

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

1

Aggiungi questo script al tuo .bashrc. Quindi puoi chiamare coffeeo fare qualsiasi cosa localmente. Questo è utile per il tuo laptop, ma non usarlo sul tuo server.

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;

nota : questo script esegue un cdcomando, e dopo ogni chiamata cdlo controlla node_modules/.bine lo aggiunge al proprio $PATH.

note2 : è possibile modificare la terza riga in NODE_MODULES=$(npm bin);. Ma ciò renderebbe il cdcomando troppo lento.


1
Utilizzare $(npm bin)invece di hardcoding ./node_modules/.bin.
bfontaine,

Hmm, $(npm bin)sembra troppo lento da usare con ogni cdcomando. Ho ripristinato il codice e ho aggiunto una nota per esso.
Tsutomu Kawamura,

1

Per Windows usa questo:

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

0

Ho riscontrato lo stesso problema e non mi piace particolarmente usare gli alias (come suggerito da Regular ), e se non ti piacciono anche loro, ecco un'altra soluzione alternativa che uso, devi prima creare un piccolo script eseguibile bash, dì 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 "$@"

e quindi puoi usare qualsiasi eseguibile nel tuo locale /binusando questo comando:

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

Se stai usando scriptsin package.json, allora:

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

2
questo script setenv non è necessario per gli script package.json. npm antepone già la directory node_modules / .bin locale al percorso durante l'esecuzione di npm run {scripts}.
Jasonkarns,

0

Mi piacerebbe sapere se questa è un'idea insicura / cattiva, ma dopo averci pensato un po 'non vedo un problema qui:

Modificando la soluzione insicura di Linus per aggiungerla alla fine, usando npm binper trovare la directory e facendo chiamare lo script solo npm binquando a package.jsonè presente in un genitore (per velocità), questo è ciò che mi è venuto in mente per 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
}

Per bash, invece di usare l' precmdhook, puoi usare la $PROMPT_COMMANDvariabile (non l'ho testata ma hai avuto l'idea):

__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'aggiunta npm binalla fine di $PATHpotrebbe non eseguire ciò che l'utente si aspetta: sostanzialmente un altro eseguibile ma molto probabilmente un pacchetto installato a livello globale con un'altra versione!
LoganMzz,

0

Sono un Windowsutente e questo è ciò che ha funzionato per me:

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

In bocca al lupo.


0

Nel caso in cui si stia utilizzando fish shelle non si desideri aggiungerlo $pathper motivi di sicurezza. È possibile aggiungere la seguente funzione per eseguire gli eseguibili del nodo locale.

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

Ora puoi eseguire cose come:

n coffee

o più argomenti come:

n browser-sync --version

Nota, se sei bashutente, allora @ Bob9630 risponde è la strada da percorrere sfruttando bash's $@, che non è disponibile in fishshell.


-9

Includi coffee-script in package.json con la versione specifica richiesta in ciascun progetto, in genere in questo modo:

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

Quindi eseguire npm install per installare dipendenze in ciascun progetto. Ciò installerà la versione specificata di coffee-script che sarà accessibile localmente per ogni progetto.


sì, sono arrivato così lontano come ho affermato nella mia domanda. come posso chiamare specificamente quello nel mio progetto oltre a ./node_modules/.bin/coffee?
typeoneerror

Se hai eseguito npm install con package.json nella cartella principale del tuo progetto, dovresti avere una cartella ./node_modules/.bin/coffee in questa cartella. L'uso di ./node_modules/coffee-script/bin/coffee eseguirà la versione locale del caffè mentre solo l'esecuzione del caffè eseguirà l'installazione globale. Se hai un'altra versione di caffè installata in un altro percorso all'interno di questa cartella del progetto, puoi accedervi utilizzando ./path/to/this/installation/coffee.
Almypal

Questo non ha funzionato per me. Sto cercando di usare "svgo" e funziona solo se installato a livello globale. Ho provato npm install svgoanche npm installcon package.json. Entrambi i metodi sono stati installati "correttamente", ma il comando "svgo" non è ancora disponibile.
Ryan Wheale,

1
Grunt lo usa in modo intelligente, e IMHO dovrebbe fare anche altri pacchetti. Prima installa il grunt-clipacchetto a livello globale, quindi nella directory del progetto installa qualsiasi versione (modificata) del gruntpacchetto, quindi quando esegui grunt, utilizzerà questa versione locale.
Ack
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.