Skip to main content
Module

x/quarrel/src/quarrel.ts

A Deno command line argument parser, highly configurable, and fast (well, not yet)
Latest
File
import {InvalidMiddlewareError} from './errors.ts';import * as defaults from './defaultFunctions.ts';
export interface flagOptions { middleware?: Function | null, exit?: Boolean | null | undefined, args?: Array<object> | null | undefined}
interface lessFlagOptions { middleware?: Function | null, exit?: Boolean | null}
export interface command { description: String | string, args: Array<object>}
export class Quarrel { public name: String | string; public version: String | string | Number | number public arguments: Map<String | string, command>; private methods: Map<String | string, lessFlagOptions>; public altNames: Map<String | string, String | string>; private expressions: Map<String | string, RegExp>; public flags: Array<String | string>;
public constructor(name: String | string = '', version: String | string | Number | number = '1.0.0') { //variable assignment this.arguments = new Map<String | string, command>(); this.altNames = new Map<String | string, String | string>(); this.methods = new Map<String | string, lessFlagOptions>(); this.expressions = new Map<String | string, RegExp>(); this.flags = new Array<String | string>();
//capture caller name for if no name was provided if (name === '') { let stack: any = new Error().stack; stack = stack.split('\n'); name = stack[2] .split('file:///')[1] .split(/:\w[?\w?]*/g)[0] .split('/'); name = name[name.length - 1].split('.ts')[0]; } this.name = name; this.version = version; }
public flag(shortName: String | string, longName: String | string, description: String | string, options: flagOptions | Function | null = null): void { let args: Array<object> = new Array<object>(); let middleware: Function | undefined = undefined; let exit: Boolean = false; if (options instanceof Function) { middleware = options; } else if (options instanceof Object) { if (options.middleware instanceof Function) { middleware = () => { if (options.middleware !== null) { options.middleware(this); } }; } if (options.args) args = options.args as Array<object> if (options.exit) exit = options.exit; } else { if (shortName.match(/h/i)) { middleware = defaults.help; exit = true; } else if (shortName.match(/f/i)) { middleware = defaults.force; exit = false; } else if (shortName.match(/v/i)) { middleware = defaults.version; exit = true; } else { middleware = undefined; throw new InvalidMiddlewareError(`The flag "${longName}" ("${shortName}") is not common, we do not have a default middleware method for it, please provide one.`) } } if (middleware !== undefined) { this.arguments.set(shortName, {description, args}); this.altNames.set(longName, shortName); this.methods.set(shortName, {middleware, exit}); } }
public run(): void { let matchers = { flag: /-(?:-?)\w+/, arg: /(?:=?)\w\d+|(?: ?)\w\d+/, punctuation: /[^\w\s]/, whitespace: /\s+/ } let env = Deno.args.join(' '); let flags = tokenize(env, matchers, 'invalid'); let matches = new Map<String, Array<string>>(); flags.forEach(flag => { matches.set(flag.flag.replace('-', ''), flag.args); }) this.altNames.forEach((value, arg) => { let args = matches.get(value); if (args === undefined) { args = matches.get(arg); if (args !== undefined) { flags.push(arg) this.runMiddleware(args, value); } } else { flags.push(arg) this.runMiddleware(args, value); } }) this.flags = flags; }
/* * @Params * args: Array<string> - the arguments to pass to the middleware * value: String | string - the name of the flag mapped to the middleware */ private runMiddleware(args: Array<string>, value: String | string) { let flag = this.methods.get(value) as flagOptions; if (flag.middleware !== undefined && flag.middleware !== null) { flag.middleware(this, args); if (flag.exit) { Deno.close(0); } } }}
//turn the arguments into proper flags that the parser can readfunction tokenize(string: string, parsers: any, deftok: string) { let length, arr, tokenObject, tokens = []; while (string) { tokenObject = null; length = string.length; for (let key in parsers) { arr = parsers[key].exec(string); // try to choose the best match if there are several // where "best" is the closest to the current starting point if (arr && (arr.index < length)) { tokenObject = { token: arr[0], type: key, matches: arr.slice(1) } length = arr.index; } } if (length) { // there is text between last token and currently // matched token - push that out as default or "unknown" tokens.push({ token: string.substr(0, length), type: deftok || 'unknown' }); } if (tokenObject) { // push current token onto sequence tokens.push(tokenObject); } string = string.substr(length + (tokenObject ? tokenObject.token.length : 0)); } let arg = { flag: '', args: '' }; let args = new Array<any>(); tokens.forEach(token => { if (token.type === 'flag') { if (arg.flag !== '') { if (arg.args[0] === '=') { arg.args = arg.args.substr(1, arg.args.length); } args.push({flag: arg.flag, args: arg.args.split(' ')}); } arg.args = ''; arg.flag = token.token; } else { arg.args += token.token } }) if (arg.args[0] === '=') { arg.args = arg.args.substr(1, arg.args.length); } args.push({flag: arg.flag, args: arg.args.split(' ')}); return args;}