Description
We’re going to be adding proper support for ESM in the Sentry repo.
Requirement:
- There is no
require
in ESM bundles at all - ESM files have
.mjs
file extension - CJS files have
.cjs
file extension
### Tasks
- [ ] https://github.com/getsentry/sentry-javascript/pull/10069
- [x] (pre-v8) Figure out how to use `dynamicRequire` with ESM
- [x] (pre-v8) Remove usage of `require.main.filename` for module name resolution #10061
- [x] (v8) Vendor `https-proxy-agent` which is cjs only #10088
- [x] (v8) Change build to output `mjs`/`cjs` for esm/cjs
- [x] (v8) Remove conditional import of `worker_threads` #10791
- [x] (v8) Update `inspector.d.ts` to work with esm and cjs
- [x] (v8) Remove require call in `require('inspector')` in `LocalVariables` integration
- [x] (v8) Update Rollup
- [ ] https://github.com/getsentry/sentry-javascript/pull/10928
- [ ] https://github.com/getsentry/sentry-javascript/issues/11066
- [ ] https://github.com/getsentry/sentry-javascript/issues/11067
Read below for some justification and information
Module Customization Hooks for Monkey Patching
For ESM bundles on server-side, we’ll require a minimum Node version of 18.6.0
or 20.6.0
or higher. Otherwise we’ll support CJS bundles for Node 14+ generally. This is because we want access to Node customization hooks, which allows us to add monkeypatching for esm modules programatically (doesn't require command-line loader
).
We can take some inspiration here from https://github.com/DataDog/import-in-the-middle
import { register } from 'node:module';
register('./sentry-patch.mjs', import.meta.url);
Registering hooks to affect an import still needs to happen before the import is resolved, so there are ordering issues, but I think we can work around this by recommending that Sentry.init
is called as soon as possible, and encouraging Sentry.addIntegration
patterns for integrations that require objects from different libraries (like express with app
).
The modules we need register via register
API:
Nodenode:http
andnode:https
(diagnostics channels only works well Node 16+, so we need to maintain this for Node 14- Remix
@remix-run/server-runtime
and- monkeypatchreact-router-dom
- OTEL esm instrumentation
GoogleCloudGrpc
integrationrequire('google-gax')
GoogleCloudHttp
integration withrequire('@google-cloud/common')
AWSServices
integration withrequire('aws-sdk/global')
We need to re-evaluate if we need monkeypatching for the database integrations now that we have OpenTelemetry.
Change File Structure
We’ll need to move our file structure to the following:
{
// at build time we strip `build/X`
"main": "build/cjs/index.cjs",
"module": "build/esm/index.mjs",
"types": "build/types/index.d.cts",
// note: this is an opportunity for us to explore multiple subpaths, like
// exporting from `@sentry/browser/utils`
"exports": {
"./package.json": "./package.json",
".": {
// esm
"import": {
// path must be relative
"types": "./build/types/index.d.mts",
"default": "./build/esm/index.mjs"
},
// cjs
"require": {
// path must be relative
"types": "./build/types/index.d.cts",
"default": "./build/esm/index.mjs"
}
// we might need to expose an ESM only export for customization hooks
// to use via node --loader=... API
}
},
"typesVersions": {
"<4.9": {
"build/types/index.d.cts": [
"build/types-ts3.8/index.d.cts"
],
"build/types/index.d.mts": [
"build/types-ts3.8/index.d.mts"
]
}
},
"files": [
"cjs",
"esm",
"types",
"types-ts3.8"
]
}
Shameless promo: Watch my talk if you want more details about this.
We also should change all node standard library imports to import from node:X
.
Make dynamicRequire
/loadModule
work with ESM import
This exists to trick webpack, but we use it all over the codebase. We need to add an esm compatible way.
One thing we can do is introduce an async dynamicRequire
that simulates await import
, and then build time replace the functionality of dynamicRequire
to use import
or require
under the hood?
packages/node/src/integrations/anr/index.ts
- to conditionally importworker_threads
. This is just because of Node 12 support, we’ll remove this import entirelypackages/node/src/integrations/local-variables/local-variables-async.ts
- to conditionally importnode:inspector/promises
.- All of our database integrations - these are all going away
packages/utils/src/time.ts
- this needs to be refactored entirely. We still want an isomorphic solution because we need date-time helpers that work across core/browser/node and all other packages, but what should happen is that we look for theglobalThis.performance
instead of relying onperf_hooks
. This means that if the global performance API is not available we will fall back to plain[Date.now](http://Date.now)
and similar helpers (basically only Node 14 for us). We also need to add a smarter timeOrigin calculation that reset’s itself to help alleviate issues with drifting monotonic clock. See Provide a supported way to get anchored clock times open-telemetry/opentelemetry-js#3279 (comment)
Metadata
Metadata
Assignees
Type
Projects
Status