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

Posted

技术标签:

【中文标题】使用 package.json 在全局和本地安装依赖项【英文标题】:Install dependencies globally and locally using package.json 【发布时间】:2011-09-22 18:13:28 【问题描述】:

使用 npm 我们可以使用-g 选项全局安装模块。我们如何在 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,其余的应该在本地安装。有这个选项吗?

【问题讨论】:

你不能。但是,您可以在 package.json 中为模块设置 "preferGlobal": true 是的,我知道 preferGlobal,但这会在全局范围内安装所有依赖项......无论如何,谢谢!我想没有这样的功能...... 我不这么认为。它全局安装当前模块。如果单个依赖项将其设置为 true,则它也可以全局安装。你真的应该在#node.js 中询问@isaacs 全局安装会产生依赖地狱。假设包 A 需要版本 0.3.3 和包 B 版本 0.3.4,两者都不能与其他版本一起使用。那么你需要两台机器来容纳这两个包。 这些 cmets 都不能帮助我解决这个问题...如果您的代码显示给我的不仅仅是 "preferGlobal":true... 我真的不知道把它放在哪里.json。 npmjs.org/doc/json.html NPM 文档说 preferGlobal 是针对你自己的包的,设置它会使其安装你自己的包作为全局包。不过,这似乎更像是一个指南。 【参考方案1】:

这可能是生产问题的大门。 如果项目依赖项安装在项目文件夹之外,如果其他人删除或替换您的包或更改文件夹权限,代码可能会中断。

将所有东西都放在一个文件夹中更耐用,并且使系统可预测并且维护任务更容易。

【讨论】:

【参考方案2】:

新提示:您可能不想或不需要这样做。您可能想要做的只是将用于构建/测试等的这些类型的命令依赖项放在 package.json 的 devDependencies 部分中。 任何时候你在 package.json 中使用来自 scripts 的东西,你的 devDependencies 命令(在 n​​ode_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

新的新说明:现在我们有 npx,它允许您运行 devDependencies 命令,而无需将它们添加到您的 scripts 部分(如果需要)。 例如:

npx webpack

但是如果你真的想要全局安装,你可以在 package.json 的脚本部分添加预安装:

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

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

注意:如果您使用最常见的npm 设置,其中需要安装全局节点包sudo,您可能会遇到问题。一种选择是更改您的 npm 配置,这样就没有必要了:

npm config set prefix ~/npm,通过将 export PATH=$HOME/npm/bin:$PATH 附加到您的 ~/.bashrc,将 $HOME/npm/bin 添加到 $PATH。

另一个可能更好的选择是只使用nvm 来管理 Node,您不会遇到这个问题。

【讨论】:

我无法让它与 npm i -g underscore-cli 一起使用。它给出了关于 wd 错误的警告。 wd 意思是工作目录,我猜。当我在命令行上手动执行此操作时,一切顺利,但是我希望用户能够使用简单的npm install 来处理安装我的代码 PPPaul -- 我最近再次尝试这个技巧时遇到了同样的问题。也许我的设置现在不同了,或者它只适用于某些模块。否则我猜 npm 会发生什么变化? 除此之外,您还可以预先检查软件包是否已安装:npm list module -g || npm install module -g 因为 npm 将返回正确的退出值。 @CMCDragonkai:这应该是一个单独的问题。但是,您将命令放在脚本中,并将脚本指定为要执行的命令(如"preinstall" : "scripts/preinstall.sh")。 @CMCDragonkai 将它们与&& 连接起来,例如npm install -g bower && npm install -g grunt-cli【参考方案3】:

构建您自己的脚本来安装全局依赖项。不需要太多。 package.json 的扩展性很强。

const  execSync  = require('child_process');
const fs = require('fs');

const package = JSON.parse(fs.readFileSync('package.json'));

let keys = Object.keys(package.dependencies);
let values = Object.values(package.dependencies);


for (let index = 0; index < keys.length; index++) 
    const key = keys[index];
    let value = values[index].replace("~", "").replace("^", "");

    console.log(`Installing: $key@$value globally`,);
    execSync('npm i -g ' + `$key@$value`);

使用上面的,你甚至可以使它内联,下面!

查看下面的预安装:


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

node 的作者可能不承认 package.json 是一个项目文件。但确实如此。

【讨论】:

我喜欢您的解决方案,并对脚本进行了一些更改,以便它直接与实际依赖项一起工作。我希望这对你来说很好。【参考方案4】:

您可以使用单独的文件,例如 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。当版本控制无关紧要时,有一种简单的方法来安装全局工具及其依赖项真是太好了。

现在,我使用npx、an npm package runner,而不是全局安装包。

【讨论】:

【参考方案5】:

由于下面描述的缺点,我建议遵循公认的答案:

使用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

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

虽然它使每次调用都变大了一点,但它应该正常工作,防止:

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

缺点:

$(npm bin) 无法在 Windows 上运行。 开发树中更深层次的工具不会出现在npm bin 文件夹中。 (安装 npm-run 或 npm-which 即可找到它们。)

似乎更好的解决方案是将常见任务(例如构建和缩小)放在您的 package.json 的 "scripts" section 中,正如 Jason 上面演示的那样。

【讨论】:

在您的.bashrc 中添加一个别名,以便轻松地将bin/ 目录添加到您的PATH 环境变量:alias nodebin='export PATH=$(npm bin)/:$PATH'。执行nodebin,然后你就可以像往常一样输入你的命令了。 我不知道为什么它对团队不起作用。当然,您需要对其进行设置,如果您不喜欢使用别名,那是您的选择。但是在团队中使用它不会有什么坏处。【参考方案6】:

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

问题:

我们的开发团队维护着许多我们正在迁移到 AngularJS/Bootstrap 的 .NET Web 应用程序产品。 VS2010 不适合自定义构建过程,我的开发人员经常处理我们产品的多个版本。我们的 VCS 是 Subversion(我知道,我知道。我正在尝试迁移到 Git,但我讨厌的营销人员要求很高),一个 VS 解决方案将包括几个单独的项目。我需要我的员工有一个通用的方法来初始化他们的开发环境,而不必在同一台机器上多次安装相同的 Node 包(gulp、bower 等)。

TL;DR:

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

    只有在尚未安装的情况下才应安装全局包。

    必须自动创建到全局包的本地链接。

解决方案:

我们已经拥有一个由所有开发人员和所有产品共享的通用开发框架,因此我创建了一个 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();
  ;

现在,如果我想为我们的开发人员更新一个全局工具,我会更新“包”对象并签入新脚本。我的开发人员检查它并使用“node npm-setup.js”或通过任何正在开发的产品中的“npm install”运行它以更新全局环境。整个过程需要 5 分钟。

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

.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”中列出 “依赖关系”。

编辑 2016 年 1 月 29 日

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

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

如果已安装软件包但不是请求的软件包,则会删除现有软件包并安装正确的软件包。

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

【讨论】:

这是救命稻草@sthames42!我已经拖了几个小时试图弄清楚如何做到这一点。清晰,全面,总体上很棒。 #points 问题:(a) 为什么当 Bower 已经在包列表中时,它在 postinstall 中? (b) 如何不在本地链接全局包?只是不要在命令中包含“链接”吗? @MaxRocket:很高兴我能帮上忙。我已经更新了答案以包含我最新的效果更好的答案。回答:(a) 'npm install' 完成后运行'bower install' 命令以安装 bower.json 文件中列出的 Bower 组件,此处未显示。我希望我的员工能够输入“npm install”并完全设置他们的环境,而无需输入另一个命令。 (b) 是的。 这个脚本的当前版本现在维护here。【参考方案7】:

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

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

【讨论】:

以上是关于使用 package.json 在全局和本地安装依赖项的主要内容,如果未能解决你的问题,请参考以下文章

package.json 用于全局模块安装

利用npm 安装删除模块

利用npm 安装删除模块

全局安装具有对等依赖关系的 npm 包(package.json 中未提及)

通过 package.json 安装“全局”npm 依赖项 [重复]

webpack知识