Zainstaluj zależności globalnie i lokalnie za pomocą package.json

189

Za pomocą npm możemy instalować moduły globalnie za pomocą -g opcji. Jak możemy to zrobić w pliku package.json?

Załóżmy, że są to moje zależności w pliku package.json

"dependencies": {
    "mongoose": "1.4.0",
    "node.io" : "0.3.3",
    "jquery"  : "1.5.1",
    "jsdom"   : "0.2.0",
    "cron"    : "0.1.2"
  }

Kiedy uruchamiam npm install, chcę node.iobyć instalowany tylko globalnie, a pozostałe powinny być zainstalowane lokalnie. Czy jest na to opcja?

Madhusudhan
źródło
11
Nie możesz Możesz jednak ustawić "preferGlobal": truew module package.json dla modułu.
Raynos
tak, wiem o <code> preferGlobal </code>, ale to zainstalowałoby wszystkie zależności na całym świecie ... w każdym razie Dzięki! Myślę, że nie ma takiej funkcji ...
Madhusudhan
3
Nie sądzę, że tak. Instaluje bieżący moduł globalnie. Jeśli dla pojedynczej zależności jest ustawiona wartość true, można ją również zainstalować globalnie. Naprawdę powinieneś po prostu zapytać @isaacs w # node.js
Raynos
3
Globalne instalacje mogą wywołać piekło zależności. Powiedz, że pakiet A potrzebuje wersji 0.3.3 i pakietu B w wersji 0.3.4 i oba nie działają z drugą wersją. Wtedy potrzebujesz dwóch maszyn, aby pomieścić dwa pakiety.
dokładnie
6
żaden z tych komentarzy nie pomógł mi z tym problemem ... byłoby miło, gdybyś kod pokazał mi więcej niż tylko "preferGlobal":true... tak naprawdę nie wiem, gdzie umieścić to w package.json. npmjs.org/doc/json.html Dokumentacja NPM mówi, że preferGlobal jest dla twojego własnego pakietu, a to ustawienie sprawi, że zainstaluje twój własny pakiet jako globalny. wydaje się jednak bardziej przewodnikiem.
PPPaul

Odpowiedzi:

216

Nowa uwaga: Prawdopodobnie nie chcesz lub nie musisz tego robić. To, co prawdopodobnie chcesz zrobić, to po prostu umieścić te typy zależności komend dla kompilacji / testu itp. W devDependenciessekcji pliku package.json. Za każdym razem, gdy użyjesz czegoś z scriptspliku package.json, twoje polecenia devDependencies (w node_modules / .bin) działają tak, jakby były na twojej ścieżce.

Na przykład:

npm i --save-dev mocha # Install test runner locally
npm i --save-dev babel # Install current babel locally

Następnie w package.json:

// devDependencies has mocha and babel now

"scripts": {
  "test": "mocha",
  "build": "babel -d lib src",
  "prepublish": "babel -d lib src"
}

Następnie w wierszu polecenia możesz uruchomić:

npm run build # finds babel
npm test # finds mocha

npm publish # will run babel first

Ale jeśli ty naprawdę chcesz zainstalować globalnie, możesz dodać preinstalację w sekcji skryptów pliku package.json:

"scripts": {
  "preinstall": "npm i -g themodule"
}

Więc właściwie moja instalacja npm wykonuje ponownie instalację npm .. co jest dziwne, ale wydaje się działać.

Uwaga: możesz mieć problemy, jeśli korzystasz z najczęściej stosowanej konfiguracji, w npmktórej wymagane są instalacje globalnego pakietu węzłówsudo . Jedną z opcji jest zmiana npmkonfiguracji, aby nie było to konieczne:

npm config set prefix ~/npm, dodaj $ HOME / npm / bin do $ PATH, dołączając export PATH=$HOME/npm/bin:$PATHdo swojego ~/.bashrc.

Jason Livesay
źródło
3
Nie byłem w stanie zmusić tego do pracy npm i -g underscore-cli. ostrzega przed błędem wd. wd oznacza chyba katalog roboczy. kiedy robię to ręcznie w wierszu poleceń, wszystko idzie dobrze, jednak wolałbym, gdyby użytkownik był w stanie poradzić sobie z instalowaniem mojego kodu za pomocą prostegonpm install
PPPaul
3
PPPaul - Miałem ten sam problem, kiedy ostatnio próbowałem tej sztuczki. Być może moja konfiguracja jest teraz inna lub działa tylko z niektórymi modułami. W przeciwnym razie myślę, że coś się zmieniło z npm?
Jason Livesay
9
Oprócz tego możesz wstępnie sprawdzić, czy pakiet jest już zainstalowany: npm list module -g || npm install module -gjak npm zwróci prawidłowe wartości wyjścia.
m90
3
@CMCDragonkai: To naprawdę powinno być osobne pytanie. Ale umieszczasz swoje polecenia w skrypcie i określasz skrypt jako polecenie do wykonania (jak "preinstall" : "scripts/preinstall.sh").
We Are All Monica,
1
@CMCDragonkai konkatuje ich &&, na przykładnpm install -g bower && npm install -g grunt-cli
Matsemann,
12

Ze względu na opisane poniżej wady polecam zastosować się do przyjętej odpowiedzi:

Użyj, npm install --save-dev [package_name]a następnie uruchom skrypty za pomocą:

$ npm run lint
$ npm run build
$ npm test

Oto moja oryginalna, ale niezalecana odpowiedź.


Zamiast używać instalacji globalnej, możesz dodać pakiet do swojego devDependencies( --save-dev), a następnie uruchomić plik binarny z dowolnego miejsca w projekcie:

"$(npm bin)/<executable_name>" <arguments>...

W Twoim przypadku:

"$(npm bin)"/node.io --help

Ten inżynier podał npm-execalias jako skrót. Ten inżynier używa skryptu powłoki o nazwie env.sh. Ale wolę używać $(npm bin)bezpośrednio, aby uniknąć dodatkowego pliku lub konfiguracji.

Mimo że każde połączenie jest nieco większe, powinno po prostu działać , zapobiegając:

  • potencjalne konflikty zależności z pakietami globalnymi (@nalply)
  • potrzeba sudo
  • potrzeba ustawienia prefiksu npm (chociaż i tak polecam go użyć)

Niedogodności:

  • $(npm bin) nie będzie działać w systemie Windows.
  • Narzędzia głębiej w drzewie programistów nie pojawią się w npm binfolderze. (Zainstaluj npm-run lub npm-które, aby je znaleźć).

Wydaje się lepszym rozwiązaniem jest umieszczenie typowych zadań (takich jak budowa i minifying) w sekcji „scripts” waszego package.json, jak pokazuje Jason powyżej.

joeytwiddle
źródło
Dodaj alias w twojej .bashrcłatwo dodać bin/katalog do PATHzmiennej środowiskowej: alias nodebin='export PATH=$(npm bin)/:$PATH'. Wykonaj, nodebina następnie możesz po prostu wpisać swoje polecenia jak zwykle.
gitaarik
Nie wiem, dlaczego to nie zadziała dla zespołów. Oczywiście musisz go skonfigurować, a jeśli nie lubisz używać aliasu, to twój wybór. Ale używanie go w zespole nie może zaszkodzić.
gitaarik
9

Jest to trochę stare, ale natrafiłem na wymóg, więc oto rozwiązanie, które wymyśliłem.

Problem:

Nasz zespół programistów utrzymuje wiele aplikacji internetowych .NET, które migrujemy do AngularJS / Bootstrap. VS2010 nie nadaje się łatwo do niestandardowych procesów kompilacji, a moi programiści rutynowo pracują nad wieloma wydaniami naszych produktów. Naszym VCS jest Subversion (wiem, wiem. Próbuję przejść do Git, ale mój nieznośny personel marketingowy jest tak wymagający), a jedno rozwiązanie VS będzie obejmować kilka osobnych projektów. Potrzebowałem moich pracowników, aby mieli wspólną metodę inicjowania środowiska programistycznego bez konieczności instalowania tych samych pakietów Node (przełyk, altanka itp.) Kilka razy na tej samej maszynie.

TL; DR:

  1. Potrzebujesz „npm install”, aby zainstalować globalne środowisko programistyczne Node / Bower, a także wszystkie lokalnie wymagane pakiety dla produktu .NET.

  2. Pakiety globalne powinny być instalowane tylko wtedy, gdy nie zostały jeszcze zainstalowane.

  3. Lokalne linki do globalnych pakietów muszą być tworzone automatycznie.

Rozwiązanie:

Mamy już wspólną platformę programistyczną wspólną dla wszystkich programistów i wszystkich produktów, dlatego stworzyłem skrypt NodeJS, aby w razie potrzeby zainstalować pakiety globalne i utworzyć lokalne łącza. Skrypt znajduje się w „.... \ SharedFiles” względem folderu podstawowego produktu:

/*******************************************************************************
* $Id: npm-setup.js 12785 2016-01-29 16:34:49Z sthames $
* ==============================================================================
* Parameters: 'links' - Create links in local environment, optional.
* 
* <p>NodeJS script to install common development environment packages in global
* environment. <c>packages</c> object contains list of packages to install.</p>
* 
* <p>Including 'links' creates links in local environment to global packages.</p>
* 
* <p><b>npm ls -g --json</b> command is run to provide the current list of 
* global packages for comparison to required packages. Packages are installed 
* only if not installed. If the package is installed but is not the required 
* package version, the existing package is removed and the required package is 
* installed.</p>.
*
* <p>When provided as a "preinstall" script in a "package.json" file, the "npm
* install" command calls this to verify global dependencies are installed.</p>
*******************************************************************************/
var exec = require('child_process').exec;
var fs   = require('fs');
var path = require('path');

/*---------------------------------------------------------------*/
/* List of packages to install and 'from' value to pass to 'npm  */
/* install'. Value must match the 'from' field in 'npm ls -json' */
/* so this script will recognize a package is already installed. */
/*---------------------------------------------------------------*/
var packages = 
  {
  "bower"                      :                      "[email protected]", 
  "event-stream"               :               "[email protected]",
  "gulp"                       :                       "[email protected]",
  "gulp-angular-templatecache" : "[email protected]",
  "gulp-clean"                 :                 "[email protected]", 
  "gulp-concat"                :                "[email protected]",
  "gulp-debug"                 :                 "[email protected]",
  "gulp-filter"                :                "[email protected]",
  "gulp-grep-contents"         :         "[email protected]",
  "gulp-if"                    :                    "[email protected]", 
  "gulp-inject"                :                "[email protected]", 
  "gulp-minify-css"            :            "[email protected]",
  "gulp-minify-html"           :           "[email protected]",
  "gulp-minify-inline"         :         "[email protected]",
  "gulp-ng-annotate"           :           "[email protected]",
  "gulp-processhtml"           :           "[email protected]",
  "gulp-rev"                   :                   "[email protected]",
  "gulp-rev-replace"           :           "[email protected]",
  "gulp-uglify"                :                "[email protected]",
  "gulp-useref"                :                "[email protected]",
  "gulp-util"                  :                  "[email protected]",
  "lazypipe"                   :                   "[email protected]",
  "q"                          :                          "[email protected]",
  "through2"                   :                   "[email protected]",

  /*---------------------------------------------------------------*/
  /* fork of 0.2.14 allows passing parameters to main-bower-files. */
  /*---------------------------------------------------------------*/
  "bower-main"                 : "git+https://github.com/Pyo25/bower-main.git" 
  }

/*******************************************************************************
* run */
/**
* Executes <c>cmd</c> in the shell and calls <c>cb</c> on success. Error aborts.
* 
* Note: Error code -4082 is EBUSY error which is sometimes thrown by npm for 
* reasons unknown. Possibly this is due to antivirus program scanning the file 
* but it sometimes happens in cases where an antivirus program does not explain 
* it. The error generally will not happen a second time so this method will call 
* itself to try the command again if the EBUSY error occurs.
* 
* @param  cmd  Command to execute.
* @param  cb   Method to call on success. Text returned from stdout is input.
*******************************************************************************/
var run = function(cmd, cb)
  {
  /*---------------------------------------------*/
  /* Increase the maxBuffer to 10MB for commands */
  /* with a lot of output. This is not necessary */
  /* with spawn but it has other issues.         */
  /*---------------------------------------------*/
  exec(cmd, { maxBuffer: 1000*1024 }, function(err, stdout)
    {
    if      (!err)                   cb(stdout);
    else if (err.code | 0 == -4082) run(cmd, cb);
    else throw err;
    });
  };

/*******************************************************************************
* runCommand */
/**
* Logs the command and calls <c>run</c>.
*******************************************************************************/
var runCommand = function(cmd, cb)
  {
  console.log(cmd);
  run(cmd, cb);
  }

/*******************************************************************************
* Main line
*******************************************************************************/
var doLinks  = (process.argv[2] || "").toLowerCase() == 'links';
var names    = Object.keys(packages);
var name;
var installed;
var links;

/*------------------------------------------*/
/* Get the list of installed packages for   */
/* version comparison and install packages. */
/*------------------------------------------*/
console.log('Configuring global Node environment...')
run('npm ls -g --json', function(stdout)
  {
  installed = JSON.parse(stdout).dependencies || {};
  doWhile();
  });

/*--------------------------------------------*/
/* Start of asynchronous package installation */
/* loop. Do until all packages installed.     */
/*--------------------------------------------*/
var doWhile = function()
  {
  if (name = names.shift())
    doWhile0();
  }

var doWhile0 = function()
  {
  /*----------------------------------------------*/
  /* Installed package specification comes from   */
  /* 'from' field of installed packages. Required */
  /* specification comes from the packages list.  */
  /*----------------------------------------------*/
  var current  = (installed[name] || {}).from;
  var required =   packages[name];

  /*---------------------------------------*/
  /* Install the package if not installed. */
  /*---------------------------------------*/
  if (!current)
    runCommand('npm install -g '+required, doWhile1);

  /*------------------------------------*/
  /* If the installed version does not  */
  /* match, uninstall and then install. */
  /*------------------------------------*/
  else if (current != required)
    {
    delete installed[name];
    runCommand('npm remove -g '+name, function() 
      {
      runCommand('npm remove '+name, doWhile0);
      });
    }

  /*------------------------------------*/
  /* Skip package if already installed. */
  /*------------------------------------*/
  else
    doWhile1();
  };

var doWhile1 = function()
  {
  /*-------------------------------------------------------*/
  /* Create link to global package from local environment. */
  /*-------------------------------------------------------*/
  if (doLinks && !fs.existsSync(path.join('node_modules', name)))
    runCommand('npm link '+name, doWhile);
  else
    doWhile();
  };

Teraz, jeśli chcę zaktualizować globalne narzędzie dla naszych programistów, aktualizuję obiekt „paczki” i sprawdzam nowy skrypt. Moi programiści to sprawdzają i albo uruchamiają go za pomocą „node npm-setup.js” lub „npm install” z dowolnego z opracowywanych produktów, aby zaktualizować środowisko globalne. Całość zajmuje 5 minut.

Ponadto, aby skonfigurować środowisko dla nowego programisty, muszą najpierw zainstalować tylko NodeJS i GIT dla Windows, ponownie uruchomić komputer, przejrzeć folder „Shared Files” oraz wszelkie opracowywane produkty i rozpocząć pracę.

„Package.json” dla produktu .NET wywołuje ten skrypt przed instalacją:

{ 
"name"                    : "Books",
"description"             : "Node (npm) configuration for Books Database Web Application Tools",
"version"                 : "2.1.1",
"private"                 : true,
"scripts":
  {
  "preinstall"            : "node ../../SharedFiles/npm-setup.js links",
  "postinstall"           : "bower install"
  },
"dependencies": {}
}

Notatki

  • Uwaga: odwołanie do skryptu wymaga ukośników nawet w środowisku Windows.

  • „npm ls” wyświetli „npm ERR! extraneous:” komunikaty dla wszystkich pakietów lokalnie połączonych, ponieważ nie są wymienione w zależnościach „package.json”.

Edytuj 1/29/16

Zaktualizowany npm-setup.jsskrypt powyżej został zmodyfikowany w następujący sposób:

  • Pakiet „wersja” w var packagesjest teraz wartością „pakiet” przekazywaną npm installw wierszu polecenia. Zostało to zmienione, aby umożliwić instalowanie pakietów z miejsca innego niż zarejestrowane repozytorium.

  • Jeśli pakiet jest już zainstalowany, ale nie jest tym, którego zażądano, istniejący pakiet zostanie usunięty, a poprawny zainstalowany.

  • Z nieznanych przyczyn npm podczas wykonywania instalacji lub łącza okresowo generuje błąd EBUSY (-4082). Ten błąd jest zatrzymywany, a polecenie wykonywane ponownie. Błąd rzadko zdarza się po raz drugi i wydaje się, że zawsze się usuwa.

sthames42
źródło
To jest ratownik @ sthames42! Trollowałem od wielu godzin, próbując dowiedzieć się, jak to zrobić. Jasne, kompleksowe, ogólnie niesamowite. #points Pytania: (a) Dlaczego Bower jest w postinstalacji, skoro jest już na liście pakietów? (b) Jak NIE lokalnie łączyć globalnych pakietów? Po prostu nie dołączaj „linków” do polecenia?
MaxRocket
@MaxRocket: Cieszę się, że mogłem pomóc. Zaktualizowałem odpowiedź, aby uwzględnić mój najnowszy, który działa znacznie lepiej. Odpowiedzi: (a) polecenie „bower install” jest uruchamiane po wykonaniu „npm install”, aby zainstalować komponenty Bower wymienione w pliku bower.json, który nie został tutaj pokazany. Chciałem, aby moi ludzie mogli pisać „npm install” i aby ich środowisko było w pełni skonfigurowane bez konieczności wpisywania innego polecenia. (b) Tak.
sthames42
Obecna wersja tego skryptu jest teraz utrzymywana tutaj .
sthames42
6

Możesz użyć osobnego pliku, na przykład npm_globals.txt, zamiast package.json. Ten plik zawierałby każdy moduł w nowej linii, takiej jak ta,

mongoose@1.4.0
node.io@0.3.3
jquery@1.5.1
jsdom@0.2.0
cron@0.1.2

Następnie w linii poleceń uruchom

< npm_globals.txt xargs npm install -g

Sprawdź, czy zostały poprawnie zainstalowane,

npm list -g --depth=0

Jeśli chodzi o to, czy powinieneś to zrobić, czy nie, myślę, że wszystko zależy od przypadku użycia. W przypadku większości projektów nie jest to konieczne; i bardzo pożądane jest, aby projekt package.jsonzawierał w całość te narzędzia i zależności.

Ale obecnie okazuje się, że zawsze instaluję create-react-appglobalnie inne CLI, kiedy wskakuję na nową maszynę. Fajnie jest mieć łatwy sposób na zainstalowanie globalnego narzędzia i jego zależności, gdy wersja nie ma większego znaczenia.

I obecnie używam npx, NPM pakiet biegacz , zamiast instalowania pakietów globalnie.

Atav32
źródło
3

Wszystkie moduły z pakietu.json są zainstalowane w ./node_modules/

Nie mogłem znaleźć tego wyraźnie zaznaczonego, ale jest to odwołanie do package.json dla NPM .

nibblebot
źródło
1

Zbuduj własny skrypt, aby zainstalować globalne zależności. To nie zajmuje dużo. Package.json jest dość rozbudowywalny.

const {execSync} = require('child_process');

JSON.parse(fs.readFileSync('package.json'))
     .globalDependencies.foreach(
         globaldep => execSync('npm i -g ' + globaldep)
     );

Korzystając z powyższego, możesz nawet ustawić go w linii, poniżej!

Spójrz na preinstalację poniżej:

{
  "name": "Project Name",
  "version": "0.1.0",
  "description": "Project Description",
  "main": "app.js",
  "scripts": {
    "preinstall": "node -e \"const {execSync} = require('child_process'); JSON.parse(fs.readFileSync('package.json')).globalDependencies.foreach(globaldep => execSync('npm i -g ' + globaldep));\"",
    "build": "your transpile/compile script",
    "start": "node app.js",
    "test": "./node_modules/.bin/mocha --reporter spec",
    "patch-release": "npm version patch && npm publish && git add . && git commit -m \"auto-commit\" && git push --follow-tags"
  },
  "dependencies": [
  },
  "globalDependencies": [
    "[email protected]",
    "ionic",
    "potato"
  ],
  "author": "author",
  "license": "MIT",
  "devDependencies": {
    "chai": "^4.2.0",
    "mocha": "^5.2.0"
  },
  "bin": {
    "app": "app.js"
  }
}

Autorzy węzła nie mogą przyznać pliku package.json jest plikiem projektu. Ale to jest.

TamusJRoyce
źródło