import moduleApi = require ( 'module' ) ; import process from 'process' ; import { LauncherAction } from './LauncherTypes' ; import type { IIpcTrace , IIpcDone , IIpcTraceRecord } from './LauncherTypes' ; const IPC_BATCH_SIZE = 300 ; class Launcher { public action : LauncherAction = LauncherAction . Inspect ; public targetScriptPathArg : string = '' ; public reportPath : string = '' ; private _importedModules : Set < unknown > = new Set ( ) ; private _importedModulePaths : Set < string > = new Set ( ) ; private _ipcTraceRecordsBatch : IIpcTraceRecord [ ] = [ ] ; public transformArgs ( argv ) { let nodeArg ; let remainderArgs ; [ nodeArg , , this . targetScriptPathArg , ... remainderArgs ] = argv ; return [ nodeArg , this . targetScriptPathArg , ... remainderArgs ] ; } private static _copyProperties ( dst , src ) { for ( const prop of Object . keys ( src ) ) { dst [ prop ] = src [ prop ] ; } } private _sendIpcTraceBatch ( ) { if ( this . _ipcTraceRecordsBatch . length > 0 ) { const batch = [ ... this . _ipcTraceRecordsBatch ] ; this . _ipcTraceRecordsBatch . length = 0 ; process . send ! ( { id : 'trace' , records : batch } as IIpcTrace ) ; } } /**
     * Synchronously delay for the specified time interval.
     */ private static _delayMs ( milliseconds ) { Atomics . wait ( new Int32Array ( new SharedArrayBuffer ( 4 ) ) , 0 , 0 , milliseconds ) ; } public installHook ( ) { const realRequire = moduleApi . Module . prototype . require ; const importedModules = this . _importedModules ; const importedModulePaths = this . _importedModulePaths ; const ipcTraceRecordsBatch = this . _ipcTraceRecordsBatch ; const sendIpcTraceBatch = this . _sendIpcTraceBatch . bind ( this ) ; function hookedRequire ( this , moduleName ) { const callingModuleInfo = this ; const importedModule = ( realRequire as any ) . apply ( callingModuleInfo , arguments ) ; if ( ! importedModules . has ( importedModule ) ) { importedModules . add ( importedModule ) ; let importedModuleInfo = undefined ; const children = callingModuleInfo . children || [ ] ; for ( const child of children ) { if ( child . exports === importedModule ) { importedModuleInfo = child ; break ; } } if ( importedModuleInfo === undefined ) { } else { if ( ! importedModuleInfo . filename ) { throw new Error ( 'Missing filename for ' + moduleName ) ; } if ( ! importedModulePaths . has ( importedModuleInfo . filename ) ) { importedModulePaths . add ( importedModuleInfo . filename ) ; ipcTraceRecordsBatch . push ( { importedModule : importedModuleInfo . filename , callingModule : callingModuleInfo . filename } ) ; if ( ipcTraceRecordsBatch . length >= IPC_BATCH_SIZE ) { sendIpcTraceBatch ( ) ; } } } } return importedModule ; } moduleApi . Module . prototype . require = hookedRequire ; Launcher . _copyProperties ( hookedRequire , realRequire ) ; process . on ( 'exit' , ( ) => { this . _sendIpcTraceBatch ( ) ; process . send ! ( { id : 'done' } as IIpcDone ) ; Launcher . _delayMs ( 500 ) ; } ) ; } } if ( ! process . send ) { throw new Error ( 'launcher.js must be invoked via IPC' ) ; } const launcher = new Launcher ( ) ; const originalArgv = [ ... process . argv ] ; process . argv . length = 0 ; process . argv . push ( ... launcher . transformArgs ( originalArgv ) ) ; launcher . installHook ( ) ; require ( launcher . targetScriptPathArg ) ;