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 | }
|
---|