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