UNPKG

commandstruct

Version:
721 lines (709 loc) 21.2 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/index.ts var index_exports = {}; __export(index_exports, { Arg: () => Arg, Command: () => Command, CommandError: () => CommandError, Flag: () => Flag, Program: () => Program, SingleProgram: () => SingleProgram, arg: () => arg, createCommand: () => createCommand, createProgram: () => createProgram, createSingleProgram: () => createSingleProgram, flag: () => flag, run: () => run }); module.exports = __toCommonJS(index_exports); // src/arg.ts var Arg = class { constructor(_type) { this._type = _type; } optional() { this._type = "optional"; return this; } static toObject(arg2) { return { type: arg2._type }; } static toString(arg2, name) { if (arg2._type === "required") return `<${name}>`; else return `[${name}]`; } }; function arg() { return new Arg("required"); } // src/command.ts var import_hollywood_di = require("hollywood-di"); // src/errors.ts var CommandError = class extends Error { constructor(reason, message) { super(message); this.reason = reason; this.message = message; } }; // src/flag.ts var Flag = class _Flag { constructor(_desc, _char, _param, _preserveCase, _negate) { this._desc = _desc; this._char = _char; this._param = _param; this._preserveCase = _preserveCase; this._negate = _negate; } char(char) { this._char = char; return this; } requiredParam(type) { this._param = { _type: "required", type }; return this; } optionalParam(type, defaultValue) { this._param = { _type: "optional", type, defaultValue }; return this; } withNegated(description) { this._negate = description; return this; } preserveCase() { this._preserveCase = true; return this; } static toObject(flag2) { return { desc: flag2._desc, char: flag2._char, param: flag2._param, negate: flag2._negate, preserveCase: flag2._preserveCase }; } static toString(flag2, name) { let str = `--${flag2._preserveCase ? name : _Flag.toKebabCase(name)}`; if (flag2._char) str = `-${flag2._char}, ${str}`; return str; } static toNegatedString(flag2, name) { let str = `--no-${flag2._preserveCase ? name : _Flag.toKebabCase(name)}`; return str; } static isNegated(str) { return str.startsWith("no-"); } static toKebabCase(str) { return str.split("").map((char, index) => { if (index === 0) return char.toLowerCase(); return char.toLowerCase() !== char ? `-${char.toLowerCase()}` : char; }).join(""); } static toCamelCase(str) { return str.replace(/-./g, (x) => x[1].toUpperCase()); } }; function flag(description) { return new Flag(description, void 0, void 0, false, void 0); } // src/utils.ts function registerFlags(program, flags) { for (const [name, flag2] of Object.entries(flags)) { const flagObj = Flag.toObject(flag2); if (flagObj.char !== void 0 && flagObj.char.length !== 1) { throw new CommandError( "invalid_flag", `option ${"`" + Flag.toString(flag2, name) + "`"} char must be 1 character` ); } if (flagObj.param && flagObj.negate !== void 0) { throw new CommandError( "invalid_flag", `negated option ${name} ${"`" + Flag.toString(flag2, name) + "`"} cannot have param` ); } const key = flagObj.preserveCase ? name : Flag.toKebabCase(name); if (Flag.isNegated(key)) { if (flagObj.param) { throw new CommandError( "invalid_flag", `negated option ${name} ${"`" + Flag.toString(flag2, name) + "`"} cannot have param` ); } if (flagObj.char !== void 0) { throw new CommandError( "invalid_flag", `negated option ${name} ${"`" + Flag.toString(flag2, name) + "`"} cannot have char` ); } if (flagObj.negate !== void 0) { throw new CommandError( "invalid_flag", `negated option ${name} ${"`" + Flag.toString(flag2, name) + "`"} is already negated` ); } const existingKey = flags[key.slice(3)]; if (existingKey && Flag.toObject(existingKey).param) { throw new CommandError( "invalid_flag", `negated option ${name} ${"`" + Flag.toString(flag2, name) + "`"} can only be used with boolean flags` ); } } program.option( Flag.toString(flag2, name), flagObj.desc, flagObj.negate && true ); if (flagObj.negate !== void 0) { program.option(Flag.toNegatedString(flag2, name), flagObj.negate); } } } function commandUsage(baseCmd, cmd, args) { let usage = cmd; if (baseCmd) usage = baseCmd + " " + cmd; let hasOptional = false; for (const [name, arg2] of Object.entries(args)) { const argObj = Arg.toObject(arg2); if (argObj.type === "required" && hasOptional) { throw new CommandError( "invalid_arg", `required positional argument ${"`" + Arg.toString(arg2, name) + "`"} cannot appear after an optional argument` ); } hasOptional || (hasOptional = argObj.type === "optional"); usage += " " + Arg.toString(arg2, name); } return usage; } function commandContext(args, flags, fnArgs) { const _args = Object.keys(args).reduce((acc, name, index) => { acc[name] = fnArgs[index]; return acc; }, {}); const _a = fnArgs[fnArgs.length - 1], { _ } = _a, opts = __objRest(_a, ["_"]); for (const [name, flag2] of Object.entries(flags)) { const flagObj = Flag.toObject(flag2); const key = flagObj.preserveCase ? name : Flag.toKebabCase(name); const value = opts[key]; if (!flagObj.param) { if (flagObj.negate || Flag.isNegated(key)) { const negatedKey = flagObj.negate ? `no-${key}` : key; delete opts[negatedKey]; } if (value === void 0) { if (Flag.isNegated(key)) { const negatedKey = key.slice(3); if (opts[negatedKey] === void 0) { opts[negatedKey] = true; } continue; } opts[key] = false; if (flagObj.char) opts[flagObj.char] = false; } else if (typeof value !== "boolean") { opts[key] = true; if (flagObj.char) opts[flagObj.char] = true; } continue; } if (value === void 0) { if (flagObj.param._type === "optional") { if (flagObj.param.defaultValue !== void 0) { opts[key] = flagObj.param.defaultValue; if (flagObj.char) opts[flagObj.char] = flagObj.param.defaultValue; } continue; } throw new CommandError( "invalid_flag", `option ${"`" + Flag.toString(flag2, name) + "`"} value is missing` ); } if (flagObj.param.type === "string" && typeof value !== "string") { if (typeof value !== "object") { if (value === true || value === void 0) throw new CommandError( "invalid_flag", `option ${"`" + Flag.toString(flag2, name) + "`"} value is missing` ); const parsedValue = String(value); opts[key] = parsedValue; if (flagObj.char) opts[flagObj.char] = parsedValue; } else if (Array.isArray(value)) { const val = value[value.length - 1]; const parsedValue = val === true ? "" : String(val); opts[key] = parsedValue; if (flagObj.char) opts[flagObj.char] = parsedValue; } else { throw new CommandError( "invalid_flag", `option ${"`" + Flag.toString(flag2, name) + "`"} value is not a string` ); } } if (flagObj.param.type === "number" && typeof value !== "number") { const parsedValue = Number(value); if (typeof value !== "string" || Number.isNaN(parsedValue)) { throw new CommandError( "invalid_flag", `option ${"`" + Flag.toString(flag2, name) + "`"} value is not a number` ); } opts[key] = parsedValue; if (flagObj.char) opts[flagObj.char] = parsedValue; } if (flagObj.param.type === "array") { if (Array.isArray(value)) { const parsedValue = value.map( (val) => val === true ? "" : String(val) ); opts[key] = parsedValue; if (flagObj.char) opts[flagObj.char] = parsedValue; } else { if (typeof value === "object" || value === true) throw new CommandError( "invalid_flag", `option ${"`" + Flag.toString(flag2, name) + "`"} value is not an array` ); const parsedValue = [String(value)]; opts[key] = parsedValue; if (flagObj.char) opts[flagObj.char] = parsedValue; } } } return { args: _args, flags: opts, restArgs: _ }; } // src/command.ts var Command = class { constructor(options) { this.options = options; } command(options) { var _a; const { program, programFlags, baseCmd, defaultCmd, container: parentContainer } = options; const command = program.command( commandUsage(baseCmd, this.options.name, this.options.args), this.options.description, { default: defaultCmd && defaultCmd === this } ); if (this.options.aliases.length) command.alias(...this.options.aliases); for (const example of this.options.examples) command.example(example); registerFlags(program, this.options.flags); let container = parentContainer; const subcommandTasks = []; for (const [tokens, subcommands] of this.options.subcommands) { let subContainer = container; if (tokens && subContainer) subContainer = import_hollywood_di.Hollywood.createWithParent( subContainer, tokens.tokens, tokens.options ); else if (tokens) subContainer = import_hollywood_di.Hollywood.create(tokens.tokens, tokens.options); container = subContainer; for (const subcommand of subcommands) { subcommandTasks.push(() => { subcommand.command({ program, programFlags, baseCmd: `${baseCmd ? baseCmd + " " + this.options.name : this.options.name}`, defaultCmd: defaultCmd === this ? void 0 : defaultCmd, container: subContainer }); }); } } const instances = (_a = container == null ? void 0 : container.instances) != null ? _a : {}; command.action((...fnArgs) => { const _a2 = commandContext( this.options.args, __spreadValues(__spreadValues({}, programFlags), this.options.flags), fnArgs ), { flags } = _a2, context = __objRest(_a2, ["flags"]); return this.options.action( __spreadProps(__spreadValues({}, context), { flags }), instances ); }); for (const task of subcommandTasks) task(); } }; var CommandBuilder = class { constructor(name) { this.options = { name, description: void 0, aliases: [], examples: [], args: {}, flags: {}, subcommands: [] }; } describe(description) { this.options.description = description; return this; } alias(...aliases) { this.options.aliases.push(...aliases); return this; } example(example) { this.options.examples.push(example); return this; } args(args) { this.options.args = args; return this; } flags(flags) { this.options.flags = flags; return this; } useFlags() { return this; } use() { return this; } provide(tokens, options) { this.options.subcommands.push([{ tokens, options }, []]); return this; } subcommands(...commands) { if (!this.options.subcommands.length) this.options.subcommands.push([void 0, []]); const [, subcommands] = this.options.subcommands[this.options.subcommands.length - 1]; subcommands.push(...commands); return this; } action(fn) { return new Command(__spreadProps(__spreadValues({}, this.options), { action: fn })); } }; function createCommand(name) { return new CommandBuilder(name); } // src/program.ts var import_hollywood_di2 = require("hollywood-di"); var import_sade = __toESM(require("sade")); // src/run.ts function run(_0, _1) { return __async(this, arguments, function* (cli, options, argv = process.argv) { try { const output = cli.parse(argv, { lazy: true, unknown: (options == null ? void 0 : options.errorOnUnknown) === true ? ( // will execute default unknown callback if true () => { } ) : ( // executes provided callback or allows unknown if undefined (options == null ? void 0 : options.errorOnUnknown) === false ? void 0 : options == null ? void 0 : options.errorOnUnknown ) }); if (!output) return; const res = yield output.handler.apply(null, output.args); return res; } catch (err) { if (process.exitCode === void 0) process.exitCode = 1; if (options == null ? void 0 : options.onError) options.onError(err); else if (err instanceof CommandError) console.error("error:", err.message); else throw err; } }); } // src/program.ts var Program = class { constructor(options) { this.options = options; } program() { const program = (0, import_sade.default)(this.options.name); if (this.options.version) program.version(this.options.version); if (this.options.description) program.describe(this.options.description); for (const example of this.options.examples) program.example(example); registerFlags(program, this.options.flags); for (const [container, commands] of this.options.commands) { for (const command of commands) { command.command({ program, programFlags: this.options.flags, defaultCmd: this.options.default, container }); } } return program; } run(options, argv) { return run(this.program(), options, argv); } }; var ProgramBuilder = class { constructor(name, container) { this.options = { name, version: void 0, description: void 0, examples: [], flags: {}, container, commands: [], default: void 0 }; } version(version) { this.options.version = version; return this; } describe(description) { this.options.description = description; return this; } example(example) { this.options.examples.push(example); return this; } flags(flags) { this.options.flags = flags; return this; } provide(tokens, options) { let parent = this.options.container; if (parent) this.options.container = import_hollywood_di2.Hollywood.createWithParent( parent, tokens, options ); else this.options.container = import_hollywood_di2.Hollywood.create( tokens, options ); this.options.commands.push([ this.options.container, [] ]); return this; } commands(...commands) { if (!this.options.commands.length) this.options.commands.push([ this.options.container, [] ]); const [, subcommands] = this.options.commands[this.options.commands.length - 1]; subcommands.push(...commands); return this; } default(command) { this.options.default = command; return this; } build() { return new Program(this.options); } }; function createProgram(name, container) { return new ProgramBuilder(name, container); } // src/single.ts var import_hollywood_di3 = require("hollywood-di"); var import_sade2 = __toESM(require("sade")); var SingleProgram = class { constructor(options) { this.options = options; } program() { var _a, _b; const program = (0, import_sade2.default)( commandUsage(void 0, this.options.name, this.options.args), true ); if (this.options.version) program.version(this.options.version); if (this.options.description) program.describe(this.options.description); for (const example of this.options.examples) program.example(example); registerFlags(program, this.options.flags); const instances = (_b = (_a = this.options.container) == null ? void 0 : _a.instances) != null ? _b : {}; return program.action((...fnArgs) => { const ctx = commandContext(this.options.args, this.options.flags, fnArgs); return this.options.action(ctx, instances); }); } run(options, argv) { return run(this.program(), options, argv); } }; var SingleProgramBuilder = class { constructor(name, container) { this.options = { name, version: void 0, description: void 0, examples: [], args: {}, flags: {}, container }; } version(version) { this.options.version = version; return this; } describe(description) { this.options.description = description; return this; } example(example) { this.options.examples.push(example); return this; } args(args) { this.options.args = args; return this; } flags(flags) { this.options.flags = flags; return this; } provide(tokens, options) { let parent = this.options.container; if (parent) this.options.container = import_hollywood_di3.Hollywood.createWithParent( parent, tokens, options ); else this.options.container = import_hollywood_di3.Hollywood.create( tokens, options ); return this; } action(fn) { return new SingleProgram(__spreadProps(__spreadValues({}, this.options), { action: fn })); } }; function createSingleProgram(name, container) { return new SingleProgramBuilder(name, container); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Arg, Command, CommandError, Flag, Program, SingleProgram, arg, createCommand, createProgram, createSingleProgram, flag, run });