import { EventEmitter } from 'events' ; import * as fs from 'fs-extra' ; import { IS_DRY_RUN } from '../constants' ; import { findVCCLogFile } from '../utils/find-logfile' ; import { includesFailure , includesSuccess } from '../utils/installation-sucess' ; const debug = require ( 'debug' ) ( 'windows-build-tools' ) ; export class Tailer extends EventEmitter { public logFile : string ; public encoding : string ; public tailInterval ; constructor ( logfile , encoding = 'utf8' ) { super ( ) ; this . logFile = logfile ; this . encoding = encoding ; } /**
     * Starts watching a the logfile
     */ public start ( ) { if ( this . logFile ) { debug ( `Tail: Waiting for log file to appear in ${ this . logFile }` ) ; } else { debug ( `Tail: Waiting for log file to appear. Searching in %TEMP%` ) ; } this . waitForLogFile ( ) ; } /**
     * Stop watching
     */ public stop ( ... args ) { debug ( `Tail: Stopping` , ... args ) ; this . emit ( 'exit' , ... args ) ; clearInterval ( this . tailInterval ) ; } /**
     * Start tailing things
     */ public tail ( ) { debug ( `Tail: Tailing ${ this . logFile }` ) ; this . tailInterval = setInterval ( ( ) => this . readData ( ) , 500 ) ; } public readData ( ) { if ( IS_DRY_RUN ) { this . emit ( 'lastLines' , `Dry run, we're all done` ) ; return this . stop ( 'success' ) ; } let data = '' ; try { data = fs . readFileSync ( this . logFile , this . encoding ) ; } catch ( err ) { debug ( `Tail start: Could not read logfile ${ this . logFile }: ${ err }` ) ; return ; } if ( data && data . length > 0 ) { const split = data . split ( /\r?\n/ ) || [ 'Still looking for log file...' ] ; const lastLines = split . filter ( ( l ) => l . trim ( ) . length > 0 ) . slice ( split . length - 6 , split . length ) ; this . emit ( 'lastLines' , lastLines ) ; this . handleData ( data ) ; } } param { string } data public handleData ( data ) { const { isBuildToolsSuccess , isPythonSuccess } = includesSuccess ( data ) ; if ( isBuildToolsSuccess ) { debug ( `Tail: Reporting success for VCC Build Tools` ) ; this . stop ( 'success' ) ; return ; } if ( isPythonSuccess ) { const matches = data . match ( /Property\(S\): TARGETDIR = (.*)\r\n/ ) ; let pythonPath ; if ( matches ) { pythonPath = matches [ 1 ] ; } debug ( `Tail: Reporting success for Python` ) ; this . stop ( 'success' , pythonPath ) ; return ; } const { isPythonFailure , isBuildToolsFailure } = includesFailure ( data ) ; if ( isPythonFailure || isBuildToolsFailure ) { debug ( `Tail: Reporting failure in ${ this . logFile }` ) ; this . stop ( 'failure' ) ; } } param file { string } returns { Promise . < Object > } public waitForLogFile ( ) { if ( IS_DRY_RUN ) return this . tail ( ) ; const handleStillWaiting = ( ) => { debug ( 'Tail: waitForFile: still waiting' ) ; setTimeout ( this . waitForLogFile . bind ( this ) , 2000 ) ; } ; const handleKnownPath = ( logFile ) => { fs . lstat ( logFile , ( err , stats ) => { if ( err && err . code === 'ENOENT' ) { handleStillWaiting ( ) ; } else if ( err ) { debug ( 'Tail: waitForFile: Unexpected error' , err ) ; throw err ; } else { debug ( `Tail: waitForFile: Found ${ logFile }` ) ; this . tail ( ) ; } } ) ; } ; if ( ! this . logFile ) { findVCCLogFile ( ) . then ( ( logFile ) => { debug ( `Tail: LogFile found: ${ logFile }` ) ; if ( ! logFile ) { handleStillWaiting ( ) ; } else { this . logFile = logFile ; handleKnownPath ( logFile ) ; } } ) . catch ( ( error ) => { throw new Error ( error ) ; } ) ; } else { handleKnownPath ( this . logFile ) ; } } }