[6a3a178] | 1 | import { argsert } from './argsert.js';
|
---|
| 2 | import { assertNotStrictEqual, } from './typings/common-types.js';
|
---|
| 3 | import { levenshtein as distance } from './utils/levenshtein.js';
|
---|
| 4 | import { objFilter } from './utils/obj-filter.js';
|
---|
| 5 | const specialKeys = ['$0', '--', '_'];
|
---|
| 6 | export 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 | }
|
---|