Skip to content

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

Description

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"
    }
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions