sentry-javascript
sentry-javascript copied to clipboard
No such built-in module commming from import-in-the-middle when using ts-node
Is there an existing issue for this?
- [X] I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- [X] I have reviewed the documentation https://docs.sentry.io/
- [X] I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/node
SDK Version
8.9.1
Framework Version
Node v20.12.1
Link to Sentry event
No response
SDK Setup
Sentry.init({
dsn,
environment,
enabled,
tracesSampleRate,
});
Steps to Reproduce
Migrated from v7 to v8.
Added the @sentry/node/preload and it failed to run.
#12357 is similar but it's using tsx, the loaders order is wrong according to the thread; here I'm explicitly specifing the loaders order and I'm using ts-node with EMS. Using tsx is not negotiable given that they don't support the experimental legacy decorators.
By following #12422, I was able to make it work using: tsc --build && node --import @sentry/node/preload -r dotenv/config dist/index.js; but that's by not using ts-node, which is the issue I'm bringing up.
Expected Result
No error
Actual Result
SENTRY_DEBUG=1 node --import @sentry/node/preload --import ../ts-node.register.mjs -r dotenv/config src/index.ts
Sentry Logger [debug]: @opentelemetry/api: Registered a global for diag v1.9.0.
Sentry Logger [log]: [Sentry] Preloaded Http instrumentation
Sentry Logger [log]: [Sentry] Preloaded Express instrumentation
Sentry Logger [log]: [Sentry] Preloaded Connect instrumentation
Sentry Logger [log]: [Sentry] Preloaded Fastify instrumentation
Sentry Logger [log]: [Sentry] Preloaded Hapi instrumentation
Sentry Logger [log]: [Sentry] Preloaded Koa instrumentation
Sentry Logger [log]: [Sentry] Preloaded Nest instrumentation
Sentry Logger [log]: [Sentry] Preloaded Mongo instrumentation
Sentry Logger [log]: [Sentry] Preloaded Mongoose instrumentation
Sentry Logger [log]: [Sentry] Preloaded Mysql instrumentation
Sentry Logger [log]: [Sentry] Preloaded Mysql2 instrumentation
Sentry Logger [log]: [Sentry] Preloaded Postgres instrumentation
Sentry Logger [log]: [Sentry] Preloaded Hapi instrumentation
Sentry Logger [log]: [Sentry] Preloaded Graphql instrumentation
Sentry Logger [log]: [Sentry] Preloaded Redis instrumentation
Error [ERR_UNKNOWN_BUILTIN_MODULE]: No such built-in module: node:http?iitm=true
at ModuleLoader.builtinStrategy (node:internal/modules/esm/translators:465:11)
at callTranslator (node:internal/modules/esm/loader:279:14)
at ModuleLoader.moduleProvider (node:internal/modules/esm/loader:285:30) {
code: 'ERR_UNKNOWN_BUILTIN_MODULE'
}
../ts-node.register.mjs content is:
import { register } from "node:module";
import { pathToFileURL } from "node:url";
process.on('uncaughtException', function (err) {
console.error(err);
process.exit(1);
});
register("ts-node/esm", pathToFileURL("./"));
Hi, thanks for reporting, we'll take a look!
It's a shame the stack trace from Node is so short and unhelpful!
I can reproduce this but I can also work around it by reversing the order of the --import:
node --import ./ts-node.register.mjs --import @sentry/node/preload -r dotenv/config src/index.ts
This changes the order that the various ESM loader hooks are registered and therefore the order that they're called to resolve and load source code.
By switching the order I get a different error:
[Error: ENOENT: no such file or directory, open '/path-to-app/src/controllers/users.controller.js'] {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/path-to-app/src/controllers/users.controller.js'
}
src/index.ts has an import to the controllers folder index file that looks like:
import { UserController, ...otherControllers } from './controllers/index.js';
and src/controllers/index.ts content is simply:
export * from './users.controller.js';
// ... other controllers
It's worth noticing that by switching the src/controllers/index.ts content to use .ts import termination and enabling "allowImportingTsExtensions": true in the tsconfig.json, the error goes the a different .js file termination.
export * from './users.controller.ts';
// ... other controllers
I have a bunch of imports so didn't update them all to try if the app runs correctly, will try it and share an update.
Edit 1
It seems to be an issue with index.ts files that is filled only with export * expressions.
I updated all the: export * from './path-to-file.js' to be export * from './path-to-file.ts' in index.ts file, and the app booted up.
Edit 2
By updating the exports to be explicit, export { A, B, C } from './file.js' and reverting back to the .js export syntax the app ran correctly, it is an issue with the export * from './file.js' expression.
Any update on this?
The export * from './module.js' syntax keeps failing on @sentry/[email protected]
Here's a repo on which the issue is reproduced.
https://github.com/AnthonyDugarte/sentry-node-export-all-issue
Found a monkeypatch, at https://github.com/DataDog/import-in-the-middle/blob/88605a707a63af9797986aa6a100161fe02aace6/hook.js#L169:
- if (isBareSpecifier(modFile)) {
+ if (isBareSpecifier(modFile) || srcUrl.endsWith('.ts')) {
in order to generate the exports for a module that is star exporting (export * from './module.js), it needs to resolve the actual module.
But given that modFile is a relative path: const [, modFile] = n.split('* from '), modFile is set to './module.js'.
So the condition isBareSpecifier(modFile) fails, and it simply tries to resolve as if the file actually existed, but it does not given that we are processing a typescript file on the fly via ts-node:
} else {
modUrl = new URL(modFile, srcUrl).href
}
the monkeypatch forces the tool to resolve relative paths to imports that are part of a typescript file (srcUrl.endsWith('.ts')).
by applying this patches provided here the app runs with star exports.
With the newest release of import-in-the-middle v1.9.0 this should be fixed.
If you upgrade to a fresh install of the latest version of the Node SDK it should use [email protected] by default.