1// validation-type-stuff, missing params, 2// bad implications, custom checks. 3module.exports = function (yargs, usage) { 4 var self = {} 5 6 // validate appropriate # of non-option 7 // arguments were provided, i.e., '_'. 8 self.nonOptionCount = function (argv) { 9 var demanded = yargs.getDemanded() 10 11 if (demanded._ && argv._.length < demanded._.count) { 12 if (demanded._.msg !== undefined) { 13 usage.fail(demanded._.msg) 14 } else { 15 usage.fail('Not enough non-option arguments: got ' 16 + argv._.length + ', need at least ' + demanded._.count 17 ) 18 } 19 } 20 } 21 22 // make sure that any args that require an 23 // value (--foo=bar), have a value. 24 self.missingArgumentValue = function (argv) { 25 var options = yargs.getOptions(), 26 defaultValues = [true, false, ''] 27 28 if (options.requiresArg.length > 0) { 29 var missingRequiredArgs = [] 30 31 options.requiresArg.forEach(function (key) { 32 var value = argv[key] 33 34 // if a value is explicitly requested, 35 // flag argument as missing if it does not 36 // look like foo=bar was entered. 37 if (~defaultValues.indexOf(value) 38 || (Array.isArray(value) && !value.length)) { 39 missingRequiredArgs.push(key) 40 } 41 }) 42 43 if (missingRequiredArgs.length === 1) { 44 usage.fail('Missing argument value: ' + missingRequiredArgs[0]) 45 } else if (missingRequiredArgs.length > 1) { 46 var message = 'Missing argument values: ' + missingRequiredArgs.join(', ') 47 usage.fail(message) 48 } 49 } 50 } 51 52 // make sure all the required arguments are present. 53 self.requiredArguments = function (argv) { 54 var demanded = yargs.getDemanded(), 55 missing = null 56 57 Object.keys(demanded).forEach(function (key) { 58 if (!argv.hasOwnProperty(key)) { 59 missing = missing || {} 60 missing[key] = demanded[key] 61 } 62 }) 63 64 if (missing) { 65 var customMsgs = [] 66 Object.keys(missing).forEach(function (key) { 67 var msg = missing[key].msg 68 if (msg && customMsgs.indexOf(msg) < 0) { 69 customMsgs.push(msg) 70 } 71 }) 72 73 var customMsg = customMsgs.length ? '\n' + customMsgs.join('\n') : '' 74 usage.fail('Missing required arguments: ' + Object.keys(missing).join(', ') + customMsg) 75 } 76 } 77 78 // check for unknown arguments (strict-mode). 79 self.unknownArguments = function (argv, aliases) { 80 var descriptions = usage.getDescriptions(), 81 demanded = yargs.getDemanded(), 82 unknown = [], 83 aliasLookup = {} 84 85 Object.keys(aliases).forEach(function (key) { 86 aliases[key].forEach(function (alias) { 87 aliasLookup[alias] = key 88 }) 89 }) 90 91 Object.keys(argv).forEach(function (key) { 92 if (key !== '$0' && key !== '_' && 93 !descriptions.hasOwnProperty(key) && 94 !demanded.hasOwnProperty(key) && 95 !aliasLookup.hasOwnProperty(key)) { 96 unknown.push(key) 97 } 98 }) 99 100 if (unknown.length === 1) { 101 usage.fail('Unknown argument: ' + unknown[0]) 102 } else if (unknown.length > 1) { 103 usage.fail('Unknown arguments: ' + unknown.join(', ')) 104 } 105 } 106 107 // custom checks, added using the `check` option on yargs. 108 var checks = [] 109 self.check = function (f) { 110 checks.push(f) 111 } 112 113 self.customChecks = function (argv, aliases) { 114 checks.forEach(function (f) { 115 try { 116 var result = f(argv, aliases) 117 if (!result) { 118 usage.fail('Argument check failed: ' + f.toString()) 119 } else if (typeof result === 'string') { 120 usage.fail(result) 121 } 122 } catch (err) { 123 usage.fail(err.message ? err.message : err) 124 } 125 }) 126 } 127 128 // check implications, argument foo implies => argument bar. 129 var implied = {} 130 self.implies = function (key, value) { 131 if (typeof key === 'object') { 132 Object.keys(key).forEach(function (k) { 133 self.implies(k, key[k]) 134 }) 135 } else { 136 implied[key] = value 137 } 138 } 139 self.getImplied = function () { 140 return implied 141 } 142 143 self.implications = function (argv) { 144 var implyFail = [] 145 146 Object.keys(implied).forEach(function (key) { 147 var num, 148 origKey = key, 149 value = implied[key] 150 151 // convert string '1' to number 1 152 num = Number(key) 153 key = isNaN(num) ? key : num 154 155 if (typeof key === 'number') { 156 // check length of argv._ 157 key = argv._.length >= key 158 } else if (key.match(/^--no-.+/)) { 159 // check if key doesn't exist 160 key = key.match(/^--no-(.+)/)[1] 161 key = !argv[key] 162 } else { 163 // check if key exists 164 key = argv[key] 165 } 166 167 num = Number(value) 168 value = isNaN(num) ? value : num 169 170 if (typeof value === 'number') { 171 value = argv._.length >= value 172 } else if (value.match(/^--no-.+/)) { 173 value = value.match(/^--no-(.+)/)[1] 174 value = !argv[value] 175 } else { 176 value = argv[value] 177 } 178 179 if (key && !value) { 180 implyFail.push(origKey) 181 } 182 }) 183 184 if (implyFail.length) { 185 var msg = 'Implications failed:\n' 186 187 implyFail.forEach(function (key) { 188 msg += (' ' + key + ' -> ' + implied[key]) 189 }) 190 191 usage.fail(msg) 192 } 193 } 194 195 return self 196} 197