Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ts-node doesn't run ESM modules as expected, either refusingimport statements in ts file or not being able to run .ts files #2086

Open
GovindarajanNagarajan-TomTom opened this issue Nov 16, 2023 · 6 comments

Comments

@GovindarajanNagarajan-TomTom

Search Terms

ESM import paths

Expected Behavior

I expect ts-node to be able to run typescript code using ESM module imports. However , it either fails with

SyntaxError: Cannot use import statement outside a module  

or after adding a "type": "module" to package.json, fails with

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/MY_PROJECT_DIR/workers/master.ts

The documentation is rather insufficient on how ts-node operations with tsconfig.json and the type value in package.json.
Of all of target, module and moduleResolution in tsconfig.json:compilerOptions, what should be the first value that is set (as in, does target determine module and moduleResolution's values, or vice versa ?)

Please provide some guidance on how this works.

Actual Behavior

Steps to reproduce the problem

This file creates basically parses the env variables and then creates a bunch of worker processes.

run with npx ts-node workers/master.ts

// workers/master.ts
import Redis from 'ioredis';
import { fork } from 'child_process';
import dotenv from 'dotenv';

import { Worker } from './worker';
import { isValid } from './../shared/lib/utils';

dotenv.config();
const streamNames: string[] = ["stream-1", "stream-2"];
const numWorkersPerStream: number = 5;
const REDIS_HOST = process.env.REDIS_HOST;
const REDIS_PORT = isValid(process.env.REDIS_PORT) ? parseInt(process.env.REDIS_PORT!, 10) : null;

if (!isValid(REDIS_HOST)) {
  console.error('REDIS_HOST is not a valid environmental variable');
  process.exit(1);
}

if (!isValid(REDIS_PORT)) {
  console.error('REDIS_PORT is not a valid environmental variable');
  process.exit(1);
}


const redisClient = new Redis({
  host: REDIS_HOST!,
  port: REDIS_PORT!,
});

async function createConsumerGroupIfNotExists(streamName: string, consumerGroup: string) {
  try {
    await redisClient.xgroup('CREATE', streamName, consumerGroup, '$', 'MKSTREAM');
    console.log(`Consumer group "${consumerGroup}" created (if not already exists) for stream "${streamName}"`);
  } catch (error: any) {
    if (!error.message.includes('BUSYGROUP Consumer Group name already exists')) {
      console.error('Error creating consumer group:', error);
    }
  }
}

// Create consumer groups only once
for (let i = 0; i < streamNames.length; i++) {
  const streamName = streamNames[i];
  const consumerGroup = `${streamName}-group`;
  createConsumerGroupIfNotExists(streamName, consumerGroup);
}



// Create worker processes for each stream and group combination
for (let i = 0; i < streamNames.length; i++) {
  for (let j = 0; j < numWorkersPerStream; j++) {
    const workerName: string = `worker-${streamNames[i]}-${j}`;
    const streamName: string = streamNames[i];
    const consumerGroup: string = `${streamName}-group`;
    const args: string[] = [workerName, streamName, consumerGroup, REDIS_HOST!, REDIS_PORT!.toString()];
    const workerProcess = fork("./workers/worker.ts", args);
    workerProcess.send("start"); // Signal the child process to start
  }
}%

Minimal reproduction

Specifications

OS: macOS Sonoma 14.1.1
"ts-node": "^10.9.1"
node version: 20.6.1

tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "ts-node": {
    "compilerOptions": {
       "target": "es2015",
      "module": "es2015",
      "moduleResolution": "nodenext"
    }
  }
}
@MGMehdi
Copy link

MGMehdi commented Nov 21, 2023

I've got the same issue.
I've tried to change the target and module in tsconfig.json and add type:module in package.json. Tried every combinaison but there is always something that prevents to run.

It looks like this problem has been around for a few years from the posts on github and stackoverlow.

@nikkehtine
Copy link

When I run ts-node-esm ./src/index.ts or ts-node --esm ./src/index.ts, I get this error:

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for D:\Code\simple_node_server\src\index.ts
    at new NodeError (node:internal/errors:406:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:99:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:142:36)
    at defaultLoad (node:internal/modules/esm/load:120:20)
    at nextLoad (node:internal/modules/esm/hooks:833:28)
    at load (D:\Biblioteka\pnpm-store\5\.pnpm\ts-node@10.9.1_@types+node@14.18.33_typescript@5.2.2\node_modules\ts-node\dist\child\child-loader.js:19:122)
    at nextLoad (node:internal/modules/esm/hooks:833:28)
    at Hooks.load (node:internal/modules/esm/hooks:416:26)
    at MessagePort.handleMessage (node:internal/modules/esm/worker:168:24)
    at [nodejs.internal.kHybridDispatch] (node:internal/event_target:807:20) {      
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}

tsconfig:

"compilerOptions": {
    "target": "es2016",
    "module": "esnext",
    "esModuleInterop": true,
  }

@AitorMelero
Copy link

I had the same issue on a computer with macOS, another with Windows, and another with Ubuntu, all of them running Node version 20. When testing the previous version of Node (18.18.2) on a machine with Windows and another with Ubuntu, it worked on both.

It appears that the issue arises when using version 10 of ts-node with Node version 20.

@ethanhalko
Copy link

I was finally able to get this working with Node 20 and ts-node 10 by setting module to commonjs and target to es6.

Provided your project can support your Typescript compiling to CommonJS, it works well.

@arbassic
Copy link

arbassic commented Nov 22, 2023

In the latest node.js weekly newsletter they sent an interesting gist / project stub addressing the issues - it may be helpful!

https://gist.github.com/khalidx/1c670478427cc0691bda00a80208c8cc

It is really weird that ts-node in conjuction with Node 20 is making such problems!

Also.. have you tried tsx?

@janpapenbrock
Copy link

This is probably a duplicate of #1997?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants