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