config.js 7.4 KB

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