UNPKG

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