meow
Version:
CLI app helper
146 lines (114 loc) • 4.25 kB
JavaScript
import process from 'node:process';
import { decamelizeFlagKey } from './utils.js';
const validateFlags = (flags, options) => {
for (const [flagKey, flagValue] of Object.entries(options.flags)) {
if (flagKey !== '--' && !flagValue.isMultiple && Array.isArray(flags[flagKey])) {
throw new Error(`The flag --${flagKey} can only be set once.`);
}
}
};
const validateChoicesByFlag = (flagKey, flagValue, receivedInput) => {
const {choices, isRequired} = flagValue;
if (!choices) {
return;
}
const valueMustBeOneOf = `Value must be one of: [\`${choices.join('`, `')}\`]`;
if (receivedInput === undefined) {
if (isRequired) {
return `Flag \`${decamelizeFlagKey(flagKey)}\` has no value. ${valueMustBeOneOf}`;
}
return;
}
if (Array.isArray(receivedInput)) {
const unknownValues = receivedInput.filter(value => !choices.includes(value));
if (unknownValues.length > 0) {
const valuesText = unknownValues.length > 1 ? 'values' : 'value';
return `Unknown ${valuesText} for flag \`${decamelizeFlagKey(flagKey)}\`: \`${unknownValues.join('`, `')}\`. ${valueMustBeOneOf}`;
}
} else if (!choices.includes(receivedInput)) {
return `Unknown value for flag \`${decamelizeFlagKey(flagKey)}\`: \`${receivedInput}\`. ${valueMustBeOneOf}`;
}
};
const validateChoices = (flags, receivedFlags) => {
const errors = [];
for (const [flagKey, flagValue] of Object.entries(flags)) {
const receivedInput = receivedFlags[flagKey];
const errorMessage = validateChoicesByFlag(flagKey, flagValue, receivedInput);
if (errorMessage) {
errors.push(errorMessage);
}
}
if (errors.length > 0) {
throw new Error(errors.join('\n'));
}
};
const validate = (flags, options) => {
validateFlags(flags, options);
validateChoices(options.flags, flags);
};
const reportUnknownFlags = unknownFlags => {
console.error([
`Unknown flag${unknownFlags.length > 1 ? 's' : ''}`,
...unknownFlags,
].join('\n'));
};
const isNegativeNumberArgument = value => /^-(?:\d+|\d*\.\d+)(?:e[+-]?\d+)?$/i.test(value);
const checkUnknownFlags = input => {
const unknownFlags = input.filter(item => typeof item === 'string' && item.length > 1 && item.startsWith('-') && !isNegativeNumberArgument(item));
if (unknownFlags.length > 0) {
reportUnknownFlags(unknownFlags);
process.exit(2);
}
};
const isFlagMissing = (flagName, definedFlags, receivedFlags, input) => {
const flag = definedFlags[flagName];
let isFlagRequired = true;
if (typeof flag.isRequired === 'function') {
isFlagRequired = flag.isRequired(receivedFlags, input);
if (typeof isFlagRequired !== 'boolean') {
throw new TypeError(`Return value for isRequired callback should be of type boolean, but ${typeof isFlagRequired} was returned.`);
}
}
if (receivedFlags[flagName] === undefined) {
return isFlagRequired;
}
return flag.isMultiple && receivedFlags[flagName].length === 0 && isFlagRequired;
};
const reportMissingRequiredFlags = missingRequiredFlags => {
console.error(`Missing required flag${missingRequiredFlags.length > 1 ? 's' : ''}`);
for (const flag of missingRequiredFlags) {
console.error(`\t${decamelizeFlagKey(flag.key)}${flag.shortFlag ? `, -${flag.shortFlag}` : ''}`);
}
};
const checkMissingRequiredInput = (options, input) => {
const {isRequired} = options.inputOptions;
if (!isRequired) {
return;
}
if (typeof isRequired === 'function') {
const result = isRequired(input);
if (typeof result !== 'boolean') {
throw new TypeError(`Return value for isRequired callback should be of type boolean, but ${typeof result} was returned.`);
}
if (!result) {
return;
}
}
if (input.length === 0) {
console.error('Missing required input');
process.exit(2);
}
};
const checkMissingRequiredFlags = (flags, receivedFlags, input) => {
const missingRequiredFlags = [];
for (const flagName of Object.keys(flags)) {
if (flags[flagName].isRequired && isFlagMissing(flagName, flags, receivedFlags, input)) {
missingRequiredFlags.push({key: flagName, ...flags[flagName]});
}
}
if (missingRequiredFlags.length > 0) {
reportMissingRequiredFlags(missingRequiredFlags);
process.exit(2);
}
};
export { checkMissingRequiredFlags, checkMissingRequiredInput, checkUnknownFlags, validate };