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