import { Json } from '@app-config/utils' ; import { ParsingExtension , parseRawString , guessFileType , AppConfigError } from '@app-config/core' ; import { named , forKey , validateOptions } from '@app-config/extension-utils' ; import { resolveFilepath } from '@app-config/node' ; import { exec } from 'child_process' ; import { promisify } from 'util' ; const execAsync = promisify ( exec ) ; export interface Options { command : string ; failOnStderr : boolean ; parseOutput : boolean ; trimWhitespace : boolean ; } class ExecError extends AppConfigError { name = 'ExecError' ; } function execParsingExtension ( ) { return named ( '$exec' , forKey ( '$exec' , validateOptions ( ( SchemaBuilder ) => SchemaBuilder . oneOf ( SchemaBuilder . stringSchema ( ) , SchemaBuilder . emptySchema ( ) . addString ( 'command' ) . addBoolean ( 'failOnStderr' , { } , false ) . addBoolean ( 'parseOutput' , { } , false ) . addBoolean ( 'trimWhitespace' , { } , false ) ) , ( value ) => async ( parse , _ , source ) => { let options ; if ( typeof value === 'string' ) { options = { command : value } ; } else { options = value ; } const { command , failOnStderr = false , parseOutput = false , trimWhitespace = true , } = options ; try { const dir = resolveFilepath ( source , '.' ) ; const { stdout , stderr } = await execAsync ( command , { cwd : dir } ) ; if ( failOnStderr && stderr ) { throw new ExecError ( `$exec command "${ command }" produced stderr: ${ stderr }` ) ; } let result = stdout ; if ( trimWhitespace ) { result = stdout . trim ( ) ; } if ( parseOutput ) { const fileType = await guessFileType ( stdout ) ; result = await parseRawString ( stdout , fileType ) ; } return parse ( result , { shouldFlatten : true } ) ; } catch ( err ) { if ( ! isError ( err ) ) { throw err ; } if ( err instanceof ExecError ) { throw err ; } throw new ExecError ( `$exec command "${ command }" failed with error:\n${ err . message }` ) ; } } ) ) ) ; } /**
 * Check if a value is an Error.
 *
 * This was created because `value instanceof Error` was returning false
 * for normal-looking errors coming from `child_process` `exec()` command
 */ function isError ( value ) { return typeof value === 'object' && ( value as Error ) . message !== undefined ; } export default execParsingExtension ;