1import { argsert } from './argsert.js'; 2import { assertNotStrictEqual, } from './typings/common-types.js'; 3import { levenshtein as distance } from './utils/levenshtein.js'; 4import { objFilter } from './utils/obj-filter.js'; 5const specialKeys = ['$0', '--', '_']; 6export function validation(yargs, usage, shim) { 7 const __ = shim.y18n.__; 8 const __n = shim.y18n.__n; 9 const self = {}; 10 self.nonOptionCount = function nonOptionCount(argv) { 11 const demandedCommands = yargs.getDemandedCommands(); 12 const positionalCount = argv._.length + (argv['--'] ? argv['--'].length : 0); 13 const _s = positionalCount - yargs.getInternalMethods().getContext().commands.length; 14 if (demandedCommands._ && 15 (_s < demandedCommands._.min || _s > demandedCommands._.max)) { 16 if (_s < demandedCommands._.min) { 17 if (demandedCommands._.minMsg !== undefined) { 18 usage.fail(demandedCommands._.minMsg 19 ? demandedCommands._.minMsg 20 .replace(/\$0/g, _s.toString()) 21 .replace(/\$1/, demandedCommands._.min.toString()) 22 : null); 23 } 24 else { 25 usage.fail(__n('Not enough non-option arguments: got %s, need at least %s', 'Not enough non-option arguments: got %s, need at least %s', _s, _s.toString(), demandedCommands._.min.toString())); 26 } 27 } 28 else if (_s > demandedCommands._.max) { 29 if (demandedCommands._.maxMsg !== undefined) { 30 usage.fail(demandedCommands._.maxMsg 31 ? demandedCommands._.maxMsg 32 .replace(/\$0/g, _s.toString()) 33 .replace(/\$1/, demandedCommands._.max.toString()) 34 : null); 35 } 36 else { 37 usage.fail(__n('Too many non-option arguments: got %s, maximum of %s', 'Too many non-option arguments: got %s, maximum of %s', _s, _s.toString(), demandedCommands._.max.toString())); 38 } 39 } 40 } 41 }; 42 self.positionalCount = function positionalCount(required, observed) { 43 if (observed < required) { 44 usage.fail(__n('Not enough non-option arguments: got %s, need at least %s', 'Not enough non-option arguments: got %s, need at least %s', observed, observed + '', required + '')); 45 } 46 }; 47 self.requiredArguments = function requiredArguments(argv, demandedOptions) { 48 let missing = null; 49 for (const key of Object.keys(demandedOptions)) { 50 if (!Object.prototype.hasOwnProperty.call(argv, key) || 51 typeof argv[key] === 'undefined') { 52 missing = missing || {}; 53 missing[key] = demandedOptions[key]; 54 } 55 } 56 if (missing) { 57 const customMsgs = []; 58 for (const key of Object.keys(missing)) { 59 const msg = missing[key]; 60 if (msg && customMsgs.indexOf(msg) < 0) { 61 customMsgs.push(msg); 62 } 63 } 64 const customMsg = customMsgs.length ? `\n${customMsgs.join('\n')}` : ''; 65 usage.fail(__n('Missing required argument: %s', 'Missing required arguments: %s', Object.keys(missing).length, Object.keys(missing).join(', ') + customMsg)); 66 } 67 }; 68 self.unknownArguments = function unknownArguments(argv, aliases, positionalMap, isDefaultCommand, checkPositionals = true) { 69 var _a; 70 const commandKeys = yargs 71 .getInternalMethods() 72 .getCommandInstance() 73 .getCommands(); 74 const unknown = []; 75 const currentContext = yargs.getInternalMethods().getContext(); 76 Object.keys(argv).forEach(key => { 77 if (!specialKeys.includes(key) && 78 !Object.prototype.hasOwnProperty.call(positionalMap, key) && 79 !Object.prototype.hasOwnProperty.call(yargs.getInternalMethods().getParseContext(), key) && 80 !self.isValidAndSomeAliasIsNotNew(key, aliases)) { 81 unknown.push(key); 82 } 83 }); 84 if (checkPositionals && 85 (currentContext.commands.length > 0 || 86 commandKeys.length > 0 || 87 isDefaultCommand)) { 88 argv._.slice(currentContext.commands.length).forEach(key => { 89 if (!commandKeys.includes('' + key)) { 90 unknown.push('' + key); 91 } 92 }); 93 } 94 if (checkPositionals) { 95 const demandedCommands = yargs.getDemandedCommands(); 96 const maxNonOptDemanded = ((_a = demandedCommands._) === null || _a === void 0 ? void 0 : _a.max) || 0; 97 const expected = currentContext.commands.length + maxNonOptDemanded; 98 if (expected < argv._.length) { 99 argv._.slice(expected).forEach(key => { 100 key = String(key); 101 if (!currentContext.commands.includes(key) && 102 !unknown.includes(key)) { 103 unknown.push(key); 104 } 105 }); 106 } 107 } 108 if (unknown.length) { 109 usage.fail(__n('Unknown argument: %s', 'Unknown arguments: %s', unknown.length, unknown.join(', '))); 110 } 111 }; 112 self.unknownCommands = function unknownCommands(argv) { 113 const commandKeys = yargs 114 .getInternalMethods() 115 .getCommandInstance() 116 .getCommands(); 117 const unknown = []; 118 const currentContext = yargs.getInternalMethods().getContext(); 119 if (currentContext.commands.length > 0 || commandKeys.length > 0) { 120 argv._.slice(currentContext.commands.length).forEach(key => { 121 if (!commandKeys.includes('' + key)) { 122 unknown.push('' + key); 123 } 124 }); 125 } 126 if (unknown.length > 0) { 127 usage.fail(__n('Unknown command: %s', 'Unknown commands: %s', unknown.length, unknown.join(', '))); 128 return true; 129 } 130 else { 131 return false; 132 } 133 }; 134 self.isValidAndSomeAliasIsNotNew = function isValidAndSomeAliasIsNotNew(key, aliases) { 135 if (!Object.prototype.hasOwnProperty.call(aliases, key)) { 136 return false; 137 } 138 const newAliases = yargs.parsed.newAliases; 139 return [key, ...aliases[key]].some(a => !Object.prototype.hasOwnProperty.call(newAliases, a) || !newAliases[key]); 140 }; 141 self.limitedChoices = function limitedChoices(argv) { 142 const options = yargs.getOptions(); 143 const invalid = {}; 144 if (!Object.keys(options.choices).length) 145 return; 146 Object.keys(argv).forEach(key => { 147 if (specialKeys.indexOf(key) === -1 && 148 Object.prototype.hasOwnProperty.call(options.choices, key)) { 149 [].concat(argv[key]).forEach(value => { 150 if (options.choices[key].indexOf(value) === -1 && 151 value !== undefined) { 152 invalid[key] = (invalid[key] || []).concat(value); 153 } 154 }); 155 } 156 }); 157 const invalidKeys = Object.keys(invalid); 158 if (!invalidKeys.length) 159 return; 160 let msg = __('Invalid values:'); 161 invalidKeys.forEach(key => { 162 msg += `\n ${__('Argument: %s, Given: %s, Choices: %s', key, usage.stringifiedValues(invalid[key]), usage.stringifiedValues(options.choices[key]))}`; 163 }); 164 usage.fail(msg); 165 }; 166 let implied = {}; 167 self.implies = function implies(key, value) { 168 argsert('<string|object> [array|number|string]', [key, value], arguments.length); 169 if (typeof key === 'object') { 170 Object.keys(key).forEach(k => { 171 self.implies(k, key[k]); 172 }); 173 } 174 else { 175 yargs.global(key); 176 if (!implied[key]) { 177 implied[key] = []; 178 } 179 if (Array.isArray(value)) { 180 value.forEach(i => self.implies(key, i)); 181 } 182 else { 183 assertNotStrictEqual(value, undefined, shim); 184 implied[key].push(value); 185 } 186 } 187 }; 188 self.getImplied = function getImplied() { 189 return implied; 190 }; 191 function keyExists(argv, val) { 192 const num = Number(val); 193 val = isNaN(num) ? val : num; 194 if (typeof val === 'number') { 195 val = argv._.length >= val; 196 } 197 else if (val.match(/^--no-.+/)) { 198 val = val.match(/^--no-(.+)/)[1]; 199 val = !Object.prototype.hasOwnProperty.call(argv, val); 200 } 201 else { 202 val = Object.prototype.hasOwnProperty.call(argv, val); 203 } 204 return val; 205 } 206 self.implications = function implications(argv) { 207 const implyFail = []; 208 Object.keys(implied).forEach(key => { 209 const origKey = key; 210 (implied[key] || []).forEach(value => { 211 let key = origKey; 212 const origValue = value; 213 key = keyExists(argv, key); 214 value = keyExists(argv, value); 215 if (key && !value) { 216 implyFail.push(` ${origKey} -> ${origValue}`); 217 } 218 }); 219 }); 220 if (implyFail.length) { 221 let msg = `${__('Implications failed:')}\n`; 222 implyFail.forEach(value => { 223 msg += value; 224 }); 225 usage.fail(msg); 226 } 227 }; 228 let conflicting = {}; 229 self.conflicts = function conflicts(key, value) { 230 argsert('<string|object> [array|string]', [key, value], arguments.length); 231 if (typeof key === 'object') { 232 Object.keys(key).forEach(k => { 233 self.conflicts(k, key[k]); 234 }); 235 } 236 else { 237 yargs.global(key); 238 if (!conflicting[key]) { 239 conflicting[key] = []; 240 } 241 if (Array.isArray(value)) { 242 value.forEach(i => self.conflicts(key, i)); 243 } 244 else { 245 conflicting[key].push(value); 246 } 247 } 248 }; 249 self.getConflicting = () => conflicting; 250 self.conflicting = function conflictingFn(argv) { 251 Object.keys(argv).forEach(key => { 252 if (conflicting[key]) { 253 conflicting[key].forEach(value => { 254 if (value && argv[key] !== undefined && argv[value] !== undefined) { 255 usage.fail(__('Arguments %s and %s are mutually exclusive', key, value)); 256 } 257 }); 258 } 259 }); 260 if (yargs.getInternalMethods().getParserConfiguration()['strip-dashed']) { 261 Object.keys(conflicting).forEach(key => { 262 conflicting[key].forEach(value => { 263 if (value && 264 argv[shim.Parser.camelCase(key)] !== undefined && 265 argv[shim.Parser.camelCase(value)] !== undefined) { 266 usage.fail(__('Arguments %s and %s are mutually exclusive', key, value)); 267 } 268 }); 269 }); 270 } 271 }; 272 self.recommendCommands = function recommendCommands(cmd, potentialCommands) { 273 const threshold = 3; 274 potentialCommands = potentialCommands.sort((a, b) => b.length - a.length); 275 let recommended = null; 276 let bestDistance = Infinity; 277 for (let i = 0, candidate; (candidate = potentialCommands[i]) !== undefined; i++) { 278 const d = distance(cmd, candidate); 279 if (d <= threshold && d < bestDistance) { 280 bestDistance = d; 281 recommended = candidate; 282 } 283 } 284 if (recommended) 285 usage.fail(__('Did you mean %s?', recommended)); 286 }; 287 self.reset = function reset(localLookup) { 288 implied = objFilter(implied, k => !localLookup[k]); 289 conflicting = objFilter(conflicting, k => !localLookup[k]); 290 return self; 291 }; 292 const frozens = []; 293 self.freeze = function freeze() { 294 frozens.push({ 295 implied, 296 conflicting, 297 }); 298 }; 299 self.unfreeze = function unfreeze() { 300 const frozen = frozens.pop(); 301 assertNotStrictEqual(frozen, undefined, shim); 302 ({ implied, conflicting } = frozen); 303 }; 304 return self; 305} 306