diff --git a/docs/content/commands/npm-config.md b/docs/content/commands/npm-config.md index ebff540eedfb9..51caa5a61b607 100644 --- a/docs/content/commands/npm-config.md +++ b/docs/content/commands/npm-config.md @@ -7,15 +7,15 @@ description: Manage the npm configuration files ### Synopsis ```bash -npm config set [-g|--global] -npm config get -npm config delete -npm config list [-l] [--json] +npm config set = [= ...] +npm config get [ [ ...]] +npm config delete [ ...] +npm config list [--json] npm config edit -npm get -npm set [-g|--global] +npm set = [= ...] +npm get [ [ ...]] -aliases: c +alias: c ``` ### Description @@ -39,20 +39,31 @@ Config supports the following sub-commands: #### set ```bash -npm config set key value +npm config set key=value [key=value...] +npm set key=value [key=value...] ``` -Sets the config key to the value. +Sets each of the config keys to the value provided. -If value is omitted, then it sets it to "true". +If value is omitted, then it sets it to an empty string. + +Note: for backwards compatibility, `npm config set key value` is supported +as an alias for `npm config set key=value`. #### get ```bash -npm config get key +npm config get [key ...] +npm get [key ...] ``` -Echo the config value to stdout. +Echo the config value(s) to stdout. + +If multiple keys are provided, then the values will be prefixed with the +key names. + +If no keys are provided, then this command behaves the same as `npm config +list`. #### list @@ -66,10 +77,10 @@ to show the settings in json format. #### delete ```bash -npm config delete key +npm config delete key [key ...] ``` -Deletes the key from all configuration files. +Deletes the specified keys from all configuration files. #### edit diff --git a/lib/config.js b/lib/config.js index 561c31e0b3ec5..b32cf3359d33b 100644 --- a/lib/config.js +++ b/lib/config.js @@ -15,13 +15,13 @@ const ini = require('ini') const usage = usageUtil( 'config', - 'npm config set ' + - '\nnpm config get []' + - '\nnpm config delete ' + + 'npm config set = [= ...]' + + '\nnpm config get [ [ ...]]' + + '\nnpm config delete [ ...]' + '\nnpm config list [--json]' + '\nnpm config edit' + - '\nnpm set ' + - '\nnpm get []' + '\nnpm set = [= ...]' + + '\nnpm get [ [ ...]]' ) const cmd = (args, cb) => config(args).then(() => cb()).catch(cb) @@ -63,20 +63,20 @@ const completion = (opts, cb) => { const UsageError = () => Object.assign(new Error(usage), { code: 'EUSAGE' }) -const config = async ([action, key, val]) => { +const config = async ([action, ...args]) => { npm.log.disableProgress() try { switch (action) { case 'set': - await set(key, val) + await set(args) break case 'get': - await get(key) + await get(args) break case 'delete': case 'rm': case 'del': - await del(key) + await del(args) break case 'list': case 'ls': @@ -93,46 +93,58 @@ const config = async ([action, key, val]) => { } } -const set = async (key, val) => { - if (key === undefined) - throw UsageError() - - if (val === undefined) { - if (key.indexOf('=') !== -1) { - const k = key.split('=') - key = k.shift() - val = k.join('=') - } else - val = '' +// take an array of `[key, value, k2=v2, k3, v3, ...]` and turn into +// { key: value, k2: v2, k3: v3 } +const keyValues = args => { + const kv = {} + for (let i = 0; i < args.length; i++) { + const arg = args[i].split('=') + const key = arg.shift() + const val = arg.length ? arg.join('=') + : i < args.length - 1 ? args[++i] + : '' + kv[key.trim()] = val.trim() } + return kv +} + +const set = async (args) => { + if (!args.length) + throw UsageError() - key = key.trim() - val = val.trim() - npm.log.info('config', 'set %j %j', key, val) const where = npm.flatOptions.global ? 'global' : 'user' - npm.config.set(key, val, where) - if (!npm.config.validate(where)) - npm.log.warn('config', 'omitting invalid config values') + for (const [key, val] of Object.entries(keyValues(args))) { + npm.log.info('config', 'set %j %j', key, val) + npm.config.set(key, val || '', where) + if (!npm.config.validate(where)) + npm.log.warn('config', 'omitting invalid config values') + } await npm.config.save(where) } -const get = async key => { - if (!key) +const get = async keys => { + if (!keys.length) return list() - if (!publicVar(key)) - throw `The ${key} option is protected, and cannot be retrieved in this way` + const out = [] + for (const key of keys) { + if (!publicVar(key)) + throw `The ${key} option is protected, and cannot be retrieved in this way` - output(npm.config.get(key)) + const pref = keys.length > 1 ? `${key}=` : '' + out.push(pref + npm.config.get(key)) + } + output(out.join('\n')) } -const del = async key => { - if (!key) +const del = async keys => { + if (!keys.length) throw UsageError() const where = npm.flatOptions.global ? 'global' : 'user' - npm.config.delete(key, where) + for (const key of keys) + npm.config.delete(key, where) await npm.config.save(where) } diff --git a/lib/get.js b/lib/get.js index bccdc72121f4b..ab2141e35721a 100644 --- a/lib/get.js +++ b/lib/get.js @@ -3,7 +3,7 @@ const usageUtil = require('./utils/usage.js') const usage = usageUtil( 'get', - 'npm get (See `npm config`)' + 'npm get [ ...] (See `npm config`)' ) const completion = npm.commands.config.completion diff --git a/lib/set.js b/lib/set.js index 62860e53ffeb1..fd607629328ff 100644 --- a/lib/set.js +++ b/lib/set.js @@ -1,7 +1,7 @@ module.exports = set -set.usage = 'npm set (See `npm config`)' +set.usage = 'npm set = [= ...] (See `npm config`)' var npm = require('./npm.js') diff --git a/test/lib/config.js b/test/lib/config.js index 74cd530c68270..1ccd823acb94e 100644 --- a/test/lib/config.js +++ b/test/lib/config.js @@ -212,6 +212,33 @@ t.test('config delete key', t => { }) }) +t.test('config delete multiple key', t => { + t.plan(6) + + const expect = [ + 'foo', + 'bar', + ] + + npm.config.delete = (key, where) => { + t.equal(key, expect.shift(), 'should delete expected keyword') + t.equal(where, 'user', 'should delete key from user config by default') + } + + npm.config.save = where => { + t.equal(where, 'user', 'should save user config post-delete') + } + + config(['delete', 'foo', 'bar'], (err) => { + t.ifError(err, 'npm config delete keys') + }) + + t.teardown(() => { + delete npm.config.delete + delete npm.config.save + }) +}) + t.test('config delete key --global', t => { t.plan(4) @@ -293,12 +320,43 @@ t.test('config set key=val', t => { }) }) +t.test('config set multiple keys', t => { + t.plan(11) + + const expect = [ + ['foo', 'bar'], + ['bar', 'baz'], + ['asdf', ''], + ] + const args = ['foo', 'bar', 'bar=baz', 'asdf'] + + npm.config.set = (key, val, where) => { + const [expectKey, expectVal] = expect.shift() + t.equal(key, expectKey, 'should set expected key to user config') + t.equal(val, expectVal, 'should set expected value to user config') + t.equal(where, 'user', 'should set key/val in user config by default') + } + + npm.config.save = where => { + t.equal(where, 'user', 'should save user config') + } + + config(['set', ...args], (err) => { + t.ifError(err, 'npm config set key') + }) + + t.teardown(() => { + delete npm.config.set + delete npm.config.save + }) +}) + t.test('config set key to empty value', t => { t.plan(5) npm.config.set = (key, val, where) => { t.equal(key, 'foo', 'should set expected key to user config') - t.equal(val, '', 'should set empty value to user config') + t.equal(val, '', 'should set "" to user config') t.equal(where, 'user', 'should set key/val in user config by default') } @@ -403,6 +461,36 @@ t.test('config get key', t => { }) }) +t.test('config get multiple keys', t => { + t.plan(4) + + const expect = [ + 'foo', + 'bar', + ] + + const npmConfigGet = npm.config.get + npm.config.get = (key) => { + t.equal(key, expect.shift(), 'should use expected key') + return 'asdf' + } + + npm.config.save = where => { + throw new Error('should not save') + } + + config(['get', 'foo', 'bar'], (err) => { + t.ifError(err, 'npm config get multiple keys') + t.equal(result, 'foo=asdf\nbar=asdf') + }) + + t.teardown(() => { + result = '' + npm.config.get = npmConfigGet + delete npm.config.save + }) +}) + t.test('config get private key', t => { config(['get', '//private-reg.npmjs.org/:_authThoken'], (err) => { t.match( diff --git a/test/lib/npm.js b/test/lib/npm.js index 2c71d229a7be3..dac9696ca09dd 100644 --- a/test/lib/npm.js +++ b/test/lib/npm.js @@ -342,7 +342,7 @@ t.test('npm.load', t => { /Completed in [0-9]+ms/, ], ]) - t.same(consoleLogs, [['@foo']]) + t.same(consoleLogs, [['scope=@foo\n\u2010not-a-dash=undefined']]) }) // need this here or node 10 will improperly end the promise ahead of time