npm: Upgrade to 1.3.17
[platform/upstream/nodejs.git] / deps / npm / lib / install.js
1 // npm install <pkg> <pkg> <pkg>
2 //
3 // See doc/install.md for more description
4
5 // Managing contexts...
6 // there's a lot of state associated with an "install" operation, including
7 // packages that are already installed, parent packages, current shrinkwrap, and
8 // so on. We maintain this state in a "context" object that gets passed around.
9 // every time we dive into a deeper node_modules folder, the "family" list that
10 // gets passed along uses the previous "family" list as its __proto__.  Any
11 // "resolved precise dependency" things that aren't already on this object get
12 // added, and then that's passed to the next generation of installation.
13
14 module.exports = install
15
16 install.usage = "npm install"
17               + "\nnpm install <pkg>"
18               + "\nnpm install <pkg>@<tag>"
19               + "\nnpm install <pkg>@<version>"
20               + "\nnpm install <pkg>@<version range>"
21               + "\nnpm install <folder>"
22               + "\nnpm install <tarball file>"
23               + "\nnpm install <tarball url>"
24               + "\nnpm install <git:// url>"
25               + "\nnpm install <github username>/<github project>"
26               + "\n\nCan specify one or more: npm install ./foo.tgz bar@stable /some/folder"
27               + "\nIf no argument is supplied and ./npm-shrinkwrap.json is "
28               + "\npresent, installs dependencies specified in the shrinkwrap."
29               + "\nOtherwise, installs dependencies from ./package.json."
30
31 install.completion = function (opts, cb) {
32   // install can complete to a folder with a package.json, or any package.
33   // if it has a slash, then it's gotta be a folder
34   // if it starts with https?://, then just give up, because it's a url
35   // for now, not yet implemented.
36   var registry = npm.registry
37   registry.get("/-/short", function (er, pkgs) {
38     if (er) return cb()
39     if (!opts.partialWord) return cb(null, pkgs)
40
41     var name = opts.partialWord.split("@").shift()
42     pkgs = pkgs.filter(function (p) {
43       return p.indexOf(name) === 0
44     })
45
46     if (pkgs.length !== 1 && opts.partialWord === name) {
47       return cb(null, pkgs)
48     }
49
50     registry.get(pkgs[0], function (er, d) {
51       if (er) return cb()
52       return cb(null, Object.keys(d["dist-tags"] || {})
53                 .concat(Object.keys(d.versions || {}))
54                 .map(function (t) {
55                   return pkgs[0] + "@" + t
56                 }))
57     })
58   })
59 }
60
61 var npm = require("./npm.js")
62   , semver = require("semver")
63   , readJson = require("read-package-json")
64   , readInstalled = require("read-installed")
65   , log = require("npmlog")
66   , path = require("path")
67   , fs = require("graceful-fs")
68   , cache = require("./cache.js")
69   , asyncMap = require("slide").asyncMap
70   , chain = require("slide").chain
71   , url = require("url")
72   , mkdir = require("mkdirp")
73   , lifecycle = require("./utils/lifecycle.js")
74   , archy = require("archy")
75   , isGitUrl = require("./utils/is-git-url.js")
76
77 function install (args, cb_) {
78   var hasArguments = !!args.length
79
80   function cb (er, installed) {
81     if (er) return cb_(er)
82
83     findPeerInvalid(where, function (er, problem) {
84       if (er) return cb_(er)
85
86       if (problem) {
87         var peerInvalidError = new Error("The package " + problem.name +
88           " does not satisfy its siblings' peerDependencies requirements!")
89         peerInvalidError.code = "EPEERINVALID"
90         peerInvalidError.packageName = problem.name
91         peerInvalidError.peersDepending = problem.peersDepending
92         return cb(peerInvalidError)
93       }
94
95       var tree = treeify(installed || [])
96         , pretty = prettify(tree, installed).trim()
97
98       if (pretty) console.log(pretty)
99       save(where, installed, tree, pretty, hasArguments, cb_)
100     })
101   }
102
103   // the /path/to/node_modules/..
104   var where = path.resolve(npm.dir, "..")
105
106   // internal api: install(where, what, cb)
107   if (arguments.length === 3) {
108     where = args
109     args = [].concat(cb_) // pass in [] to do default dep-install
110     cb_ = arguments[2]
111     log.verbose("install", "where,what", [where, args])
112   }
113
114   if (!npm.config.get("global")) {
115     args = args.filter(function (a) {
116       return path.resolve(a) !== where
117     })
118   }
119
120   mkdir(where, function (er, made) {
121     if (er) return cb(er)
122     // install dependencies locally by default,
123     // or install current folder globally
124     if (!args.length) {
125       var opt = { dev: npm.config.get("dev") || !npm.config.get("production") }
126
127       if (npm.config.get("global")) args = ["."]
128       else return readDependencies(null, where, opt, function (er, data) {
129         if (er) {
130           log.error("install", "Couldn't read dependencies")
131           return cb(er)
132         }
133         var deps = Object.keys(data.dependencies || {})
134         log.verbose("install", "where, deps", [where, deps])
135         var context = { family: {}
136                       , ancestors: {}
137                       , explicit: false
138                       , parent: data
139                       , wrap: null }
140
141         if (data.name === path.basename(where) &&
142             path.basename(path.dirname(where)) === "node_modules") {
143           // Only include in ancestry if it can actually be required.
144           // Otherwise, it does not count.
145           context.family[data.name] =
146             context.ancestors[data.name] = data.version
147         }
148
149         installManyTop(deps.map(function (dep) {
150           var target = data.dependencies[dep]
151             , parsed = url.parse(target.replace(/^git\+/, "git"))
152           target = dep + "@" + target
153           return target
154         }), where, context, function(er, results) {
155           if (er) return cb(er, results)
156           lifecycle(data, "prepublish", where, function(er) {
157             return cb(er, results)
158           })
159         })
160       })
161     }
162
163     // initial "family" is the name:version of the root, if it's got
164     // a package.json file.
165     var jsonFile = path.resolve(where, "package.json")
166     readJson(jsonFile, log.warn, function (er, data) {
167       if (er
168           && er.code !== "ENOENT"
169           && er.code !== "ENOTDIR") return cb(er)
170       if (er) data = null
171       var context = { family: {}
172                     , ancestors: {}
173                     , explicit: true
174                     , parent: data
175                     , wrap: null }
176       if (data) {
177         context.family[data.name] = context.ancestors[data.name] = data.version
178       }
179       var fn = npm.config.get("global") ? installMany : installManyTop
180       fn(args, where, context, cb)
181     })
182   })
183 }
184
185 function findPeerInvalid (where, cb) {
186   readInstalled(where, log.warn, function (er, data) {
187     if (er) return cb(er)
188
189     cb(null, findPeerInvalid_(data.dependencies, []))
190   })
191 }
192
193 function findPeerInvalid_ (packageMap, fpiList) {
194   if (fpiList.indexOf(packageMap) !== -1)
195     return
196
197   fpiList.push(packageMap)
198
199   for (var packageName in packageMap) {
200     var pkg = packageMap[packageName]
201
202     if (pkg.peerInvalid) {
203       var peersDepending = {};
204       for (peerName in packageMap) {
205         var peer = packageMap[peerName]
206         if (peer.peerDependencies && peer.peerDependencies[packageName]) {
207           peersDepending[peer.name + "@" + peer.version] =
208             peer.peerDependencies[packageName]
209         }
210       }
211       return { name: pkg.name, peersDepending: peersDepending }
212     }
213
214     if (pkg.dependencies) {
215       var invalid = findPeerInvalid_(pkg.dependencies, fpiList)
216       if (invalid)
217         return invalid
218     }
219   }
220
221   return null
222 }
223
224 // reads dependencies for the package at "where". There are several cases,
225 // depending on our current state and the package's configuration:
226 //
227 // 1. If "context" is specified, then we examine the context to see if there's a
228 //    shrinkwrap there. In that case, dependencies are read from the shrinkwrap.
229 // 2. Otherwise, if an npm-shrinkwrap.json file is present, dependencies are
230 //    read from there.
231 // 3. Otherwise, dependencies come from package.json.
232 //
233 // Regardless of which case we fall into, "cb" is invoked with a first argument
234 // describing the full package (as though readJson had been used) but with
235 // "dependencies" read as described above. The second argument to "cb" is the
236 // shrinkwrap to use in processing this package's dependencies, which may be
237 // "wrap" (in case 1) or a new shrinkwrap (in case 2).
238 function readDependencies (context, where, opts, cb) {
239   var wrap = context ? context.wrap : null
240
241   readJson( path.resolve(where, "package.json")
242           , log.warn
243           , function (er, data) {
244     if (er && er.code === "ENOENT") er.code = "ENOPACKAGEJSON"
245     if (er)  return cb(er)
246
247     if (opts && opts.dev) {
248       if (!data.dependencies) data.dependencies = {}
249       Object.keys(data.devDependencies || {}).forEach(function (k) {
250         data.dependencies[k] = data.devDependencies[k]
251       })
252     }
253
254     if (!npm.config.get("optional") && data.optionalDependencies) {
255       Object.keys(data.optionalDependencies).forEach(function (d) {
256         delete data.dependencies[d]
257       })
258     }
259
260     // User has opted out of shrinkwraps entirely
261     if (npm.config.get("shrinkwrap") === false)
262       return cb(null, data, null)
263
264     if (wrap) {
265       log.verbose("readDependencies: using existing wrap", [where, wrap])
266       var rv = {}
267       Object.keys(data).forEach(function (key) {
268         rv[key] = data[key]
269       })
270       rv.dependencies = {}
271       Object.keys(wrap).forEach(function (key) {
272         log.verbose("from wrap", [key, wrap[key]])
273         rv.dependencies[key] = readWrap(wrap[key])
274       })
275       log.verbose("readDependencies returned deps", rv.dependencies)
276       return cb(null, rv, wrap)
277     }
278
279     var wrapfile = path.resolve(where, "npm-shrinkwrap.json")
280
281     fs.readFile(wrapfile, "utf8", function (er, wrapjson) {
282       if (er) {
283         log.verbose("readDependencies", "using package.json deps")
284         return cb(null, data, null)
285       }
286
287       try {
288         var newwrap = JSON.parse(wrapjson)
289       } catch (ex) {
290         return cb(ex)
291       }
292
293       log.info("shrinkwrap", "file %j", wrapfile)
294       var rv = {}
295       Object.keys(data).forEach(function (key) {
296         rv[key] = data[key]
297       })
298       rv.dependencies = {}
299       Object.keys(newwrap.dependencies || {}).forEach(function (key) {
300         rv.dependencies[key] = readWrap(newwrap.dependencies[key])
301       })
302
303       // fold in devDependencies if not already present, at top level
304       if (opts && opts.dev) {
305         Object.keys(data.devDependencies || {}).forEach(function (k) {
306           rv.dependencies[k] = rv.dependencies[k] || data.devDependencies[k]
307         })
308       }
309
310       log.verbose("readDependencies returned deps", rv.dependencies)
311       return cb(null, rv, newwrap.dependencies)
312     })
313   })
314 }
315
316 function readWrap (w) {
317   return (w.resolved) ? w.resolved
318        : (w.from && url.parse(w.from).protocol) ? w.from
319        : w.version
320 }
321
322 // if the -S|--save option is specified, then write installed packages
323 // as dependencies to a package.json file.
324 // This is experimental.
325 function save (where, installed, tree, pretty, hasArguments, cb) {
326   if (!hasArguments ||
327       !npm.config.get("save") &&
328       !npm.config.get("save-dev") &&
329       !npm.config.get("save-optional") ||
330       npm.config.get("global")) {
331     return cb(null, installed, tree, pretty)
332   }
333
334   var saveBundle = npm.config.get('save-bundle')
335
336   // each item in the tree is a top-level thing that should be saved
337   // to the package.json file.
338   // The relevant tree shape is { <folder>: {what:<pkg>} }
339   var saveTarget = path.resolve(where, "package.json")
340     , things = Object.keys(tree).map(function (k) {
341         // if "what" was a url, then save that instead.
342         var t = tree[k]
343           , u = url.parse(t.from)
344           , w = t.what.split("@")
345         if (u && u.protocol) w[1] = t.from
346         return w
347       }).reduce(function (set, k) {
348         var rangeDescriptor = semver.valid(k[1], true) &&
349                               semver.gte(k[1], "0.1.0", true)
350                             ? "~" : ""
351         set[k[0]] = rangeDescriptor + k[1]
352         return set
353       }, {})
354
355   // don't use readJson, because we don't want to do all the other
356   // tricky npm-specific stuff that's in there.
357   fs.readFile(saveTarget, function (er, data) {
358     // ignore errors here, just don't save it.
359     try {
360       data = JSON.parse(data.toString("utf8"))
361     } catch (ex) {
362       er = ex
363     }
364
365     if (er) {
366       return cb(null, installed, tree, pretty)
367     }
368
369     var deps = npm.config.get("save-optional") ? "optionalDependencies"
370              : npm.config.get("save-dev") ? "devDependencies"
371              : "dependencies"
372
373     if (saveBundle) {
374       var bundle = data.bundleDependencies || data.bundledDependencies
375       delete data.bundledDependencies
376       if (!Array.isArray(bundle)) bundle = []
377       data.bundleDependencies = bundle
378     }
379
380     log.verbose('saving', things)
381     data[deps] = data[deps] || {}
382     Object.keys(things).forEach(function (t) {
383       data[deps][t] = things[t]
384       if (saveBundle) {
385         var i = bundle.indexOf(t)
386         if (i === -1) bundle.push(t)
387       }
388     })
389
390     data = JSON.stringify(data, null, 2) + "\n"
391     fs.writeFile(saveTarget, data, function (er) {
392       cb(er, installed, tree, pretty)
393     })
394   })
395 }
396
397
398 // Outputting *all* the installed modules is a bit confusing,
399 // because the length of the path does not make it clear
400 // that the submodules are not immediately require()able.
401 // TODO: Show the complete tree, ls-style, but only if --long is provided
402 function prettify (tree, installed) {
403   if (npm.config.get("json")) {
404     function red (set, kv) {
405       set[kv[0]] = kv[1]
406       return set
407     }
408
409     tree = Object.keys(tree).map(function (p) {
410       if (!tree[p]) return null
411       var what = tree[p].what.split("@")
412         , name = what.shift()
413         , version = what.join("@")
414         , o = { name: name, version: version, from: tree[p].from }
415       o.dependencies = tree[p].children.map(function P (dep) {
416          var what = dep.what.split("@")
417            , name = what.shift()
418            , version = what.join("@")
419            , o = { version: version, from: dep.from }
420          o.dependencies = dep.children.map(P).reduce(red, {})
421          return [name, o]
422        }).reduce(red, {})
423        return o
424     })
425
426     return JSON.stringify(tree, null, 2)
427   }
428   if (npm.config.get("parseable")) return parseable(installed)
429
430   return Object.keys(tree).map(function (p) {
431     return archy({ label: tree[p].what + " " + p
432                  , nodes: (tree[p].children || []).map(function P (c) {
433                      if (npm.config.get("long")) {
434                        return { label: c.what, nodes: c.children.map(P) }
435                      }
436                      var g = c.children.map(function (g) {
437                        return g.what
438                      }).join(", ")
439                      if (g) g = " (" + g + ")"
440                      return c.what + g
441                    })
442                  })
443   }).join("\n")
444 }
445
446 function parseable (installed) {
447   var long = npm.config.get("long")
448     , cwd = process.cwd()
449   return installed.map(function (item) {
450     return path.resolve(cwd, item[1]) +
451          ( long ?  ":" + item[0] : "" )
452   }).join("\n")
453 }
454
455 function treeify (installed) {
456   // each item is [what, where, parent, parentDir]
457   // If no parent, then report it.
458   // otherwise, tack it into the parent's children list.
459   // If the parent isn't a top-level then ignore it.
460   var whatWhere = installed.reduce(function (l, r) {
461     var parentDir = r[3]
462       , parent = r[2]
463       , where = r[1]
464       , what = r[0]
465       , from = r[4]
466     l[where] = { parentDir: parentDir
467                , parent: parent
468                , children: []
469                , where: where
470                , what: what
471                , from: from }
472     return l
473   }, {})
474
475   // log.warn("install", whatWhere, "whatWhere")
476   return Object.keys(whatWhere).reduce(function (l, r) {
477     var ww = whatWhere[r]
478     //log.warn("r, ww", [r, ww])
479     if (!ww.parent) {
480       l[r] = ww
481     } else {
482       var p = whatWhere[ww.parentDir]
483       if (p) p.children.push(ww)
484       else l[r] = ww
485     }
486     return l
487   }, {})
488 }
489
490
491 // just like installMany, but also add the existing packages in
492 // where/node_modules to the family object.
493 function installManyTop (what, where, context, cb_) {
494   function cb (er, d) {
495     if (context.explicit || er) return cb_(er, d)
496     // since this wasn't an explicit install, let's build the top
497     // folder, so that `npm install` also runs the lifecycle scripts.
498     npm.commands.build([where], false, true, function (er) {
499       return cb_(er, d)
500     })
501   }
502
503   if (context.explicit) return next()
504
505   readJson(path.join(where, "package.json"), log.warn, function (er, data) {
506     if (er) return next(er)
507     lifecycle(data, "preinstall", where, next)
508   })
509
510   function next (er) {
511     if (er) return cb(er)
512     installManyTop_(what, where, context, cb)
513   }
514 }
515
516 function installManyTop_ (what, where, context, cb) {
517   var nm = path.resolve(where, "node_modules")
518     , names = context.explicit
519             ? what.map(function (w) { return w.split(/@/).shift() })
520             : []
521
522   fs.readdir(nm, function (er, pkgs) {
523     if (er) return installMany(what, where, context, cb)
524     pkgs = pkgs.filter(function (p) {
525       return !p.match(/^[\._-]/)
526     })
527     asyncMap(pkgs.map(function (p) {
528       return path.resolve(nm, p, "package.json")
529     }), function (jsonfile, cb) {
530       readJson(jsonfile, log.warn, function (er, data) {
531         if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
532         if (er) return cb(null, [])
533         return cb(null, [[data.name, data.version]])
534       })
535     }, function (er, packages) {
536       // if there's nothing in node_modules, then don't freak out.
537       if (er) packages = []
538       // add all the existing packages to the family list.
539       // however, do not add to the ancestors list.
540       packages.forEach(function (p) {
541         context.family[p[0]] = p[1]
542       })
543       return installMany(what, where, context, cb)
544     })
545   })
546 }
547
548 function installMany (what, where, context, cb) {
549   // readDependencies takes care of figuring out whether the list of
550   // dependencies we'll iterate below comes from an existing shrinkwrap from a
551   // parent level, a new shrinkwrap at this level, or package.json at this
552   // level, as well as which shrinkwrap (if any) our dependencies should use.
553   var opt = { dev: npm.config.get("dev") }
554   readDependencies(context, where, opt, function (er, data, wrap) {
555     if (er) data = {}
556
557     var parent = data
558
559     var d = data.dependencies || {}
560
561     // if we're explicitly installing "what" into "where", then the shrinkwrap
562     // for "where" doesn't apply. This would be the case if someone were adding
563     // a new package to a shrinkwrapped package. (data.dependencies will not be
564     // used here except to indicate what packages are already present, so
565     // there's no harm in using that.)
566     if (context.explicit) wrap = null
567
568     // what is a list of things.
569     // resolve each one.
570     asyncMap( what
571             , targetResolver(where, context, d)
572             , function (er, targets) {
573
574       if (er) return cb(er)
575
576       // each target will be a data object corresponding
577       // to a package, folder, or whatever that is in the cache now.
578       var newPrev = Object.create(context.family)
579         , newAnc = Object.create(context.ancestors)
580
581       newAnc[data.name] = data.version
582       targets.forEach(function (t) {
583         newPrev[t.name] = t.version
584       })
585       log.silly("resolved", targets)
586       targets.filter(function (t) { return t }).forEach(function (t) {
587         log.info("install", "%s into %s", t._id, where)
588       })
589       asyncMap(targets, function (target, cb) {
590         log.info("installOne", target._id)
591         var wrapData = wrap ? wrap[target.name] : null
592         var newWrap = wrapData && wrapData.dependencies
593                     ? wrap[target.name].dependencies || {}
594                     : null
595         var newContext = { family: newPrev
596                          , ancestors: newAnc
597                          , parent: parent
598                          , explicit: false
599                          , wrap: newWrap }
600         installOne(target, where, newContext, cb)
601       }, cb)
602     })
603   })
604 }
605
606 function targetResolver (where, context, deps) {
607   var alreadyInstalledManually = context.explicit ? [] : null
608     , nm = path.resolve(where, "node_modules")
609     , parent = context.parent
610     , wrap = context.wrap
611
612   if (!context.explicit) fs.readdir(nm, function (er, inst) {
613     if (er) return alreadyInstalledManually = []
614
615     // don't even mess with non-package looking things
616     inst = inst.filter(function (p) {
617       return !p.match(/^[\._-]/)
618     })
619
620     asyncMap(inst, function (pkg, cb) {
621       readJson(path.resolve(nm, pkg, "package.json"), log.warn, function (er, d) {
622         if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
623         // error means it's not a package, most likely.
624         if (er) return cb(null, [])
625
626         // if it's a bundled dep, then assume that anything there is valid.
627         // otherwise, make sure that it's a semver match with what we want.
628         var bd = parent.bundleDependencies
629         if (bd && bd.indexOf(d.name) !== -1 ||
630             semver.satisfies(d.version, deps[d.name] || "*", true)) {
631           return cb(null, d.name)
632         }
633
634         // something is there, but it's not satisfactory.  Clobber it.
635         return cb(null, [])
636       })
637     }, function (er, inst) {
638       // this is the list of things that are valid and should be ignored.
639       alreadyInstalledManually = inst
640     })
641   })
642
643   var to = 0
644   return function resolver (what, cb) {
645     if (!alreadyInstalledManually) return setTimeout(function () {
646       resolver(what, cb)
647     }, to++)
648
649     // now we know what's been installed here manually,
650     // or tampered with in some way that npm doesn't want to overwrite.
651     if (alreadyInstalledManually.indexOf(what.split("@").shift()) !== -1) {
652       log.verbose("already installed", "skipping %s %s", what, where)
653       return cb(null, [])
654     }
655
656     // check for a version installed higher in the tree.
657     // If installing from a shrinkwrap, it must match exactly.
658     if (context.family[what]) {
659       if (wrap && wrap[what].version === context.family[what]) {
660         log.verbose("shrinkwrap", "use existing", what)
661         return cb(null, [])
662       }
663     }
664
665     // if it's identical to its parent, then it's probably someone
666     // doing `npm install foo` inside of the foo project.  Print
667     // a warning, and skip it.
668     if (parent && parent.name === what && !npm.config.get("force")) {
669       log.warn("install", "Refusing to install %s as a dependency of itself"
670               , what)
671       return cb(null, [])
672     }
673
674     if (wrap) {
675       var name = what.split(/@/).shift()
676       if (wrap[name]) {
677         var wrapTarget = readWrap(wrap[name])
678         what = name + "@" + wrapTarget
679       } else {
680         log.verbose("shrinkwrap", "skipping %s (not in shrinkwrap)", what)
681       }
682     } else if (deps[what]) {
683       what = what + "@" + deps[what]
684     }
685
686     cache.add(what, function (er, data) {
687       if (er && parent && parent.optionalDependencies &&
688           parent.optionalDependencies.hasOwnProperty(what.split("@")[0])) {
689         log.warn("optional dep failed, continuing", what)
690         log.verbose("optional dep failed, continuing", [what, er])
691         return cb(null, [])
692       }
693
694       // if the target is a git repository, we always want to fetch it
695       var isGit = false
696         , maybeGit = what.split("@").pop()
697
698       if (maybeGit)
699         isGit = isGitUrl(url.parse(maybeGit))
700
701       if (!er &&
702           data &&
703           !context.explicit &&
704           context.family[data.name] === data.version &&
705           !npm.config.get("force") &&
706           !isGit) {
707         log.info("already installed", data.name + "@" + data.version)
708         return cb(null, [])
709       }
710
711       if (data && !data._from) data._from = what
712
713       return cb(er, data || [])
714     })
715   }
716 }
717
718 // we've already decided to install this.  if anything's in the way,
719 // then uninstall it first.
720 function installOne (target, where, context, cb) {
721   // the --link flag makes this a "link" command if it's at the
722   // the top level.
723   if (where === npm.prefix && npm.config.get("link")
724       && !npm.config.get("global")) {
725     return localLink(target, where, context, cb)
726   }
727   installOne_(target, where, context, function (er, installedWhat) {
728
729     // check if this one is optional to its parent.
730     if (er && context.parent && context.parent.optionalDependencies &&
731         context.parent.optionalDependencies.hasOwnProperty(target.name)) {
732       log.warn("optional dep failed, continuing", target._id)
733       log.verbose("optional dep failed, continuing", [target._id, er])
734       er = null
735     }
736
737     cb(er, installedWhat)
738   })
739
740 }
741
742 function localLink (target, where, context, cb) {
743   log.verbose("localLink", target._id)
744   var jsonFile = path.resolve( npm.globalDir, target.name
745                              , "package.json" )
746     , parent = context.parent
747
748   readJson(jsonFile, log.warn, function (er, data) {
749     if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
750     if (er || data._id === target._id) {
751       if (er) {
752         install( path.resolve(npm.globalDir, "..")
753                , target._id
754                , function (er) {
755           if (er) return cb(er, [])
756           thenLink()
757         })
758       } else thenLink()
759
760       function thenLink () {
761         npm.commands.link([target.name], function (er, d) {
762           log.silly("localLink", "back from link", [er, d])
763           cb(er, [resultList(target, where, parent && parent._id)])
764         })
765       }
766
767     } else {
768       log.verbose("localLink", "install locally (no link)", target._id)
769       installOne_(target, where, context, cb)
770     }
771   })
772 }
773
774 function resultList (target, where, parentId) {
775   var nm = path.resolve(where, "node_modules")
776     , targetFolder = path.resolve(nm, target.name)
777     , prettyWhere = where
778
779   if (!npm.config.get("global")) {
780     prettyWhere = path.relative(process.cwd(), where)
781   }
782
783   if (prettyWhere === ".") prettyWhere = null
784
785   if (!npm.config.get("global")) {
786     // print out the folder relative to where we are right now.
787     targetFolder = path.relative(process.cwd(), targetFolder)
788   }
789
790   return [ target._id
791          , targetFolder
792          , prettyWhere && parentId
793          , parentId && prettyWhere
794          , target._from ]
795 }
796
797 // name => install locations
798 var installOnesInProgress = Object.create(null)
799
800 function isIncompatibleInstallOneInProgress(target, where) {
801   return target.name in installOnesInProgress &&
802          installOnesInProgress[target.name].indexOf(where) !== -1
803 }
804
805 function installOne_ (target, where, context, cb) {
806   var nm = path.resolve(where, "node_modules")
807     , targetFolder = path.resolve(nm, target.name)
808     , prettyWhere = path.relative(process.cwd(), where)
809     , parent = context.parent
810
811   if (prettyWhere === ".") prettyWhere = null
812
813   if (isIncompatibleInstallOneInProgress(target, where)) {
814     var prettyTarget = path.relative(process.cwd(), targetFolder)
815
816     // just call back, with no error.  the error will be detected in the
817     // final check for peer-invalid dependencies
818     return cb()
819   }
820
821   if (!(target.name in installOnesInProgress)) {
822     installOnesInProgress[target.name] = []
823   }
824   installOnesInProgress[target.name].push(where)
825   var indexOfIOIP = installOnesInProgress[target.name].length - 1
826
827   chain
828     ( [ [checkEngine, target]
829       , [checkPlatform, target]
830       , [checkCycle, target, context.ancestors]
831       , [checkGit, targetFolder]
832       , [write, target, targetFolder, context] ]
833     , function (er, d) {
834         installOnesInProgress[target.name].splice(indexOfIOIP, 1)
835
836         if (er) return cb(er)
837
838         d.push(resultList(target, where, parent && parent._id))
839         cb(er, d)
840       }
841     )
842 }
843
844 function checkEngine (target, cb) {
845   var npmv = npm.version
846     , force = npm.config.get("force")
847     , nodev = force ? null : npm.config.get("node-version")
848     , strict = npm.config.get("engine-strict") || target.engineStrict
849     , eng = target.engines
850   if (!eng) return cb()
851   if (nodev && eng.node && !semver.satisfies(nodev, eng.node)
852       || eng.npm && !semver.satisfies(npmv, eng.npm)) {
853     if (strict) {
854       var er = new Error("Unsupported")
855       er.code = "ENOTSUP"
856       er.required = eng
857       er.pkgid = target._id
858       return cb(er)
859     } else {
860       log.warn( "engine", "%s: wanted: %j (current: %j)"
861               , target._id, eng, {node: nodev, npm: npm.version} )
862     }
863   }
864   return cb()
865 }
866
867 function checkPlatform (target, cb) {
868   var platform = process.platform
869     , arch = process.arch
870     , osOk = true
871     , cpuOk = true
872     , force = npm.config.get("force")
873
874   if (force) {
875     return cb()
876   }
877
878   if (target.os) {
879     osOk = checkList(platform, target.os)
880   }
881   if (target.cpu) {
882     cpuOk = checkList(arch, target.cpu)
883   }
884   if (!osOk || !cpuOk) {
885     var er = new Error("Unsupported")
886     er.code = "EBADPLATFORM"
887     er.os = target.os || ['any']
888     er.cpu = target.cpu || ['any']
889     er.pkgid = target._id
890     return cb(er)
891   }
892   return cb()
893 }
894
895 function checkList (value, list) {
896   var tmp
897     , match = false
898     , blc = 0
899   if (typeof list === "string") {
900     list = [list]
901   }
902   if (list.length === 1 && list[0] === "any") {
903     return true
904   }
905   for (var i = 0; i < list.length; ++i) {
906     tmp = list[i]
907     if (tmp[0] === '!') {
908       tmp = tmp.slice(1)
909       if (tmp === value) {
910         return false
911       }
912       ++blc
913     } else {
914       match = match || tmp === value
915     }
916   }
917   return match || blc === list.length
918 }
919
920 function checkCycle (target, ancestors, cb) {
921   // there are some very rare and pathological edge-cases where
922   // a cycle can cause npm to try to install a never-ending tree
923   // of stuff.
924   // Simplest:
925   //
926   // A -> B -> A' -> B' -> A -> B -> A' -> B' -> A -> ...
927   //
928   // Solution: Simply flat-out refuse to install any name@version
929   // that is already in the prototype tree of the ancestors object.
930   // A more correct, but more complex, solution would be to symlink
931   // the deeper thing into the new location.
932   // Will do that if anyone whines about this irl.
933   //
934   // Note: `npm install foo` inside of the `foo` package will abort
935   // earlier if `--force` is not set.  However, if it IS set, then
936   // we need to still fail here, but just skip the first level. Of
937   // course, it'll still fail eventually if it's a true cycle, and
938   // leave things in an undefined state, but that's what is to be
939   // expected when `--force` is used.  That is why getPrototypeOf
940   // is used *twice* here: to skip the first level of repetition.
941
942   var p = Object.getPrototypeOf(Object.getPrototypeOf(ancestors))
943     , name = target.name
944     , version = target.version
945   while (p && p !== Object.prototype && p[name] !== version) {
946     p = Object.getPrototypeOf(p)
947   }
948   if (p[name] !== version) return cb()
949
950   var er = new Error("Unresolvable cycle detected")
951   var tree = [target._id, JSON.parse(JSON.stringify(ancestors))]
952     , t = Object.getPrototypeOf(ancestors)
953   while (t && t !== Object.prototype) {
954     if (t === p) t.THIS_IS_P = true
955     tree.push(JSON.parse(JSON.stringify(t)))
956     t = Object.getPrototypeOf(t)
957   }
958   log.verbose("unresolvable dependency tree", tree)
959   er.pkgid = target._id
960   er.code = "ECYCLE"
961   return cb(er)
962 }
963
964 function checkGit (folder, cb) {
965   // if it's a git repo then don't touch it!
966   fs.lstat(folder, function (er, s) {
967     if (er || !s.isDirectory()) return cb()
968     else checkGit_(folder, cb)
969   })
970 }
971
972 function checkGit_ (folder, cb) {
973   fs.stat(path.resolve(folder, ".git"), function (er, s) {
974     if (!er && s.isDirectory()) {
975       var e = new Error("Appears to be a git repo or submodule.")
976       e.path = folder
977       e.code = "EISGIT"
978       return cb(e)
979     }
980     cb()
981   })
982 }
983
984 function write (target, targetFolder, context, cb_) {
985   var up = npm.config.get("unsafe-perm")
986     , user = up ? null : npm.config.get("user")
987     , group = up ? null : npm.config.get("group")
988     , family = context.family
989
990   function cb (er, data) {
991     // cache.unpack returns the data object, and all we care about
992     // is the list of installed packages from that last thing.
993     if (!er) return cb_(er, data)
994
995     if (false === npm.config.get("rollback")) return cb_(er)
996     npm.commands.unbuild([targetFolder], true, function (er2) {
997       if (er2) log.error("error rolling back", target._id, er2)
998       return cb_(er, data)
999     })
1000   }
1001
1002   var bundled = []
1003
1004   chain
1005     ( [ [ cache.unpack, target.name, target.version, targetFolder
1006         , null, null, user, group ]
1007       , [ fs, "writeFile"
1008         , path.resolve(targetFolder, "package.json")
1009         , JSON.stringify(target, null, 2) + "\n" ]
1010       , [ lifecycle, target, "preinstall", targetFolder ]
1011       , function (cb) {
1012           if (!target.bundleDependencies) return cb()
1013
1014           var bd = path.resolve(targetFolder, "node_modules")
1015           fs.readdir(bd, function (er, b) {
1016             // nothing bundled, maybe
1017             if (er) return cb()
1018             bundled = b || []
1019             cb()
1020           })
1021         } ]
1022
1023     // nest the chain so that we can throw away the results returned
1024     // up until this point, since we really don't care about it.
1025     , function X (er) {
1026       if (er) return cb(er)
1027
1028       // before continuing to installing dependencies, check for a shrinkwrap.
1029       var opt = { dev: npm.config.get("dev") }
1030       readDependencies(context, targetFolder, opt, function (er, data, wrap) {
1031         var deps = prepareForInstallMany(data, "dependencies", bundled, wrap,
1032             family)
1033         var depsTargetFolder = targetFolder
1034         var depsContext = { family: family
1035                          , ancestors: context.ancestors
1036                          , parent: target
1037                          , explicit: false
1038                          , wrap: wrap }
1039
1040         var peerDeps = prepareForInstallMany(data, "peerDependencies", bundled,
1041             wrap, family)
1042         var pdTargetFolder = path.resolve(targetFolder, "..", "..")
1043         var pdContext = context
1044
1045         var actions =
1046           [ [ installManyAndBuild, deps, depsTargetFolder, depsContext ] ]
1047
1048         if (peerDeps.length > 0) {
1049           actions.push(
1050             [ installMany, peerDeps, pdTargetFolder, pdContext ]
1051           )
1052         }
1053
1054         chain(actions, cb)
1055       })
1056     })
1057 }
1058
1059 function installManyAndBuild (deps, targetFolder, context, cb) {
1060   installMany(deps, targetFolder, context, function (er, d) {
1061     log.verbose("about to build", targetFolder)
1062     if (er) return cb(er)
1063     npm.commands.build( [targetFolder]
1064                       , npm.config.get("global")
1065                       , true
1066                       , function (er) { return cb(er, d) })
1067   })
1068 }
1069
1070 function prepareForInstallMany (packageData, depsKey, bundled, wrap, family) {
1071   var deps = Object.keys(packageData[depsKey] || {})
1072
1073   // don't install bundleDependencies, unless they're missing.
1074   if (packageData.bundleDependencies) {
1075     deps = deps.filter(function (d) {
1076       return packageData.bundleDependencies.indexOf(d) === -1 ||
1077              bundled.indexOf(d) === -1
1078     })
1079   }
1080
1081   return deps.filter(function (d) {
1082     // prefer to not install things that are satisfied by
1083     // something in the "family" list, unless we're installing
1084     // from a shrinkwrap.
1085     if (wrap) return wrap
1086     if (semver.validRange(family[d], true))
1087       return !semver.satisfies(family[d], packageData[depsKey][d], true)
1088     return true
1089   }).map(function (d) {
1090     var t = packageData[depsKey][d]
1091       , parsed = url.parse(t.replace(/^git\+/, "git"))
1092     t = d + "@" + t
1093     return t
1094   })
1095 }