UNPKG

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