@@ -29,7 +29,7 @@ export abstract class InstrumentationBase<T = any>
2929 extends InstrumentationAbstract
3030 implements types . Instrumentation {
3131 private _modules : InstrumentationModuleDefinition < T > [ ] ;
32- private _hooks : RequireInTheMiddle . Hooked [ ] = [ ] ;
32+ private _hooked = false ;
3333 private _enabled = false ;
3434
3535 constructor (
@@ -142,7 +142,7 @@ export abstract class InstrumentationBase<T = any>
142142 this . _enabled = true ;
143143
144144 // already hooked, just call patch again
145- if ( this . _hooks . length > 0 ) {
145+ if ( this . _hooked ) {
146146 for ( const module of this . _modules ) {
147147 if ( typeof module . patch === 'function' && module . moduleExports ) {
148148 module . patch ( module . moduleExports , module . moduleVersion ) ;
@@ -158,22 +158,20 @@ export abstract class InstrumentationBase<T = any>
158158
159159 this . _warnOnPreloadedModules ( ) ;
160160 for ( const module of this . _modules ) {
161- this . _hooks . push (
162- RequireInTheMiddle (
163- [ module . name ] ,
164- { internals : true } ,
165- ( exports , name , baseDir ) => {
166- return this . _onRequire < typeof exports > (
167- ( module as unknown ) as InstrumentationModuleDefinition <
168- typeof exports
161+ requireInTheMiddleSingleton . register (
162+ module . name ,
163+ ( exports , name , baseDir ) => {
164+ return this . _onRequire < typeof exports > (
165+ ( module as unknown ) as InstrumentationModuleDefinition <
166+ typeof exports
169167 > ,
170- exports ,
171- name ,
172- baseDir
173- ) ;
174- }
175- )
168+ exports ,
169+ name ,
170+ baseDir
171+ ) ;
172+ }
176173 ) ;
174+ this . _hooked = true ;
177175 }
178176 }
179177
@@ -210,3 +208,41 @@ function isSupported(supportedVersions: string[], version?: string, includePrere
210208 return satisfies ( version , supportedVersion , { includePrerelease } ) ;
211209 } ) ;
212210}
211+
212+ /**
213+ * Singleton class for `require-in-the-middle`
214+ * Allows instrumentation plugins to patch modules with only a single `require` patch
215+ * TODO: Move this to a higher level to ensure it is a singleton across the Node.js process
216+ */
217+ class RequireInTheMiddleSingleton {
218+ private _modulesToHook : Array < { moduleName : string , onRequire : RequireInTheMiddle . OnRequireFn } > = [ ] ;
219+
220+ constructor ( ) {
221+ this . initialize ( ) ;
222+ }
223+
224+ initialize ( ) {
225+ RequireInTheMiddle (
226+ // Intercept all `require` calls; we will filter the matching ones below
227+ null ,
228+ { internals : true } ,
229+ ( exports , name , baseDir ) => {
230+ const matches = this . _modulesToHook . filter ( ( { moduleName : hookedName } ) => {
231+ return name === hookedName || name . startsWith ( hookedName + path . sep ) ;
232+ } ) ;
233+
234+ for ( const { onRequire } of matches ) {
235+ exports = onRequire ( exports , name , baseDir ) ;
236+ }
237+
238+ return exports ;
239+ }
240+ ) ;
241+ }
242+
243+ register ( moduleName : string , onRequire : RequireInTheMiddle . OnRequireFn ) {
244+ this . _modulesToHook . push ( { moduleName, onRequire } ) ;
245+ }
246+ }
247+
248+ const requireInTheMiddleSingleton = new RequireInTheMiddleSingleton ( ) ;
0 commit comments