UNPKG

tiny-bin

Version:

A library for building tiny and beautiful command line apps.

160 lines (159 loc) 6.79 kB
/* IMPORT */ import parseArgv from 'tiny-parse-argv'; import Command from './command.js'; import { camelCase, castArray, getClosest, isArray, sum } from '../utils.js'; /* MAIN */ class CommandDefault extends Command { /* CONSTRUCTOR */ constructor(bin) { super(bin, { name: '_default', description: 'Execute the default action', hidden: true }); } /* API */ async run(options, argv) { const name = this.bin.commands.getById(options._[0]) ? options._[0] : this.name; if (options['help'] || name === 'help') { return this.bin.commands.run('help', options, argv); } else if (options['version'] || options['v']) { return this.bin.commands.run('_version', options, argv); } else { //TODO: Detect conflicting options const isDefault = (name === this.name); if (isDefault && !this.handler) { this.bin.fail('Command handler not defined for default command'); } const command = this.bin.commands.getByIdOrFail(name); const options = [...this.bin.command.options.getAll(), ...command.options.getAll()]; const minArgs = command.arguments.getAll().filter(arg => arg.required).length; const maxArgs = sum(command.arguments.getAll().map(arg => arg.variadic ? Infinity : 1)); const parseArgvOptions = { known: [], boolean: [], integer: [], number: [], string: [], eager: [], required: [], unary: [], variadic: [], alias: {}, default: {}, incompatible: {}, validators: {}, onIncompatible: (options) => { this.bin.fail(`Incompatible options: "${options[0][0]}" and "${options[0][1]}" cannot be used together`); }, onInvalid: (options) => { this.bin.fail(`Invalid value for "${options[0]}" option`); }, onMissing: (options) => { this.bin.fail(`Missing required option: "${options[0]}"`); }, onUnknown: (options) => { const closest = getClosest(parseArgvOptions.known, options[0], 3, true); this.bin.fail(`Unknown option: "${options[0]}"${closest ? `. Did you mean "${closest}"?` : ''}`); } }; options.forEach(option => { var _a, _b; parseArgvOptions.known.push(...option.data.alls); if (option.data.type === 'boolean') { parseArgvOptions.boolean.push(...option.data.alls); } if (option.data.type === 'integer') { parseArgvOptions.integer.push(...option.data.alls); } if (option.data.type === 'number') { parseArgvOptions.number.push(...option.data.alls); } if (option.data.type === 'string') { parseArgvOptions.string.push(...option.data.alls); } if (option.eager) { parseArgvOptions.eager.push(...option.data.alls); } if (option.incompatible) { const incompatible = ((_a = parseArgvOptions.incompatible)[_b = option.data.alls[0]] || (_a[_b] = [])); incompatible.push(...option.incompatible); } if (option.required) { parseArgvOptions.required.push(...option.data.alls); } if (!option.variadic) { parseArgvOptions.unary.push(...option.data.alls); } if (option.variadic) { parseArgvOptions.variadic.push(...option.data.alls); } if (option.validate) { parseArgvOptions.validators[option.data.alls[0]] = option.validate; } if ('default' in option) { parseArgvOptions.default[option.data.alls[0]] = option.default; } const [first, ...rest] = option.data.alls; parseArgvOptions.alias[first] = rest; }); const parsed = parseArgv(argv, parseArgvOptions); options.forEach(option => { if (option.data.type !== 'string') return; const name = option.data.alls[0]; const value = parsed[name]; if (!value) return; const enums = option.enum; if (!enums) return; const values = castArray(value); values.forEach(value => { if (enums.includes(value)) return; this.bin.fail(`Invalid value for "${option.data.alls[0]}" option, received "${value}" but only ${enums.map(e => `"${e}"`).join(', ')} are supported`); }); }); options.forEach(option => { const name = option.data.alls[0]; const value = parsed[name]; if (!isArray(value)) return; if (value.length < 2) return; if (option.variadic) return; this.bin.fail(`Expected 1 value for "${option.data.alls[0]}" option, but received "${value.length}" values`); }); Object.keys(parsed).forEach(key => { const camelKey = camelCase(key); if (camelKey === key) return; parsed[camelKey] = parsed[key]; }); if (!isDefault) { parsed._.shift(); } const actualArgs = parsed._.length; if (actualArgs < minArgs || actualArgs > maxArgs) { if (minArgs === maxArgs) { this.bin.fail(`Expected ${minArgs} arguments, but received ${actualArgs} arguments`); } else { this.bin.fail(`Expected between ${minArgs} and ${maxArgs} arguments, but received ${actualArgs} arguments`); } } if (isDefault) { return this.handler(parsed, parsed._, parsed['--']); //TSC } else { return command.run(parsed, argv); } } } } /* EXPORT */ export default CommandDefault;