UNPKG

meow

Version:
148 lines (121 loc) 5.14 kB
import process from 'node:process'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { a as readPackageUpSync, n as normalizePackageData } from './dependencies.js'; import { joinFlagKeys, decamelizeFlagKey } from './utils.js'; const isObject = value => Object.prototype.toString.call(value) === '[object Object]'; const validateOptions = options => { if (!isObject(options.flags)) { throw new TypeError('The `flags` option must be an object.'); } if (options.input !== undefined && typeof options.input !== 'string' && !isObject(options.input)) { throw new TypeError('The `input` option must be a string or an object.'); } if (isObject(options.input) && Object.hasOwn(options.input, 'isRequired') && typeof options.input.isRequired !== 'boolean' && typeof options.input.isRequired !== 'function') { throw new TypeError('The `input.isRequired` option must be a boolean or a function.'); } if (options.commands !== undefined) { if (!Array.isArray(options.commands)) { throw new TypeError('The `commands` option must be an array of strings.'); } if (options.commands.length === 0) { throw new TypeError('The `commands` option must contain at least one command.'); } if (options.commands.some(command => typeof command !== 'string' || command === '' || command.startsWith('-') || /\s/.test(command))) { throw new TypeError('The `commands` option must be an array of non-empty strings without whitespace that do not start with `-`.'); } } const invalidOptionFilters = { flags: { keyContainsDashes: { filter: ([flagKey]) => flagKey.includes('-') && flagKey !== '--', message: flagKeys => `Flag keys may not contain '-'. Invalid flags: ${joinFlagKeys(flagKeys, '')}`, }, aliasIsSet: { filter: ([, flag]) => Object.hasOwn(flag, 'alias'), message: flagKeys => `The option \`alias\` has been renamed to \`shortFlag\`. The following flags need to be updated: ${joinFlagKeys(flagKeys)}`, }, choicesNotAnArray: { filter: ([, flag]) => Object.hasOwn(flag, 'choices') && !Array.isArray(flag.choices), message: flagKeys => `The option \`choices\` must be an array. Invalid flags: ${joinFlagKeys(flagKeys)}`, }, choicesNotMatchFlagType: { filter: ([, flag]) => flag.type && Array.isArray(flag.choices) && flag.choices.some(choice => typeof choice !== flag.type), message(flagKeys) { const flagKeysAndTypes = flagKeys.map(flagKey => `(\`${decamelizeFlagKey(flagKey)}\`, type: '${options.flags[flagKey].type}')`); return `Each value of the option \`choices\` must be of the same type as its flag. Invalid flags: ${flagKeysAndTypes.join(', ')}`; }, }, defaultNotInChoices: { filter: ([, flag]) => Object.hasOwn(flag, 'default') && Array.isArray(flag.choices) && ![flag.default].flat().every(value => flag.choices.includes(value)), message: flagKeys => `Each value of the option \`default\` must exist within the option \`choices\`. Invalid flags: ${joinFlagKeys(flagKeys)}`, }, }, }; const errorMessages = []; for (const [optionKey, filters] of Object.entries(invalidOptionFilters)) { const optionEntries = Object.entries(options[optionKey]); for (const {filter, message} of Object.values(filters)) { const invalidOptions = optionEntries.filter(option => filter(option)); const invalidOptionKeys = invalidOptions.map(([key]) => key); if (invalidOptions.length > 0) { errorMessages.push(message(invalidOptionKeys)); } } } if (errorMessages.length > 0) { throw new Error(errorMessages.join('\n')); } }; const buildOptions = (helpText, options) => { if (typeof helpText !== 'string') { options = helpText; helpText = ''; } if (!options.importMeta?.url) { throw new TypeError('The `importMeta` option is required. Its value must be `import.meta`.'); } const foundPackage = options.pkg ?? readPackageUpSync({ cwd: path.dirname(fileURLToPath(options.importMeta.url)), normalize: false, })?.packageJson; const pkg = foundPackage ?? {}; let isPackageNormalized = false; const getPackage = () => { if (!isPackageNormalized) { normalizePackageData(pkg); isPackageNormalized = true; } return pkg; }; const parsedOptions = { argv: process.argv.slice(2), inferType: false, input: 'string', description: pkg.description ?? false, help: helpText, version: pkg.version || 'No version found', autoHelp: true, autoVersion: true, booleanDefault: false, allowUnknownFlags: true, helpIndent: 2, ...options, pkg, getPackage, }; if (parsedOptions.flags === undefined) { parsedOptions.flags = {}; } validateOptions(parsedOptions); const inputOptions = parsedOptions.input; if (inputOptions && typeof inputOptions === 'object' && !Array.isArray(inputOptions)) { const {isRequired, ...inputArgumentOptions} = inputOptions; parsedOptions.inputOptions = isRequired === undefined ? {} : {isRequired}; parsedOptions.input = Object.keys(inputArgumentOptions).length > 0 ? inputArgumentOptions : 'string'; } else { parsedOptions.inputOptions = {}; } return parsedOptions; }; export { buildOptions };