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