UNPKG

14 kBJavaScriptView Raw
1;(function () {
2 // windows: running 'npm blah' in this folder will invoke WSH, not node.
3 /* globals WScript */
4 if (typeof WScript !== 'undefined') {
5 WScript.echo(
6 'npm does not work when run\n' +
7 'with the Windows Scripting Host\n\n' +
8 '"cd" to a different directory,\n' +
9 'or type "npm.cmd <args>",\n' +
10 'or type "node npm <args>".'
11 )
12 WScript.quit(1)
13 return
14 }
15
16 var unsupported = require('../lib/utils/unsupported.js')
17 unsupported.checkForBrokenNode()
18
19 var gfs = require('graceful-fs')
20 // Patch the global fs module here at the app level
21 var fs = gfs.gracefulify(require('fs'))
22
23 var EventEmitter = require('events').EventEmitter
24 var npm = module.exports = new EventEmitter()
25 var npmconf = require('./config/core.js')
26 var log = require('npmlog')
27 var inspect = require('util').inspect
28
29 // capture global logging
30 process.on('log', function (level) {
31 try {
32 return log[level].apply(log, [].slice.call(arguments, 1))
33 } catch (ex) {
34 log.verbose('attempt to log ' + inspect(arguments) + ' crashed: ' + ex.message)
35 }
36 })
37
38 var path = require('path')
39 var abbrev = require('abbrev')
40 var which = require('which')
41 var glob = require('glob')
42 var rimraf = require('rimraf')
43 var lazyProperty = require('lazy-property')
44 var parseJSON = require('./utils/parse-json.js')
45 var clientConfig = require('./config/reg-client.js')
46 var aliases = require('./config/cmd-list').aliases
47 var cmdList = require('./config/cmd-list').cmdList
48 var plumbing = require('./config/cmd-list').plumbing
49 var output = require('./utils/output.js')
50 var startMetrics = require('./utils/metrics.js').start
51 var perf = require('./utils/perf.js')
52
53 perf.emit('time', 'npm')
54 perf.on('timing', function (name, finished) {
55 log.timing(name, 'Completed in', finished + 'ms')
56 })
57
58 npm.config = {
59 loaded: false,
60 get: function () {
61 throw new Error('npm.load() required')
62 },
63 set: function () {
64 throw new Error('npm.load() required')
65 }
66 }
67
68 npm.commands = {}
69
70 // TUNING
71 npm.limit = {
72 fetch: 10,
73 action: 50
74 }
75 // ***
76
77 npm.lockfileVersion = 1
78
79 npm.rollbacks = []
80
81 try {
82 // startup, ok to do this synchronously
83 var j = parseJSON(fs.readFileSync(
84 path.join(__dirname, '../package.json')) + '')
85 npm.name = j.name
86 npm.version = j.version
87 } catch (ex) {
88 try {
89 log.info('error reading version', ex)
90 } catch (er) {}
91 npm.version = ex
92 }
93
94 var commandCache = {}
95 var aliasNames = Object.keys(aliases)
96
97 var littleGuys = [ 'isntall', 'verison' ]
98 var fullList = cmdList.concat(aliasNames).filter(function (c) {
99 return plumbing.indexOf(c) === -1
100 })
101 var abbrevs = abbrev(fullList)
102
103 // we have our reasons
104 fullList = npm.fullList = fullList.filter(function (c) {
105 return littleGuys.indexOf(c) === -1
106 })
107
108 var registryRefer
109 var registryLoaded
110
111 Object.keys(abbrevs).concat(plumbing).forEach(function addCommand (c) {
112 Object.defineProperty(npm.commands, c, { get: function () {
113 if (!loaded) {
114 throw new Error(
115 'Call npm.load(config, cb) before using this command.\n' +
116 'See the README.md or bin/npm-cli.js for example usage.'
117 )
118 }
119 var a = npm.deref(c)
120 if (c === 'la' || c === 'll') {
121 npm.config.set('long', true)
122 }
123
124 npm.command = c
125 if (commandCache[a]) return commandCache[a]
126
127 var cmd = require(path.join(__dirname, a + '.js'))
128
129 commandCache[a] = function () {
130 var args = Array.prototype.slice.call(arguments, 0)
131 if (typeof args[args.length - 1] !== 'function') {
132 args.push(defaultCb)
133 }
134 if (args.length === 1) args.unshift([])
135
136 // Options are prefixed by a hyphen-minus (-, \u2d).
137 // Other dash-type chars look similar but are invalid.
138 Array(args[0]).forEach(function (arg) {
139 if (/^[\u2010-\u2015\u2212\uFE58\uFE63\uFF0D]/.test(arg)) {
140 log.error('arg', 'Argument starts with non-ascii dash, this is probably invalid:', arg)
141 }
142 })
143
144 if (!registryRefer) {
145 registryRefer = [a].concat(args[0]).map(function (arg) {
146 // exclude anything that might be a URL, path, or private module
147 // Those things will always have a slash in them somewhere
148 if (arg && arg.match && arg.match(/\/|\\/)) {
149 return '[REDACTED]'
150 } else {
151 return arg
152 }
153 }).filter(function (arg) {
154 return arg && arg.match
155 }).join(' ')
156 if (registryLoaded) npm.registry.refer = registryRefer
157 }
158
159 cmd.apply(npm, args)
160 }
161
162 Object.keys(cmd).forEach(function (k) {
163 commandCache[a][k] = cmd[k]
164 })
165
166 return commandCache[a]
167 },
168 enumerable: fullList.indexOf(c) !== -1,
169 configurable: true })
170
171 // make css-case commands callable via camelCase as well
172 if (c.match(/-([a-z])/)) {
173 addCommand(c.replace(/-([a-z])/g, function (a, b) {
174 return b.toUpperCase()
175 }))
176 }
177 })
178
179 function defaultCb (er, data) {
180 log.disableProgress()
181 if (er) console.error(er.stack || er.message)
182 else output(data)
183 }
184
185 npm.deref = function (c) {
186 if (!c) return ''
187 if (c.match(/[A-Z]/)) {
188 c = c.replace(/([A-Z])/g, function (m) {
189 return '-' + m.toLowerCase()
190 })
191 }
192 if (plumbing.indexOf(c) !== -1) return c
193 var a = abbrevs[c]
194 while (aliases[a]) {
195 a = aliases[a]
196 }
197 return a
198 }
199
200 var loaded = false
201 var loading = false
202 var loadErr = null
203 var loadListeners = []
204
205 function loadCb (er) {
206 loadListeners.forEach(function (cb) {
207 process.nextTick(cb.bind(npm, er, npm))
208 })
209 loadListeners.length = 0
210 }
211
212 npm.load = function (cli, cb_) {
213 if (!cb_ && typeof cli === 'function') {
214 cb_ = cli
215 cli = {}
216 }
217 if (!cb_) cb_ = function () {}
218 if (!cli) cli = {}
219 loadListeners.push(cb_)
220 if (loaded || loadErr) return cb(loadErr)
221 if (loading) return
222 loading = true
223 var onload = true
224
225 function cb (er) {
226 if (loadErr) return
227 loadErr = er
228 if (er) return cb_(er)
229 log.notice('CANARY', 'npmc is experimental software. If you find an issue, please file it in the main npm repository, and call out that you were using npmc.')
230 if (npm.config.get('force')) {
231 log.warn('using --force', 'I sure hope you know what you are doing.')
232 }
233 npm.config.loaded = true
234 loaded = true
235 loadCb(loadErr = er)
236 onload = onload && npm.config.get('onload-script')
237 if (onload) {
238 try {
239 require(onload)
240 } catch (err) {
241 log.warn('onload-script', 'failed to require onload script', onload)
242 log.warn('onload-script', err)
243 }
244 onload = false
245 }
246 }
247
248 log.pause()
249
250 load(npm, cli, cb)
251 }
252
253 function load (npm, cli, cb) {
254 which(process.argv[0], function (er, node) {
255 if (!er && node.toUpperCase() !== process.execPath.toUpperCase()) {
256 log.verbose('node symlink', node)
257 process.execPath = node
258 process.installPrefix = path.resolve(node, '..', '..')
259 }
260
261 // look up configs
262 var builtin = path.resolve(__dirname, '..', 'npmrc')
263 npmconf.load(cli, builtin, function (er, config) {
264 if (er === config) er = null
265
266 npm.config = config
267 if (er) return cb(er)
268
269 // if the 'project' config is not a filename, and we're
270 // not in global mode, then that means that it collided
271 // with either the default or effective userland config
272 if (!config.get('global') &&
273 config.sources.project &&
274 config.sources.project.type !== 'ini') {
275 log.verbose(
276 'config',
277 'Skipping project config: %s. (matches userconfig)',
278 config.localPrefix + '/.npmrc'
279 )
280 }
281
282 // Include npm-version and node-version in user-agent
283 var ua = config.get('user-agent') || ''
284 ua = ua.replace(/\{node-version\}/gi, process.version)
285 ua = ua.replace(/\{npm-version\}/gi, npm.version)
286 ua = ua.replace(/\{platform\}/gi, process.platform)
287 ua = ua.replace(/\{arch\}/gi, process.arch)
288 config.set('user-agent', ua)
289
290 if (config.get('metrics-registry') == null) {
291 config.set('metrics-registry', config.get('registry'))
292 }
293
294 var color = config.get('color')
295
296 if (npm.config.get('timing') && npm.config.get('loglevel') === 'notice') {
297 log.level = 'timing'
298 } else {
299 log.level = config.get('loglevel')
300 }
301 log.heading = config.get('heading') || 'npm'
302 log.stream = config.get('logstream')
303
304 switch (color) {
305 case 'always':
306 npm.color = true
307 break
308 case false:
309 npm.color = false
310 break
311 default:
312 npm.color = process.stdout.isTTY && process.env['TERM'] !== 'dumb'
313 break
314 }
315 if (npm.color) {
316 log.enableColor()
317 } else {
318 log.disableColor()
319 }
320
321 if (config.get('unicode')) {
322 log.enableUnicode()
323 } else {
324 log.disableUnicode()
325 }
326
327 if (config.get('progress') && process.stderr.isTTY && process.env['TERM'] !== 'dumb') {
328 log.enableProgress()
329 } else {
330 log.disableProgress()
331 }
332
333 glob(path.resolve(npm.cache, '_logs', '*-debug.log'), function (er, files) {
334 if (er) return cb(er)
335
336 while (files.length >= npm.config.get('logs-max')) {
337 rimraf.sync(files[0])
338 files.splice(0, 1)
339 }
340 })
341
342 log.resume()
343
344 var umask = npm.config.get('umask')
345 npm.modes = {
346 exec: parseInt('0777', 8) & (~umask),
347 file: parseInt('0666', 8) & (~umask),
348 umask: umask
349 }
350
351 var gp = Object.getOwnPropertyDescriptor(config, 'globalPrefix')
352 Object.defineProperty(npm, 'globalPrefix', gp)
353
354 var lp = Object.getOwnPropertyDescriptor(config, 'localPrefix')
355 Object.defineProperty(npm, 'localPrefix', lp)
356
357 config.set('scope', scopeifyScope(config.get('scope')))
358 npm.projectScope = config.get('scope') ||
359 scopeifyScope(getProjectScope(npm.prefix))
360
361 // at this point the configs are all set.
362 // go ahead and spin up the registry client.
363 lazyProperty(npm, 'registry', function () {
364 registryLoaded = true
365 var RegClient = require('npm-registry-client')
366 var registry = new RegClient(clientConfig(npm, log, npm.config))
367 registry.version = npm.version
368 registry.refer = registryRefer
369 return registry
370 })
371
372 startMetrics()
373
374 return cb(null, npm)
375 })
376 })
377 }
378
379 Object.defineProperty(npm, 'prefix',
380 {
381 get: function () {
382 return npm.config.get('global') ? npm.globalPrefix : npm.localPrefix
383 },
384 set: function (r) {
385 var k = npm.config.get('global') ? 'globalPrefix' : 'localPrefix'
386 npm[k] = r
387 return r
388 },
389 enumerable: true
390 })
391
392 Object.defineProperty(npm, 'bin',
393 {
394 get: function () {
395 if (npm.config.get('global')) return npm.globalBin
396 return path.resolve(npm.root, '.bin')
397 },
398 enumerable: true
399 })
400
401 Object.defineProperty(npm, 'globalBin',
402 {
403 get: function () {
404 var b = npm.globalPrefix
405 if (process.platform !== 'win32') b = path.resolve(b, 'bin')
406 return b
407 }
408 })
409
410 Object.defineProperty(npm, 'dir',
411 {
412 get: function () {
413 if (npm.config.get('global')) return npm.globalDir
414 return path.resolve(npm.prefix, 'node_modules')
415 },
416 enumerable: true
417 })
418
419 Object.defineProperty(npm, 'globalDir',
420 {
421 get: function () {
422 return (process.platform !== 'win32')
423 ? path.resolve(npm.globalPrefix, 'lib', 'node_modules')
424 : path.resolve(npm.globalPrefix, 'node_modules')
425 },
426 enumerable: true
427 })
428
429 Object.defineProperty(npm, 'root',
430 { get: function () { return npm.dir } })
431
432 Object.defineProperty(npm, 'cache',
433 { get: function () { return npm.config.get('cache') },
434 set: function (r) { return npm.config.set('cache', r) },
435 enumerable: true
436 })
437
438 var tmpFolder
439 var rand = require('crypto').randomBytes(4).toString('hex')
440 Object.defineProperty(npm, 'tmp',
441 {
442 get: function () {
443 if (!tmpFolder) tmpFolder = 'npm-' + process.pid + '-' + rand
444 return path.resolve(npm.config.get('tmp'), tmpFolder)
445 },
446 enumerable: true
447 })
448
449 // the better to repl you with
450 Object.getOwnPropertyNames(npm.commands).forEach(function (n) {
451 if (npm.hasOwnProperty(n) || n === 'config') return
452
453 Object.defineProperty(npm, n, { get: function () {
454 return function () {
455 var args = Array.prototype.slice.call(arguments, 0)
456 var cb = defaultCb
457
458 if (args.length === 1 && Array.isArray(args[0])) {
459 args = args[0]
460 }
461
462 if (typeof args[args.length - 1] === 'function') {
463 cb = args.pop()
464 }
465 npm.commands[n](args, cb)
466 }
467 },
468 enumerable: false,
469 configurable: true })
470 })
471
472 if (require.main === module) {
473 require('../bin/npm-cli.js')
474 }
475
476 function scopeifyScope (scope) {
477 return (!scope || scope[0] === '@') ? scope : ('@' + scope)
478 }
479
480 function getProjectScope (prefix) {
481 try {
482 var pkg = JSON.parse(fs.readFileSync(path.join(prefix, 'package.json')))
483 if (typeof pkg.name !== 'string') return ''
484 var sep = pkg.name.indexOf('/')
485 if (sep === -1) return ''
486 return pkg.name.slice(0, sep)
487 } catch (ex) {
488 return ''
489 }
490 }
491})()