UNPKG

@jirutka/ajv-cli

Version:

CLI for Ajv JSON Schema Validator with human-friendly error messages

127 lines 4.41 kB
import { typeFlag } from 'type-flag'; import { UsageError } from './errors.js'; import { arrify } from './utils.js'; function createOptionParser(expectedValues, parse) { const expected = arrify(expectedValues); // The parser is a function that throws an error on invalid value mainly for // compatibility with type-flag. const parser = (input) => { const value = parse(input); if (value == null) { throw new TypeError(`must be ${joinCommaOr(expected)}`); } return value; }; parser.parse = parse; parser.expectedValues = expected; return parser; } export const AnyOf = (...parsers) => (input) => { for (const parser of parsers) { const value = 'parse' in parser ? parser.parse(input) : parser(input); if (value != null) { return value; } } const expected = parsers.flatMap(parser => 'expectedValues' in parser ? parser.expectedValues : parser.name.toLowerCase()); throw new UsageError(`must be ${joinCommaOr(expected)}`); }; export const Bool = createOptionParser(['true', 'false'], input => { switch (input) { case 'true': case '': return true; case 'false': return false; } }); export const Const = (value) => createOptionParser(`"${value}"`, input => (input === value ? input : undefined)); export const Enum = (...members) => createOptionParser(members.map(s => `"${s}"`), input => { if (members.includes(input)) { return input; } }); export const Uint = createOptionParser('a positive integer', input => { if (/^\d+/.test(input)) { return Number(input); } }); export function parseArgv(schema, argv) { const required = []; // Wrap type functions to wrap errors. const newSchema = Object.entries(schema).reduce((acc, [key, flagSchema]) => { if (key === '_') { return acc; } const [type, isArray] = parseFlagType(flagSchema); const parse = applyParser.bind(null, type, key); if (flagSchema.required) { required.push(key); } acc[key] = { ...(isFlagType(flagSchema) ? {} : flagSchema), type: isArray ? [parse] : parse, }; return acc; }, {}); const { _: args, flags, unknownFlags } = typeFlag(newSchema, argv); // Skip all following checks if --help is provided. if (flags.help) { return [flags, args]; } const unknowns = Object.keys(unknownFlags).map(flag => flag.length > 1 ? `--${flag}` : `-${flag}`); if (unknowns.length > 0) { throw new UsageError(`Unknown options: ${unknowns.join(', ')} (see --help)`); } if ('_' in schema && !isFlagType(schema._)) { const schema_ = schema._; if (schema_.minItems != null && args.length < schema_.minItems) { throw new UsageError('Missing required arguments'); } if (schema_.maxItems != null && args.length > schema_.maxItems) { throw new UsageError('Too many arguments'); } } for (const flag of required) { const value = flags[flag]; if (value === undefined || (Array.isArray(value) && value.length === 0)) { throw new UsageError(`Missing required option: --${flag}`); } } return [flags, args]; } function applyParser(typeFn, option, value) { try { return typeFn(value); } catch (err) { const optionDesc = [ option.length > 1 ? '--' : '-', option, '=', /\s/.test(value) ? `"${value}"` : value, ].join(''); throw new UsageError(`Option ${optionDesc} is invalid: value ${err.message}`); } } function isFlagType(typeOrSchema) { return (typeof typeOrSchema === 'function' || (Array.isArray(typeOrSchema) && typeof typeOrSchema === 'function')); } function joinCommaOr(words) { if (words.length > 1) { return words.slice(0, -1).join(', ') + ' or ' + words.at(-1); } return words[0]; } // Copied from https://github.com/privatenumber/type-flag/blob/v3.0.0/src/utils.ts function parseFlagType(schema) { if (typeof schema === 'function') { return [schema, false]; } if (Array.isArray(schema)) { return [schema[0], true]; } return parseFlagType(schema.type); } //# sourceMappingURL=args-parser.js.map