11import { existsSync , readFileSync } from 'node:fs' ;
22import { dirname , join } from 'node:path' ;
33import type { IntegrationFn } from '@sentry/core' ;
4- import { defineIntegration , logger } from '@sentry/core' ;
5- import { DEBUG_BUILD } from '../debug-build' ;
4+ import { defineIntegration } from '@sentry/core' ;
65import { isCjs } from '../utils/commonjs' ;
76
8- let moduleCache : { [ key : string ] : string } ;
7+ type ModuleInfo = Record < string , string > ;
8+
9+ let moduleCache : ModuleInfo | undefined ;
910
1011const INTEGRATION_NAME = 'Modules' ;
1112
12- const _modulesIntegration = ( ( ) => {
13- // This integration only works in CJS contexts
14- if ( ! isCjs ( ) ) {
15- DEBUG_BUILD &&
16- logger . warn (
17- 'modulesIntegration only works in CommonJS (CJS) environments. Remove this integration if you are using ESM.' ,
18- ) ;
19- return {
20- name : INTEGRATION_NAME ,
21- } ;
22- }
13+ declare const __SENTRY_SERVER_MODULES__ : Record < string , string > ;
14+
15+ /**
16+ * This is replaced at build time with the modules loaded by the server.
17+ */
18+ const SERVER_MODULES = typeof __SENTRY_SERVER_MODULES__ === 'undefined' ? { } : __SENTRY_SERVER_MODULES__ ;
2319
20+ const _modulesIntegration = ( ( ) => {
2421 return {
2522 name : INTEGRATION_NAME ,
2623 processEvent ( event ) {
@@ -36,13 +33,14 @@ const _modulesIntegration = (() => {
3633
3734/**
3835 * Add node modules / packages to the event.
39- *
40- * Only works in CommonJS (CJS) environments.
36+ * For this, multiple sources are used:
37+ * - They can be injected at build time into the __SENTRY_SERVER_MODULES__ variable (e.g. in Next.js)
38+ * - They are extracted from the dependencies & devDependencies in the package.json file
39+ * - They are extracted from the require.cache (CJS only)
4140 */
4241export const modulesIntegration = defineIntegration ( _modulesIntegration ) ;
4342
44- /** Extract information about paths */
45- function getPaths ( ) : string [ ] {
43+ function getRequireCachePaths ( ) : string [ ] {
4644 try {
4745 return require . cache ? Object . keys ( require . cache as Record < string , unknown > ) : [ ] ;
4846 } catch ( e ) {
@@ -51,17 +49,23 @@ function getPaths(): string[] {
5149}
5250
5351/** Extract information about package.json modules */
54- function collectModules ( ) : {
55- [ name : string ] : string ;
56- } {
52+ function collectModules ( ) : ModuleInfo {
53+ return {
54+ ...SERVER_MODULES ,
55+ ...getModulesFromPackageJson ( ) ,
56+ ...( isCjs ( ) ? collectRequireModules ( ) : { } ) ,
57+ } ;
58+ }
59+
60+ /** Extract information about package.json modules from require.cache */
61+ function collectRequireModules ( ) : ModuleInfo {
5762 const mainPaths = require . main ?. paths || [ ] ;
58- const paths = getPaths ( ) ;
59- const infos : {
60- [ name : string ] : string ;
61- } = { } ;
62- const seen : {
63- [ path : string ] : boolean ;
64- } = { } ;
63+ const paths = getRequireCachePaths ( ) ;
64+
65+ // We start with the modules from package.json (if possible)
66+ // These may be overwritten by more specific versions from the require.cache
67+ const infos : ModuleInfo = { } ;
68+ const seen = new Set < string > ( ) ;
6569
6670 paths . forEach ( path => {
6771 let dir = path ;
@@ -71,15 +75,15 @@ function collectModules(): {
7175 const orig = dir ;
7276 dir = dirname ( orig ) ;
7377
74- if ( ! dir || orig === dir || seen [ orig ] ) {
78+ if ( ! dir || orig === dir || seen . has ( orig ) ) {
7579 return undefined ;
7680 }
7781 if ( mainPaths . indexOf ( dir ) < 0 ) {
7882 return updir ( ) ;
7983 }
8084
8185 const pkgfile = join ( orig , 'package.json' ) ;
82- seen [ orig ] = true ;
86+ seen . add ( orig ) ;
8387
8488 if ( ! existsSync ( pkgfile ) ) {
8589 return updir ( ) ;
@@ -103,9 +107,34 @@ function collectModules(): {
103107}
104108
105109/** Fetches the list of modules and the versions loaded by the entry file for your node.js app. */
106- function _getModules ( ) : { [ key : string ] : string } {
110+ function _getModules ( ) : ModuleInfo {
107111 if ( ! moduleCache ) {
108112 moduleCache = collectModules ( ) ;
109113 }
110114 return moduleCache ;
111115}
116+
117+ interface PackageJson {
118+ dependencies ?: Record < string , string > ;
119+ devDependencies ?: Record < string , string > ;
120+ }
121+
122+ function getPackageJson ( ) : PackageJson {
123+ try {
124+ const filePath = join ( process . cwd ( ) , 'package.json' ) ;
125+ const packageJson = JSON . parse ( readFileSync ( filePath , 'utf8' ) ) as PackageJson ;
126+
127+ return packageJson ;
128+ } catch ( e ) {
129+ return { } ;
130+ }
131+ }
132+
133+ function getModulesFromPackageJson ( ) : ModuleInfo {
134+ const packageJson = getPackageJson ( ) ;
135+
136+ return {
137+ ...packageJson . dependencies ,
138+ ...packageJson . devDependencies ,
139+ } ;
140+ }
0 commit comments