UNPKG

decline-ts

Version:

Composable command-line parser for TypeScript - a (partial) porting of Scala decline using fp-ts

93 lines (92 loc) 5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Help = void 0; const fp_ts_1 = require("fp-ts"); const function_1 = require("fp-ts/function"); const Opts_1 = require("./Opts"); const Usage_1 = require("./Usage"); const StringUtils_1 = require("./utils/StringUtils"); var Help; (function (Help) { /** * Constructors */ Help.fromCommand = (parser) => { const commands = commandList(parser.opts); const commandHelp = fp_ts_1.readonlyArray.isEmpty(commands) ? fp_ts_1.readonlyArray.empty : function_1.pipe(commands, fp_ts_1.readonlyArray.chain(command => [ withIndent(4, command.name), withIndent(8, command.header), ]), texts => function_1.pipe(fp_ts_1.readonlyArray.cons('Subcommands:', texts), StringUtils_1.StringUtils.mkString('\n'), fp_ts_1.readonlyArray.of)); const optionsDetail = detail(parser.opts); const optionsHelp = fp_ts_1.readonlyArray.isEmpty(optionsDetail) ? fp_ts_1.readonlyArray.empty : function_1.pipe(fp_ts_1.readonlyArray.cons('Options:', optionsDetail), StringUtils_1.StringUtils.mkString('\n'), fp_ts_1.readonlyArray.of); return { errors: fp_ts_1.readonlyArray.empty, prefix: fp_ts_1.readonlyNonEmptyArray.of(parser.name), usage: function_1.pipe(parser.opts, Usage_1.Usage.fromOpts, fp_ts_1.readonlyArray.chain(Usage_1.Usage.show)), body: fp_ts_1.readonlyArray.cons(parser.header, [...optionsHelp, ...commandHelp]), }; }; /** * Methods */ Help.withErrors = (moreErrors) => (help) => (Object.assign(Object.assign({}, help), { errors: [...help.errors, ...moreErrors] })); Help.withPrefix = (prefix) => (help) => (Object.assign(Object.assign({}, help), { prefix: function_1.pipe(prefix, fp_ts_1.readonlyArray.reduceRight(help.prefix, fp_ts_1.readonlyNonEmptyArray.cons)) })); Help.stringify = (help) => { const maybeErrors = fp_ts_1.readonlyArray.isEmpty(help.errors) ? fp_ts_1.readonlyArray.empty : function_1.pipe(help.errors, StringUtils_1.StringUtils.mkString('\n'), fp_ts_1.readonlyArray.of); const prefixString = function_1.pipe(help.prefix, StringUtils_1.StringUtils.mkString(' ')); const usageString = fp_ts_1.readonlyArray.isEmpty(help.usage) ? `Usage: ${prefixString}` : help.usage.length === 1 ? `Usage: ${prefixString} ${help.usage[0]}` // TODO: cast bad. : function_1.pipe(fp_ts_1.readonlyArray.cons('Usage:', help.usage), StringUtils_1.StringUtils.mkString(`\n ${prefixString} `)); return function_1.pipe([...maybeErrors, ...fp_ts_1.readonlyArray.cons(usageString, help.body)], StringUtils_1.StringUtils.mkString('\n\n')); }; })(Help = exports.Help || (exports.Help = {})); const optionList = (opts) => { switch (opts._tag) { case 'Pure': return fp_ts_1.option.some(fp_ts_1.readonlyArray.empty); case 'App': return function_1.pipe(fp_ts_1.apply.sequenceT(fp_ts_1.option.option)(optionList(opts.f), optionList(opts.a)), fp_ts_1.option.map(([a, b]) => [...a, ...b])); case 'OrElse': const b = optionList(opts.b); return function_1.pipe(optionList(opts.a), fp_ts_1.option.map(a => function_1.pipe(b, fp_ts_1.option.fold(() => a, _ => [...a, ..._]))), fp_ts_1.option.alt(() => b)); case 'Single': return fp_ts_1.option.some(fp_ts_1.readonlyArray.of([opts.opt, false])); case 'Repeated': return fp_ts_1.option.some(fp_ts_1.readonlyArray.of([opts.opt, true])); case 'Subcommand': return fp_ts_1.option.some(fp_ts_1.readonlyArray.empty); case 'Validate': return optionList(opts.value); case 'HelpFlag': return optionList(opts.flag); } }; const commandList = (opts) => { switch (opts._tag) { case 'App': return [...commandList(opts.f), ...commandList(opts.a)]; case 'OrElse': return [...commandList(opts.a), ...commandList(opts.b)]; case 'Subcommand': return fp_ts_1.readonlyArray.of(opts.command); case 'Validate': return commandList(opts.value); default: return fp_ts_1.readonlyArray.empty; } }; const getOptBooleanTupleEq = () => fp_ts_1.eq.getTupleEq(Opts_1.Opts.Opt.eq(), fp_ts_1.eq.eqBoolean); const detail = (opts) => function_1.pipe(optionList(opts), fp_ts_1.option.getOrElse(() => fp_ts_1.readonlyArray.empty), fp_ts_1.readonlyArray.uniq(getOptBooleanTupleEq()), fp_ts_1.readonlyArray.chain( // ([opt, _]) => // if (opt._tag === 'Regular') TODO // if (opt._tag === 'Flag') TODO () => fp_ts_1.readonlyArray.empty)); const withIndent = (indent, str) => function_1.pipe(str.split('\n'), fp_ts_1.readonlyArray.map(_ => `${' '.repeat(indent)}${_}`), StringUtils_1.StringUtils.mkString('\n'));