"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/npm/lib/install.js" (7 Feb 2017, 25562 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 'use strict'
    2 // npm install <pkg> <pkg> <pkg>
    3 //
    4 // See doc/cli/npm-install.md for more description
    5 //
    6 // Managing contexts...
    7 // there's a lot of state associated with an "install" operation, including
    8 // packages that are already installed, parent packages, current shrinkwrap, and
    9 // so on. We maintain this state in a "context" object that gets passed around.
   10 // every time we dive into a deeper node_modules folder, the "family" list that
   11 // gets passed along uses the previous "family" list as its __proto__.  Any
   12 // "resolved precise dependency" things that aren't already on this object get
   13 // added, and then that's passed to the next generation of installation.
   14 
   15 module.exports = install
   16 module.exports.Installer = Installer
   17 
   18 var usage = require('./utils/usage')
   19 
   20 install.usage = usage(
   21   'install',
   22   '\nnpm install (with no args, in package dir)' +
   23   '\nnpm install [<@scope>/]<pkg>' +
   24   '\nnpm install [<@scope>/]<pkg>@<tag>' +
   25   '\nnpm install [<@scope>/]<pkg>@<version>' +
   26   '\nnpm install [<@scope>/]<pkg>@<version range>' +
   27   '\nnpm install <folder>' +
   28   '\nnpm install <tarball file>' +
   29   '\nnpm install <tarball url>' +
   30   '\nnpm install <git:// url>' +
   31   '\nnpm install <github username>/<github project>',
   32   '[--save|--save-dev|--save-optional] [--save-exact]'
   33 )
   34 
   35 install.completion = function (opts, cb) {
   36   validate('OF', arguments)
   37   // install can complete to a folder with a package.json, or any package.
   38   // if it has a slash, then it's gotta be a folder
   39   // if it starts with https?://, then just give up, because it's a url
   40   if (/^https?:\/\//.test(opts.partialWord)) {
   41     // do not complete to URLs
   42     return cb(null, [])
   43   }
   44 
   45   if (/\//.test(opts.partialWord)) {
   46     // Complete fully to folder if there is exactly one match and it
   47     // is a folder containing a package.json file.  If that is not the
   48     // case we return 0 matches, which will trigger the default bash
   49     // complete.
   50     var lastSlashIdx = opts.partialWord.lastIndexOf('/')
   51     var partialName = opts.partialWord.slice(lastSlashIdx + 1)
   52     var partialPath = opts.partialWord.slice(0, lastSlashIdx)
   53     if (partialPath === '') partialPath = '/'
   54 
   55     var annotatePackageDirMatch = function (sibling, cb) {
   56       var fullPath = path.join(partialPath, sibling)
   57       if (sibling.slice(0, partialName.length) !== partialName) {
   58         return cb(null, null) // not name match
   59       }
   60       fs.readdir(fullPath, function (err, contents) {
   61         if (err) return cb(null, { isPackage: false })
   62 
   63         cb(
   64           null,
   65           {
   66             fullPath: fullPath,
   67             isPackage: contents.indexOf('package.json') !== -1
   68           }
   69         )
   70       })
   71     }
   72 
   73     return fs.readdir(partialPath, function (err, siblings) {
   74       if (err) return cb(null, []) // invalid dir: no matching
   75 
   76       asyncMap(siblings, annotatePackageDirMatch, function (err, matches) {
   77         if (err) return cb(err)
   78 
   79         var cleaned = matches.filter(function (x) { return x !== null })
   80         if (cleaned.length !== 1) return cb(null, [])
   81         if (!cleaned[0].isPackage) return cb(null, [])
   82 
   83         // Success - only one match and it is a package dir
   84         return cb(null, [cleaned[0].fullPath])
   85       })
   86     })
   87   }
   88 
   89   // FIXME: there used to be registry completion here, but it stopped making
   90   // sense somewhere around 50,000 packages on the registry
   91   cb()
   92 }
   93 
   94 // system packages
   95 var fs = require('fs')
   96 var path = require('path')
   97 
   98 // dependencies
   99 var log = require('npmlog')
  100 var readPackageTree = require('read-package-tree')
  101 var chain = require('slide').chain
  102 var asyncMap = require('slide').asyncMap
  103 var archy = require('archy')
  104 var mkdirp = require('mkdirp')
  105 var rimraf = require('rimraf')
  106 var iferr = require('iferr')
  107 var validate = require('aproba')
  108 
  109 // npm internal utils
  110 var npm = require('./npm.js')
  111 var locker = require('./utils/locker.js')
  112 var lock = locker.lock
  113 var unlock = locker.unlock
  114 var ls = require('./ls.js')
  115 var parseJSON = require('./utils/parse-json.js')
  116 var output = require('./utils/output.js')
  117 
  118 // install specific libraries
  119 var copyTree = require('./install/copy-tree.js')
  120 var readShrinkwrap = require('./install/read-shrinkwrap.js')
  121 var recalculateMetadata = require('./install/deps.js').recalculateMetadata
  122 var loadDeps = require('./install/deps.js').loadDeps
  123 var loadDevDeps = require('./install/deps.js').loadDevDeps
  124 var getAllMetadata = require('./install/deps.js').getAllMetadata
  125 var loadRequestedDeps = require('./install/deps.js').loadRequestedDeps
  126 var loadExtraneous = require('./install/deps.js').loadExtraneous
  127 var diffTrees = require('./install/diff-trees.js')
  128 var checkPermissions = require('./install/check-permissions.js')
  129 var decomposeActions = require('./install/decompose-actions.js')
  130 var filterInvalidActions = require('./install/filter-invalid-actions.js')
  131 var validateTree = require('./install/validate-tree.js')
  132 var validateArgs = require('./install/validate-args.js')
  133 var saveRequested = require('./install/save.js').saveRequested
  134 var getSaveType = require('./install/save.js').getSaveType
  135 var doSerialActions = require('./install/actions.js').doSerial
  136 var doReverseSerialActions = require('./install/actions.js').doReverseSerial
  137 var doParallelActions = require('./install/actions.js').doParallel
  138 var doOneAction = require('./install/actions.js').doOne
  139 var removeObsoleteDep = require('./install/deps.js').removeObsoleteDep
  140 var packageId = require('./utils/package-id.js')
  141 var moduleName = require('./utils/module-name.js')
  142 var errorMessage = require('./utils/error-message.js')
  143 var andIgnoreErrors = require('./install/and-ignore-errors.js')
  144 
  145 function unlockCB (lockPath, name, cb) {
  146   validate('SSF', arguments)
  147   return function (installEr) {
  148     var args = arguments
  149     try {
  150       unlock(lockPath, name, reportErrorAndReturn)
  151     } catch (unlockEx) {
  152       process.nextTick(function () {
  153         reportErrorAndReturn(unlockEx)
  154       })
  155     }
  156     function reportErrorAndReturn (unlockEr) {
  157       if (installEr) {
  158         if (unlockEr && unlockEr.code !== 'ENOTLOCKED') {
  159           log.warn('unlock' + name, unlockEr)
  160         }
  161         return cb.apply(null, args)
  162       }
  163       if (unlockEr) return cb(unlockEr)
  164       return cb.apply(null, args)
  165     }
  166   }
  167 }
  168 
  169 function install (where, args, cb) {
  170   if (!cb) {
  171     cb = args
  172     args = where
  173     where = null
  174   }
  175   var globalTop = path.resolve(npm.globalDir, '..')
  176   if (!where) {
  177     where = npm.config.get('global')
  178           ? globalTop
  179           : npm.prefix
  180   }
  181   validate('SAF', [where, args, cb])
  182   // the /path/to/node_modules/..
  183   var dryrun = !!npm.config.get('dry-run')
  184 
  185   if (npm.config.get('dev')) {
  186     log.warn('install', 'Usage of the `--dev` option is deprecated. Use `--only=dev` instead.')
  187   }
  188 
  189   if (where === globalTop && !args.length) {
  190     args = ['.']
  191   }
  192   args = args.filter(function (a) {
  193     return path.resolve(a) !== npm.prefix
  194   })
  195 
  196   new Installer(where, dryrun, args).run(cb)
  197 }
  198 
  199 function Installer (where, dryrun, args) {
  200   validate('SBA', arguments)
  201   this.where = where
  202   this.dryrun = dryrun
  203   this.args = args
  204   this.currentTree = null
  205   this.idealTree = null
  206   this.differences = []
  207   this.todo = []
  208   this.progress = {}
  209   this.noPackageJsonOk = !!args.length
  210   this.topLevelLifecycles = !args.length
  211   this.npat = npm.config.get('npat')
  212   this.dev = npm.config.get('dev') || (!/^prod(uction)?$/.test(npm.config.get('only')) && !npm.config.get('production')) || /^dev(elopment)?$/.test(npm.config.get('only'))
  213   this.prod = !/^dev(elopment)?$/.test(npm.config.get('only'))
  214   this.rollback = npm.config.get('rollback')
  215   this.link = npm.config.get('link')
  216   this.global = this.where === path.resolve(npm.globalDir, '..')
  217 }
  218 Installer.prototype = {}
  219 
  220 Installer.prototype.run = function (cb) {
  221   validate('F', arguments)
  222 
  223   // FIXME: This is bad and I should feel bad.
  224   // lib/install needs to have some way of sharing _limited_
  225   // state with the things it calls. Passing the object is too
  226   // much. The global config is WAY too much. =( =(
  227   // But not having this is gonna break linked modules in
  228   // subtle stupid ways, and refactoring all this code isn't
  229   // the right thing to do just yet.
  230   if (this.global) {
  231     var prevGlobal = npm.config.get('global')
  232     npm.config.set('global', true)
  233     var next = cb
  234     cb = function () {
  235       npm.config.set('global', prevGlobal)
  236       next.apply(null, arguments)
  237     }
  238   }
  239 
  240   var installSteps = []
  241   var postInstallSteps = []
  242   installSteps.push(
  243     [this.newTracker(log, 'loadCurrentTree', 4)],
  244     [this, this.loadCurrentTree],
  245     [this, this.finishTracker, 'loadCurrentTree'],
  246 
  247     [this.newTracker(log, 'loadIdealTree', 12)],
  248     [this, this.loadIdealTree],
  249     [this, this.finishTracker, 'loadIdealTree'],
  250 
  251     [this, this.debugTree, 'currentTree', 'currentTree'],
  252     [this, this.debugTree, 'idealTree', 'idealTree'],
  253 
  254     [this.newTracker(log, 'generateActionsToTake')],
  255     [this, this.generateActionsToTake],
  256     [this, this.finishTracker, 'generateActionsToTake'],
  257 
  258     [this, this.debugActions, 'diffTrees', 'differences'],
  259     [this, this.debugActions, 'decomposeActions', 'todo'])
  260   if (!this.dryrun) {
  261     installSteps.push(
  262       [this.newTracker(log, 'executeActions', 8)],
  263       [this, this.executeActions],
  264       [this, this.finishTracker, 'executeActions'])
  265     var node_modules = path.resolve(this.where, 'node_modules')
  266     var staging = path.resolve(node_modules, '.staging')
  267     postInstallSteps.push(
  268       [this.newTracker(log, 'rollbackFailedOptional', 1)],
  269       [this, this.rollbackFailedOptional, staging, this.todo],
  270       [this, this.finishTracker, 'rollbackFailedOptional'],
  271       [this, this.commit, staging, this.todo],
  272       [this.newTracker(log, 'runTopLevelLifecycles', 2)],
  273       [this, this.runTopLevelLifecycles],
  274       [this, this.finishTracker, 'runTopLevelLifecycles'])
  275 
  276     if (getSaveType(this.args)) {
  277       postInstallSteps.push(
  278         [this, this.saveToDependencies])
  279     }
  280   }
  281   postInstallSteps.push(
  282     [this, this.printInstalled])
  283 
  284   var self = this
  285   chain(installSteps, function (installEr) {
  286     if (installEr) self.failing = true
  287     chain(postInstallSteps, function (postInstallEr) {
  288       if (self.idealTree) {
  289         self.idealTree.warnings.forEach(function (warning) {
  290           if (warning.code === 'EPACKAGEJSON' && self.global) return
  291           if (warning.code === 'ENOTDIR') return
  292           errorMessage(warning).summary.forEach(function (logline) {
  293             log.warn.apply(log, logline)
  294           })
  295         })
  296       }
  297       if (installEr && postInstallEr) {
  298         var msg = errorMessage(postInstallEr)
  299         msg.summary.forEach(function (logline) {
  300           log.warn.apply(log, logline)
  301         })
  302         msg.detail.forEach(function (logline) {
  303           log.verbose.apply(log, logline)
  304         })
  305       }
  306       cb(installEr || postInstallEr, self.getInstalledModules(), self.idealTree)
  307     })
  308   })
  309 }
  310 
  311 Installer.prototype.loadArgMetadata = function (next) {
  312   var self = this
  313   getAllMetadata(this.args, this.currentTree, process.cwd(), iferr(next, function (args) {
  314     self.args = args
  315     next()
  316   }))
  317 }
  318 
  319 Installer.prototype.newTracker = function (tracker, name, size) {
  320   validate('OS', [tracker, name])
  321   if (size) validate('N', [size])
  322   this.progress[name] = tracker.newGroup(name, size)
  323   var self = this
  324   return function (next) {
  325     self.progress[name].silly(name, 'Starting')
  326     next()
  327   }
  328 }
  329 
  330 Installer.prototype.finishTracker = function (name, cb) {
  331   validate('SF', arguments)
  332   this.progress[name].silly(name, 'Finishing')
  333   this.progress[name].finish()
  334   cb()
  335 }
  336 
  337 Installer.prototype.loadCurrentTree = function (cb) {
  338   validate('F', arguments)
  339   log.silly('install', 'loadCurrentTree')
  340   var todo = []
  341   if (this.global) {
  342     todo.push([this, this.readGlobalPackageData])
  343   } else {
  344     todo.push([this, this.readLocalPackageData])
  345   }
  346   todo.push(
  347     [this, this.normalizeTree, log.newGroup('normalizeTree')])
  348   chain(todo, cb)
  349 }
  350 
  351 Installer.prototype.loadIdealTree = function (cb) {
  352   validate('F', arguments)
  353   log.silly('install', 'loadIdealTree')
  354 
  355   chain([
  356     [this.newTracker(this.progress.loadIdealTree, 'cloneCurrentTree')],
  357     [this, this.cloneCurrentTreeToIdealTree],
  358     [this, this.finishTracker, 'cloneCurrentTree'],
  359 
  360     [this.newTracker(this.progress.loadIdealTree, 'loadShrinkwrap')],
  361     [this, this.loadShrinkwrap],
  362     [this, this.finishTracker, 'loadShrinkwrap'],
  363 
  364     [this.newTracker(this.progress.loadIdealTree, 'loadAllDepsIntoIdealTree', 10)],
  365     [this, this.loadAllDepsIntoIdealTree],
  366     [this, this.finishTracker, 'loadAllDepsIntoIdealTree'],
  367 
  368     // TODO: Remove this (should no longer be necessary, instead counter productive)
  369     [this, function (next) { recalculateMetadata(this.idealTree, log, next) }]
  370   ], cb)
  371 }
  372 
  373 Installer.prototype.loadAllDepsIntoIdealTree = function (cb) {
  374   validate('F', arguments)
  375   log.silly('install', 'loadAllDepsIntoIdealTree')
  376   var saveDeps = getSaveType(this.args)
  377 
  378   var cg = this.progress.loadAllDepsIntoIdealTree
  379   var installNewModules = !!this.args.length
  380   var steps = []
  381 
  382   if (installNewModules) {
  383     steps.push([validateArgs, this.idealTree, this.args])
  384     steps.push([loadRequestedDeps, this.args, this.idealTree, saveDeps, cg.newGroup('loadRequestedDeps')])
  385   } else {
  386     if (this.prod) {
  387       steps.push(
  388         [loadDeps, this.idealTree, cg.newGroup('loadDeps')])
  389     }
  390     if (this.dev) {
  391       steps.push(
  392         [loadDevDeps, this.idealTree, cg.newGroup('loadDevDeps')])
  393     }
  394   }
  395   steps.push(
  396     [loadExtraneous.andResolveDeps, this.idealTree, cg.newGroup('loadExtraneous')])
  397   chain(steps, cb)
  398 }
  399 
  400 Installer.prototype.generateActionsToTake = function (cb) {
  401   validate('F', arguments)
  402   log.silly('install', 'generateActionsToTake')
  403   var cg = this.progress.generateActionsToTake
  404   chain([
  405     [validateTree, this.idealTree, cg.newGroup('validateTree')],
  406     [diffTrees, this.currentTree, this.idealTree, this.differences, cg.newGroup('diffTrees')],
  407     [this, this.computeLinked],
  408     [filterInvalidActions, this.where, this.differences],
  409     [checkPermissions, this.differences],
  410     [decomposeActions, this.differences, this.todo]
  411   ], cb)
  412 }
  413 
  414 Installer.prototype.computeLinked = function (cb) {
  415   validate('F', arguments)
  416   if (!this.link || this.global) return cb()
  417   var linkTodoList = []
  418   var self = this
  419   asyncMap(this.differences, function (action, next) {
  420     var cmd = action[0]
  421     var pkg = action[1]
  422     if (cmd !== 'add' && cmd !== 'update') return next()
  423     var isReqByTop = pkg.requiredBy.filter(function (mod) { return mod.isTop }).length
  424     var isReqByUser = pkg.userRequired
  425     var isExtraneous = pkg.requiredBy.length === 0
  426     if (!isReqByTop && !isReqByUser && !isExtraneous) return next()
  427     isLinkable(pkg, function (install, link) {
  428       if (install) linkTodoList.push(['global-install', pkg])
  429       if (link) linkTodoList.push(['global-link', pkg])
  430       if (install || link) removeObsoleteDep(pkg)
  431       next()
  432     })
  433   }, function () {
  434     if (linkTodoList.length === 0) return cb()
  435     self.differences.length = 0
  436     Array.prototype.push.apply(self.differences, linkTodoList)
  437     diffTrees(self.currentTree, self.idealTree, self.differences, log.newGroup('d2'), cb)
  438   })
  439 }
  440 
  441 function isLinkable (pkg, cb) {
  442   var globalPackage = path.resolve(npm.globalPrefix, 'lib', 'node_modules', moduleName(pkg))
  443   var globalPackageJson = path.resolve(globalPackage, 'package.json')
  444   fs.stat(globalPackage, function (er) {
  445     if (er) return cb(true, true)
  446     fs.readFile(globalPackageJson, function (er, data) {
  447       var json = parseJSON.noExceptions(data)
  448       cb(false, json && json.version === pkg.package.version)
  449     })
  450   })
  451 }
  452 
  453 Installer.prototype.executeActions = function (cb) {
  454   validate('F', arguments)
  455   log.silly('install', 'executeActions')
  456   var todo = this.todo
  457   var cg = this.progress.executeActions
  458 
  459   var node_modules = path.resolve(this.where, 'node_modules')
  460   var staging = path.resolve(node_modules, '.staging')
  461   var steps = []
  462   var trackLifecycle = cg.newGroup('lifecycle')
  463 
  464   cb = unlockCB(node_modules, '.staging', cb)
  465 
  466   steps.push(
  467     [doSerialActions, 'global-install', staging, todo, trackLifecycle.newGroup('global-install')],
  468     [doParallelActions, 'fetch', staging, todo, cg.newGroup('fetch', 10)],
  469     [lock, node_modules, '.staging'],
  470     [rimraf, staging],
  471     [mkdirp, staging],
  472     [doParallelActions, 'extract', staging, todo, cg.newGroup('extract', 10)],
  473     [doParallelActions, 'preinstall', staging, todo, trackLifecycle.newGroup('preinstall')],
  474     [doReverseSerialActions, 'remove', staging, todo, cg.newGroup('remove')],
  475     [doSerialActions, 'move', staging, todo, cg.newGroup('move')],
  476     [doSerialActions, 'finalize', staging, todo, cg.newGroup('finalize')],
  477     [doSerialActions, 'build', staging, todo, trackLifecycle.newGroup('build')],
  478     [doSerialActions, 'global-link', staging, todo, trackLifecycle.newGroup('global-link')],
  479     [doParallelActions, 'update-linked', staging, todo, trackLifecycle.newGroup('update-linked')],
  480     [doSerialActions, 'install', staging, todo, trackLifecycle.newGroup('install')],
  481     [doSerialActions, 'postinstall', staging, todo, trackLifecycle.newGroup('postinstall')])
  482   if (this.npat) {
  483     steps.push(
  484       [doParallelActions, 'test', staging, todo, trackLifecycle.newGroup('npat')])
  485   }
  486 
  487   var self = this
  488   chain(steps, function (er) {
  489     if (!er || self.rollback) {
  490       rimraf(staging, function () { cb(er) })
  491     } else {
  492       cb(er)
  493     }
  494   })
  495 }
  496 
  497 Installer.prototype.rollbackFailedOptional = function (staging, actionsToRun, cb) {
  498   if (!this.rollback) return cb()
  499   var failed = actionsToRun.map(function (action) {
  500     return action[1]
  501   }).filter(function (pkg) {
  502     return pkg.failed && pkg.rollback
  503   })
  504   asyncMap(failed, function (pkg, next) {
  505     asyncMap(pkg.rollback, function (rollback, done) {
  506       rollback(staging, pkg, done)
  507     }, next)
  508   }, cb)
  509 }
  510 
  511 Installer.prototype.commit = function (staging, actionsToRun, cb) {
  512   var toCommit = actionsToRun.map(function (action) { return action[1] }).filter(function (pkg) { return !pkg.failed && pkg.commit })
  513   asyncMap(toCommit, function (pkg, next) {
  514     asyncMap(pkg.commit, function (commit, done) {
  515       commit(staging, pkg, done)
  516     }, function () {
  517       pkg.commit = []
  518       next.apply(null, arguments)
  519     })
  520   }, cb)
  521 }
  522 
  523 Installer.prototype.runTopLevelLifecycles = function (cb) {
  524   validate('F', arguments)
  525   if (this.failing) return cb()
  526   log.silly('install', 'runTopLevelLifecycles')
  527   var steps = []
  528   var trackLifecycle = this.progress.runTopLevelLifecycles
  529   if (!this.topLevelLifecycles) {
  530     trackLifecycle.finish()
  531     return cb()
  532   }
  533 
  534   steps.push(
  535     [doOneAction, 'preinstall', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('preinstall:.')],
  536     [doOneAction, 'build', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('build:.')],
  537     [doOneAction, 'install', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('install:.')],
  538     [doOneAction, 'postinstall', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('postinstall:.')])
  539   if (this.npat) {
  540     steps.push(
  541       [doOneAction, 'test', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('npat:.')])
  542   }
  543   if (this.dev) {
  544     steps.push(
  545       [doOneAction, 'prepublish', this.idealTree.path, this.idealTree, trackLifecycle.newGroup('prepublish')])
  546   }
  547   chain(steps, cb)
  548 }
  549 
  550 Installer.prototype.saveToDependencies = function (cb) {
  551   validate('F', arguments)
  552   if (this.failing) return cb()
  553   log.silly('install', 'saveToDependencies')
  554   saveRequested(this.args, this.idealTree, cb)
  555 }
  556 
  557 Installer.prototype.readGlobalPackageData = function (cb) {
  558   validate('F', arguments)
  559   log.silly('install', 'readGlobalPackageData')
  560   var self = this
  561   this.loadArgMetadata(iferr(cb, function () {
  562     mkdirp(self.where, iferr(cb, function () {
  563       var pkgs = {}
  564       self.args.forEach(function (pkg) {
  565         pkgs[pkg.name] = true
  566       })
  567       readPackageTree(self.where, function (ctx, kid) { return ctx.parent || pkgs[kid] }, iferr(cb, function (currentTree) {
  568         self.currentTree = currentTree
  569         return cb()
  570       }))
  571     }))
  572   }))
  573 }
  574 
  575 Installer.prototype.readLocalPackageData = function (cb) {
  576   validate('F', arguments)
  577   log.silly('install', 'readLocalPackageData')
  578   var self = this
  579   mkdirp(this.where, iferr(cb, function () {
  580     readPackageTree(self.where, iferr(cb, function (currentTree) {
  581       self.currentTree = currentTree
  582       self.currentTree.warnings = []
  583       if (currentTree.error && currentTree.error.code === 'EJSONPARSE') {
  584         return cb(currentTree.error)
  585       }
  586       if (!self.noPackageJsonOk && !currentTree.package) {
  587         log.error('install', "Couldn't read dependencies")
  588         var er = new Error("ENOENT, open '" + path.join(self.where, 'package.json') + "'")
  589         er.code = 'ENOPACKAGEJSON'
  590         er.errno = 34
  591         return cb(er)
  592       }
  593       if (!currentTree.package) currentTree.package = {}
  594       if (currentTree.package._shrinkwrap) {
  595         self.loadArgMetadata(cb)
  596       } else {
  597         fs.readFile(path.join(self.where, 'npm-shrinkwrap.json'), function (er, data) {
  598           if (er) return self.loadArgMetadata(cb)
  599           try {
  600             currentTree.package._shrinkwrap = parseJSON(data)
  601           } catch (ex) {
  602             return cb(ex)
  603           }
  604           return self.loadArgMetadata(cb)
  605         })
  606       }
  607     }))
  608   }))
  609 }
  610 
  611 Installer.prototype.cloneCurrentTreeToIdealTree = function (cb) {
  612   validate('F', arguments)
  613   log.silly('install', 'cloneCurrentTreeToIdealTree')
  614   this.idealTree = copyTree(this.currentTree)
  615   this.idealTree.warnings = []
  616   cb()
  617 }
  618 
  619 Installer.prototype.loadShrinkwrap = function (cb) {
  620   validate('F', arguments)
  621   log.silly('install', 'loadShrinkwrap')
  622   var installNewModules = !!this.args.length
  623   if (installNewModules) {
  624     readShrinkwrap(this.idealTree, cb)
  625   } else {
  626     readShrinkwrap.andInflate(this.idealTree, cb)
  627   }
  628 }
  629 
  630 Installer.prototype.normalizeTree = function (log, cb) {
  631   validate('OF', arguments)
  632   log.silly('install', 'normalizeTree')
  633   recalculateMetadata(this.currentTree, log, iferr(cb, function (tree) {
  634     tree.children.forEach(function (child) {
  635       if (child.requiredBy.length === 0) {
  636         child.existing = true
  637       }
  638     })
  639     cb(null, tree)
  640   }))
  641 }
  642 
  643 Installer.prototype.getInstalledModules = function () {
  644   return this.differences.filter(function (action) {
  645     var mutation = action[0]
  646     return (mutation === 'add' || mutation === 'update')
  647   }).map(function (action) {
  648     var child = action[1]
  649     return [child.package._id, child.path]
  650   })
  651 }
  652 
  653 Installer.prototype.printInstalled = function (cb) {
  654   validate('F', arguments)
  655   log.silly('install', 'printInstalled')
  656   var self = this
  657   this.differences.forEach(function (action) {
  658     var mutation = action[0]
  659     var child = action[1]
  660     var name = packageId(child)
  661     var where = path.relative(self.where, child.path)
  662     if (mutation === 'remove') {
  663       output('- ' + name + ' ' + where)
  664     } else if (mutation === 'move') {
  665       var oldWhere = path.relative(self.where, child.fromPath)
  666       output(name + ' ' + oldWhere + ' -> ' + where)
  667     }
  668   })
  669   var addedOrMoved = this.differences.filter(function (action) {
  670     var mutation = action[0]
  671     var child = action[1]
  672     return !child.failed && (mutation === 'add' || mutation === 'update')
  673   }).map(function (action) {
  674     var child = action[1]
  675     return child.path
  676   })
  677   if (!addedOrMoved.length) return cb()
  678   // TODO: remove the recalculateMetadata, should not be needed
  679   recalculateMetadata(this.idealTree, log, iferr(cb, function (tree) {
  680     // These options control both how installs happen AND how `ls` shows output.
  681     // Something like `npm install --production` only installs production deps.
  682     // By contrast `npm install --production foo` installs `foo` and the
  683     // `production` option is ignored. But when it comes time for `ls` to show
  684     // its output, it excludes the thing we just installed because that flag.
  685     // The summary output we get should be unfiltered, showing everything
  686     // installed, so we clear these options before calling `ls`.
  687     npm.config.set('production', false)
  688     npm.config.set('dev', false)
  689     npm.config.set('only', '')
  690     npm.config.set('also', '')
  691     ls.fromTree(self.where, tree, addedOrMoved, false, andIgnoreErrors(cb))
  692   }))
  693 }
  694 
  695 Installer.prototype.debugActions = function (name, actionListName, cb) {
  696   validate('SSF', arguments)
  697   var actionsToLog = this[actionListName]
  698   log.silly(name, 'action count', actionsToLog.length)
  699   actionsToLog.forEach(function (action) {
  700     log.silly(name, action.map(function (value) {
  701       return (value && value.package) ? packageId(value) : value
  702     }).join(' '))
  703   })
  704   cb()
  705 }
  706 
  707 // This takes an object and a property name instead of a value to allow us
  708 // to define the arguments for use by chain before the property exists yet.
  709 Installer.prototype.debugTree = function (name, treeName, cb) {
  710   validate('SSF', arguments)
  711   log.silly(name, this.prettify(this[treeName]).trim())
  712   cb()
  713 }
  714 
  715 Installer.prototype.prettify = function (tree) {
  716   validate('O', arguments)
  717   var seen = {}
  718   function byName (aa, bb) {
  719     return packageId(aa).localeCompare(packageId(bb))
  720   }
  721   function expandTree (tree) {
  722     seen[tree.path] = true
  723     return {
  724       label: packageId(tree),
  725       nodes: tree.children.filter(function (tree) { return !seen[tree.path] }).sort(byName).map(expandTree)
  726     }
  727   }
  728   return archy(expandTree(tree), '', { unicode: npm.config.get('unicode') })
  729 }