npm: Upgrade to 1.3.19
[platform/upstream/nodejs.git] / deps / npm / lib / npm.js
1 ;(function(){
2 // windows: running "npm blah" in this folder will invoke WSH, not node.
3 if (typeof WScript !== "undefined") {
4   WScript.echo("npm does not work when run\n"
5               +"with the Windows Scripting Host\n\n"
6               +"'cd' to a different directory,\n"
7               +"or type 'npm.cmd <args>',\n"
8               +"or type 'node npm <args>'.")
9   WScript.quit(1)
10   return
11 }
12
13
14 // monkey-patch support for 0.6 child processes
15 require('child-process-close')
16
17 var EventEmitter = require("events").EventEmitter
18   , npm = module.exports = new EventEmitter
19   , config = require("./config.js")
20   , npmconf = require("npmconf")
21   , log = require("npmlog")
22   , fs = require("graceful-fs")
23   , path = require("path")
24   , abbrev = require("abbrev")
25   , which = require("which")
26   , semver = require("semver")
27   , findPrefix = require("./utils/find-prefix.js")
28   , getUid = require("uid-number")
29   , mkdirp = require("mkdirp")
30   , slide = require("slide")
31   , chain = slide.chain
32   , RegClient = require("npm-registry-client")
33
34 npm.config = {loaded: false}
35
36 // /usr/local is often a read-only fs, which is not
37 // well handled by node or mkdirp.  Just double-check
38 // in the case of errors when making the prefix dirs.
39 function mkdir (p, cb) {
40   mkdirp(p, function (er, made) {
41     // it could be that we couldn't create it, because it
42     // already exists, and is on a read-only fs.
43     if (er) {
44       return fs.stat(p, function (er2, st) {
45         if (er2 || !st.isDirectory()) return cb(er)
46         return cb(null, made)
47       })
48     }
49     return cb(er, made)
50   })
51 }
52
53 npm.commands = {}
54
55 try {
56   var pv = process.version.replace(/^v/, '')
57   // startup, ok to do this synchronously
58   var j = JSON.parse(fs.readFileSync(
59     path.join(__dirname, "../package.json"))+"")
60   npm.version = j.version
61   npm.nodeVersionRequired = j.engines.node
62   if (!semver.satisfies(pv, j.engines.node)) {
63     log.warn("unsupported version", [""
64             ,"npm requires node version: "+j.engines.node
65             ,"And you have: "+pv
66             ,"which is not satisfactory."
67             ,""
68             ,"Bad things will likely happen.  You have been warned."
69             ,""].join("\n"))
70   }
71 } catch (ex) {
72   try {
73     log.info("error reading version", ex)
74   } catch (er) {}
75   npm.version = ex
76 }
77
78 var commandCache = {}
79   // short names for common things
80   , aliases = { "rm" : "uninstall"
81               , "r" : "uninstall"
82               , "un" : "uninstall"
83               , "unlink" : "uninstall"
84               , "remove" : "uninstall"
85               , "rb" : "rebuild"
86               , "list" : "ls"
87               , "la" : "ls"
88               , "ll" : "ls"
89               , "ln" : "link"
90               , "i" : "install"
91               , "isntall" : "install"
92               , "up" : "update"
93               , "c" : "config"
94               , "info" : "view"
95               , "show" : "view"
96               , "find" : "search"
97               , "s" : "search"
98               , "se" : "search"
99               , "author" : "owner"
100               , "home" : "docs"
101               , "issues": "bugs"
102               , "unstar": "star" // same function
103               , "apihelp" : "help"
104               , "login": "adduser"
105               , "add-user": "adduser"
106               , "tst": "test"
107               , "find-dupes": "dedupe"
108               , "ddp": "dedupe"
109               , "v": "view"
110               }
111
112   , aliasNames = Object.keys(aliases)
113   // these are filenames in .
114   , cmdList = [ "install"
115               , "uninstall"
116               , "cache"
117               , "config"
118               , "set"
119               , "get"
120               , "update"
121               , "outdated"
122               , "prune"
123               , "submodule"
124               , "pack"
125               , "dedupe"
126
127               , "rebuild"
128               , "link"
129
130               , "publish"
131               , "star"
132               , "stars"
133               , "tag"
134               , "adduser"
135               , "unpublish"
136               , "owner"
137               , "deprecate"
138               , "shrinkwrap"
139
140               , "help"
141               , "help-search"
142               , "ls"
143               , "search"
144               , "view"
145               , "init"
146               , "version"
147               , "edit"
148               , "explore"
149               , "docs"
150               , "repo"
151               , "bugs"
152               , "faq"
153               , "root"
154               , "prefix"
155               , "bin"
156               , "whoami"
157
158               , "test"
159               , "stop"
160               , "start"
161               , "restart"
162               , "run-script"
163               , "completion"
164               ]
165   , plumbing = [ "build"
166                , "unbuild"
167                , "xmas"
168                , "substack"
169                , "visnup"
170                ]
171   , fullList = npm.fullList = cmdList.concat(aliasNames).filter(function (c) {
172       return plumbing.indexOf(c) === -1
173     })
174   , abbrevs = abbrev(fullList)
175
176 Object.keys(abbrevs).concat(plumbing).forEach(function addCommand (c) {
177   Object.defineProperty(npm.commands, c, { get : function () {
178     if (!loaded) throw new Error(
179       "Call npm.load(config, cb) before using this command.\n"+
180       "See the README.md or cli.js for example usage.")
181     var a = npm.deref(c)
182     if (c === "la" || c === "ll") {
183       npm.config.set("long", true)
184     }
185     npm.command = c
186     if (commandCache[a]) return commandCache[a]
187     var cmd = require(__dirname+"/"+a+".js")
188     commandCache[a] = function () {
189       var args = Array.prototype.slice.call(arguments, 0)
190       if (typeof args[args.length - 1] !== "function") {
191         args.push(defaultCb)
192       }
193       if (args.length === 1) args.unshift([])
194       cmd.apply(npm, args)
195     }
196     Object.keys(cmd).forEach(function (k) {
197       commandCache[a][k] = cmd[k]
198     })
199     return commandCache[a]
200   }, enumerable: fullList.indexOf(c) !== -1 })
201
202   // make css-case commands callable via camelCase as well
203   if (c.match(/\-([a-z])/)) {
204     addCommand(c.replace(/\-([a-z])/g, function (a, b) {
205       return b.toUpperCase()
206     }))
207   }
208 })
209
210 function defaultCb (er, data) {
211   if (er) console.error(er.stack || er.message)
212   else console.log(data)
213 }
214
215 npm.deref = function (c) {
216   if (!c) return ""
217   if (c.match(/[A-Z]/)) c = c.replace(/([A-Z])/g, function (m) {
218     return "-" + m.toLowerCase()
219   })
220   if (plumbing.indexOf(c) !== -1) return c
221   var a = abbrevs[c]
222   if (aliases[a]) a = aliases[a]
223   return a
224 }
225
226 var loaded = false
227   , loading = false
228   , loadErr = null
229   , loadListeners = []
230
231 function loadCb (er) {
232   loadListeners.forEach(function (cb) {
233     process.nextTick(cb.bind(npm, er, npm))
234   })
235   loadListeners.length = 0
236 }
237
238 npm.load = function (cli, cb_) {
239   if (!cb_ && typeof cli === "function") cb_ = cli , cli = {}
240   if (!cb_) cb_ = function () {}
241   if (!cli) cli = {}
242   loadListeners.push(cb_)
243   if (loaded || loadErr) return cb(loadErr)
244   if (loading) return
245   loading = true
246   var onload = true
247
248   function cb (er) {
249     if (loadErr) return
250     if (npm.config.get("force")) {
251       log.warn("using --force", "I sure hope you know what you are doing.")
252     }
253     npm.config.loaded = true
254     loaded = true
255     loadCb(loadErr = er)
256     if (onload = onload && npm.config.get("onload-script")) {
257       require(onload)
258       onload = false
259     }
260   }
261
262   log.pause()
263
264   load(npm, cli, cb)
265 }
266
267 function load (npm, cli, cb) {
268   which(process.argv[0], function (er, node) {
269     if (!er && node.toUpperCase() !== process.execPath.toUpperCase()) {
270       log.verbose("node symlink", node)
271       process.execPath = node
272       process.installPrefix = path.resolve(node, "..", "..")
273     }
274
275     // look up configs
276     //console.error("about to look up configs")
277
278     var builtin = path.resolve(__dirname, "..", "npmrc")
279     npmconf.load(cli, builtin, function (er, config) {
280       if (er === config) er = null
281
282       npm.config = config
283
284       var color = config.get("color")
285
286       log.level = config.get("loglevel")
287       log.heading = config.get("heading") || "npm"
288       log.stream = config.get("logstream")
289       switch (color) {
290         case "always": log.enableColor(); break
291         case false: log.disableColor(); break
292       }
293       log.resume()
294
295       if (er) return cb(er)
296
297       // see if we need to color normal output
298       switch (color) {
299         case "always":
300           npm.color = true
301           break
302         case false:
303           npm.color = false
304           break
305         default:
306           var tty = require("tty")
307           if (process.stdout.isTTY) npm.color = true
308           else if (!tty.isatty) npm.color = true
309           else if (tty.isatty(1)) npm.color = true
310           else npm.color = false
311           break
312       }
313
314       // at this point the configs are all set.
315       // go ahead and spin up the registry client.
316       var token = config.get("_token")
317       if (typeof token === "string") {
318         try {
319           token = JSON.parse(token)
320           config.set("_token", token, "user")
321           config.save("user")
322         } catch (e) { token = null }
323       }
324
325       npm.registry = new RegClient(npm.config)
326
327       // save the token cookie in the config file
328       if (npm.registry.couchLogin) {
329         npm.registry.couchLogin.tokenSet = function (tok) {
330           npm.config.set("_token", tok, "user")
331           // ignore save error.  best effort.
332           npm.config.save("user")
333         }
334       }
335
336       var umask = npm.config.get("umask")
337       npm.modes = { exec: 0777 & (~umask)
338                   , file: 0666 & (~umask)
339                   , umask: umask }
340
341       chain([ [ loadPrefix, npm, cli ]
342             , [ setUser, config, config.root ]
343             , [ loadUid, npm ]
344             ], cb)
345     })
346   })
347 }
348
349 function loadPrefix (npm, config, cb) {
350   // try to guess at a good node_modules location.
351   var p
352     , gp
353   if (!Object.prototype.hasOwnProperty.call(config, "prefix")) {
354     p = process.cwd()
355   } else {
356     p = npm.config.get("prefix")
357   }
358   gp = npm.config.get("prefix")
359
360   findPrefix(p, function (er, p) {
361     Object.defineProperty(npm, "localPrefix",
362       { get : function () { return p }
363       , set : function (r) { return p = r }
364       , enumerable : true
365       })
366     // the prefix MUST exist, or else nothing works.
367     if (!npm.config.get("global")) {
368       mkdir(p, next)
369     } else {
370       next(er)
371     }
372   })
373
374   gp = path.resolve(gp)
375   Object.defineProperty(npm, "globalPrefix",
376     { get : function () { return gp }
377     , set : function (r) { return gp = r }
378     , enumerable : true
379     })
380   // the prefix MUST exist, or else nothing works.
381   mkdir(gp, next)
382
383
384   var i = 2
385     , errState = null
386   function next (er) {
387     if (errState) return
388     if (er) return cb(errState = er)
389     if (--i === 0) return cb()
390   }
391 }
392
393
394 function loadUid (npm, cb) {
395   // if we're not in unsafe-perm mode, then figure out who
396   // to run stuff as.  Do this first, to support `npm update npm -g`
397   if (!npm.config.get("unsafe-perm")) {
398     getUid(npm.config.get("user"), npm.config.get("group"), cb)
399   } else {
400     process.nextTick(cb)
401   }
402 }
403
404 function setUser (cl, dc, cb) {
405   // If global, leave it as-is.
406   // If not global, then set the user to the owner of the prefix folder.
407   // Just set the default, so it can be overridden.
408   if (cl.get("global")) return cb()
409   if (process.env.SUDO_UID) {
410     dc.user = +(process.env.SUDO_UID)
411     return cb()
412   }
413
414   var prefix = path.resolve(cl.get("prefix"))
415   mkdir(prefix, function (er) {
416     if (er) {
417       log.error("could not create prefix dir", prefix)
418       return cb(er)
419     }
420     fs.stat(prefix, function (er, st) {
421       dc.user = st && st.uid
422       return cb(er)
423     })
424   })
425 }
426
427
428 Object.defineProperty(npm, "prefix",
429   { get : function () {
430       return npm.config.get("global") ? npm.globalPrefix : npm.localPrefix
431     }
432   , set : function (r) {
433       var k = npm.config.get("global") ? "globalPrefix" : "localPrefix"
434       return npm[k] = r
435     }
436   , enumerable : true
437   })
438
439 Object.defineProperty(npm, "bin",
440   { get : function () {
441       if (npm.config.get("global")) return npm.globalBin
442       return path.resolve(npm.root, ".bin")
443     }
444   , enumerable : true
445   })
446
447 Object.defineProperty(npm, "globalBin",
448   { get : function () {
449       var b = npm.globalPrefix
450       if (process.platform !== "win32") b = path.resolve(b, "bin")
451       return b
452     }
453   })
454
455 Object.defineProperty(npm, "dir",
456   { get : function () {
457       if (npm.config.get("global")) return npm.globalDir
458       return path.resolve(npm.prefix, "node_modules")
459     }
460   , enumerable : true
461   })
462
463 Object.defineProperty(npm, "globalDir",
464   { get : function () {
465       return (process.platform !== "win32")
466            ? path.resolve(npm.globalPrefix, "lib", "node_modules")
467            : path.resolve(npm.globalPrefix, "node_modules")
468     }
469   , enumerable : true
470   })
471
472 Object.defineProperty(npm, "root",
473   { get : function () { return npm.dir } })
474
475 Object.defineProperty(npm, "cache",
476   { get : function () { return npm.config.get("cache") }
477   , set : function (r) { return npm.config.set("cache", r) }
478   , enumerable : true
479   })
480
481 var tmpFolder
482 var crypto = require("crypto")
483 var rand = crypto.randomBytes(6)
484                  .toString("base64")
485                  .replace(/\//g, '_')
486                  .replace(/\+/, '-')
487 Object.defineProperty(npm, "tmp",
488   { get : function () {
489       if (!tmpFolder) tmpFolder = "npm-" + process.pid + "-" + rand
490       return path.resolve(npm.config.get("tmp"), tmpFolder)
491     }
492   , enumerable : true
493   })
494
495 // the better to repl you with
496 Object.getOwnPropertyNames(npm.commands).forEach(function (n) {
497   if (npm.hasOwnProperty(n) || n === "config") return
498
499   Object.defineProperty(npm, n, { get: function () {
500     return function () {
501       var args = Array.prototype.slice.call(arguments, 0)
502         , cb = defaultCb
503
504       if (args.length === 1 && Array.isArray(args[0])) {
505         args = args[0]
506       }
507
508       if (typeof args[args.length - 1] === "function") {
509         cb = args.pop()
510       }
511
512       npm.commands[n](args, cb)
513     }
514   }, enumerable: false, configurable: true })
515 })
516
517 if (require.main === module) {
518   require("../bin/npm-cli.js")
519 }
520 })()