file copyright author Vivek M. Chawla <@VivekMChawla>
 *  summary description version license import { JsonMap } from '@salesforce/ts-types' ; import { SfdxCliError } from '../../sfdx-falcon-error' ; import { ShellError } from '../../sfdx-falcon-error' ; import { updateObserver } from '../../sfdx-falcon-notifications' ; import { FalconProgressNotifications } from '../../sfdx-falcon-notifications' ; import { SfdxFalconResult } from '../../sfdx-falcon-result' ; import { SfdxFalconResultType } from '../../sfdx-falcon-result' ; import { Subscriber } from '../../sfdx-falcon-types' ; import { safeParse } from '../../sfdx-falcon-util' ; import { detectSalesforceCliError } from '../../sfdx-falcon-util/sfdx' ; const shell = require ( 'shelljs' ) ; const dbgNs = 'EXECUTOR:sfdx:' ; interface description export interface SfdxCommandDefinition { command : string ; progressMsg : string ; errorMsg : string ; successMsg : string ; commandArgs : string [ ] ; commandFlags : JsonMap ; observer : Subscriber ; } interface description export interface ExecutorResultDetail { sfdxCommandDef : SfdxCommandDefinition ; sfdxCommandString : string ; stdOutParsed : JsonMap ; stdOutBuffer : string ; stdErrBuffer : string ; } function param { SfdxCommandDefinition } sfdxCommandDef returns { Promise < SfdxFalconExecutorResponse > } description version public async export async function executeSfdxCommand ( sfdxCommandDef ) { const executorResult = new SfdxFalconResult ( `sfdx:executeSfdxCommand` , SfdxFalconResultType . EXECUTOR ) ; const executorResultDetail = { sfdxCommandDef : sfdxCommandDef , sfdxCommandString : null , stdOutParsed : null , stdOutBuffer : null , stdErrBuffer : null } as ExecutorResultDetail ; executorResult . detail = executorResultDetail ; executorResult . debugResult ( 'Executor Result Initialized' , `${ dbgNs }executeSfdxCommand:` ) ; const sfdxCommandString = parseSfdxCommand ( sfdxCommandDef ) ; executorResultDetail . sfdxCommandString = sfdxCommandString ; executorResult . debugResult ( 'Parsed SFDX Command Object to String' , `${ dbgNs }executeSfdxCommand:` ) ; return new Promise ( ( resolve , reject ) => { let stdOutBuffer = '' ; let stdErrBuffer = '' ; shell . env [ 'FORCE_COLOR' ] = 0 ; shell . env [ 'SFDX_JSON_TO_STDOUT' ] = 'true' ; shell . env [ 'SFDX_AUTOUPDATE_DISABLE' ] = 'true' ; const childProcess = shell . exec ( sfdxCommandString , { silent : true , async : true } ) ; updateObserver ( sfdxCommandDef . observer , `[0.000s] Executing ${ sfdxCommandDef . command }` ) ; const progressNotifications = FalconProgressNotifications . start ( sfdxCommandDef . progressMsg , 1000 , executorResult , sfdxCommandDef . observer ) ; childProcess . stdout . on ( 'data' , ( stdOutDataStream ) => { stdOutBuffer += stdOutDataStream ; } ) ; childProcess . stderr . on ( 'data' , ( stdErrDataStream ) => { stdErrBuffer += stdErrDataStream ; } ) ; childProcess . on ( 'close' , ( code , signal ) => { FalconProgressNotifications . finish ( progressNotifications ) ; executorResultDetail . stdOutBuffer = stdOutBuffer ; executorResultDetail . stdErrBuffer = stdErrBuffer ; const stdOutJsonResponse = stdOutBuffer . substring ( stdOutBuffer . indexOf ( '{' ) , stdOutBuffer . lastIndexOf ( '}' ) + 1 ) ; const parsedCliResponse = safeParse ( stdOutJsonResponse ) as JsonMap ; if ( code !== 0 ) { if ( detectSalesforceCliError ( parsedCliResponse ) ) { executorResult . failure ( new SfdxCliError ( sfdxCommandString , stdOutJsonResponse , sfdxCommandDef . errorMsg , `${ dbgNs }executeSfdxCommand` ) ) ; executorResult . debugResult ( 'CLI Command Failed' , `${ dbgNs }executeSfdxCommand:` ) ; resolve ( executorResult ) ; } else { executorResult . error ( new ShellError ( sfdxCommandString , code , signal , stdErrBuffer , stdOutBuffer , `${ dbgNs }executeSfdxCommand` ) ) ; executorResult . debugResult ( 'CLI Command Shell Error' , `${ dbgNs }executeSfdxCommand:` ) ; reject ( executorResult ) ; } } else { if ( parsedCliResponse . unparsed ) { executorResult . error ( new ShellError ( sfdxCommandString , code , signal , stdErrBuffer , stdOutBuffer , `${ dbgNs }executeSfdxCommand` ) ) ; executorResult . debugResult ( 'CLI Command Shell Error' , `${ dbgNs }executeSfdxCommand:` ) ; reject ( executorResult ) ; } if ( detectSalesforceCliError ( parsedCliResponse ) ) { executorResult . error ( new SfdxCliError ( sfdxCommandString , stdOutJsonResponse , sfdxCommandDef . errorMsg , `${ dbgNs }executeSfdxCommand` ) ) ; executorResult . debugResult ( 'CLI Command Failed' , `${ dbgNs }executeSfdxCommand:` ) ; reject ( executorResult ) ; } executorResultDetail . stdOutParsed = parsedCliResponse ; updateObserver ( sfdxCommandDef . observer , `[${ executorResult . durationString }] SUCCESS: ${ sfdxCommandDef . successMsg }` ) ; executorResult . success ( ) ; executorResult . debugResult ( 'CLI Command Succeeded' , `${ dbgNs }executeSfdxCommand:` ) ; resolve ( executorResult ) ; } } ) ; } ) as Promise < SfdxFalconResult > ; } function param { SfdxCommandDefinition } sfdxCommand returns { string } description private function parseSfdxCommand ( sfdxCommand ) { let parsedCommand = `sfdx ${ sfdxCommand . command }` ; for ( const argument of sfdxCommand . commandArgs ) { parsedCommand += ' ' + sanitizeArgument ( argument ) ; } for ( const objectKey of Object . keys ( sfdxCommand . commandFlags ) ) { if ( objectKey . substr ( 0 , 5 ) . toUpperCase ( ) !== 'FLAG_' ) { continue ; } const flag = objectKey . substring ( 5 ) . toLowerCase ( ) ; const value = sfdxCommand . commandFlags [ objectKey ] as string ; const hyphen = flag . length === 1 ? '-' : '--' ; const resolvedFlag = sanitizeFlag ( hyphen + flag ) ; if ( typeof value === 'boolean' ) { parsedCommand += ` ${ resolvedFlag }` ; continue ; } parsedCommand += ` ${ resolvedFlag } ${ sanitizeArgument ( value ) }` ; } return parsedCommand ; } function param { string } argument returns { string } description private function sanitizeArgument ( argument ) { argument = String ( argument ) . trim ( ) ; if ( /[^A-Za-z0-9_\/:=-]/ . test ( argument ) ) { argument = "'" + argument . replace ( /'/g , "'\\''" ) + "'" ; argument = argument . replace ( /^(?:'')+/g , '' ) . replace ( /\\'''/g , "\\'" ) ; } return argument ; } function param { string } flag returns { string } description private function sanitizeFlag ( flag ) { flag = String ( flag ) . trim ( ) ; flag = flag . replace ( /[^a-z0-9-]/gim , "" ) ; return flag ; }