@@ -49,6 +49,11 @@ for (const builtin of RAW_BUILTINS) {
4949// eslint-disable-next-line eslint-rules/eslint-process-env
5050const DD_IAST_ENABLED = process . env . DD_IAST_ENABLED ?. toLowerCase ( ) === 'true' || process . env . DD_IAST_ENABLED === '1'
5151
52+ const DEBUGGER_WORKER_FILENAME = 'dd-trace-debugger-worker.cjs'
53+
54+ // Path pattern to match the debugger index.js inside dd-trace package
55+ const DEBUGGER_INDEX_PATH = path . join ( 'dd-trace' , 'src' , 'debugger' , 'index.js' )
56+
5257module . exports . name = 'datadog-esbuild'
5358
5459function isESMBuild ( build ) {
@@ -372,7 +377,146 @@ register(${JSON.stringify(toRegister)}, _, set, get, ${JSON.stringify(data.raw)}
372377 resolveDir : path . dirname ( args . path ) ,
373378 }
374379 }
380+
381+ // Rewrite the debugger worker path so it points to the emitted worker bundle
382+ if ( args . path . includes ( DEBUGGER_INDEX_PATH ) ) {
383+ log . debug ( 'TRANSFORM DEBUGGER PATH: %s' , args . path )
384+ let contents = fs . readFileSync ( args . path , 'utf8' )
385+
386+ // Replace: join(__dirname, 'devtools_client', 'index.js')
387+ // With: join(__dirname, 'dd-trace-debugger-worker.cjs')
388+ // The bundled __dirname will point to the output directory where we emit the worker bundle
389+ contents = contents . replaceAll (
390+ / j o i n \( _ _ d i r n a m e , \s * [ ' " ] d e v t o o l s _ c l i e n t [ ' " ] , \s * [ ' " ] i n d e x \. j s [ ' " ] \) / g,
391+ `join(__dirname, '${ DEBUGGER_WORKER_FILENAME } ')`
392+ )
393+
394+ return {
395+ contents,
396+ loader : 'js' ,
397+ resolveDir : path . dirname ( args . path ) ,
398+ }
399+ }
375400 } )
401+
402+ // Build the Dynamic Instrumentation worker bundle as a secondary artifact
403+ build . onEnd ( async ( result ) => {
404+ if ( result . errors . length > 0 ) {
405+ log . debug ( 'Skipping DI worker build due to main build errors' )
406+ return
407+ }
408+
409+ const outputDir = getOutputDirectory ( build . initialOptions )
410+ if ( ! outputDir ) {
411+ log . warn (
412+ // eslint-disable-next-line @stylistic/max-len
413+ 'Cannot emit Live Debugger/Dynamic Instrumentation worker bundle. No outfile or outdir specified. LD/DI will not work in the bundled application.'
414+ )
415+ return
416+ }
417+
418+ await buildDebuggerWorker ( build . initialOptions , outputDir , build . esbuild )
419+ } )
420+ }
421+
422+ /**
423+ * Determine the output directory from esbuild options
424+ *
425+ * @param {object } initialOptions - esbuild initial options
426+ * @returns {string | null } - Output directory path or null
427+ */
428+ function getOutputDirectory ( initialOptions ) {
429+ if ( initialOptions . outdir ) {
430+ return initialOptions . outdir
431+ }
432+ if ( initialOptions . outfile ) {
433+ return path . dirname ( initialOptions . outfile )
434+ }
435+ return null
436+ }
437+
438+ /**
439+ * Build the Dynamic Instrumentation worker bundle
440+ *
441+ * @param {object } parentOptions - Parent build options
442+ * @param {string } outputDir - Output directory for the worker bundle
443+ * @param {object } esbuild - esbuild module instance from the parent build
444+ */
445+ async function buildDebuggerWorker ( parentOptions , outputDir , esbuild ) {
446+ if ( ! esbuild ) {
447+ log . warn ( 'esbuild instance not available. LD/DI worker bundle cannot be built.' )
448+ return
449+ }
450+
451+ // Resolve the devtools_client entry point from the installed dd-trace package
452+ let workerEntryPoint
453+ try {
454+ // First try to resolve dd-trace to find its installation path
455+ // eslint-disable-next-line n/no-missing-require -- dd-trace is a peer dependency
456+ const ddTracePath = require . resolve ( 'dd-trace/package.json' )
457+ workerEntryPoint = path . join ( path . dirname ( ddTracePath ) , 'src' , 'debugger' , 'devtools_client' , 'index.js' )
458+ } catch {
459+ // Fallback: resolve relative to this plugin (for development/linked packages)
460+ workerEntryPoint = path . join ( __dirname , '..' , 'dd-trace' , 'src' , 'debugger' , 'devtools_client' , 'index.js' )
461+ }
462+
463+ if ( ! fs . existsSync ( workerEntryPoint ) ) {
464+ log . warn (
465+ 'Could not find DI worker entry point at %s. LD/DI will not work in the bundled application.' ,
466+ workerEntryPoint
467+ )
468+ return
469+ }
470+
471+ const workerOutfile = path . join ( outputDir , DEBUGGER_WORKER_FILENAME )
472+
473+ log . debug ( 'Building DI worker bundle: %s -> %s' , workerEntryPoint , workerOutfile )
474+
475+ // Plugin to patch the trace/span lookup to use global._ddtrace
476+ const patchDDTracePlugin = {
477+ name : 'patch-ddtrace-lookup' ,
478+ setup ( workerBuild ) {
479+ workerBuild . onLoad ( { filter : / d e v t o o l s _ c l i e n t [ / \\ ] i n d e x \. j s $ / } , ( args ) => {
480+ let contents = fs . readFileSync ( args . path , 'utf8' )
481+
482+ // Replace the dd-trace require expression with a bundler-safe version
483+ // Original: global.require('dd-trace').scope().active()?.context()
484+ // New: (global._ddtrace ?? global.require?.('dd-trace'))?.scope?.()?.active?.()?.context?.()
485+ contents = contents . replaceAll (
486+ / g l o b a l \. r e q u i r e \( [ ' " ] d d - t r a c e [ ' " ] \) \. s c o p e \( \) \. a c t i v e \( \) \? \. c o n t e x t \( \) / g,
487+ "(global._ddtrace ?? global.require?.('dd-trace'))?.scope?.()?.active?.()?.context?.()"
488+ )
489+
490+ return {
491+ contents,
492+ loader : 'js' ,
493+ }
494+ } )
495+ } ,
496+ }
497+
498+ try {
499+ await esbuild . build ( {
500+ entryPoints : [ workerEntryPoint ] ,
501+ bundle : true ,
502+ platform : 'node' ,
503+ format : 'cjs' ,
504+ outfile : workerOutfile ,
505+ target : parentOptions . target || [ 'node18' ] ,
506+ // Keep Node.js builtins external
507+ external : [ ...RAW_BUILTINS , ...RAW_BUILTINS . map ( m => `node:${ m } ` ) ] ,
508+ // Ensure function/class names are preserved for debugging
509+ keepNames : true ,
510+ plugins : [ patchDDTracePlugin ] ,
511+ // Don't minify the worker - keep it debuggable
512+ minify : false ,
513+ logLevel : 'warning' ,
514+ } )
515+
516+ log . debug ( 'DI worker bundle emitted: %s' , workerOutfile )
517+ } catch ( err ) {
518+ log . warn ( 'Failed to build DI worker bundle: %s' , err . message )
519+ }
376520}
377521
378522// @see https://github.com/nodejs/node/issues/47000
0 commit comments