UNPKG

peowly

Version:

meow inspired parseArgs() based CLI parser with help text helpers

119 lines (101 loc) 3.06 kB
// eslint-disable-next-line n/no-unsupported-features/node-builtins import { parseArgs } from 'node:util'; import { defaultFlags } from './flags.js'; import { formatHelpMessage } from './format-help.js'; import { filter } from './utils.js'; // TODO: Add helper that translates a meow config into this config // TODO: Add type tests that verifies overlap and differences to meow /** * @template {import('./flag-types.d.ts').AnyFlags} Flags * @param {import('./peowly-types.d.ts').PeowlyOptions<Flags>} options * @returns {import('./peowly-types.d.ts').PeowlyResult<Flags>} */ export function peowly (options) { const pkg = options?.pkg; const name = options?.name || ( Array.isArray(pkg?.bin) ? Object.keys(pkg?.bin).at(0) : pkg?.name ); const { args = process.argv.slice(2), description = pkg?.description, examples, help: baseHelp, indent = 2, name: _name, // destructed to get rid of it from parseArgsOptions options: flags = {}, pkg: _pkg, // destructed to get rid of it from parseArgsOptions processTitle = name, returnRemainderArgs, usage, version = pkg?.version, ...parseArgsOptions } = options || {}; if (processTitle) { process.title = processTitle; } let help = baseHelp || formatHelpMessage(name || 'nameless', { examples, flags, indent, usage, }); help = '\n' + (description ? ''.padEnd(indent) + description + '\n\n' : '') + help; /** @type {import('./flag-types.d.ts').AnyFlags & typeof defaultFlags} */ const resolvedFlags = { ...flags, ...defaultFlags }; const showHelp = (/** @type {number | undefined} */ code) => { // eslint-disable-next-line no-console console.log(help); // eslint-disable-next-line unicorn/no-process-exit process.exit(typeof code === 'number' ? code : 2); // Default to code 2 for incorrect usage (#47) }; if (args.includes('--help')) { showHelp(0); } if (args.includes('--version')) { // eslint-disable-next-line no-console console.log(version || 'no version'); // eslint-disable-next-line unicorn/no-process-exit process.exit(0); } const { positionals, tokens, values: { help: _helpFlag, version: _versionFlag, ...values }, } = parseArgs({ args, allowPositionals: true, ...parseArgsOptions, options: resolvedFlags, strict: !returnRemainderArgs, tokens: true, }); /** @type {string[]} */ let remainderArgs = []; if (returnRemainderArgs) { /** @type {Array<string|undefined>} */ const sourceArgs = [...args]; for (const token of tokens) { if (token.kind !== 'option') { continue; } if (!resolvedFlags[token.name]) { delete values[token.name]; } else { sourceArgs[token.index] = undefined; } } remainderArgs = filter(sourceArgs); } return { input: positionals, flags: /** @type {import('./peowly-types.d.ts').TypedFlags<Flags>} */ (values), remainderArgs, showHelp, }; }