bob-core 1.4.0 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/cjs/index.js +4 -4
- package/dist/cjs/package-88kG_8yl.cjs +1 -0
- package/dist/cjs/src/Cli.d.ts +17 -10
- package/dist/cjs/src/Command.d.ts +47 -34
- package/dist/cjs/src/CommandIO.d.ts +12 -0
- package/dist/cjs/src/CommandParser.d.ts +76 -44
- package/dist/cjs/src/CommandRegistry.d.ts +7 -6
- package/dist/cjs/src/CommandSignatureParser.d.ts +85 -0
- package/dist/cjs/src/CommandWithSignature.d.ts +37 -0
- package/dist/cjs/src/ExceptionHandler.d.ts +3 -0
- package/dist/cjs/src/Logger.d.ts +17 -0
- package/dist/cjs/src/commands/HelpCommand.d.ts +2 -4
- package/dist/cjs/src/contracts/CommandOption.d.ts +3 -4
- package/dist/cjs/src/contracts/LoggerContract.d.ts +14 -0
- package/dist/cjs/src/contracts/index.d.ts +1 -0
- package/dist/cjs/src/errors/BadCommandOption.d.ts +1 -1
- package/dist/cjs/src/errors/BadCommandParameter.d.ts +1 -1
- package/dist/cjs/src/errors/BobError.d.ts +2 -1
- package/dist/cjs/src/errors/CommandNotFoundError.d.ts +1 -1
- package/dist/cjs/src/errors/InvalidOption.d.ts +4 -4
- package/dist/cjs/src/errors/MissingRequiredArgumentValue.d.ts +3 -4
- package/dist/cjs/src/errors/MissingRequiredOptionValue.d.ts +6 -0
- package/dist/cjs/src/errors/MissingSignatureArgument.d.ts +4 -4
- package/dist/cjs/src/errors/MissingSignatureOption.d.ts +4 -4
- package/dist/cjs/src/errors/index.d.ts +6 -0
- package/dist/cjs/src/index.d.ts +7 -1
- package/dist/cjs/src/lib/optionHelpers.d.ts +5 -0
- package/dist/cjs/src/lib/types.d.ts +29 -0
- package/dist/cjs/src/lib/valueConverter.d.ts +10 -0
- package/dist/cjs/src/options/HelpOption.d.ts +4 -2
- package/dist/cjs/src/testFixtures.d.ts +11 -0
- package/dist/esm/index.js +819 -367
- package/dist/esm/package-COKp1myj.js +31 -0
- package/dist/esm/src/Cli.d.ts +17 -10
- package/dist/esm/src/Command.d.ts +47 -34
- package/dist/esm/src/CommandIO.d.ts +12 -0
- package/dist/esm/src/CommandParser.d.ts +76 -44
- package/dist/esm/src/CommandRegistry.d.ts +7 -6
- package/dist/esm/src/CommandSignatureParser.d.ts +85 -0
- package/dist/esm/src/CommandWithSignature.d.ts +37 -0
- package/dist/esm/src/ExceptionHandler.d.ts +3 -0
- package/dist/esm/src/Logger.d.ts +17 -0
- package/dist/esm/src/commands/HelpCommand.d.ts +2 -4
- package/dist/esm/src/contracts/CommandOption.d.ts +3 -4
- package/dist/esm/src/contracts/LoggerContract.d.ts +14 -0
- package/dist/esm/src/contracts/index.d.ts +1 -0
- package/dist/esm/src/errors/BadCommandOption.d.ts +1 -1
- package/dist/esm/src/errors/BadCommandParameter.d.ts +1 -1
- package/dist/esm/src/errors/BobError.d.ts +2 -1
- package/dist/esm/src/errors/CommandNotFoundError.d.ts +1 -1
- package/dist/esm/src/errors/InvalidOption.d.ts +4 -4
- package/dist/esm/src/errors/MissingRequiredArgumentValue.d.ts +3 -4
- package/dist/esm/src/errors/MissingRequiredOptionValue.d.ts +6 -0
- package/dist/esm/src/errors/MissingSignatureArgument.d.ts +4 -4
- package/dist/esm/src/errors/MissingSignatureOption.d.ts +4 -4
- package/dist/esm/src/errors/index.d.ts +6 -0
- package/dist/esm/src/index.d.ts +7 -1
- package/dist/esm/src/lib/optionHelpers.d.ts +5 -0
- package/dist/esm/src/lib/types.d.ts +29 -0
- package/dist/esm/src/lib/valueConverter.d.ts +10 -0
- package/dist/esm/src/options/HelpOption.d.ts +4 -2
- package/dist/esm/src/testFixtures.d.ts +11 -0
- package/package.json +5 -1
- package/dist/cjs/package-6sByjm31.cjs +0 -1
- package/dist/esm/package-eljsfLkU.js +0 -31
package/README.md
CHANGED
|
@@ -25,9 +25,9 @@ cli.run('commandName', 'arg1', 'arg2', 'arg3');
|
|
|
25
25
|
Create a command in the `commands` folder:
|
|
26
26
|
|
|
27
27
|
```typescript
|
|
28
|
-
import {
|
|
28
|
+
import { LegacyCommand } from 'bob-core';
|
|
29
29
|
|
|
30
|
-
export default class MyCommand extends
|
|
30
|
+
export default class MyCommand extends LegacyCommand {
|
|
31
31
|
public name = 'my-command {arg1} {--option1}';
|
|
32
32
|
public description = 'This is my command';
|
|
33
33
|
|
|
@@ -87,7 +87,7 @@ sub:
|
|
|
87
87
|
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
##
|
|
90
|
+
## LegacyCommand help
|
|
91
91
|
|
|
92
92
|
You can also display the help of a specific command.
|
|
93
93
|
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("minimist"),a=require("chalk"),w=require("prompts"),W=require("node:fs"),D=require("path"),B=require("string-similarity");function L(r){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(r){for(const t in r)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(r,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>r[t]})}}return e.default=r,Object.freeze(e)}const _=L(W),G=L(B);class y extends Error{}class U extends y{constructor(e){let t=`Argument "${e.param}" value is invalid.`;e.reason?t+=` Reason: ${e.reason}`:t+=` Value: "${e.value}"`,super(t),this.param=e}pretty(e){e.log(a` {white.bgRed ERROR } Argument {bold.yellow ${this.param.param}} value is invalid. `),(this.param.value||this.param.reason)&&e.log(""),this.param.value&&e.log(a` {blue Value}: ${this.param.value}`),this.param.reason&&e.log(a` {yellow Reason}: ${this.param.reason}`)}}class C extends y{constructor(e){let t=`Option "${e.option}" value is invalid.`;e.reason?t+=` Reason: ${e.reason}`:t+=` Value: "${e.value}"`,super(t),this.param=e}pretty(e){e.log(a` {white.bgRed ERROR } Option {bold.yellow ${this.param.option}} value is invalid. `),(this.param.value||this.param.reason)&&e.log(""),this.param.value&&e.log(a` {blue Value}: ${this.param.value}`),this.param.reason&&e.log(a` {yellow Reason}: ${this.param.reason}`)}}function k(r){if(r==="string"||r==="number")return null;if(r==="boolean")return!1;if(Array.isArray(r)&&r.length===1){if(r[0]==="string")return[];if(r[0]==="number")return[]}throw new Error("Invalid option type: "+r)}function N(r){return typeof r=="string"?k(r):Array.isArray(r)?[]:typeof r=="object"&&r.type?r.default!==void 0?r.default:k(r.type):null}function f(r){return typeof r=="string"||Array.isArray(r)?{type:r,default:N(r),description:"",alias:[],required:!1,variadic:!1}:{type:r.type,default:r.default??N(r.type),description:r.description??"",alias:r.alias?Array.isArray(r.alias)?r.alias:[r.alias]:[],required:r.required??!1,variadic:r.variadic??!1}}class I extends y{constructor(e,t={}){super(`Invalid option ${e} in not recognized`),this.option=e,this.optionsSchema=t}pretty(e){const t=Object.entries(this.optionsSchema);if(t.length>0){e.log(a`\n{yellow Available options}:`);for(const[n,i]of t){const s=f(i),o=typeof s.alias=="string"?[s.alias]:s.alias,l=Array.isArray(s.type)?`[${s.type[0]}]`:s.type,u=`--${n}${o.length>0?o.map(m=>`, -${m}`).join(""):""}`,d=" ".repeat(30-u.length);e.log(a` {green ${u}} ${d} ${s.description||"\b"} {white (${l})}`)}e.log("")}e.log(a`{white.bgRed ERROR } Option {bold.yellow ${this.option}} is not recognized.`)}}class j extends y{constructor(e){super(`Command "${e}" not found.`),this.command=e}pretty(e){e.log(a`{bgRed ERROR } Command {yellow ${this.command}} not found.`)}}class v extends y{constructor(e,t){super(`Missing ${e} in the command signature`),this.argument=e,this.argumentsSchema=t}pretty(e){const t=Object.entries(this.argumentsSchema);if(t.length){e.log(a`\n{yellow Available arguments}:`);for(const[n,i]of t){const s=f(i),o=Array.isArray(s.type)?`[${s.type[0]}]`:s.type,l=o?a`{white (${o})}`:"",u=" ".repeat(20-n.length);e.log(a` {green ${n}} ${u} ${s.description??"\b"} ${l}`)}e.log("")}e.log(a`{white.bgRed ERROR } Argument {bold.yellow ${this.argument}} is missing in the signature.`)}}class b extends y{constructor(e,t){super(`Missing ${e} in the command signature`),this.option=e,this.optionsSchema=t}pretty(e){const t=Object.entries(this.optionsSchema);if(t.length){e.log(a`{yellow Available options}:`);for(const[n,i]of t){const s=f(i),o=Array.isArray(s.type)?`[${s.type[0]}]`:s.type,l=o?a`{white (${o})}`:"",u=" ".repeat(20-n.length);e.log(a` {green ${n}} ${u} ${s.description??"\b"} ${l}`)}e.log("")}e.log(a`{white.bgRed ERROR } Option {bold.yellow ${this.option}} is missing in the signature.`)}}class O extends y{constructor(e){super(`Argument "${e}" is required.`),this.argument=e}pretty(e){e.log(a`{white.bgRed ERROR } Argument {bold.yellow ${this.argument}} is required.`)}}class F extends y{constructor(e){super(`Argument "${e}" is required.`),this.option=e}pretty(e){e.log(a`{white.bgRed ERROR } Option {bold.yellow ${this.option}} is required.`)}}function $(r,e,t,n){if(r==null)return n??null;if(e==="string")return String(r);if(e==="number"){const i=Number(r);if(isNaN(i))throw new C({option:t,reason:`Expected a number, got "${r}"`});return i}if(e==="boolean")return typeof r=="boolean"?r:r==="true"||r==="1"?!0:r==="false"||r==="0"?!1:!!r;if(Array.isArray(e)){const i=e[0],s=Array.isArray(r)?r:[r];if(i==="string")return s.map(o=>String(o));if(i==="number")return s.map(o=>{const l=Number(o);if(isNaN(l))throw new C({option:t,reason:`Expected array of numbers, got "${o}" in array`});return l})}return r}class S{options;parsedOptions=null;arguments;parsedArguments=null;io;constructor(e){this.options=e.options,this.arguments=e.arguments,this.io=e.io}init(e){const{_:t,...n}=P(e);return this.validateUnknownOptions(n),this.parsedOptions=this.handleOptions(n),this.parsedArguments=this.handleArguments(t),{options:this.parsedOptions,arguments:this.parsedArguments}}async validate(){for(const e in this.options)if(f(this.options[e]).required&&(this.parsedOptions?.[e]===void 0||this.parsedOptions?.[e]===null))throw new F(e);for(const e in this.arguments)if(f(this.arguments[e]).required&&(this.parsedArguments?.[e]===void 0||this.parsedArguments?.[e]===null))throw new O(e)}option(e){if(!this.parsedOptions)throw new Error("Options have not been parsed yet. Call init() first.");return this.parsedOptions[e]}argument(e){if(!this.parsedArguments)throw new Error("Arguments have not been parsed yet. Call init() first.");return this.parsedArguments[e]}validateUnknownOptions(e){const t=new Set;for(const n in this.options){t.add(n);const i=f(this.options[n]);for(const s of i.alias)t.add(s)}for(const n in e)if(!t.has(n))throw new I(n,this.options)}handleOptions(e){const t={};for(const n in this.options){const i=f(this.options[n]);t[n]=this.resolveOptionValue(n,i,e,"option")}return t}handleArguments(e){const t={},n=[...e];for(const i in this.arguments){const s=f(this.arguments[i]);if(s.variadic){t[i]=this.handleVariadicArgument(i,s,n);continue}t[i]=this.resolveArgumentValue(i,s,n.shift())}return t}handleVariadicArgument(e,t,n){return n.length?$(n,t.type,e,t.default):t.default}resolveArgumentValue(e,t,n){return n===void 0?t.default:$(n,t.type,e,t.default)}resolveOptionValue(e,t,n,i){let s;const o=[e,...t.alias];for(const l of o)if(l in n){s=n[l];break}if(s===void 0){if(t.required)throw new C({option:e,reason:`${i==="option"?"Option":"Argument"} is required but not provided`});return t.default}return $(s,t.type,e,t.default)}optionDefinitions(){const e={};for(const t in this.options)e[t]=f(this.options[t]);return e}argumentDefinitions(){const e={};for(const t in this.arguments)e[t]=f(this.arguments[t]);return e}availableOptions(){return Object.keys(this.options)}availableArguments(){return Object.keys(this.arguments)}}function R(r){return new Array(r+5).join(" ")}class V{type="boolean";option="help";alias=["h"];default=!1;description=a`Display help for the given command. When no command is given display help for the {green list} command`;async handler(){const e=this.parser.argumentDefinitions(),t=this.parser.optionDefinitions(),n=Object.entries(e),i=Object.entries(t),s=i.map(([m,c])=>{const h=Array.isArray(c.alias)?c.alias:c.alias?[c.alias]:[];return{name:m,...c,optionWithAlias:`--${m}${h.map(p=>`, -${p}`).join("")}`}}),o=n.filter(([,m])=>m.required);this.io.log(a`{yellow Description}:`),this.io.log(a` ${this.description}\n`),this.io.log(a`{yellow Usage}:`),this.io.log(a` ${this.command} ${o.length>0?o.map(([m])=>`<${m}>`).join(" "):"\b"} [options]`);const l=Math.max(...s.map(m=>m.optionWithAlias.length),0),u=Math.max(...n.map(([m])=>m.length),0),d=u>l?u:l;if(n.length>0){this.io.log(a`\n{yellow Arguments}:`);for(const[m,c]of n){const h=R(d-m.length);let p=a` {green ${m}} ${h} ${c.description??"\b"}`;if(c.default!==void 0&&!c.required){const T=(Array.isArray(c.type)?`[${c.type[0]}]`:c.type)==="array"||Array.isArray(c.type)?JSON.stringify(c.default):c.default;p+=a` {yellow [default: ${T}]}`}c.variadic&&(p+=a` {white (variadic)}`),this.io.log(p)}}if(i.length>0){this.io.log(a`\n{yellow Options}:`);for(const m of s){const c=R(d-m.optionWithAlias.length);let h=a`{green ${m.optionWithAlias}} ${c} ${m.description??"\b"}`;if(m.type){const p=Array.isArray(m.type)?`[${m.type[0]}]`:m.type;h+=a` {white (${p})}`}if(m.default!==void 0&&!m.required){const q=(Array.isArray(m.type)?`[${m.type[0]}]`:m.type)==="array"||Array.isArray(m.type)?JSON.stringify(m.default):m.default;h+=a` {yellow [default: ${q}]}`}this.io.log(h)}}if(this.commandsExamples.length>0){this.io.log(a`\n{yellow Examples}:`);let m=process.argv[0].split("/").pop();m==="node"&&(m+=" "+process.argv[1].split("/").pop());for(const[c,h]of this.commandsExamples.entries())c>0&&this.io.log(""),this.io.log(` ${h.description}
|
|
2
|
+
`),this.io.log(a` {green ${m} ${h.command}}`)}return-1}}class x{logger;constructor(e){this.logger=e}log(...e){this.logger.log(...e)}info(...e){this.logger.info(...e)}warn(...e){this.logger.warn(...e)}error(...e){this.logger.error(...e)}debug(...e){this.logger.debug(...e)}verbose(...e){this.logger.verbose(...e)}async askForConfirmation(e="Do you want to continue?",t){return(await w({type:"confirm",name:"value",message:e,initial:t??!1})).value}async askForInput(e,t,n){return(await w({type:"text",name:"value",message:e,initial:t,...n}))?.value??null}async askForToggle(e,t,n){return(await w({type:"toggle",name:"value",message:e,initial:t,...n}))?.value??null}async askForSelect(e,t,n){if(t.length===0)throw new Error("No options provided");const i=[];for(const o of t)typeof o=="string"?i.push({title:o,value:o}):i.push(o);return(await w({type:"select",name:"value",message:e,choices:i,...n}))?.value??null}newLoader(e="",t=["⠙","⠘","⠰","⠴","⠤","⠦","⠆","⠃","⠋","⠉"],n=100){let i=e,s=null,o=0;const l=setInterval(function(){s&&(process.stdout.write(new TextEncoder().encode("\r"+" ".repeat(s.length+5)+"\r")),s=null),process.stdout.write(new TextEncoder().encode("\r"+t[o++]+" "+i)),o=o%t.length},n),u=()=>{clearInterval(l),process.stdout.write(new TextEncoder().encode("\r"+" ".repeat(i.length+5)+"\r"))};return{[Symbol.dispose]:u,[Symbol.asyncDispose]:u,updateText:d=>{s=i,i=d},stop:u}}}class A{_command;description="";commandsExamples=[];get command(){return this._command}ctx;io;logger;_handler;parser;tmp;defaultOptions(){return[new V]}newCommandParser(e){return new S({io:e.io,options:e.options,arguments:e.arguments})}newCommandIO(e){return new x(e.logger)}constructor(e,t){this._command=e,this.description=t?.description??"";const n=this.defaultOptions();if(n.length>0){this.tmp={options:{},arguments:{}};for(const i of n)this.tmp.options[i.option]=i}}handler(e){return this._handler=e,this}options(e){return this.tmp={options:{...this.tmp?.options??{},...e},arguments:this.tmp?.arguments??{}},this}arguments(e){return this.tmp={options:this.tmp?.options??{},arguments:{...this.tmp?.arguments??{},...e}},this}async run(e){if(!this._handler&&!this.handle)throw new Error(`No handler defined for command ${this.command||"(unknown)"}`);let t;if(this.ctx=e.ctx,this.logger=e.logger,this.io=this.newCommandIO({logger:e.logger}),e&&"args"in e){const s=this.tmp?.options??{};for(const o of this.defaultOptions())o.option in s||(s[o.option]=o);this.parser=this.newCommandParser({io:this.io,options:s,arguments:this.tmp?.arguments??{}}),t=this.parser.init(e.args);for(const o of this.defaultOptions())if(t.options[o.option]===!0){const l=await o.handler.call(this);if(l&&l!==0)return l}await this.parser.validate()}else t={options:e.options,arguments:e.arguments};const n=this.preHandle?await this.preHandle():null;if(n&&n!==0)return n;if(!this._handler&&this.handle)this._handler=this.handle.bind(this);else if(!this._handler)throw new Error(`No handler defined for command ${this.command||"(unknown)"}`);return await this._handler(e.ctx,t)??0}}class g extends S{command;argumentsSchema;optionsSchema;constructor(e){const t=g.parseSignature(e.signature,e.helperDefinitions,e.defaultOptions);super({io:e.io,options:t.options,arguments:t.arguments}),this.command=t.command,this.optionsSchema=t.options,this.argumentsSchema=t.arguments}static parseSignature(e,t,n){const[i,...s]=e.split(/\{(.*?)\}/g).map(u=>u.trim()).filter(Boolean),o={},l={};for(const u of s){const{name:d,isOption:m,definition:c}=g.parseParamSignature(u,t);m?o[d]=c:l[d]=c}for(const u of n)o[u.option]={type:u.type,required:u.required,alias:u.alias,variadic:u.variadic??!1,description:u.description,default:u.default??null};return{command:i,options:o,arguments:l}}option(e){if(!this.optionsSchema[e])throw new b(e,this.optionsSchema);return super.option(e)}setOption(e,t){if(!this.optionsSchema[e])throw new b(e,this.optionsSchema);this.parsedOptions&&(this.parsedOptions[e]=t)}optionHelp(e){if(!this.optionsSchema[e])throw new b(e,this.optionsSchema);return f(this.optionsSchema[e]).description}argumentHelp(e){if(!this.argumentsSchema[e])throw new v(e,this.argumentsSchema);return f(this.argumentsSchema[e]).description}argument(e){if(!this.argumentsSchema[e])throw new v(e,this.argumentsSchema);return super.argument(e)}setArgument(e,t){if(!this.argumentsSchema[e])throw new v(e,this.argumentsSchema);this.parsedArguments&&(this.parsedArguments[e]=t)}getArgumentSignatures(){return this.argumentsSchema}getOptionSignatures(){return this.optionsSchema}static parseParamSignature(e,t){let n=e,i=!1;const s={required:!0,type:"string",description:void 0,default:null,variadic:!1};if(n.includes(":")){const[o,l]=n.split(":");n=o.trim(),s.description=l.trim()}if(n.includes("=")){const[o,l]=n.split("=");n=o.trim(),s.default=l.trim(),s.required=!1,s.default.length?s.default==="true"?(s.default=!0,s.type="boolean"):s.default==="false"&&(s.default=!1,s.type="boolean"):s.default=null}else n.startsWith("--")&&(s.required=!1,s.default=!1,s.type="boolean");if(n.includes("|")){const[o,...l]=n.split("|");n=o.trim(),s.alias=l.map(u=>u.trim())}return n.startsWith("--")&&(i=!0,n=n.slice(2)),s.default==="*"&&(s.default=[],s.type=["string"]),n.endsWith("?")&&(s.required=!1,n=n.slice(0,-1)),n.endsWith("*")&&(s.type=["string"],s.variadic=!0,s.default=[],n=n.slice(0,-1)),s.description=s.description??t[n]??t[`--${n}`],{name:n,isOption:i,definition:s}}async validate(){for(const e in this.argumentsSchema){const t=f(this.argumentsSchema[e]),n=this.argument(e);if(!n&&t.required){const i=await this.promptForArgument(e,t);if(i)this.setArgument(e,i);else throw new O(e)}if(t.variadic&&t.required&&typeof n=="object"&&!n?.length)throw new O(e)}}async promptForArgument(e,t){if(t.type!=="string")return null;let n=a`{yellow.bold ${e}} is required`;return t.description&&(n+=a`: {gray (${t.description})}`),n+=`
|
|
3
|
+
`,await this.io.askForInput(n,t.default,{validate:i=>i?.trim()?.length?!0:`${e} cannot be empty`})}optionValues(){if(!this.parsedOptions)throw new Error("Options have not been parsed yet. Call init() first.");return this.parsedOptions}argumentValues(){if(!this.parsedArguments)throw new Error("Arguments have not been parsed yet. Call init() first.");return this.parsedArguments}}class z extends A{helperDefinitions={};get command(){return this.parser?this.parser.command:this.signature.split(" ")[0]}newCommandParser(e){return new g({io:e.io,signature:this.signature,helperDefinitions:this.helperDefinitions,defaultOptions:this.defaultOptions()})}constructor(){super("")}setOption(e,t){this.parser.setOption(e,t)}setArgument(e,t){this.parser instanceof g&&this.parser.setArgument(e,t)}option(e,t=null){return this.parser instanceof g?this.parser.option(e)??t:t}optionBoolean(e,t=!1){return this.parser instanceof g?this.parser.option(e)??t:t}optionArray(e,t=[]){if(this.parser instanceof g){const n=this.parser.option(e);if(!Array.isArray(n))throw new Error(`Option ${e} is not an array`);if(n.length)return n}return t}optionNumber(e,t=null){if(this.parser instanceof g){const n=this.parser.option(e);return n?typeof n=="number"?n:parseInt(n):t}return t}argument(e,t=null){return this.parser instanceof g?this.parser.argument(e)??t:t}argumentArray(e,t=[]){if(this.parser instanceof g){const n=this.parser.argument(e);if(!Array.isArray(n))throw new Error(`Argument ${e} is not an array`);if(n?.length)return n}return t}argumentBoolean(e,t=!1){return this.parser instanceof g?this.parser.argument(e)??t:t}argumentNumber(e,t=null){if(this.parser instanceof g){const n=this.parser.argument(e);return n?typeof n=="number"?n:parseInt(n):t}return t}async askForConfirmation(...e){return this.io.askForConfirmation(...e)}async askForInput(...e){return this.io.askForInput(...e)}async askForSelect(...e){return this.io.askForSelect(...e)}newLoader(...e){return this.io.newLoader(...e)}}class E{level;constructor(e={}){this.level=e.level??"info"}shouldLog(e){const t=["debug","verbose","info","warn","error","silent"],n=t.indexOf(this.level);return t.indexOf(e)>=n}setLevel(e){this.level=e}getLevel(){return this.level}log(...e){console.log(...e)}info(...e){this.shouldLog("info")&&console.log(...e)}warn(...e){this.shouldLog("warn")&&console.warn(...e)}error(...e){this.shouldLog("error")&&console.error(...e)}debug(...e){this.shouldLog("debug")&&console.log(...e)}verbose(...e){this.shouldLog("verbose")&&console.log(...e)}}class H{commands={};io;logger;get CommandIOClass(){return x}constructor(e){this.logger=e??new E,this.io=new this.CommandIOClass(this.logger)}getAvailableCommands(){return Object.keys(this.commands)}getCommands(){return Object.values(this.commands)}commandResolver=async e=>{let t=(await import(e)).default;if(!t)throw new Error(`The command at path ${e} does not have a default export.`);t?.default&&(t=t.default);let n;if(typeof t=="function")n=new t;else if(t instanceof A)n=t;else throw new Error(`The command at path ${e} is not a valid command class.`);return n};setCommandResolver(e){this.commandResolver=e}registerCommand(e,t=!1){const n=e.command;if(!n)throw new Error("Command signature is invalid, it must have a command name.");if(!t&&this.commands[n])throw new Error(`Command ${n} already registered.`);this.commands[n]=e}async loadCommandsPath(e){for await(const t of this.listCommandsFiles(e))try{const n=await this.commandResolver(t);this.registerCommand(n)}catch(n){throw new Error(`Command ${t} failed to load. ${n}`,{cause:n})}}async runCommand(e,t,...n){const i=typeof t=="string"?this.commands[t]:t,s=typeof t=="string"?t:i.command;if(!i){const o=await this.suggestCommand(s);return o?await this.runCommand(e,o,...n):1}return await i.run({ctx:e,logger:this.logger,args:n})??0}async suggestCommand(e){const t=this.getAvailableCommands(),{bestMatch:n,bestMatchIndex:i,ratings:s}=G.findBestMatch(e,t),o=s.filter(l=>l.rating>.3).map(l=>l.target);if(n.rating>0&&o.length<=1||n.rating>.7&&o.length>1){const l=t[i];return await this.askRunSimilarCommand(e,l)?l:null}if(o.length){this.io.error(a`{bgRed ERROR } Command {yellow ${e}} not found.\n`);const l=await this.io.askForSelect(a`{green Did you mean to run one of these commands instead?}`,o);if(l)return l}throw new j(e)}async askRunSimilarCommand(e,t){return this.io.error(a`{bgRed ERROR } Command {yellow ${e}} not found.\n`),this.io.askForConfirmation(a`{green Do you want to run {yellow ${t}} instead?} `)}async*listCommandsFiles(e){const t=_.readdirSync(e,{withFileTypes:!0});for(const n of t){const i=D.resolve(e,n.name);if(n.isDirectory())yield*this.listCommandsFiles(D.resolve(e,n.name));else{if(!i.endsWith(".ts")&&!i.endsWith(".js"))continue;yield i}}}}class J extends A{constructor(e){super("help",{description:a.bold("Show help information about the CLI and its commands")}),this.opts=e}async handle(){const e=this.opts.commandRegistry.getCommands(),t=this.opts.cliName??"Bob CLI",n=this.opts.cliVersion??"0.0.0",i=(await Promise.resolve().then(()=>require("./package-88kG_8yl.cjs")))?.default?.version??"0.0.0";this.io.log(a`${t} {green ${n}} (core: {yellow ${i}})
|
|
4
4
|
|
|
5
5
|
{yellow Usage}:
|
|
6
6
|
command [options] [arguments]
|
|
7
7
|
|
|
8
8
|
{yellow Available commands}:
|
|
9
|
-
`);const
|
|
9
|
+
`);const s=Math.max(...e.map(u=>u.command.length))??0,o={};for(const u of e){const d=u.command.split(":")[0];o[d]||(o[d]=[]),o[d].push(u)}const l=Object.entries(o).sort(([u],[d])=>u.toLowerCase().localeCompare(d.toLowerCase())).sort(([,u],[,d])=>u.length-d.length);for(const[u,d]of l){const m=d.length>1;m&&this.io.log(a`{yellow ${u}}:`);const c=d.sort((h,p)=>h.command.toLowerCase().localeCompare(p.command.toLowerCase()));for(const h of c){let p=R(s-h.command.length);m&&(p=p.slice(2)),this.io.log(a`${m?" ":""}{green ${h.command}} ${p} ${h.description}`)}}}}class M{logger;constructor(e){this.logger=e}handle(e){if(e instanceof y)return e.pretty(this.logger),-1;throw e}}class K{ctx;logger;commandRegistry;exceptionHandler;helpCommand;newCommandRegistry(e){return new H(e.logger)}newHelpCommand(e){return new J(e)}newExceptionHandler(e){return new M(e.logger)}constructor(e={}){this.ctx=e.ctx,this.logger=e.logger??new E,this.commandRegistry=this.newCommandRegistry({logger:this.logger}),this.exceptionHandler=this.newExceptionHandler({logger:this.logger}),this.helpCommand=this.newHelpCommand({cliName:e.name,cliVersion:e.version,commandRegistry:this.commandRegistry})}setCommandResolver(e){this.commandRegistry.setCommandResolver(e)}async withCommands(...e){for(const t of e)typeof t=="string"?await this.commandRegistry.loadCommandsPath(t):typeof t=="function"?this.registerCommand(new t):this.registerCommand(t)}async runCommand(e,...t){return e?await this.commandRegistry.runCommand(this.ctx,e,...t).catch(this.exceptionHandler.handle.bind(this.exceptionHandler)):await this.runHelpCommand()}async runHelpCommand(){return await this.runCommand(this.helpCommand)}registerCommand(e){this.commandRegistry.registerCommand(e)}}exports.BadCommandOption=C;exports.BadCommandParameter=U;exports.BobError=y;exports.Cli=K;exports.Command=A;exports.CommandIO=x;exports.CommandNotFoundError=j;exports.CommandParser=S;exports.CommandRegistry=H;exports.CommandSignatureParser=g;exports.CommandWithSignature=z;exports.ExceptionHandler=M;exports.HelpOption=V;exports.InvalidOption=I;exports.Logger=E;exports.MissingRequiredArgumentValue=O;exports.MissingRequiredOptionValue=F;exports.MissingSignatureArgument=v;exports.MissingSignatureOption=b;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e="bob-core",s="2.0.0-beta.2",t="BOB Core",i="module",n=!1,r=["dist/**"],o={".":{import:"./dist/esm/index.js",require:"./dist/cjs/index.js"}},c={"*":{"*":["./dist/cjs/*.d.ts"]}},d={start:"node -r @swc-node/register debug/main.ts",build:"rimraf ./dist && vite build",typecheck:"tsc --noEmit",prepare:"npm run build",test:"vitest run"},p="Léo Hubert",a="ISC",m={"@faker-js/faker":"^10.0.0","@swc-node/register":"^1.11.1","@types/minimist":"^1.2.5","@types/node":"^20.14.5","@types/prompts":"^2.4.9","@types/string-similarity":"^4.0.2","@vitest/coverage-v8":"^3.2.4",rimraf:"^6.0.1",tsx:"^4.20.6",typescript:"^5.7.3",vite:"^7.1.6","vite-plugin-dts":"^4.5.4",vitest:"^3.2.4"},l={chalk:"^4.1.2",minimist:"^1.2.8",prompts:"^2.4.2","string-similarity":"^4.0.4"},u={name:e,version:s,description:t,type:i,sideEffects:n,files:r,exports:o,typesVersions:c,scripts:d,author:p,license:a,devDependencies:m,dependencies:l};exports.author=p;exports.default=u;exports.dependencies=l;exports.description=t;exports.devDependencies=m;exports.exports=o;exports.files=r;exports.license=a;exports.name=e;exports.scripts=d;exports.sideEffects=n;exports.type=i;exports.typesVersions=c;exports.version=s;
|
package/dist/cjs/src/Cli.d.ts
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
import { CommandRegistry } from './CommandRegistry.js';
|
|
2
|
-
import {
|
|
3
|
-
import { default as HelpCommand } from './commands/HelpCommand.js';
|
|
2
|
+
import { default as HelpCommand, HelpCommandOptions } from './commands/HelpCommand.js';
|
|
4
3
|
import { ExceptionHandler } from './ExceptionHandler.js';
|
|
5
|
-
|
|
4
|
+
import { Command } from './Command.js';
|
|
5
|
+
import { Logger } from './Logger.js';
|
|
6
|
+
export type CliOptions<C = any> = {
|
|
6
7
|
ctx?: C;
|
|
7
8
|
name?: string;
|
|
8
9
|
version?: string;
|
|
10
|
+
logger?: Logger;
|
|
9
11
|
};
|
|
10
|
-
export declare class Cli<C> {
|
|
12
|
+
export declare class Cli<C = any> {
|
|
13
|
+
private readonly ctx?;
|
|
14
|
+
private readonly logger;
|
|
11
15
|
readonly commandRegistry: CommandRegistry;
|
|
12
16
|
private readonly exceptionHandler;
|
|
13
|
-
private readonly ctx?;
|
|
14
17
|
private readonly helpCommand;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
protected newCommandRegistry(opts: {
|
|
19
|
+
logger: Logger;
|
|
20
|
+
}): CommandRegistry;
|
|
21
|
+
protected newHelpCommand(opts: HelpCommandOptions): HelpCommand;
|
|
22
|
+
protected newExceptionHandler(opts: {
|
|
23
|
+
logger: Logger;
|
|
24
|
+
}): ExceptionHandler;
|
|
18
25
|
constructor(opts?: CliOptions<C>);
|
|
19
26
|
setCommandResolver(resolver: (path: string) => Promise<Command<C>>): void;
|
|
20
|
-
withCommands(...commands: Array<Command<C> | {
|
|
27
|
+
withCommands(...commands: Array<Command<C, any, any> | {
|
|
21
28
|
new (): Command<C>;
|
|
22
29
|
} | string>): Promise<void>;
|
|
23
|
-
runCommand(command: string | Command, ...args: any[]): Promise<number>;
|
|
30
|
+
runCommand(command: string | Command | undefined, ...args: any[]): Promise<number>;
|
|
24
31
|
runHelpCommand(): Promise<number>;
|
|
25
32
|
protected registerCommand(command: Command<C>): void;
|
|
26
33
|
}
|
|
@@ -1,45 +1,58 @@
|
|
|
1
|
+
import { OptionsSchema, OptionsObject, ArgumentsSchema, ArgumentsObject } from './lib/types.js';
|
|
1
2
|
import { CommandParser } from './CommandParser.js';
|
|
3
|
+
import { CommandSignatureParser } from './CommandSignatureParser.js';
|
|
2
4
|
import { CommandOption } from './contracts/index.js';
|
|
3
5
|
import { CommandIO } from './CommandIO.js';
|
|
6
|
+
import { Logger } from './Logger.js';
|
|
7
|
+
export type CommandHandlerOptions<Options extends OptionsSchema, Arguments extends ArgumentsSchema> = {
|
|
8
|
+
options: OptionsObject<Options>;
|
|
9
|
+
arguments: ArgumentsObject<Arguments>;
|
|
10
|
+
};
|
|
11
|
+
export type CommandHandler<C, Options extends OptionsSchema, Arguments extends ArgumentsSchema> = (ctx: C, opts: CommandHandlerOptions<Options, Arguments>) => Promise<number | void> | number | void;
|
|
12
|
+
type BaseCommandRunOption<C> = {
|
|
13
|
+
logger: Logger;
|
|
14
|
+
ctx: C;
|
|
15
|
+
};
|
|
16
|
+
export type CommandRunArgsOption<C> = {
|
|
17
|
+
args: string[];
|
|
18
|
+
} & BaseCommandRunOption<C>;
|
|
19
|
+
export type CommandRunParsedOption<C, Options extends OptionsSchema, Arguments extends ArgumentsSchema> = {
|
|
20
|
+
options: OptionsObject<Options>;
|
|
21
|
+
arguments: ArgumentsObject<Arguments>;
|
|
22
|
+
} & BaseCommandRunOption<C>;
|
|
23
|
+
export type CommandRunOption<C, Options extends OptionsSchema, Arguments extends ArgumentsSchema> = CommandRunArgsOption<C> | CommandRunParsedOption<C, Options, Arguments>;
|
|
4
24
|
export type CommandExample = {
|
|
5
25
|
description: string;
|
|
6
26
|
command: string;
|
|
7
27
|
};
|
|
8
|
-
export declare
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
protected ctx: C;
|
|
12
|
-
protected helperDefinitions: {
|
|
13
|
-
[key: string]: string;
|
|
14
|
-
};
|
|
28
|
+
export declare class Command<C = any, Options extends OptionsSchema = {}, Arguments extends ArgumentsSchema = {}> {
|
|
29
|
+
readonly _command: string;
|
|
30
|
+
readonly description: string;
|
|
15
31
|
protected commandsExamples: CommandExample[];
|
|
16
|
-
|
|
32
|
+
get command(): string;
|
|
33
|
+
protected ctx: C;
|
|
17
34
|
protected io: CommandIO;
|
|
18
|
-
protected
|
|
35
|
+
protected logger: Logger;
|
|
36
|
+
protected _handler?: CommandHandler<C, Options, Arguments>;
|
|
37
|
+
protected parser: CommandParser<Options, Arguments>;
|
|
38
|
+
private tmp?;
|
|
39
|
+
protected defaultOptions(): CommandOption<Command<any, any, any>>[];
|
|
40
|
+
protected newCommandParser(opts: {
|
|
41
|
+
io: CommandIO;
|
|
42
|
+
options: Options;
|
|
43
|
+
arguments: Arguments;
|
|
44
|
+
}): CommandParser<Options, Arguments> | CommandSignatureParser;
|
|
45
|
+
protected newCommandIO(opts: {
|
|
46
|
+
logger: Logger;
|
|
47
|
+
}): CommandIO;
|
|
48
|
+
constructor(command: string, opts?: {
|
|
49
|
+
description?: string;
|
|
50
|
+
});
|
|
19
51
|
protected preHandle?(): Promise<void | number>;
|
|
20
|
-
protected
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
run(
|
|
25
|
-
protected setOption(name: string, value: any): void;
|
|
26
|
-
protected setArgument(name: string, value: any): void;
|
|
27
|
-
protected option<T = string>(key: string): T | null;
|
|
28
|
-
protected option<T = string>(key: string, defaultValue: T): NoInfer<T>;
|
|
29
|
-
protected optionBoolean(key: string, defaultValue?: boolean): boolean;
|
|
30
|
-
protected optionArray<T = string>(key: string, defaultValue?: Array<T>): Array<NoInfer<T>>;
|
|
31
|
-
protected optionNumber(key: string): number | null;
|
|
32
|
-
protected optionNumber(key: string, defaultValue: number): number;
|
|
33
|
-
protected argument<T = string>(key: string): T | null;
|
|
34
|
-
protected argument<T = string>(key: string, defaultValue: T): NoInfer<T>;
|
|
35
|
-
protected argumentArray<T = string>(key: string, defaultValue?: Array<any>): Array<T>;
|
|
36
|
-
protected argumentBoolean(key: string, defaultValue?: boolean): boolean;
|
|
37
|
-
protected argumentNumber(key: string, defaultValue?: number | null): number | null;
|
|
38
|
-
/**
|
|
39
|
-
* Prompt utils
|
|
40
|
-
*/
|
|
41
|
-
askForConfirmation(...opts: Parameters<typeof this.io.askForConfirmation>): ReturnType<typeof this.io.askForConfirmation>;
|
|
42
|
-
askForInput(...opts: Parameters<typeof this.io.askForInput>): ReturnType<typeof this.io.askForInput>;
|
|
43
|
-
askForSelect(...opts: Parameters<typeof this.io.askForSelect>): ReturnType<typeof this.io.askForSelect>;
|
|
44
|
-
newLoader(...opts: Parameters<typeof this.io.newLoader>): ReturnType<typeof this.io.newLoader>;
|
|
52
|
+
protected handle?(ctx: C, opts: CommandHandlerOptions<Options, Arguments>): Promise<number | void> | number | void;
|
|
53
|
+
handler(handler: CommandHandler<C, Options, Arguments>): Command<C, Options, Arguments>;
|
|
54
|
+
options<Opts extends OptionsSchema>(opts: Opts): Command<C, Options & Opts, Arguments>;
|
|
55
|
+
arguments<Args extends ArgumentsSchema>(args: Args): Command<C, Options, Arguments & Args>;
|
|
56
|
+
run(opts: CommandRunOption<C, Options, Arguments>): Promise<number | void>;
|
|
45
57
|
}
|
|
58
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Logger } from './Logger.js';
|
|
1
2
|
export type SelectOption = {
|
|
2
3
|
title: string;
|
|
3
4
|
value?: any;
|
|
@@ -6,6 +7,17 @@ export type SelectOption = {
|
|
|
6
7
|
description?: string | undefined;
|
|
7
8
|
};
|
|
8
9
|
export declare class CommandIO {
|
|
10
|
+
private logger;
|
|
11
|
+
constructor(logger: Logger);
|
|
12
|
+
/**
|
|
13
|
+
* Logger methods
|
|
14
|
+
*/
|
|
15
|
+
log(...args: any[]): void;
|
|
16
|
+
info(...args: any[]): void;
|
|
17
|
+
warn(...args: any[]): void;
|
|
18
|
+
error(...args: any[]): void;
|
|
19
|
+
debug(...args: any[]): void;
|
|
20
|
+
verbose(...args: any[]): void;
|
|
9
21
|
/**
|
|
10
22
|
* Prompt utils
|
|
11
23
|
*/
|
|
@@ -1,48 +1,80 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OptionsSchema, OptionReturnType, OptionsObject } from './lib/types.js';
|
|
2
|
+
import { OptionDetails } from './lib/optionHelpers.js';
|
|
2
3
|
import { CommandIO } from './CommandIO.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Parses command-line arguments into typed options and arguments
|
|
6
|
+
* Handles validation, type conversion, and default values
|
|
7
|
+
*/
|
|
8
|
+
export declare class CommandParser<Options extends OptionsSchema, Arguments extends OptionsSchema> {
|
|
9
|
+
protected options: Options;
|
|
10
|
+
protected parsedOptions: OptionsObject<Options> | null;
|
|
11
|
+
protected arguments: Arguments;
|
|
12
|
+
protected parsedArguments: OptionsObject<Arguments> | null;
|
|
13
|
+
protected io: CommandIO;
|
|
14
|
+
constructor(opts: {
|
|
15
|
+
io: CommandIO;
|
|
16
|
+
options: Options;
|
|
17
|
+
arguments: Arguments;
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Parses raw command-line arguments into structured options and arguments
|
|
21
|
+
* @param args - Raw command line arguments (typically from process.argv.slice(2))
|
|
22
|
+
* @returns Object containing parsed options and arguments
|
|
23
|
+
* @throws {InvalidOption} If an unknown option is provided
|
|
24
|
+
* @throws {BadCommandOption} If a value cannot be converted to the expected type
|
|
25
|
+
*/
|
|
26
|
+
init(args: string[]): {
|
|
27
|
+
options: OptionsObject<Options>;
|
|
28
|
+
arguments: OptionsObject<Arguments>;
|
|
18
29
|
};
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
private argumentsSignature;
|
|
24
|
-
private optionSignatures;
|
|
25
|
-
private optionAliases;
|
|
26
|
-
constructor(io: CommandIO, signature: string, helperDefinitions: {
|
|
27
|
-
[key: string]: string;
|
|
28
|
-
}, defaultCommandOptions: CommandOption<any>[], ...args: any[]);
|
|
29
|
-
option(name: string): any;
|
|
30
|
-
setOption(name: string, value: any): void;
|
|
31
|
-
optionHelp(name: string): string | undefined;
|
|
32
|
-
argument(name: string): any;
|
|
33
|
-
setArgument(name: string, value: any): void;
|
|
34
|
-
argumentHelp(name: string): string | undefined;
|
|
35
|
-
getArgumentSignatures(): {
|
|
36
|
-
[argument: string]: ArgSignature;
|
|
37
|
-
};
|
|
38
|
-
getOptionSignatures(): {
|
|
39
|
-
[option: string]: ArgSignature;
|
|
40
|
-
};
|
|
41
|
-
private getParamValue;
|
|
42
|
-
private handleArguments;
|
|
43
|
-
private handleOptions;
|
|
44
|
-
private parseSignature;
|
|
45
|
-
private parseDefaultOptions;
|
|
46
|
-
private parseParamSignature;
|
|
30
|
+
/**
|
|
31
|
+
* Validates the parsed options and arguments
|
|
32
|
+
* @throws {Error} If validation fails
|
|
33
|
+
*/
|
|
47
34
|
validate(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Retrieves a parsed option value by name
|
|
37
|
+
* @param name - The option name
|
|
38
|
+
* @returns The typed option value
|
|
39
|
+
* @throws {Error} If init() has not been called yet
|
|
40
|
+
*/
|
|
41
|
+
option<OptsName extends keyof Options>(name: OptsName): OptionReturnType<Options[OptsName]>;
|
|
42
|
+
/**
|
|
43
|
+
* Retrieves a parsed argument value by name
|
|
44
|
+
* @param name - The argument name
|
|
45
|
+
* @returns The typed argument value
|
|
46
|
+
* @throws {Error} If init() has not been called yet
|
|
47
|
+
*/
|
|
48
|
+
argument<ArgName extends keyof Arguments>(name: ArgName): OptionReturnType<Arguments[ArgName]>;
|
|
49
|
+
/**
|
|
50
|
+
* Validates that all provided options are recognized
|
|
51
|
+
* @throws {InvalidOption} If an unknown option is found
|
|
52
|
+
*/
|
|
53
|
+
private validateUnknownOptions;
|
|
54
|
+
/**
|
|
55
|
+
* Processes named options from minimist output
|
|
56
|
+
*/
|
|
57
|
+
private handleOptions;
|
|
58
|
+
/**
|
|
59
|
+
* Processes positional arguments from minimist output
|
|
60
|
+
*/
|
|
61
|
+
private handleArguments;
|
|
62
|
+
/**
|
|
63
|
+
* Handles variadic arguments that consume all remaining positional values
|
|
64
|
+
*/
|
|
65
|
+
private handleVariadicArgument;
|
|
66
|
+
/**
|
|
67
|
+
* Resolves a single positional argument value with defaults and type conversion
|
|
68
|
+
* Note: Does not validate required arguments - validation happens in subclass validate() methods
|
|
69
|
+
*/
|
|
70
|
+
private resolveArgumentValue;
|
|
71
|
+
/**
|
|
72
|
+
* Resolves an option value from the parsed option values object
|
|
73
|
+
* Handles alias resolution, defaults, and type conversion
|
|
74
|
+
*/
|
|
75
|
+
private resolveOptionValue;
|
|
76
|
+
optionDefinitions(): Record<string, OptionDetails>;
|
|
77
|
+
argumentDefinitions(): Record<string, OptionDetails>;
|
|
78
|
+
availableOptions(): string[];
|
|
79
|
+
availableArguments(): string[];
|
|
48
80
|
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { Command } from './Command.js';
|
|
2
1
|
import { CommandIO } from './CommandIO.js';
|
|
2
|
+
import { Command } from './Command.js';
|
|
3
|
+
import { Logger } from './Logger.js';
|
|
3
4
|
export type CommandResolver = (path: string) => Promise<Command>;
|
|
4
5
|
export declare class CommandRegistry {
|
|
5
6
|
private readonly commands;
|
|
6
7
|
protected readonly io: CommandIO;
|
|
7
|
-
|
|
8
|
+
protected readonly logger: Logger;
|
|
8
9
|
protected get CommandIOClass(): typeof CommandIO;
|
|
9
|
-
constructor();
|
|
10
|
+
constructor(logger?: Logger);
|
|
10
11
|
getAvailableCommands(): string[];
|
|
11
|
-
getCommands(): Command
|
|
12
|
+
getCommands(): Array<Command>;
|
|
12
13
|
private commandResolver;
|
|
13
|
-
setCommandResolver(resolver:
|
|
14
|
-
registerCommand(command: Command, force?: boolean): void;
|
|
14
|
+
setCommandResolver(resolver: CommandResolver): void;
|
|
15
|
+
registerCommand(command: Command<any, any, any>, force?: boolean): void;
|
|
15
16
|
loadCommandsPath(commandsPath: string): Promise<void>;
|
|
16
17
|
runCommand(ctx: any, command: string | Command, ...args: any[]): Promise<number>;
|
|
17
18
|
private suggestCommand;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { CommandOption } from './contracts/index.js';
|
|
2
|
+
import { CommandIO } from './CommandIO.js';
|
|
3
|
+
import { OptionsObject, OptionsSchema } from './lib/types.js';
|
|
4
|
+
import { CommandParser } from './CommandParser.js';
|
|
5
|
+
/**
|
|
6
|
+
* Extends CommandParser to parse command signatures like "command {arg} {--option}"
|
|
7
|
+
* Handles interactive prompting for missing required arguments via CommandIO
|
|
8
|
+
*/
|
|
9
|
+
export declare class CommandSignatureParser<Opts extends OptionsSchema = any, Args extends OptionsSchema = any> extends CommandParser<Opts, Args> {
|
|
10
|
+
readonly command: string;
|
|
11
|
+
private readonly argumentsSchema;
|
|
12
|
+
private readonly optionsSchema;
|
|
13
|
+
constructor(opts: {
|
|
14
|
+
io: CommandIO;
|
|
15
|
+
signature: string;
|
|
16
|
+
helperDefinitions: {
|
|
17
|
+
[key: string]: string;
|
|
18
|
+
};
|
|
19
|
+
defaultOptions: CommandOption<any>[];
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Parses command signature string into command name and parameter schemas
|
|
23
|
+
* Example: "migrate {name} {--force}" -> { command: "migrate", arguments: {name: ...}, options: {force: ...} }
|
|
24
|
+
*/
|
|
25
|
+
private static parseSignature;
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves an option value by name, with signature validation
|
|
28
|
+
*/
|
|
29
|
+
option(name: any): any;
|
|
30
|
+
/**
|
|
31
|
+
* Sets an option value programmatically
|
|
32
|
+
*/
|
|
33
|
+
setOption(name: string, value: any): void;
|
|
34
|
+
/**
|
|
35
|
+
* Retrieves the description/help text for an option
|
|
36
|
+
*/
|
|
37
|
+
optionHelp(name: string): string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves the description/help text for an argument
|
|
40
|
+
*/
|
|
41
|
+
argumentHelp(name: string): string | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Retrieves an argument value by name, with signature validation
|
|
44
|
+
*/
|
|
45
|
+
argument(name: any): any;
|
|
46
|
+
/**
|
|
47
|
+
* Sets an argument value programmatically
|
|
48
|
+
*/
|
|
49
|
+
setArgument(name: string, value: any): void;
|
|
50
|
+
/**
|
|
51
|
+
* Returns all argument definitions from the signature
|
|
52
|
+
*/
|
|
53
|
+
getArgumentSignatures(): OptionsSchema;
|
|
54
|
+
/**
|
|
55
|
+
* Returns all option definitions from the signature
|
|
56
|
+
*/
|
|
57
|
+
getOptionSignatures(): OptionsSchema;
|
|
58
|
+
/**
|
|
59
|
+
* Parses a single parameter signature like "{name}" or "{--force}" or "{files*}"
|
|
60
|
+
* Extracts name, type, default value, aliases, description, etc.
|
|
61
|
+
*
|
|
62
|
+
* Signature syntax:
|
|
63
|
+
* - {arg} -> required string argument
|
|
64
|
+
* - {arg?} -> optional argument
|
|
65
|
+
* - {arg=default} -> argument with default value
|
|
66
|
+
* - {arg*} -> variadic argument (array)
|
|
67
|
+
* - {arg:desc} -> argument with description
|
|
68
|
+
* - {--opt} -> boolean option
|
|
69
|
+
* - {--opt=} -> string option
|
|
70
|
+
* - {--opt|o} -> option with alias
|
|
71
|
+
*/
|
|
72
|
+
private static parseParamSignature;
|
|
73
|
+
/**
|
|
74
|
+
* Validates that all required arguments are present
|
|
75
|
+
* If missing, prompts the user via CommandIO to provide them
|
|
76
|
+
* @throws {MissingRequiredArgumentValue} If a required argument cannot be obtained
|
|
77
|
+
*/
|
|
78
|
+
validate(): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Prompts the user to provide a missing argument value via CommandIO
|
|
81
|
+
*/
|
|
82
|
+
private promptForArgument;
|
|
83
|
+
optionValues(): OptionsObject<Opts>;
|
|
84
|
+
argumentValues(): OptionsObject<Args>;
|
|
85
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Command, CommandHandlerOptions } from './Command.js';
|
|
2
|
+
import { CommandIO } from './CommandIO.js';
|
|
3
|
+
import { CommandSignatureParser } from './CommandSignatureParser.js';
|
|
4
|
+
import { OptionsSchema } from './lib/types.js';
|
|
5
|
+
export declare abstract class CommandWithSignature<C = any, Opts extends OptionsSchema = {}, Args extends OptionsSchema = {}> extends Command<C, Opts, Args> {
|
|
6
|
+
abstract signature: string;
|
|
7
|
+
abstract description: string;
|
|
8
|
+
protected helperDefinitions: {
|
|
9
|
+
[key: string]: string;
|
|
10
|
+
};
|
|
11
|
+
get command(): string;
|
|
12
|
+
parser: CommandSignatureParser<Opts, Args>;
|
|
13
|
+
protected newCommandParser(opts: {
|
|
14
|
+
io: CommandIO;
|
|
15
|
+
options: Opts;
|
|
16
|
+
arguments: Args;
|
|
17
|
+
}): CommandSignatureParser;
|
|
18
|
+
constructor();
|
|
19
|
+
protected abstract handle(ctx: C, opts: CommandHandlerOptions<Opts, Args>): Promise<number | void>;
|
|
20
|
+
protected setOption(name: string, value: any): void;
|
|
21
|
+
protected setArgument(name: string, value: any): void;
|
|
22
|
+
protected option<T = string>(key: string): T | null;
|
|
23
|
+
protected option<T = string>(key: string, defaultValue: T): NoInfer<T>;
|
|
24
|
+
protected optionBoolean(key: string, defaultValue?: boolean): boolean;
|
|
25
|
+
protected optionArray<T = string>(key: string, defaultValue?: Array<T>): Array<NoInfer<T>>;
|
|
26
|
+
protected optionNumber(key: string): number | null;
|
|
27
|
+
protected optionNumber(key: string, defaultValue: number): number;
|
|
28
|
+
protected argument<T = string>(key: string): T | null;
|
|
29
|
+
protected argument<T = string>(key: string, defaultValue: T): NoInfer<T>;
|
|
30
|
+
protected argumentArray<T = string>(key: string, defaultValue?: Array<any>): Array<T>;
|
|
31
|
+
protected argumentBoolean(key: string, defaultValue?: boolean): boolean;
|
|
32
|
+
protected argumentNumber(key: string, defaultValue?: number | null): number | null;
|
|
33
|
+
askForConfirmation(...opts: Parameters<typeof this.io.askForConfirmation>): ReturnType<typeof this.io.askForConfirmation>;
|
|
34
|
+
askForInput(...opts: Parameters<typeof this.io.askForInput>): ReturnType<typeof this.io.askForInput>;
|
|
35
|
+
askForSelect(...opts: Parameters<typeof this.io.askForSelect>): ReturnType<typeof this.io.askForSelect>;
|
|
36
|
+
newLoader(...opts: Parameters<typeof this.io.newLoader>): ReturnType<typeof this.io.newLoader>;
|
|
37
|
+
}
|