• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* eslint-disable standard/no-callback-literal */
2module.exports = config
3
4var log = require('npmlog')
5var npm = require('./npm.js')
6var npmconf = require('./config/core.js')
7var fs = require('graceful-fs')
8var writeFileAtomic = require('write-file-atomic')
9var types = npmconf.defs.types
10var ini = require('ini')
11var editor = require('editor')
12var os = require('os')
13var path = require('path')
14var mkdirp = require('gentle-fs').mkdir
15var umask = require('./utils/umask')
16var usage = require('./utils/usage')
17var output = require('./utils/output')
18var noProgressTillDone = require('./utils/no-progress-while-running').tillDone
19
20config.usage = usage(
21  'config',
22  'npm config set <key> <value>' +
23  '\nnpm config get [<key>]' +
24  '\nnpm config delete <key>' +
25  '\nnpm config list [--json]' +
26  '\nnpm config edit' +
27  '\nnpm set <key> <value>' +
28  '\nnpm get [<key>]'
29)
30config.completion = function (opts, cb) {
31  var argv = opts.conf.argv.remain
32  if (argv[1] !== 'config') argv.unshift('config')
33  if (argv.length === 2) {
34    var cmds = ['get', 'set', 'delete', 'ls', 'rm', 'edit']
35    if (opts.partialWord !== 'l') cmds.push('list')
36    return cb(null, cmds)
37  }
38
39  var action = argv[2]
40  switch (action) {
41    case 'set':
42      // todo: complete with valid values, if possible.
43      if (argv.length > 3) return cb(null, [])
44      // fallthrough
45      /* eslint no-fallthrough:0 */
46    case 'get':
47    case 'delete':
48    case 'rm':
49      return cb(null, Object.keys(types))
50    case 'edit':
51    case 'list':
52    case 'ls':
53      return cb(null, [])
54    default:
55      return cb(null, [])
56  }
57}
58
59// npm config set key value
60// npm config get key
61// npm config list
62function config (args, cb) {
63  var action = args.shift()
64  switch (action) {
65    case 'set':
66      return set(args[0], args[1], cb)
67    case 'get':
68      return get(args[0], cb)
69    case 'delete':
70    case 'rm':
71    case 'del':
72      return del(args[0], cb)
73    case 'list':
74    case 'ls':
75      return npm.config.get('json') ? listJson(cb) : list(cb)
76    case 'edit':
77      return edit(cb)
78    default:
79      return unknown(action, cb)
80  }
81}
82
83function edit (cb) {
84  var e = npm.config.get('editor')
85  var which = npm.config.get('global') ? 'global' : 'user'
86  var f = npm.config.get(which + 'config')
87  if (!e) return cb(new Error('No EDITOR config or environ set.'))
88  npm.config.save(which, function (er) {
89    if (er) return cb(er)
90    fs.readFile(f, 'utf8', function (er, data) {
91      if (er) data = ''
92      data = [
93        ';;;;',
94        '; npm ' + (npm.config.get('global')
95          ? 'globalconfig' : 'userconfig') + ' file',
96        '; this is a simple ini-formatted file',
97        '; lines that start with semi-colons are comments.',
98        '; read `npm help config` for help on the various options',
99        ';;;;',
100        '',
101        data
102      ].concat([
103        ';;;;',
104        '; all options with default values',
105        ';;;;'
106      ]).concat(Object.keys(npmconf.defaults).reduce(function (arr, key) {
107        var obj = {}
108        obj[key] = npmconf.defaults[key]
109        if (key === 'logstream') return arr
110        return arr.concat(
111          ini.stringify(obj)
112            .replace(/\n$/m, '')
113            .replace(/^/g, '; ')
114            .replace(/\n/g, '\n; ')
115            .split('\n'))
116      }, []))
117        .concat([''])
118        .join(os.EOL)
119      mkdirp(path.dirname(f), function (er) {
120        if (er) return cb(er)
121        writeFileAtomic(
122          f,
123          data,
124          function (er) {
125            if (er) return cb(er)
126            editor(f, { editor: e }, noProgressTillDone(cb))
127          }
128        )
129      })
130    })
131  })
132}
133
134function del (key, cb) {
135  if (!key) return cb(new Error('no key provided'))
136  var where = npm.config.get('global') ? 'global' : 'user'
137  npm.config.del(key, where)
138  npm.config.save(where, cb)
139}
140
141function set (key, val, cb) {
142  if (key === undefined) {
143    return unknown('', cb)
144  }
145  if (val === undefined) {
146    if (key.indexOf('=') !== -1) {
147      var k = key.split('=')
148      key = k.shift()
149      val = k.join('=')
150    } else {
151      val = ''
152    }
153  }
154  key = key.trim()
155  val = val.trim()
156  log.info('config', 'set %j %j', key, val)
157  var where = npm.config.get('global') ? 'global' : 'user'
158  if (key.match(/umask/)) val = umask.fromString(val)
159  npm.config.set(key, val, where)
160  npm.config.save(where, cb)
161}
162
163function get (key, cb) {
164  if (!key) return list(cb)
165  if (!publicVar(key)) {
166    return cb(new Error('---sekretz---'))
167  }
168  var val = npm.config.get(key)
169  if (key.match(/umask/)) val = umask.toString(val)
170  output(val)
171  cb()
172}
173
174function sort (a, b) {
175  return a > b ? 1 : -1
176}
177
178function publicVar (k) {
179  return !(k.charAt(0) === '_' || k.indexOf(':_') !== -1)
180}
181
182function getKeys (data) {
183  return Object.keys(data).filter(publicVar).sort(sort)
184}
185
186function listJson (cb) {
187  const publicConf = npm.config.keys.reduce((publicConf, k) => {
188    var value = npm.config.get(k)
189
190    if (publicVar(k) &&
191        // argv is not really config, it's command config
192        k !== 'argv' &&
193        // logstream is a Stream, and would otherwise produce circular refs
194        k !== 'logstream') publicConf[k] = value
195
196    return publicConf
197  }, {})
198
199  output(JSON.stringify(publicConf, null, 2))
200  return cb()
201}
202
203function listFromSource (title, conf, long) {
204  var confKeys = getKeys(conf)
205  var msg = ''
206
207  if (confKeys.length) {
208    msg += '; ' + title + '\n'
209    confKeys.forEach(function (k) {
210      var val = JSON.stringify(conf[k])
211      if (conf[k] !== npm.config.get(k)) {
212        if (!long) return
213        msg += '; ' + k + ' = ' + val + ' (overridden)\n'
214      } else msg += k + ' = ' + val + '\n'
215    })
216    msg += '\n'
217  }
218
219  return msg
220}
221
222function list (cb) {
223  var msg = ''
224  var long = npm.config.get('long')
225
226  var cli = npm.config.sources.cli.data
227  var cliKeys = getKeys(cli)
228  if (cliKeys.length) {
229    msg += '; cli configs\n'
230    cliKeys.forEach(function (k) {
231      if (cli[k] && typeof cli[k] === 'object') return
232      if (k === 'argv') return
233      msg += k + ' = ' + JSON.stringify(cli[k]) + '\n'
234    })
235    msg += '\n'
236  }
237
238  // env configs
239  msg += listFromSource('environment configs', npm.config.sources.env.data, long)
240
241  // project config file
242  var project = npm.config.sources.project
243  msg += listFromSource('project config ' + project.path, project.data, long)
244
245  // user config file
246  msg += listFromSource('userconfig ' + npm.config.get('userconfig'), npm.config.sources.user.data, long)
247
248  // global config file
249  msg += listFromSource('globalconfig ' + npm.config.get('globalconfig'), npm.config.sources.global.data, long)
250
251  // builtin config file
252  var builtin = npm.config.sources.builtin || {}
253  if (builtin && builtin.data) {
254    msg += listFromSource('builtin config ' + builtin.path, builtin.data, long)
255  }
256
257  // only show defaults if --long
258  if (!long) {
259    msg += '; node bin location = ' + process.execPath + '\n' +
260           '; cwd = ' + process.cwd() + '\n' +
261           '; HOME = ' + process.env.HOME + '\n' +
262           '; "npm config ls -l" to show all defaults.\n'
263
264    output(msg)
265    return cb()
266  }
267
268  var defaults = npmconf.defaults
269  var defKeys = getKeys(defaults)
270  msg += '; default values\n'
271  defKeys.forEach(function (k) {
272    if (defaults[k] && typeof defaults[k] === 'object') return
273    var val = JSON.stringify(defaults[k])
274    if (defaults[k] !== npm.config.get(k)) {
275      msg += '; ' + k + ' = ' + val + ' (overridden)\n'
276    } else msg += k + ' = ' + val + '\n'
277  })
278  msg += '\n'
279
280  output(msg)
281  return cb()
282}
283
284function unknown (action, cb) {
285  cb('Usage:\n' + config.usage)
286}
287