import commander from "commander" ; import fs from "fs" ; import { Logger , transports } from "winston" ; import { logger } from "../logger" ; import { ConfigYaml } from "../types" ; import { hasValue } from "./validator" ; import { build as buildError } from "./errorBuilder" ; import { errorStatusCode } from "./errorStatusCode" ; /**
 * Command Option
 */ export interface CommandOption { arg : string ; description : string ; inherit ? : string ; required ? : boolean ; defaultValue ? : string | boolean ; } /**
 * Command Descriptor
 */ export interface CommandBuildElements { command : string ; alias : string ; description : string ; disabled ? : boolean ; options ? : CommandOption [ ] ; } interface CommandVariableName2Opt { opt : CommandOption ; variableName : string ; } param opt export const argToVariableName = ( opt ) : string => { const match = opt . arg . match ( /\s?--([-\w]+)\s?/ ) ; if ( match ) { return match [ 1 ] . replace ( /\.?(-[a-z])/g , ( _ , y ) => { return y . toUpperCase ( ) ; } ) . replace ( /-/g , "" ) ; } throw buildError ( errorStatusCode . COMMANDER_ERR , { errorKey : "commander-err-cannot-locate-opt" , values : [ opt . arg ] , } ) ; } ; param command param decorator return export const build = ( command , decorator ) : commander . Command => { const cmd = command . command ( decorator . command ) . alias ( decorator . alias ) . description ( decorator . description ) ; ( decorator . options || [ ] ) . forEach ( ( opt ) => { if ( opt . defaultValue !== undefined ) { cmd . option ( opt . arg , opt . description , opt . defaultValue ) ; } else { cmd . option ( opt . arg , opt . description ) ; } } ) ; return cmd ; } ; param decorator param values param toThrow return export const validateForRequiredValues = ( decorator , opts , toThrow = false ) : string [ ] => { const required = ( decorator . options || [ ] ) . filter ( ( opt ) => opt . required ) ; if ( required . length === 0 ) { return [ ] ; } const values = opts as CommandValues ; const mapVariableName2Opt = [ ] ; required . forEach ( ( opt ) => { mapVariableName2Opt . push ( { opt , variableName : argToVariableName ( opt ) , } ) ; } ) ; const missingItems = mapVariableName2Opt . filter ( ( item ) => ! hasValue ( values [ item . variableName ] ) ) ; const errors = missingItems . map ( ( item ) => item . opt . arg ) ; if ( toThrow && errors . length !== 0 ) { throw buildError ( errorStatusCode . VALIDATION_ERR , { errorKey : "validation-err-missing-vals" , values : [ errors . join ( "\n " ) ] , } ) ; } if ( errors . length !== 0 ) { logger . error ( `the following arguments are required:\n ${ errors . join ( "\n " ) }` ) ; } return errors ; } ; param log param exitFn param statusCode export const exit = ( log , exitFn , statusCode , timeout = 10000 ) : Promise < void > => { return new Promise ( ( resolve ) => { const hasFileLogger = log . transports . some ( ( t ) => { if ( t instanceof transports . File ) { fs . watchFile ( t . filename , ( curr ) => { if ( curr . size > 0 ) { exitFn ( statusCode ) ; resolve ( ) ; } } ) ; return true ; } return false ; } ) ; if ( ! hasFileLogger ) { exitFn ( statusCode ) ; resolve ( ) ; } else { setTimeout ( ( ) => { exitFn ( statusCode ) ; resolve ( ) ; } , timeout ) ; } } ) ; } ; param decorator param name export const getOption = ( decorator , name ) : CommandOption | undefined => { return decorator . options ?. find ( ( opt ) => { const match = opt . arg . match ( /\s?--([-\w]+)\s?/ ) ; return match && match [ 1 ] === name ; } ) ; } ; interface ConfigValues { [ key ] : string | ConfigValues | undefined ; } interface CommandValues { [ key ] : string | undefined ; } param decorator param config param opts export const populateInheritValueFromConfig = ( decorator , config , opts ) : void => { if ( config && decorator . options ) { const mapOpts = opts as CommandValues ; decorator . options . filter ( ( o ) => ! ! o . inherit ) . forEach ( ( option ) => { const name = argToVariableName ( option ) ; if ( ! hasValue ( mapOpts [ name ] ) ) { let cfg = config as ConfigValues ; const arr = option . inherit ! . split ( "." ) ; while ( cfg && typeof config === "object" && arr . length > 0 ) { const k = arr . shift ( ) ; if ( k ) { cfg = ( cfg as ConfigValues ) [ k ] ; } } if ( typeof cfg === "string" ) { mapOpts [ name ] = cfg ; } } } ) ; } } ;