使用package.json在全局和本地安装依赖项


189

使用npm,我们可以使用-goption 在全局安装模块。我们如何在package.json文件中执行此操作?

假设这些是我在package.json文件中的依赖项

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

当我运行时npm install,我只想node.io全局安装,其余的应该本地安装。有这个选项吗?


11
你不能 但是,您可以"preferGlobal": true在package.json内设置模块。
雷诺斯2011年

是的,我知道<code> preferGlobal </ code>,但这将在全球范围内安装所有依赖项……谢谢!我想没有这样的功能...
Madhusudhan

3
我不这样认为。它将全局安装当前模块。如果单个依赖项将其设置为true,那么它也可以全局安装。真的,您应该在#node.js中询问@isaacs
Raynos 2011年

3
全局安装会产生依赖地狱。假设程序包A需要版本0.3.3,程序包B需要版本0.3.4,并且两者都不能与其他版本一起使用。然后,您需要两台机器来容纳这两个软件包。
2011年

6
这些注释都不能帮助我解决这个问题……如果您的代码向我展示的不仅仅是我……那真是太好了"preferGlobal":true……我真的不知道将其放在package.json中。npmjs.org/doc/json.html NPM文档说,preferredGlobal用于您自己的软件包,对其进行设置将使其以全局方式安装您自己的软件包。不过,它似乎更像是一个指南。
PPPaul,2012年

Answers:


216

新注:您可能不需要或不需要这样做。您可能想要做的就是将那些用于构建/测试等命令依赖性的类型放在devDependenciespackage.json 的部分中。 每当您在package.json中使用某些内容时scripts,devDependencies命令(在node_modules / .bin中)的行为就好像它们在您的路径中一样。

例如:

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

然后在package.json中:

// devDependencies has mocha and babel now

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

然后在命令提示符下运行:

npm run build # finds babel
npm test # finds mocha

npm publish # will run babel first

但是,如果您确实要全局安装,则可以在package.json的脚本部分中添加预安装:

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

所以实际上我的npm install再次执行npm install ..这很奇怪,但似乎可以工作。

注意:如果使用最常见的安装npm程序来安装全局Node软件包,则可能会有问题sudo。一种选择是更改npm配置,因此这不是必需的:

npm config set prefix ~/npm,通过将$ HOME / npm / bin附加export PATH=$HOME/npm/bin:$PATH到$ PATH来将其添加到$ PATH中~/.bashrc


3
我无法使用它npm i -g underscore-cli。它给出了关于wd错误的警告。我想wd表示工作目录。当我在命令行上手动执行此操作时,一切顺利,但是,我希望用户能够使用简单的方法来安装我的代码npm install
PPPaul,2012年

3
PPPaul-最近再次尝试此技巧时,我遇到了同样的问题。也许我的设置现在有所不同,或者仅适用于某些模块。否则我猜npm有什么改变吗?
Jason Livesay

9
除此之外,您还可以预先检查软件包是否已安装:npm list module -g || npm install module -g因为npm将返回正确的退出值。
m90 2013年

3
@CMCDragonkai:这确实应该是一个单独的问题。但是,您将命令放在脚本中,然后将脚本指定为要执行的命令(如"preinstall" : "scripts/preinstall.sh")。
我们都是莫妮卡

1
@CMCDragonkai与他们建立联系&&,例如npm install -g bower && npm install -g grunt-cli
Matsemann

12

由于以下缺点,我建议您遵循公认的答案:

在以下情况下使用npm install --save-dev [package_name]然后执行脚本:

$ npm run lint
$ npm run build
$ npm test

我的原始但不建议的答案如下。


您可以将包添加到devDependencies--save-dev)中,然后从项目内部的任何位置运行二进制文件,而不是使用全局安装:

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

在您的情况下:

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

该工程师提供了npm-exec别名作为快捷方式。 该工程师使用称为的shellscript env.sh。但是我更喜欢$(npm bin)直接使用,以避免任何额外的文件或设置。

尽管它使每个调用都大一点,但它应该可以正常工作,可以防止:

  • 与全局包的潜在依赖冲突(@nalply)
  • 需要 sudo
  • 需要设置一个npm前缀(尽管我仍然建议使用一个)

缺点:

  • $(npm bin) 在Windows上将无法使用。
  • 开发树中较深的工具不会显示在该npm bin文件夹中。(安装npm-runnpm-可以找到它们。)

似乎更好的解决方案是将常见任务(例如构建和缩小)放在您的“脚本”部分package.json,如Jason上面演示的那样。


在中添加一个别名,.bashrc以轻松地将bin/目录添加到您的PATH环境变量:中alias nodebin='export PATH=$(npm bin)/:$PATH'。执行nodebin,然后您只需照常键入命令即可。
gitaarik 2014年

我不知道为什么它对团队不起作用。当然,您需要对其进行设置,如果您不喜欢使用别名,那是您的选择。但是在团队中使用它不会有伤害。
gitaarik 2014年

9

这有点旧,但是我遇到了要求,所以这是我想出的解决方案。

问题:

我们的开发团队维护着许多要迁移到AngularJS / Bootstrap的.NET Web应用程序产品。VS2010不能轻易地用于自定义构建过程,而我的开发人员通常会在产品的多个版本上进行工作。我们的VCS是Subversion(我知道,我知道。我正在尝试迁移到Git,但我讨厌的营销人员要求很高),并且一个VS解决方案将包含几个单独的项目。我需要我的员工采用一种通用的方法来初始化其开发环境,而不必在同一台计算机上多次安装相同的Node包(gulp,bower等)。

TL; DR:

  1. 需要“ npm install”来安装全局Node / Bower开发环境以及.NET产品的所有本地必需的软件包。

  2. 仅在尚未安装全局软件包的情况下才应安装。

  3. 必须自动创建指向全局包的本地链接。

解决方案:

我们已经有一个由所有开发人员和所有产品共享的通用开发框架,因此我创建了一个NodeJS脚本来在需要时安装全局软件包并创建本地链接。脚本位于相对于产品基本文件夹的“ .... \ SharedFiles”中:

/*******************************************************************************
* $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"                      :                      "bower@1.7.2", 
  "event-stream"               :               "event-stream@3.3.2",
  "gulp"                       :                       "gulp@3.9.0",
  "gulp-angular-templatecache" : "gulp-angular-templatecache@1.8.0",
  "gulp-clean"                 :                 "gulp-clean@0.3.1", 
  "gulp-concat"                :                "gulp-concat@2.6.0",
  "gulp-debug"                 :                 "gulp-debug@2.1.2",
  "gulp-filter"                :                "gulp-filter@3.0.1",
  "gulp-grep-contents"         :         "gulp-grep-contents@0.0.1",
  "gulp-if"                    :                    "gulp-if@2.0.0", 
  "gulp-inject"                :                "gulp-inject@3.0.0", 
  "gulp-minify-css"            :            "gulp-minify-css@1.2.3",
  "gulp-minify-html"           :           "gulp-minify-html@1.0.5",
  "gulp-minify-inline"         :         "gulp-minify-inline@0.1.1",
  "gulp-ng-annotate"           :           "gulp-ng-annotate@1.1.0",
  "gulp-processhtml"           :           "gulp-processhtml@1.1.0",
  "gulp-rev"                   :                   "gulp-rev@6.0.1",
  "gulp-rev-replace"           :           "gulp-rev-replace@0.4.3",
  "gulp-uglify"                :                "gulp-uglify@1.5.1",
  "gulp-useref"                :                "gulp-useref@3.0.4",
  "gulp-util"                  :                  "gulp-util@3.0.7",
  "lazypipe"                   :                   "lazypipe@1.0.1",
  "q"                          :                          "q@1.4.1",
  "through2"                   :                   "through2@2.0.0",

  /*---------------------------------------------------------------*/
  /* 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();
  };

现在,如果要为开发人员更新全局工具,则更新“ packages”对象并签入新脚本。我的开发人员将其签出并使用“ node npm-setup.js”运行它,或者通过正在开发的任何产品的“ npm install”运行它来更新全局环境。整个过程需要5分钟。

此外,要为新开发人员配置环境,他们必须首先仅安装Windows版NodeJS和GIT,重新启动计算机,签出“共享文件”文件夹和任何开发中的产品,然后开始工作。

.NET产品的“ package.json”在安装之前会调用以下脚本:

{ 
"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": {}
}

笔记

  • 请注意,即使在Windows环境中,脚本参考也需要正斜杠。

  • 对于所有本地链接的软件包,“ npm ls”将给出“ npm ERR!Extraneous:”消息,因为它们未在“ package.json”“依赖项”中列出。

编辑1/29/16

npm-setup.js上面更新的脚本已被修改如下:

  • var packages现在,“版本”中的软件包是npm install在命令行上传递给“软件包”的值。进行了更改,以允许从注册的存储库以外的其他位置安装软件包。

  • 如果该软件包已经安装但不是要求的软件包,则将删除现有软件包并安装正确的软件包。

  • 由于未知原因,npm在执行安装或链接时会定期抛出EBUSY错误(-4082)。将捕获此错误,并重新执行命令。该错误很少再次发生,并且似乎总是可以清除。


这是一个救星@ sthames42!我花了好几个小时试图弄清楚该怎么做。清晰,全面,总体来说很棒。#points问题:(a)为什么Bower已经在软件包列表中,所以为什么在安装后进行安装?(b)如何不在本地链接全局软件包?只是在命令中不包括“链接”?
MaxRocket

@MaxRocket:很高兴我能帮上忙。我已经更新了答案,以包括最新的答案,效果更好。答案:(a)完成“ npm install”后,将运行“ bower install”命令,以安装未在此处显示的bower.json文件中列出的Bower组件。我希望我的员工能够键入“ npm install”并完全设置其环境,而无需键入其他命令。(b)是的。
sthames42 '16

这个脚本的当前版本,现在保持在这里
sthames42 '16

6

您可以使用单独的文件,例如npm_globals.txt,而不是package.json。该文件将在每个新行中包含每个模块,如下所示:

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

然后在命令行中运行

< npm_globals.txt xargs npm install -g

检查它们是否正确安装,

npm list -g --depth=0

至于是否应该执行此操作,我认为这完全取决于用例。对于大多数项目,这不是必需的。并且最好将您的项目package.json将这些工具和依赖项封装在一起。

但是如今,我发现,create-react-app当我跳到新机器上时,我总是在全球范围内安装CLI和其他CLI。当版本控制无关紧要时,有一种简单的方法可以很好地安装全局工具及其依赖项。

而如今,我使用npx一包NPM亚军,而不是全球安装软件包。


3

package.json中的所有模块都安装到./node_modules/

我找不到明确说明的内容,但这是NPM的package.json参考。


1

构建自己的脚本以安装全局依赖项。不需要很多。package.json非常可扩展。

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

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

使用上面的代码,您甚至可以在下面使其内联!

在下面查看预安装:

{
  "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": [
    "cordova@8.1.2",
    "ionic",
    "potato"
  ],
  "author": "author",
  "license": "MIT",
  "devDependencies": {
    "chai": "^4.2.0",
    "mocha": "^5.2.0"
  },
  "bin": {
    "app": "app.js"
  }
}

节点的作者可能不承认package.json是一个项目文件。但它是。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.