bob-core 2.0.2 → 2.1.0
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/dist/cjs/{package-B3VP5AzR.cjs → package-DYSfqWOt.cjs} +1 -1
- package/dist/cjs/src/Command.d.ts +9 -0
- package/dist/cjs/src/CommandParser.d.ts +4 -0
- package/dist/cjs/src/errors/TooManyArguments.d.ts +8 -0
- package/dist/cjs/src/errors/index.d.ts +1 -0
- package/dist/cjs/src/index.js +11 -11
- package/dist/esm/{package-DuYxJEEx.js → package-uVOgoItG.js} +1 -1
- package/dist/esm/src/Command.d.ts +9 -0
- package/dist/esm/src/CommandParser.d.ts +4 -0
- package/dist/esm/src/errors/TooManyArguments.d.ts +8 -0
- package/dist/esm/src/errors/index.d.ts +1 -0
- package/dist/esm/src/index.js +127 -85
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e="bob-core",t="2.0
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e="bob-core",t="2.1.0",s="BOB Core",i="module",n="./dist/cjs/src/index.js",r="./dist/esm/src/index.js",c="./dist/esm/src/index.d.ts",o=["dist"],d={".":{import:{types:"./dist/esm/src/index.d.ts",default:"./dist/esm/src/index.js"},require:{types:"./dist/cjs/src/index.d.ts",default:"./dist/cjs/src/index.js"}}},p={start:"node -r @swc-node/register debug/main.ts",build:"rimraf ./dist && vite build",typecheck:"tsc --noEmit",prepack:"npm run build",test:"vitest run",lint:"eslint .","lint:fix":"eslint . --fix"},l="Léo Hubert",m="ISC",a={"@eslint/js":"^9.37.0","@faker-js/faker":"^10.0.0","@swc-node/register":"^1.11.1","@trivago/prettier-plugin-sort-imports":"^5.2.2","@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",eslint:"^9.37.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4",prettier:"^3.6.2",rimraf:"^6.0.1",tsx:"^4.20.6",typescript:"^5.9.3","typescript-eslint":"^8.46.0",vite:"^7.2.7","vite-plugin-dts":"^4.5.4",vitest:"^3.2.4"},u={chalk:"^4.1.2",minimist:"^1.2.8",prompts:"^2.4.2"},y={name:e,version:t,description:s,type:i,main:n,module:r,types:c,files:o,exports:d,scripts:p,author:l,license:m,devDependencies:a,dependencies:u};exports.author=l;exports.default=y;exports.dependencies=u;exports.description=s;exports.devDependencies=a;exports.exports=d;exports.files=o;exports.license=m;exports.main=n;exports.module=r;exports.name=e;exports.scripts=p;exports.type=i;exports.types=c;exports.version=t;
|
|
@@ -32,6 +32,10 @@ export declare class Command<C extends ContextDefinition = ContextDefinition, Op
|
|
|
32
32
|
protected io: CommandIO;
|
|
33
33
|
protected parser: CommandParser<Options, Arguments>;
|
|
34
34
|
protected disablePromptingFlag: boolean;
|
|
35
|
+
protected allowUnknownOptionsFlag: boolean;
|
|
36
|
+
protected hiddenFlag: boolean;
|
|
37
|
+
protected disableDefaultOptionsFlag: boolean;
|
|
38
|
+
protected strictModeFlag: boolean;
|
|
35
39
|
protected preHandle?(): Promise<void | number>;
|
|
36
40
|
protected _preHandler?: CommandHandler<C, Options, Arguments>;
|
|
37
41
|
protected handle?(ctx: C, opts: CommandHandlerOptions<Options, Arguments>): Promise<number | void> | number | void;
|
|
@@ -51,6 +55,11 @@ export declare class Command<C extends ContextDefinition = ContextDefinition, Op
|
|
|
51
55
|
arguments?: Arguments;
|
|
52
56
|
});
|
|
53
57
|
disablePrompting(): this;
|
|
58
|
+
allowUnknownOptions(): this;
|
|
59
|
+
hidden(): this;
|
|
60
|
+
get isHidden(): boolean;
|
|
61
|
+
disableDefaultOptions(): this;
|
|
62
|
+
strictMode(): this;
|
|
54
63
|
preHandler(handler: CommandHandler<C, Options, Arguments>): this;
|
|
55
64
|
handler(handler: CommandHandler<C, Options, Arguments>): this;
|
|
56
65
|
options<Opts extends OptionsSchema>(opts: Opts): Command<C, Options & Opts, Arguments>;
|
|
@@ -12,6 +12,8 @@ export declare class CommandParser<Options extends OptionsSchema, Arguments exte
|
|
|
12
12
|
protected parsedArguments: OptionsObject<Arguments> | null;
|
|
13
13
|
protected io: CommandIO;
|
|
14
14
|
protected shouldPromptForMissingOptions: boolean;
|
|
15
|
+
protected shouldValidateUnknownOptions: boolean;
|
|
16
|
+
protected shouldRejectExtraArguments: boolean;
|
|
15
17
|
constructor(opts: {
|
|
16
18
|
io: CommandIO;
|
|
17
19
|
options: Options;
|
|
@@ -93,6 +95,8 @@ export declare class CommandParser<Options extends OptionsSchema, Arguments exte
|
|
|
93
95
|
* Useful for non-interactive environments
|
|
94
96
|
*/
|
|
95
97
|
disablePrompting(): this;
|
|
98
|
+
allowUnknownOptions(): this;
|
|
99
|
+
strictMode(): this;
|
|
96
100
|
/**
|
|
97
101
|
* Prompts the user to provide a missing argument value via CommandIO
|
|
98
102
|
* Used by validate() when shouldPromptForMissingArgs is enabled
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Logger } from '../Logger.js';
|
|
2
|
+
import { BobError } from './BobError.js';
|
|
3
|
+
export declare class TooManyArguments extends BobError {
|
|
4
|
+
readonly expected: number;
|
|
5
|
+
readonly received: number;
|
|
6
|
+
constructor(expected: number, received: number);
|
|
7
|
+
pretty(logger: Logger): void;
|
|
8
|
+
}
|
package/dist/cjs/src/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("prompts"),a=require("chalk"),
|
|
2
|
-
${a.yellow("Available options")}:`);for(const[i,n]of e){const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("prompts"),a=require("chalk"),j=require("minimist"),T=require("node:fs"),k=require("node:path");class x{logger;constructor(t){this.logger=t.logger}log(...t){this.logger.log(...t)}info(...t){this.logger.info(...t)}warn(...t){this.logger.warn(...t)}error(...t){this.logger.error(...t)}debug(...t){this.logger.debug(...t)}async askForConfirmation(t="Do you want to continue?",e){return(await y({type:"confirm",name:"value",message:t,initial:e??!1})).value}async askForInput(t,e,i){return(await y({type:"text",name:"value",message:t,initial:e,...i}))?.value??null}async askForDate(t,e,i){return(await y({type:"date",name:"value",message:t,initial:e,...i}))?.value??null}async askForList(t,e,i){return(await y({type:"list",name:"value",message:t,initial:e,...i}))?.value??null}async askForToggle(t,e,i){return(await y({type:"toggle",name:"value",message:t,initial:e,...i}))?.value??null}async askForSelect(t,e,i){if(e.length===0)throw new Error("No options provided");const n=[];for(const o of e)typeof o=="string"?n.push({title:o,value:o}):n.push(o);return(await y({type:"select",name:"value",message:t,choices:n,...i}))?.value??null}newLoader(t="",e=["⠙","⠘","⠰","⠴","⠤","⠦","⠆","⠃","⠋","⠉"],i=100){let n=t,s=null,o=0;const u=setInterval(function(){s&&(process.stdout.write(new TextEncoder().encode("\r"+" ".repeat(s.length+5)+"\r")),s=null),process.stdout.write(new TextEncoder().encode("\r"+e[o++]+" "+n)),o=o%e.length},i),m=()=>{clearInterval(u),process.stdout.write(new TextEncoder().encode("\r"+" ".repeat(n.length+5)+"\r"))};return{[Symbol.dispose]:m,[Symbol.asyncDispose]:m,updateText:h=>{s=n,n=h},stop:m}}}class f extends Error{$type="BobError"}function q(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"||Array.isArray(r)?q(r):r.default!==void 0?r.default:q(r.type)}function g(r){return typeof r=="string"||Array.isArray(r)?{alias:[],default:N(r),description:"",required:!1,secret:!1,type:r,variadic:!1}:{alias:r.alias?Array.isArray(r.alias)?r.alias:[r.alias]:[],default:r.default??N(r.type),description:r.description??"",required:r.required??!1,secret:r.secret??!1,type:r.type,variadic:r.variadic??!1}}class $ extends f{constructor(t,e={}){super(`Invalid option ${t} in not recognized`),this.option=t,this.optionsSchema=e}pretty(t){const e=Object.entries(this.optionsSchema);if(e.length>0){t.log(`
|
|
2
|
+
${a.yellow("Available options")}:`);for(const[i,n]of e){const s=g(n),o=s.alias?typeof s.alias=="string"?[s.alias]:s.alias:[],u=Array.isArray(s.type)?`[${s.type[0]}]`:s.type,m=`--${i}${o.length>0?o.map(l=>`, -${l}`).join(""):""}`,h=" ".repeat(30-m.length);t.log(` ${a.green(m)} ${h} ${s.description||"\b"} ${a.white(`(${u})`)}`)}t.log("")}t.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is not recognized.`)}}class A extends f{constructor(t){super(`Argument "${t}" is required.`),this.argument=t}pretty(t){t.log(`${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.argument)} is required.`)}}class V extends f{constructor(t){super(`Argument "${t}" is required.`),this.option=t}pretty(t){t.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is required.`)}}class W extends f{constructor(t){let e=`Argument "${t.param}" value is invalid.`;t.reason?e+=` Reason: ${t.reason}`:e+=` Value: "${t.value}"`,super(e),this.param=t}pretty(t){t.log(` ${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.param.param)} value is invalid. `),(this.param.value||this.param.reason)&&t.log(""),this.param.value&&t.log(` ${a.blue("Value")}: ${this.param.value}`),this.param.reason&&t.log(` ${a.yellow("Reason")}: ${this.param.reason}`)}}class v extends f{constructor(t){let e=`Option "${t.option}" value is invalid.`;t.reason?e+=` Reason: ${t.reason}`:e+=` Value: "${t.value}"`,super(e),this.param=t}pretty(t){t.log(` ${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.param.option)} value is invalid. `),(this.param.value||this.param.reason)&&t.log(""),this.param.value&&t.log(` ${a.blue("Value")}: ${this.param.value}`),this.param.reason&&t.log(` ${a.yellow("Reason")}: ${this.param.reason}`)}}class P extends f{constructor(t){super(`Command "${t}" not found.`),this.command=t}pretty(t){t.log(`${a.bgRed(" ERROR ")} Command ${a.yellow(this.command)} not found.`)}}class I extends f{constructor(t,e){super(`Too many arguments provided. Expected ${t}, got ${e}.`),this.expected=t,this.received=e}pretty(t){t.log(`${a.white.bgRed(" ERROR ")} Too many arguments provided. Expected ${a.bold.yellow(String(this.expected))}, got ${a.bold.yellow(String(this.received))}.`)}}function w(r,t,e,i){if(r==null)return i??null;if(t==="string")return String(r);if(t==="number"){const n=Number(r);if(isNaN(n))throw new v({option:e,reason:`Expected a number, got "${r}"`});return n}if(t==="boolean")return typeof r=="boolean"?r:r==="true"||r==="1"?!0:r==="false"||r==="0"?!1:!!r;if(Array.isArray(t)){const n=t[0],s=Array.isArray(r)?r:[r];if(n==="string")return s.map(o=>String(o));if(n==="number")return s.map(o=>{const u=Number(o);if(isNaN(u))throw new v({option:e,reason:`Expected array of numbers, got "${o}" in array`});return u})}return r}class E{options;parsedOptions=null;arguments;parsedArguments=null;io;shouldPromptForMissingOptions=!0;shouldValidateUnknownOptions=!0;shouldRejectExtraArguments=!1;constructor(t){this.options=t.options,this.arguments=t.arguments,this.io=t.io}init(t){const{_:e,...i}=j(t);return this.shouldValidateUnknownOptions&&this.validateUnknownOptions(i),this.parsedOptions=this.handleOptions(i),this.parsedArguments=this.handleArguments(e),{options:this.parsedOptions,arguments:this.parsedArguments}}async validate(){for(const t in this.options)if(g(this.options[t]).required&&(this.parsedOptions?.[t]===void 0||this.parsedOptions?.[t]===null))throw new V(t);for(const t in this.arguments){const e=g(this.arguments[t]),i=this.parsedArguments?.[t];if(e.required&&i==null){if(this.shouldPromptForMissingOptions){const n=await this.promptForArgument(t,e);if(n&&this.parsedArguments){this.parsedArguments[t]=w(n,e.type,t);continue}}throw new A(t)}if(e.required&&e.variadic&&Array.isArray(i)&&i.length===0){if(this.shouldPromptForMissingOptions){const n=await this.promptForArgument(t,e);if(n&&this.parsedArguments){this.parsedArguments[t]=w(n,e.type,t);continue}}throw new A(t)}}}option(t,e){if(!this.parsedOptions)throw new Error("Options have not been parsed yet. Call init() first.");return this.isEmptyValue(this.parsedOptions[t])&&e!==void 0?e:this.parsedOptions[t]}setOption(t,e){if(!this.parsedOptions)throw new Error("Options have not been parsed yet. Call init() first.");if(!(t in this.options))throw new $(t,this.options);this.parsedOptions[t]=e}argument(t,e){if(!this.parsedArguments)throw new Error("Arguments have not been parsed yet. Call init() first.");return this.isEmptyValue(this.parsedArguments[t])&&e!==void 0?e:this.parsedArguments[t]}setArgument(t,e){if(!this.parsedArguments)throw new Error("Arguments have not been parsed yet. Call init() first.");if(!(t in this.arguments))throw new $(t,this.arguments);this.parsedArguments[t]=e}isEmptyValue(t){return t==null||Array.isArray(t)&&t.length===0}validateUnknownOptions(t){const e=new Set;for(const i in this.options){e.add(i);const n=g(this.options[i]);for(const s of n.alias)e.add(s)}for(const i in t)if(!e.has(i))throw new $(i,this.options)}handleOptions(t){const e={};for(const i in this.options){const n=g(this.options[i]);e[i]=this.resolveOptionValue(i,n,t)}return e}handleArguments(t){const e={},i=[...t],n=Object.keys(this.arguments).length;for(const s in this.arguments){const o=g(this.arguments[s]);if(o.variadic){e[s]=this.handleVariadicArgument(s,o,i),i.length=0;continue}e[s]=this.resolveArgumentValue(s,o,i.shift())}if(this.shouldRejectExtraArguments&&i.length>0)throw new I(n,n+i.length);return e}handleVariadicArgument(t,e,i){return i.length?w(i,e.type,t,e.default):e.default}resolveArgumentValue(t,e,i){return i===void 0?e.default:w(i,e.type,t,e.default)}resolveOptionValue(t,e,i){let n;const s=[t,...e.alias];for(const o of s)if(o in i){n=i[o];break}if(n===void 0){if(e.required)throw new v({option:t,reason:"Required option is missing"});return e.default}return w(n,e.type,t,e.default)}optionDefinitions(){const t={};for(const e in this.options)t[e]=g(this.options[e]);return t}argumentDefinitions(){const t={};for(const e in this.arguments)t[e]=g(this.arguments[e]);return t}availableOptions(){return Object.keys(this.options)}availableArguments(){return Object.keys(this.arguments)}disablePrompting(){return this.shouldPromptForMissingOptions=!1,this}allowUnknownOptions(){return this.shouldValidateUnknownOptions=!1,this}strictMode(){return this.shouldRejectExtraArguments=!0,this}async promptForArgument(t,e){if(!Array.isArray(e.type)&&!["string","number","secret"].includes(e.type))return null;let i=`${a.yellow.bold(t)} is required`;return e.description&&(i+=`: ${a.gray(`(${e.description})`)}`),i+=` ${a.green(`(${e.type}${e.variadic==!0?"[]":""})`)}
|
|
3
3
|
`,Array.isArray(e.type)?(i+=`Please provide one or more values, separated by commas:
|
|
4
|
-
`,await this.io.askForList(i,void 0,{separator:",",validate:n=>{if(!n.length)return"Please enter at least one value";if(e.type[0]==="number"){for(const
|
|
5
|
-
`),this.io.log(a.yellow("Usage:")),this.io.log(` ${this.command} ${o.length>0?o.map(([l])=>`<${l}>`).join(" "):"\b"} [options]`);const u=Math.max(...
|
|
6
|
-
${a.yellow("Arguments")}:`);for(const[l,d]of i){const
|
|
7
|
-
${a.yellow("Options")}:`);for(const l of
|
|
8
|
-
${a.yellow("Examples")}:`);let l=process.argv[0].split("/").pop();l==="node"&&(l+=" "+process.argv[1].split("/").pop());for(const[d,
|
|
9
|
-
`),this.io.log(` ${a.green(`${l} ${
|
|
10
|
-
`);const u=await this.io.askForSelect(a.green("Did you mean to run one of these commands instead?"),o);if(u)return u}throw new
|
|
11
|
-
`),this.io.askForConfirmation(`${a.green(`Do you want to run ${a.yellow(e)} instead?`)} `)}async*listCommandsFiles(t){const e=
|
|
4
|
+
`,await this.io.askForList(i,void 0,{separator:",",validate:n=>{if(!n.length)return"Please enter at least one value";if(e.type[0]==="number"){for(const s of n.split(","))if(isNaN(Number(s)))return"Please enter only valid numbers"}return!0}})):await this.io.askForInput(i,void 0,{type:e.type==="number"?"number":e.secret?"password":"text",validate:n=>{if(n==null||typeof n=="string"&&!n.length)return"This value is required";if(e.type==="number"){const s=Number(n);if(isNaN(s))return"Please enter a valid number"}else if(e.type==="string"&&(typeof n!="string"||!n.length))return"Please enter a valid text";return!0}})}}function R(r){return new Array(r+5).join(" ")}class L{type="boolean";option="help";alias=["h"];default=!1;description=`Display help for the given command. When no command is given display help for the ${a.green("list")} command`;async handler(){const t=this.parser.argumentDefinitions(),e=this.parser.optionDefinitions(),i=Object.entries(t),n=Object.entries(e),s=n.map(([l,d])=>{const p=Array.isArray(d.alias)?d.alias:d.alias?[d.alias]:[];return{name:l,...d,optionWithAlias:`--${l}${p.map(c=>`, -${c}`).join("")}`}}),o=i.filter(([,l])=>l.required);this.io.log(a.yellow("Description:")),this.io.log(` ${this.description}
|
|
5
|
+
`),this.io.log(a.yellow("Usage:")),this.io.log(` ${this.command} ${o.length>0?o.map(([l])=>`<${l}>`).join(" "):"\b"} [options]`);const u=Math.max(...s.map(l=>l.optionWithAlias.length),0),m=Math.max(...i.map(([l])=>l.length),0),h=m>u?m:u;if(i.length>0){this.io.log(`
|
|
6
|
+
${a.yellow("Arguments")}:`);for(const[l,d]of i){const p=R(h-l.length);let c=` ${a.green(l)} ${p} ${d.description??"\b"}`;if(d.default!==void 0&&!d.required){const B=(Array.isArray(d.type)?`[${d.type[0]}]`:d.type)==="array"||Array.isArray(d.type)?JSON.stringify(d.default):d.default;c+=` ${a.yellow(`[default: ${B}]`)}`}d.variadic&&(c+=` ${a.white("(variadic)")}`),this.io.log(c)}}if(n.length>0){this.io.log(`
|
|
7
|
+
${a.yellow("Options")}:`);for(const l of s){const d=R(h-l.optionWithAlias.length);let p=`${a.green(l.optionWithAlias)} ${d} ${l.description??"\b"}`;if(l.type){const c=Array.isArray(l.type)?`[${l.type[0]}]`:l.type;p+=` ${a.white(`(${c})`)}`}if(l.default!==void 0&&!l.required){const S=(Array.isArray(l.type)?`[${l.type[0]}]`:l.type)==="array"||Array.isArray(l.type)?JSON.stringify(l.default):l.default;p+=` ${a.yellow(`[default: ${S}]`)}`}this.io.log(p)}}if(this.commandsExamples.length>0){this.io.log(`
|
|
8
|
+
${a.yellow("Examples")}:`);let l=process.argv[0].split("/").pop();l==="node"&&(l+=" "+process.argv[1].split("/").pop());for(const[d,p]of this.commandsExamples.entries())d>0&&this.io.log(""),this.io.log(` ${p.description}
|
|
9
|
+
`),this.io.log(` ${a.green(`${l} ${p.command}`)}`)}return-1}}class C{$type="BobCommand";_command;description;group;commandsExamples=[];get command(){return this._command}ctx;io;parser;disablePromptingFlag=!1;allowUnknownOptionsFlag=!1;hiddenFlag=!1;disableDefaultOptionsFlag=!1;strictModeFlag=!1;_preHandler;_handler;tmp;defaultOptions(){return this.disableDefaultOptionsFlag?[]:[new L]}newCommandParser(t){return new E({io:t.io,options:t.options,arguments:t.arguments})}newCommandIO(t){return new x(t)}constructor(t,e){this._command=t,this.description=e?.description??"",this.group=e?.group,this.tmp={options:e?.options??{},arguments:e?.arguments??{}};const i=this.defaultOptions();if(i.length>0)for(const n of i)this.tmp.options[n.option]=n}disablePrompting(){return this.disablePromptingFlag=!0,this}allowUnknownOptions(){return this.allowUnknownOptionsFlag=!0,this}hidden(){return this.hiddenFlag=!0,this}get isHidden(){return this.hiddenFlag}disableDefaultOptions(){return this.disableDefaultOptionsFlag=!0,this}strictMode(){return this.strictModeFlag=!0,this}preHandler(t){return this._preHandler=t,this}handler(t){return this._handler=t,this}options(t){return this.tmp={options:{...this.tmp?.options??{},...t},arguments:this.tmp?.arguments??{}},this}arguments(t){return this.tmp={options:this.tmp?.options??{},arguments:{...this.tmp?.arguments??{},...t}},this}async run(t){if(!this._handler&&!this.handle)throw new Error(`No handler defined for command ${this.command||"(unknown)"}`);let e;if(this.ctx=t.ctx,this.io=this.newCommandIO({logger:t.logger}),t&&"args"in t){const n=this.tmp?.options??{};for(const s of this.defaultOptions())s.option in n||(n[s.option]=s);this.parser=this.newCommandParser({io:this.io,options:n,arguments:this.tmp?.arguments??{}}),this.allowUnknownOptionsFlag&&this.parser.allowUnknownOptions(),this.strictModeFlag&&this.parser.strictMode(),e=this.parser.init(t.args);for(const s of this.defaultOptions())if(e.options[s.option]===!0){const o=await s.handler.call(this);if(o&&o!==0)return o}this.disablePromptingFlag&&this.parser.disablePrompting(),await this.parser.validate()}else e={options:t.options,arguments:t.arguments};if(!this._preHandler&&this.preHandle&&(this._preHandler=this.preHandle.bind(this)),this._preHandler){const n=await this._preHandler(t.ctx,e);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(t.ctx,e)??0}}class b extends E{command;constructor(t){const e=b.parseSignature(t.signature,t.helperDefinitions,t.defaultOptions);super({io:t.io,options:e.options,arguments:e.arguments}),this.command=e.command}static parseSignature(t,e,i){const[n,...s]=t.split(/\{(.*?)\}/g).map(m=>m.trim()).filter(Boolean),o={},u={};for(const m of s){const{name:h,isOption:l,definition:d}=b.parseParamSignature(m,e);l?o[h]=d:u[h]=d}for(const m of i)o[m.option]={type:m.type,required:m.required,alias:m.alias,variadic:m.variadic??!1,description:m.description,default:m.default??null};return{command:n,options:o,arguments:u}}static parseParamSignature(t,e){let i=t,n=!1;const s={required:!0,type:"string",description:void 0,default:null,variadic:!1};if(i.includes(":")){const[o,u]=i.split(":");i=o.trim(),s.description=u.trim()}if(i.includes("=")){const[o,u]=i.split("=");i=o.trim(),s.default=u.trim(),s.required=!1,typeof s.default=="string"&&!s.default.length?s.default=null:s.default==="true"?(s.default=!0,s.type="boolean"):s.default==="false"&&(s.default=!1,s.type="boolean")}else i.startsWith("--")&&(s.required=!1,s.default=!1,s.type="boolean");if(i.includes("|")){const[o,...u]=i.split("|");i=o.trim(),s.alias=u.map(m=>m.trim())}return i.startsWith("--")&&(n=!0,i=i.slice(2)),s.default==="*"&&(s.default=[],s.type=["string"]),i.endsWith("?")&&(s.required=!1,i=i.slice(0,-1)),i.endsWith("*")&&(s.type=["string"],s.variadic=!0,s.default=[],i=i.slice(0,-1)),s.description=s.description??e[i]??e[`--${i}`],{name:i,isOption:n,definition:s}}}class _ extends C{helperDefinitions={};get command(){return this.parser?this.parser.command:this.signature.split(" ")[0]}newCommandParser(t){return new b({io:t.io,signature:this.signature,helperDefinitions:this.helperDefinitions,defaultOptions:this.defaultOptions()})}constructor(){super("")}option(t,e=null){return this.parser.option(t,e)}argument(t,e=null){return this.parser.argument(t,e)}async askForConfirmation(...t){return this.io.askForConfirmation(...t)}async askForInput(...t){return this.io.askForInput(...t)}async askForSelect(...t){return this.io.askForSelect(...t)}newLoader(...t){return this.io.newLoader(...t)}}class F{level;constructor(t={}){this.level=t.level??"info"}shouldLog(t){const e=["debug","info","warn","error"],i=e.indexOf(this.level);return e.indexOf(t)>=i}setLevel(t){this.level=t}getLevel(){return this.level}log(...t){console.log(...t)}info(...t){this.shouldLog("info")&&console.log(...t)}warn(...t){this.shouldLog("warn")&&console.warn(...t)}error(...t){this.shouldLog("error")&&console.error(...t)}debug(...t){this.shouldLog("debug")&&console.log(...t)}}class H{getBigrams(t){const e=[],i=t.toLowerCase();for(let n=0;n<i.length-1;n++)e.push(i.slice(n,n+2));return e}calculateSimilarity(t,e){if(t===e)return 1;if(t.length<2||e.length<2)return 0;const i=this.getBigrams(t),n=this.getBigrams(e),s=new Set(n);let o=0;for(const u of i)s.has(u)&&(o++,s.delete(u));return 2*o/(i.length+n.length)}findBestMatch(t,e){const i=e.map(o=>({target:o,rating:this.calculateSimilarity(t,o)}));let n=0,s=i[0]?.rating??0;for(let o=1;o<i.length;o++)i[o].rating>s&&(s=i[o].rating,n=o);return{ratings:i,bestMatch:i[n],bestMatchIndex:n}}}function U(r){return typeof r=="object"&&r!==null&&"$type"in r&&r.$type==="BobError"}function O(r){return typeof r=="object"&&r!==null&&(r instanceof C||"$type"in r&&r.$type==="BobCommand")}class M{commands={};io;logger;stringSimilarity;newCommandIO(t){return new x(t)}constructor(t){this.logger=t?.logger??new F,this.io=this.newCommandIO({logger:this.logger}),this.stringSimilarity=t?.stringSimilarity??new H}getAvailableCommands(){return Object.keys(this.commands)}getCommands(){return Object.values(this.commands)}importFile=async t=>(await import(t)).default;commandResolver=async t=>{let e=await this.importFile(t);return e?(e&&typeof e=="object"&&"default"in e&&(e=e.default),typeof e=="function"?new e:O(e)?e:null):null};withCommandResolver(t){return this.commandResolver=t,this}withFileImporter(t){return this.importFile=t,this}registerCommand(t,e=!1){if(!O(t))throw new Error("Invalid command, it must extend the Command class.");const i=t.command;if(!i)throw new Error("Cannot register a command with no name.");if(!e&&this.commands[i])throw new Error(`Command ${i} already registered.`);this.commands[i]=t}async loadCommandsPath(t){for await(const e of this.listCommandsFiles(t))try{const i=await this.commandResolver(e);O(i)&&this.registerCommand(i)}catch(i){throw new Error(`Command ${e} failed to load. ${i}`,{cause:i})}}async runCommand(t,e,...i){const n=typeof e=="string"?this.commands[e]:e,s=typeof e=="string"?e:n.command;if(!n){const o=await this.suggestCommand(s);return o?await this.runCommand(t,o,...i):1}return await n.run({ctx:t,logger:this.logger,args:i})??0}async suggestCommand(t){const e=this.getAvailableCommands(),{bestMatch:i,bestMatchIndex:n,ratings:s}=this.stringSimilarity.findBestMatch(t,e),o=s.filter(u=>u.rating>.3).map(u=>u.target);if(i&&(i.rating>0&&o.length<=1||i.rating>.7&&o.length>1)){const u=e[n];return await this.askRunSimilarCommand(t,u)?u:null}if(o.length){this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(t)} not found.
|
|
10
|
+
`);const u=await this.io.askForSelect(a.green("Did you mean to run one of these commands instead?"),o);if(u)return u}throw new P(t)}async askRunSimilarCommand(t,e){return this.io.error(`${a.bgRed(" ERROR ")} Command ${a.yellow(t)} not found.
|
|
11
|
+
`),this.io.askForConfirmation(`${a.green(`Do you want to run ${a.yellow(e)} instead?`)} `)}async*listCommandsFiles(t){const e=T.readdirSync(t,{withFileTypes:!0});for(const i of e){const n=k.resolve(t,i.name);if(i.isDirectory())yield*this.listCommandsFiles(k.resolve(t,i.name));else{if(!n.endsWith(".ts")&&!n.endsWith(".js")&&!n.endsWith(".mjs")&&!n.endsWith(".cjs"))continue;yield n}}}}class D{logger;constructor(t){this.logger=t}handle(t){if(U(t))return t.pretty(this.logger),-1;throw t}}class G extends C{constructor(t){super("help",{description:a.bold("Show help information about the CLI and its commands")}),this.opts=t}async handle(){const t=this.opts.commandRegistry.getCommands().filter(m=>!m.isHidden),e=this.opts.cliName??"Bob CLI",i=this.opts.cliVersion??"0.0.0",n=(await Promise.resolve().then(()=>require("../package-DYSfqWOt.cjs")))?.default?.version??"0.0.0";this.io.log(`${e} ${a.green(i)} (core: ${a.yellow(n)})
|
|
12
12
|
|
|
13
13
|
${a.yellow("Usage")}:
|
|
14
14
|
command [options] [arguments]
|
|
15
15
|
|
|
16
16
|
${a.yellow("Available commands")}:
|
|
17
|
-
`);const
|
|
17
|
+
`);const s=Math.max(...t.map(m=>m.command.length))??0,o={};for(const m of t){const h=m.group??m.command.split(":")[0];o[h]||(o[h]=[]),o[h].push(m)}const u=Object.entries(o).sort(([m],[h])=>m.toLowerCase().localeCompare(h.toLowerCase())).sort(([,m],[,h])=>m.length-h.length);for(const[m,h]of u){const l=h.length>1;l&&this.io.log(a.yellow(`${m}:`));const d=h.sort((p,c)=>p.command.toLowerCase().localeCompare(c.command.toLowerCase()));for(const p of d){let c=R(s-p.command.length);l&&(c=c.slice(2)),this.io.log(`${l?" ":""}${a.green(p.command)} ${c} ${p.description}`)}}}}class z{ctx;logger;commandRegistry;exceptionHandler;helpCommand;newCommandRegistry(t){return new M(t)}newHelpCommand(t){return new G(t)}newExceptionHandler(t){return new D(t.logger)}constructor(t={}){this.ctx=t.ctx,this.logger=t.logger??new F,this.commandRegistry=this.newCommandRegistry({logger:this.logger}),this.exceptionHandler=this.newExceptionHandler({logger:this.logger}),this.helpCommand=this.newHelpCommand({cliName:t.name,cliVersion:t.version,commandRegistry:this.commandRegistry})}withCommandResolver(t){return this.commandRegistry.withCommandResolver(t),this}withFileImporter(t){return this.commandRegistry.withFileImporter(t),this}async withCommands(...t){for(const e of t)typeof e=="string"?await this.commandRegistry.loadCommandsPath(e):typeof e=="function"?this.registerCommand(new e):this.registerCommand(e)}async runCommand(t,...e){return t?await this.commandRegistry.runCommand(this.ctx??{},t,...e).catch(this.exceptionHandler.handle.bind(this.exceptionHandler)):await this.runHelpCommand()}async runHelpCommand(){return await this.runCommand(this.helpCommand)}registerCommand(t){this.commandRegistry.registerCommand(t)}}exports.BadCommandOption=v;exports.BadCommandParameter=W;exports.BobError=f;exports.Cli=z;exports.Command=C;exports.CommandIO=x;exports.CommandNotFoundError=P;exports.CommandParser=E;exports.CommandRegistry=M;exports.CommandSignatureParser=b;exports.CommandWithSignature=_;exports.ExceptionHandler=D;exports.HelpOption=L;exports.InvalidOption=$;exports.Logger=F;exports.MissingRequiredArgumentValue=A;exports.MissingRequiredOptionValue=V;exports.StringSimilarity=H;exports.TooManyArguments=I;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const t = "bob-core", s = "2.0
|
|
1
|
+
const t = "bob-core", s = "2.1.0", e = "BOB Core", i = "module", n = "./dist/cjs/src/index.js", r = "./dist/esm/src/index.js", c = "./dist/esm/src/index.d.ts", o = ["dist"], d = { ".": { import: { types: "./dist/esm/src/index.d.ts", default: "./dist/esm/src/index.js" }, require: { types: "./dist/cjs/src/index.d.ts", default: "./dist/cjs/src/index.js" } } }, p = { start: "node -r @swc-node/register debug/main.ts", build: "rimraf ./dist && vite build", typecheck: "tsc --noEmit", prepack: "npm run build", test: "vitest run", lint: "eslint .", "lint:fix": "eslint . --fix" }, l = "Léo Hubert", m = "ISC", a = { "@eslint/js": "^9.37.0", "@faker-js/faker": "^10.0.0", "@swc-node/register": "^1.11.1", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@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", eslint: "^9.37.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", prettier: "^3.6.2", rimraf: "^6.0.1", tsx: "^4.20.6", typescript: "^5.9.3", "typescript-eslint": "^8.46.0", vite: "^7.2.7", "vite-plugin-dts": "^4.5.4", vitest: "^3.2.4" }, u = { chalk: "^4.1.2", minimist: "^1.2.8", prompts: "^2.4.2" }, x = {
|
|
2
2
|
name: t,
|
|
3
3
|
version: s,
|
|
4
4
|
description: e,
|
|
@@ -32,6 +32,10 @@ export declare class Command<C extends ContextDefinition = ContextDefinition, Op
|
|
|
32
32
|
protected io: CommandIO;
|
|
33
33
|
protected parser: CommandParser<Options, Arguments>;
|
|
34
34
|
protected disablePromptingFlag: boolean;
|
|
35
|
+
protected allowUnknownOptionsFlag: boolean;
|
|
36
|
+
protected hiddenFlag: boolean;
|
|
37
|
+
protected disableDefaultOptionsFlag: boolean;
|
|
38
|
+
protected strictModeFlag: boolean;
|
|
35
39
|
protected preHandle?(): Promise<void | number>;
|
|
36
40
|
protected _preHandler?: CommandHandler<C, Options, Arguments>;
|
|
37
41
|
protected handle?(ctx: C, opts: CommandHandlerOptions<Options, Arguments>): Promise<number | void> | number | void;
|
|
@@ -51,6 +55,11 @@ export declare class Command<C extends ContextDefinition = ContextDefinition, Op
|
|
|
51
55
|
arguments?: Arguments;
|
|
52
56
|
});
|
|
53
57
|
disablePrompting(): this;
|
|
58
|
+
allowUnknownOptions(): this;
|
|
59
|
+
hidden(): this;
|
|
60
|
+
get isHidden(): boolean;
|
|
61
|
+
disableDefaultOptions(): this;
|
|
62
|
+
strictMode(): this;
|
|
54
63
|
preHandler(handler: CommandHandler<C, Options, Arguments>): this;
|
|
55
64
|
handler(handler: CommandHandler<C, Options, Arguments>): this;
|
|
56
65
|
options<Opts extends OptionsSchema>(opts: Opts): Command<C, Options & Opts, Arguments>;
|
|
@@ -12,6 +12,8 @@ export declare class CommandParser<Options extends OptionsSchema, Arguments exte
|
|
|
12
12
|
protected parsedArguments: OptionsObject<Arguments> | null;
|
|
13
13
|
protected io: CommandIO;
|
|
14
14
|
protected shouldPromptForMissingOptions: boolean;
|
|
15
|
+
protected shouldValidateUnknownOptions: boolean;
|
|
16
|
+
protected shouldRejectExtraArguments: boolean;
|
|
15
17
|
constructor(opts: {
|
|
16
18
|
io: CommandIO;
|
|
17
19
|
options: Options;
|
|
@@ -93,6 +95,8 @@ export declare class CommandParser<Options extends OptionsSchema, Arguments exte
|
|
|
93
95
|
* Useful for non-interactive environments
|
|
94
96
|
*/
|
|
95
97
|
disablePrompting(): this;
|
|
98
|
+
allowUnknownOptions(): this;
|
|
99
|
+
strictMode(): this;
|
|
96
100
|
/**
|
|
97
101
|
* Prompts the user to provide a missing argument value via CommandIO
|
|
98
102
|
* Used by validate() when shouldPromptForMissingArgs is enabled
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Logger } from '../Logger.js';
|
|
2
|
+
import { BobError } from './BobError.js';
|
|
3
|
+
export declare class TooManyArguments extends BobError {
|
|
4
|
+
readonly expected: number;
|
|
5
|
+
readonly received: number;
|
|
6
|
+
constructor(expected: number, received: number);
|
|
7
|
+
pretty(logger: Logger): void;
|
|
8
|
+
}
|
package/dist/esm/src/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import y from "prompts";
|
|
2
2
|
import a from "chalk";
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
class
|
|
3
|
+
import q from "minimist";
|
|
4
|
+
import I from "node:fs";
|
|
5
|
+
import x from "node:path";
|
|
6
|
+
class S {
|
|
7
7
|
logger;
|
|
8
8
|
constructor(t) {
|
|
9
9
|
this.logger = t.logger;
|
|
@@ -30,7 +30,7 @@ class k {
|
|
|
30
30
|
* Prompt utils
|
|
31
31
|
*/
|
|
32
32
|
async askForConfirmation(t = "Do you want to continue?", e) {
|
|
33
|
-
return (await
|
|
33
|
+
return (await y({
|
|
34
34
|
type: "confirm",
|
|
35
35
|
name: "value",
|
|
36
36
|
message: t,
|
|
@@ -38,7 +38,7 @@ class k {
|
|
|
38
38
|
})).value;
|
|
39
39
|
}
|
|
40
40
|
async askForInput(t, e, i) {
|
|
41
|
-
return (await
|
|
41
|
+
return (await y({
|
|
42
42
|
type: "text",
|
|
43
43
|
name: "value",
|
|
44
44
|
message: t,
|
|
@@ -47,7 +47,7 @@ class k {
|
|
|
47
47
|
}))?.value ?? null;
|
|
48
48
|
}
|
|
49
49
|
async askForDate(t, e, i) {
|
|
50
|
-
return (await
|
|
50
|
+
return (await y({
|
|
51
51
|
type: "date",
|
|
52
52
|
name: "value",
|
|
53
53
|
message: t,
|
|
@@ -56,7 +56,7 @@ class k {
|
|
|
56
56
|
}))?.value ?? null;
|
|
57
57
|
}
|
|
58
58
|
async askForList(t, e, i) {
|
|
59
|
-
return (await
|
|
59
|
+
return (await y({
|
|
60
60
|
type: "list",
|
|
61
61
|
name: "value",
|
|
62
62
|
message: t,
|
|
@@ -65,7 +65,7 @@ class k {
|
|
|
65
65
|
}))?.value ?? null;
|
|
66
66
|
}
|
|
67
67
|
async askForToggle(t, e, i) {
|
|
68
|
-
return (await
|
|
68
|
+
return (await y({
|
|
69
69
|
type: "toggle",
|
|
70
70
|
name: "value",
|
|
71
71
|
message: t,
|
|
@@ -79,7 +79,7 @@ class k {
|
|
|
79
79
|
const n = [];
|
|
80
80
|
for (const o of e)
|
|
81
81
|
typeof o == "string" ? n.push({ title: o, value: o }) : n.push(o);
|
|
82
|
-
return (await
|
|
82
|
+
return (await y({
|
|
83
83
|
type: "select",
|
|
84
84
|
name: "value",
|
|
85
85
|
message: t,
|
|
@@ -104,10 +104,10 @@ class k {
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
class
|
|
107
|
+
class f extends Error {
|
|
108
108
|
$type = "BobError";
|
|
109
109
|
}
|
|
110
|
-
function
|
|
110
|
+
function F(r) {
|
|
111
111
|
if (r === "string" || r === "number") return null;
|
|
112
112
|
if (r === "boolean") return !1;
|
|
113
113
|
if (Array.isArray(r) && r.length === 1) {
|
|
@@ -117,7 +117,7 @@ function x(r) {
|
|
|
117
117
|
throw new Error("Invalid option type: " + r);
|
|
118
118
|
}
|
|
119
119
|
function E(r) {
|
|
120
|
-
return typeof r == "string" || Array.isArray(r) ?
|
|
120
|
+
return typeof r == "string" || Array.isArray(r) ? F(r) : r.default !== void 0 ? r.default : F(r.type);
|
|
121
121
|
}
|
|
122
122
|
function g(r) {
|
|
123
123
|
return typeof r == "string" || Array.isArray(r) ? {
|
|
@@ -138,7 +138,7 @@ function g(r) {
|
|
|
138
138
|
variadic: r.variadic ?? !1
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
|
-
class
|
|
141
|
+
class b extends f {
|
|
142
142
|
constructor(t, e = {}) {
|
|
143
143
|
super(`Invalid option ${t} in not recognized`), this.option = t, this.optionsSchema = e;
|
|
144
144
|
}
|
|
@@ -156,7 +156,7 @@ ${a.yellow("Available options")}:`);
|
|
|
156
156
|
t.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is not recognized.`);
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
-
class
|
|
159
|
+
class k extends f {
|
|
160
160
|
constructor(t) {
|
|
161
161
|
super(`Argument "${t}" is required.`), this.argument = t;
|
|
162
162
|
}
|
|
@@ -164,7 +164,7 @@ class F extends y {
|
|
|
164
164
|
t.log(`${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.argument)} is required.`);
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
|
-
class
|
|
167
|
+
class D extends f {
|
|
168
168
|
constructor(t) {
|
|
169
169
|
super(`Argument "${t}" is required.`), this.option = t;
|
|
170
170
|
}
|
|
@@ -172,7 +172,7 @@ class P extends y {
|
|
|
172
172
|
t.log(`${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.option)} is required.`);
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
-
class
|
|
175
|
+
class Q extends f {
|
|
176
176
|
constructor(t) {
|
|
177
177
|
let e = `Argument "${t.param}" value is invalid.`;
|
|
178
178
|
t.reason ? e += ` Reason: ${t.reason}` : e += ` Value: "${t.value}"`, super(e), this.param = t;
|
|
@@ -181,7 +181,7 @@ class K extends y {
|
|
|
181
181
|
t.log(` ${a.white.bgRed(" ERROR ")} Argument ${a.bold.yellow(this.param.param)} value is invalid. `), (this.param.value || this.param.reason) && t.log(""), this.param.value && t.log(` ${a.blue("Value")}: ${this.param.value}`), this.param.reason && t.log(` ${a.yellow("Reason")}: ${this.param.reason}`);
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
-
class
|
|
184
|
+
class C extends f {
|
|
185
185
|
constructor(t) {
|
|
186
186
|
let e = `Option "${t.option}" value is invalid.`;
|
|
187
187
|
t.reason ? e += ` Reason: ${t.reason}` : e += ` Value: "${t.value}"`, super(e), this.param = t;
|
|
@@ -190,7 +190,7 @@ class b extends y {
|
|
|
190
190
|
t.log(` ${a.white.bgRed(" ERROR ")} Option ${a.bold.yellow(this.param.option)} value is invalid. `), (this.param.value || this.param.reason) && t.log(""), this.param.value && t.log(` ${a.blue("Value")}: ${this.param.value}`), this.param.reason && t.log(` ${a.yellow("Reason")}: ${this.param.reason}`);
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
-
class H extends
|
|
193
|
+
class H extends f {
|
|
194
194
|
constructor(t) {
|
|
195
195
|
super(`Command "${t}" not found.`), this.command = t;
|
|
196
196
|
}
|
|
@@ -198,6 +198,16 @@ class H extends y {
|
|
|
198
198
|
t.log(`${a.bgRed(" ERROR ")} Command ${a.yellow(this.command)} not found.`);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
+
class P extends f {
|
|
202
|
+
constructor(t, e) {
|
|
203
|
+
super(`Too many arguments provided. Expected ${t}, got ${e}.`), this.expected = t, this.received = e;
|
|
204
|
+
}
|
|
205
|
+
pretty(t) {
|
|
206
|
+
t.log(
|
|
207
|
+
`${a.white.bgRed(" ERROR ")} Too many arguments provided. Expected ${a.bold.yellow(String(this.expected))}, got ${a.bold.yellow(String(this.received))}.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
201
211
|
function w(r, t, e, i) {
|
|
202
212
|
if (r == null)
|
|
203
213
|
return i ?? null;
|
|
@@ -206,7 +216,7 @@ function w(r, t, e, i) {
|
|
|
206
216
|
if (t === "number") {
|
|
207
217
|
const n = Number(r);
|
|
208
218
|
if (isNaN(n))
|
|
209
|
-
throw new
|
|
219
|
+
throw new C({
|
|
210
220
|
option: e,
|
|
211
221
|
reason: `Expected a number, got "${r}"`
|
|
212
222
|
});
|
|
@@ -222,7 +232,7 @@ function w(r, t, e, i) {
|
|
|
222
232
|
return s.map((o) => {
|
|
223
233
|
const u = Number(o);
|
|
224
234
|
if (isNaN(u))
|
|
225
|
-
throw new
|
|
235
|
+
throw new C({
|
|
226
236
|
option: e,
|
|
227
237
|
reason: `Expected array of numbers, got "${o}" in array`
|
|
228
238
|
});
|
|
@@ -238,6 +248,8 @@ class N {
|
|
|
238
248
|
parsedArguments = null;
|
|
239
249
|
io;
|
|
240
250
|
shouldPromptForMissingOptions = !0;
|
|
251
|
+
shouldValidateUnknownOptions = !0;
|
|
252
|
+
shouldRejectExtraArguments = !1;
|
|
241
253
|
constructor(t) {
|
|
242
254
|
this.options = t.options, this.arguments = t.arguments, this.io = t.io;
|
|
243
255
|
}
|
|
@@ -250,8 +262,8 @@ class N {
|
|
|
250
262
|
* @throws {BadCommandOption} If a value cannot be converted to the expected type
|
|
251
263
|
*/
|
|
252
264
|
init(t) {
|
|
253
|
-
const { _: e, ...i } =
|
|
254
|
-
return this.validateUnknownOptions(i), this.parsedOptions = this.handleOptions(i), this.parsedArguments = this.handleArguments(e), {
|
|
265
|
+
const { _: e, ...i } = q(t);
|
|
266
|
+
return this.shouldValidateUnknownOptions && this.validateUnknownOptions(i), this.parsedOptions = this.handleOptions(i), this.parsedArguments = this.handleArguments(e), {
|
|
255
267
|
options: this.parsedOptions,
|
|
256
268
|
arguments: this.parsedArguments
|
|
257
269
|
};
|
|
@@ -263,7 +275,7 @@ class N {
|
|
|
263
275
|
async validate() {
|
|
264
276
|
for (const t in this.options)
|
|
265
277
|
if (g(this.options[t]).required && (this.parsedOptions?.[t] === void 0 || this.parsedOptions?.[t] === null))
|
|
266
|
-
throw new
|
|
278
|
+
throw new D(t);
|
|
267
279
|
for (const t in this.arguments) {
|
|
268
280
|
const e = g(this.arguments[t]), i = this.parsedArguments?.[t];
|
|
269
281
|
if (e.required && i == null) {
|
|
@@ -274,7 +286,7 @@ class N {
|
|
|
274
286
|
continue;
|
|
275
287
|
}
|
|
276
288
|
}
|
|
277
|
-
throw new
|
|
289
|
+
throw new k(t);
|
|
278
290
|
}
|
|
279
291
|
if (e.required && e.variadic && Array.isArray(i) && i.length === 0) {
|
|
280
292
|
if (this.shouldPromptForMissingOptions) {
|
|
@@ -284,7 +296,7 @@ class N {
|
|
|
284
296
|
continue;
|
|
285
297
|
}
|
|
286
298
|
}
|
|
287
|
-
throw new
|
|
299
|
+
throw new k(t);
|
|
288
300
|
}
|
|
289
301
|
}
|
|
290
302
|
}
|
|
@@ -304,7 +316,7 @@ class N {
|
|
|
304
316
|
if (!this.parsedOptions)
|
|
305
317
|
throw new Error("Options have not been parsed yet. Call init() first.");
|
|
306
318
|
if (!(t in this.options))
|
|
307
|
-
throw new
|
|
319
|
+
throw new b(t, this.options);
|
|
308
320
|
this.parsedOptions[t] = e;
|
|
309
321
|
}
|
|
310
322
|
/**
|
|
@@ -323,7 +335,7 @@ class N {
|
|
|
323
335
|
if (!this.parsedArguments)
|
|
324
336
|
throw new Error("Arguments have not been parsed yet. Call init() first.");
|
|
325
337
|
if (!(t in this.arguments))
|
|
326
|
-
throw new
|
|
338
|
+
throw new b(t, this.arguments);
|
|
327
339
|
this.parsedArguments[t] = e;
|
|
328
340
|
}
|
|
329
341
|
// === PRIVATE HELPERS ===
|
|
@@ -349,7 +361,7 @@ class N {
|
|
|
349
361
|
}
|
|
350
362
|
for (const i in t)
|
|
351
363
|
if (!e.has(i))
|
|
352
|
-
throw new
|
|
364
|
+
throw new b(i, this.options);
|
|
353
365
|
}
|
|
354
366
|
/**
|
|
355
367
|
* Processes named options from minimist output
|
|
@@ -366,15 +378,17 @@ class N {
|
|
|
366
378
|
* Processes positional arguments from minimist output
|
|
367
379
|
*/
|
|
368
380
|
handleArguments(t) {
|
|
369
|
-
const e = {}, i = [...t];
|
|
370
|
-
for (const
|
|
371
|
-
const
|
|
372
|
-
if (
|
|
373
|
-
e[
|
|
381
|
+
const e = {}, i = [...t], n = Object.keys(this.arguments).length;
|
|
382
|
+
for (const s in this.arguments) {
|
|
383
|
+
const o = g(this.arguments[s]);
|
|
384
|
+
if (o.variadic) {
|
|
385
|
+
e[s] = this.handleVariadicArgument(s, o, i), i.length = 0;
|
|
374
386
|
continue;
|
|
375
387
|
}
|
|
376
|
-
e[
|
|
388
|
+
e[s] = this.resolveArgumentValue(s, o, i.shift());
|
|
377
389
|
}
|
|
390
|
+
if (this.shouldRejectExtraArguments && i.length > 0)
|
|
391
|
+
throw new P(n, n + i.length);
|
|
378
392
|
return e;
|
|
379
393
|
}
|
|
380
394
|
/**
|
|
@@ -404,7 +418,7 @@ class N {
|
|
|
404
418
|
}
|
|
405
419
|
if (n === void 0) {
|
|
406
420
|
if (e.required)
|
|
407
|
-
throw new
|
|
421
|
+
throw new C({
|
|
408
422
|
option: t,
|
|
409
423
|
reason: "Required option is missing"
|
|
410
424
|
});
|
|
@@ -437,6 +451,12 @@ class N {
|
|
|
437
451
|
disablePrompting() {
|
|
438
452
|
return this.shouldPromptForMissingOptions = !1, this;
|
|
439
453
|
}
|
|
454
|
+
allowUnknownOptions() {
|
|
455
|
+
return this.shouldValidateUnknownOptions = !1, this;
|
|
456
|
+
}
|
|
457
|
+
strictMode() {
|
|
458
|
+
return this.shouldRejectExtraArguments = !0, this;
|
|
459
|
+
}
|
|
440
460
|
/**
|
|
441
461
|
* Prompts the user to provide a missing argument value via CommandIO
|
|
442
462
|
* Used by validate() when shouldPromptForMissingArgs is enabled
|
|
@@ -478,10 +498,10 @@ class N {
|
|
|
478
498
|
});
|
|
479
499
|
}
|
|
480
500
|
}
|
|
481
|
-
function
|
|
501
|
+
function O(r) {
|
|
482
502
|
return new Array(r + 5).join(" ");
|
|
483
503
|
}
|
|
484
|
-
class
|
|
504
|
+
class M {
|
|
485
505
|
type = "boolean";
|
|
486
506
|
option = "help";
|
|
487
507
|
alias = ["h"];
|
|
@@ -503,11 +523,11 @@ class D {
|
|
|
503
523
|
this.io.log(`
|
|
504
524
|
${a.yellow("Arguments")}:`);
|
|
505
525
|
for (const [l, d] of i) {
|
|
506
|
-
const p =
|
|
526
|
+
const p = O(h - l.length);
|
|
507
527
|
let c = ` ${a.green(l)} ${p} ${d.description ?? "\b"}`;
|
|
508
528
|
if (d.default !== void 0 && !d.required) {
|
|
509
|
-
const
|
|
510
|
-
c += ` ${a.yellow(`[default: ${
|
|
529
|
+
const L = (Array.isArray(d.type) ? `[${d.type[0]}]` : d.type) === "array" || Array.isArray(d.type) ? JSON.stringify(d.default) : d.default;
|
|
530
|
+
c += ` ${a.yellow(`[default: ${L}]`)}`;
|
|
511
531
|
}
|
|
512
532
|
d.variadic && (c += ` ${a.white("(variadic)")}`), this.io.log(c);
|
|
513
533
|
}
|
|
@@ -516,15 +536,15 @@ ${a.yellow("Arguments")}:`);
|
|
|
516
536
|
this.io.log(`
|
|
517
537
|
${a.yellow("Options")}:`);
|
|
518
538
|
for (const l of s) {
|
|
519
|
-
const d =
|
|
539
|
+
const d = O(h - l.optionWithAlias.length);
|
|
520
540
|
let p = `${a.green(l.optionWithAlias)} ${d} ${l.description ?? "\b"}`;
|
|
521
541
|
if (l.type) {
|
|
522
542
|
const c = Array.isArray(l.type) ? `[${l.type[0]}]` : l.type;
|
|
523
543
|
p += ` ${a.white(`(${c})`)}`;
|
|
524
544
|
}
|
|
525
545
|
if (l.default !== void 0 && !l.required) {
|
|
526
|
-
const
|
|
527
|
-
p += ` ${a.yellow(`[default: ${
|
|
546
|
+
const R = (Array.isArray(l.type) ? `[${l.type[0]}]` : l.type) === "array" || Array.isArray(l.type) ? JSON.stringify(l.default) : l.default;
|
|
547
|
+
p += ` ${a.yellow(`[default: ${R}]`)}`;
|
|
528
548
|
}
|
|
529
549
|
this.io.log(p);
|
|
530
550
|
}
|
|
@@ -554,11 +574,15 @@ class A {
|
|
|
554
574
|
io;
|
|
555
575
|
parser;
|
|
556
576
|
disablePromptingFlag = !1;
|
|
577
|
+
allowUnknownOptionsFlag = !1;
|
|
578
|
+
hiddenFlag = !1;
|
|
579
|
+
disableDefaultOptionsFlag = !1;
|
|
580
|
+
strictModeFlag = !1;
|
|
557
581
|
_preHandler;
|
|
558
582
|
_handler;
|
|
559
583
|
tmp;
|
|
560
584
|
defaultOptions() {
|
|
561
|
-
return [new
|
|
585
|
+
return this.disableDefaultOptionsFlag ? [] : [new M()];
|
|
562
586
|
}
|
|
563
587
|
newCommandParser(t) {
|
|
564
588
|
return new N({
|
|
@@ -568,7 +592,7 @@ class A {
|
|
|
568
592
|
});
|
|
569
593
|
}
|
|
570
594
|
newCommandIO(t) {
|
|
571
|
-
return new
|
|
595
|
+
return new S(t);
|
|
572
596
|
}
|
|
573
597
|
constructor(t, e) {
|
|
574
598
|
this._command = t, this.description = e?.description ?? "", this.group = e?.group, this.tmp = {
|
|
@@ -583,6 +607,21 @@ class A {
|
|
|
583
607
|
disablePrompting() {
|
|
584
608
|
return this.disablePromptingFlag = !0, this;
|
|
585
609
|
}
|
|
610
|
+
allowUnknownOptions() {
|
|
611
|
+
return this.allowUnknownOptionsFlag = !0, this;
|
|
612
|
+
}
|
|
613
|
+
hidden() {
|
|
614
|
+
return this.hiddenFlag = !0, this;
|
|
615
|
+
}
|
|
616
|
+
get isHidden() {
|
|
617
|
+
return this.hiddenFlag;
|
|
618
|
+
}
|
|
619
|
+
disableDefaultOptions() {
|
|
620
|
+
return this.disableDefaultOptionsFlag = !0, this;
|
|
621
|
+
}
|
|
622
|
+
strictMode() {
|
|
623
|
+
return this.strictModeFlag = !0, this;
|
|
624
|
+
}
|
|
586
625
|
preHandler(t) {
|
|
587
626
|
return this._preHandler = t, this;
|
|
588
627
|
}
|
|
@@ -621,7 +660,7 @@ class A {
|
|
|
621
660
|
io: this.io,
|
|
622
661
|
options: n,
|
|
623
662
|
arguments: this.tmp?.arguments ?? {}
|
|
624
|
-
}), e = this.parser.init(t.args);
|
|
663
|
+
}), this.allowUnknownOptionsFlag && this.parser.allowUnknownOptions(), this.strictModeFlag && this.parser.strictMode(), e = this.parser.init(t.args);
|
|
625
664
|
for (const s of this.defaultOptions())
|
|
626
665
|
if (e.options[s.option] === !0) {
|
|
627
666
|
const o = await s.handler.call(this);
|
|
@@ -719,7 +758,7 @@ class $ extends N {
|
|
|
719
758
|
return i.startsWith("--") && (n = !0, i = i.slice(2)), s.default === "*" && (s.default = [], s.type = ["string"]), i.endsWith("?") && (s.required = !1, i = i.slice(0, -1)), i.endsWith("*") && (s.type = ["string"], s.variadic = !0, s.default = [], i = i.slice(0, -1)), s.description = s.description ?? e[i] ?? e[`--${i}`], { name: i, isOption: n, definition: s };
|
|
720
759
|
}
|
|
721
760
|
}
|
|
722
|
-
class
|
|
761
|
+
class X extends A {
|
|
723
762
|
helperDefinitions = {};
|
|
724
763
|
get command() {
|
|
725
764
|
return this.parser ? this.parser.command : this.signature.split(" ")[0];
|
|
@@ -755,7 +794,7 @@ class Q extends A {
|
|
|
755
794
|
return this.io.newLoader(...t);
|
|
756
795
|
}
|
|
757
796
|
}
|
|
758
|
-
class
|
|
797
|
+
class V {
|
|
759
798
|
level;
|
|
760
799
|
constructor(t = {}) {
|
|
761
800
|
this.level = t.level ?? "info";
|
|
@@ -826,22 +865,22 @@ class B {
|
|
|
826
865
|
};
|
|
827
866
|
}
|
|
828
867
|
}
|
|
829
|
-
function
|
|
868
|
+
function j(r) {
|
|
830
869
|
return typeof r == "object" && r !== null && "$type" in r && r.$type === "BobError";
|
|
831
870
|
}
|
|
832
|
-
function
|
|
871
|
+
function v(r) {
|
|
833
872
|
return typeof r == "object" && r !== null && (r instanceof A || "$type" in r && r.$type === "BobCommand");
|
|
834
873
|
}
|
|
835
|
-
class
|
|
874
|
+
class T {
|
|
836
875
|
commands = {};
|
|
837
876
|
io;
|
|
838
877
|
logger;
|
|
839
878
|
stringSimilarity;
|
|
840
879
|
newCommandIO(t) {
|
|
841
|
-
return new
|
|
880
|
+
return new S(t);
|
|
842
881
|
}
|
|
843
882
|
constructor(t) {
|
|
844
|
-
this.logger = t?.logger ?? new
|
|
883
|
+
this.logger = t?.logger ?? new V(), this.io = this.newCommandIO({
|
|
845
884
|
logger: this.logger
|
|
846
885
|
}), this.stringSimilarity = t?.stringSimilarity ?? new B();
|
|
847
886
|
}
|
|
@@ -854,7 +893,7 @@ class M {
|
|
|
854
893
|
importFile = async (t) => (await import(t)).default;
|
|
855
894
|
commandResolver = async (t) => {
|
|
856
895
|
let e = await this.importFile(t);
|
|
857
|
-
return e ? (e && typeof e == "object" && "default" in e && (e = e.default), typeof e == "function" ? new e() :
|
|
896
|
+
return e ? (e && typeof e == "object" && "default" in e && (e = e.default), typeof e == "function" ? new e() : v(e) ? e : null) : null;
|
|
858
897
|
};
|
|
859
898
|
withCommandResolver(t) {
|
|
860
899
|
return this.commandResolver = t, this;
|
|
@@ -863,9 +902,11 @@ class M {
|
|
|
863
902
|
return this.importFile = t, this;
|
|
864
903
|
}
|
|
865
904
|
registerCommand(t, e = !1) {
|
|
905
|
+
if (!v(t))
|
|
906
|
+
throw new Error("Invalid command, it must extend the Command class.");
|
|
866
907
|
const i = t.command;
|
|
867
908
|
if (!i)
|
|
868
|
-
throw new Error("
|
|
909
|
+
throw new Error("Cannot register a command with no name.");
|
|
869
910
|
if (!e && this.commands[i])
|
|
870
911
|
throw new Error(`Command ${i} already registered.`);
|
|
871
912
|
this.commands[i] = t;
|
|
@@ -874,7 +915,7 @@ class M {
|
|
|
874
915
|
for await (const e of this.listCommandsFiles(t))
|
|
875
916
|
try {
|
|
876
917
|
const i = await this.commandResolver(e);
|
|
877
|
-
|
|
918
|
+
v(i) && this.registerCommand(i);
|
|
878
919
|
} catch (i) {
|
|
879
920
|
throw new Error(`Command ${e} failed to load. ${i}`, {
|
|
880
921
|
cause: i
|
|
@@ -913,11 +954,11 @@ class M {
|
|
|
913
954
|
`), this.io.askForConfirmation(`${a.green(`Do you want to run ${a.yellow(e)} instead?`)} `);
|
|
914
955
|
}
|
|
915
956
|
async *listCommandsFiles(t) {
|
|
916
|
-
const e =
|
|
957
|
+
const e = I.readdirSync(t, { withFileTypes: !0 });
|
|
917
958
|
for (const i of e) {
|
|
918
|
-
const n =
|
|
959
|
+
const n = x.resolve(t, i.name);
|
|
919
960
|
if (i.isDirectory())
|
|
920
|
-
yield* this.listCommandsFiles(
|
|
961
|
+
yield* this.listCommandsFiles(x.resolve(t, i.name));
|
|
921
962
|
else {
|
|
922
963
|
if (!n.endsWith(".ts") && !n.endsWith(".js") && !n.endsWith(".mjs") && !n.endsWith(".cjs"))
|
|
923
964
|
continue;
|
|
@@ -926,25 +967,25 @@ class M {
|
|
|
926
967
|
}
|
|
927
968
|
}
|
|
928
969
|
}
|
|
929
|
-
class
|
|
970
|
+
class W {
|
|
930
971
|
logger;
|
|
931
972
|
constructor(t) {
|
|
932
973
|
this.logger = t;
|
|
933
974
|
}
|
|
934
975
|
handle(t) {
|
|
935
|
-
if (
|
|
976
|
+
if (j(t))
|
|
936
977
|
return t.pretty(this.logger), -1;
|
|
937
978
|
throw t;
|
|
938
979
|
}
|
|
939
980
|
}
|
|
940
|
-
class
|
|
981
|
+
class _ extends A {
|
|
941
982
|
constructor(t) {
|
|
942
983
|
super("help", {
|
|
943
984
|
description: a.bold("Show help information about the CLI and its commands")
|
|
944
985
|
}), this.opts = t;
|
|
945
986
|
}
|
|
946
987
|
async handle() {
|
|
947
|
-
const t = this.opts.commandRegistry.getCommands(), e = this.opts.cliName ?? "Bob CLI", i = this.opts.cliVersion ?? "0.0.0", n = (await import("../package-
|
|
988
|
+
const t = this.opts.commandRegistry.getCommands().filter((m) => !m.isHidden), e = this.opts.cliName ?? "Bob CLI", i = this.opts.cliVersion ?? "0.0.0", n = (await import("../package-uVOgoItG.js"))?.default?.version ?? "0.0.0";
|
|
948
989
|
this.io.log(`${e} ${a.green(i)} (core: ${a.yellow(n)})
|
|
949
990
|
|
|
950
991
|
${a.yellow("Usage")}:
|
|
@@ -963,29 +1004,29 @@ ${a.yellow("Available commands")}:
|
|
|
963
1004
|
l && this.io.log(a.yellow(`${m}:`));
|
|
964
1005
|
const d = h.sort((p, c) => p.command.toLowerCase().localeCompare(c.command.toLowerCase()));
|
|
965
1006
|
for (const p of d) {
|
|
966
|
-
let c =
|
|
1007
|
+
let c = O(s - p.command.length);
|
|
967
1008
|
l && (c = c.slice(2)), this.io.log(`${l ? " " : ""}${a.green(p.command)} ${c} ${p.description}`);
|
|
968
1009
|
}
|
|
969
1010
|
}
|
|
970
1011
|
}
|
|
971
1012
|
}
|
|
972
|
-
class
|
|
1013
|
+
class Y {
|
|
973
1014
|
ctx;
|
|
974
1015
|
logger;
|
|
975
1016
|
commandRegistry;
|
|
976
1017
|
exceptionHandler;
|
|
977
1018
|
helpCommand;
|
|
978
1019
|
newCommandRegistry(t) {
|
|
979
|
-
return new
|
|
1020
|
+
return new T(t);
|
|
980
1021
|
}
|
|
981
1022
|
newHelpCommand(t) {
|
|
982
|
-
return new
|
|
1023
|
+
return new _(t);
|
|
983
1024
|
}
|
|
984
1025
|
newExceptionHandler(t) {
|
|
985
|
-
return new
|
|
1026
|
+
return new W(t.logger);
|
|
986
1027
|
}
|
|
987
1028
|
constructor(t = {}) {
|
|
988
|
-
this.ctx = t.ctx, this.logger = t.logger ?? new
|
|
1029
|
+
this.ctx = t.ctx, this.logger = t.logger ?? new V(), this.commandRegistry = this.newCommandRegistry({
|
|
989
1030
|
logger: this.logger
|
|
990
1031
|
}), this.exceptionHandler = this.newExceptionHandler({
|
|
991
1032
|
logger: this.logger
|
|
@@ -1016,22 +1057,23 @@ class X {
|
|
|
1016
1057
|
}
|
|
1017
1058
|
}
|
|
1018
1059
|
export {
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1060
|
+
C as BadCommandOption,
|
|
1061
|
+
Q as BadCommandParameter,
|
|
1062
|
+
f as BobError,
|
|
1063
|
+
Y as Cli,
|
|
1023
1064
|
A as Command,
|
|
1024
|
-
|
|
1065
|
+
S as CommandIO,
|
|
1025
1066
|
H as CommandNotFoundError,
|
|
1026
1067
|
N as CommandParser,
|
|
1027
|
-
|
|
1068
|
+
T as CommandRegistry,
|
|
1028
1069
|
$ as CommandSignatureParser,
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
B as StringSimilarity
|
|
1070
|
+
X as CommandWithSignature,
|
|
1071
|
+
W as ExceptionHandler,
|
|
1072
|
+
M as HelpOption,
|
|
1073
|
+
b as InvalidOption,
|
|
1074
|
+
V as Logger,
|
|
1075
|
+
k as MissingRequiredArgumentValue,
|
|
1076
|
+
D as MissingRequiredOptionValue,
|
|
1077
|
+
B as StringSimilarity,
|
|
1078
|
+
P as TooManyArguments
|
|
1037
1079
|
};
|