sentry-javascript icon indicating copy to clipboard operation
sentry-javascript copied to clipboard

No such built-in module commming from import-in-the-middle when using ts-node

Open AnthonyDugarte opened this issue 1 year ago • 7 comments

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("./"));

AnthonyDugarte avatar Jun 12 '24 19:06 AnthonyDugarte

Hi, thanks for reporting, we'll take a look!

Lms24 avatar Jun 13 '24 07:06 Lms24

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.

timfish avatar Jun 13 '24 22:06 timfish

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.

AnthonyDugarte avatar Jun 14 '24 17:06 AnthonyDugarte

Any update on this?

The export * from './module.js' syntax keeps failing on @sentry/[email protected]

AnthonyDugarte avatar Jun 18 '24 00:06 AnthonyDugarte

Here's a repo on which the issue is reproduced.

https://github.com/AnthonyDugarte/sentry-node-export-all-issue

AnthonyDugarte avatar Jun 18 '24 01:06 AnthonyDugarte

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')).

AnthonyDugarte avatar Jun 18 '24 05:06 AnthonyDugarte

by applying this patches provided here the app runs with star exports.

AnthonyDugarte avatar Jun 18 '24 06:06 AnthonyDugarte

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.

AbhiPrasad avatar Jul 08 '24 18:07 AbhiPrasad