Ts-node using worker_thread cause `cannot use import statement outside a module`

3.3k Views Asked by At

I have this simple code to try worker threads.

// thread.ts
import { Worker, isMainThread } from 'worker_threads';

if (isMainThread) {
    const worker = new Worker(__filename);
} else {
    console.log('hello from worker');
}

Apparently when I try to run the file with ts-node thread.ts, i got this error:

(node:13200) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.

SyntaxError: Cannot use import statement outside a module

But if I compile & invoke the file manually tsc && dist/thread.js, it worked just fine

I try to experiment further, if I put the code that I want to run inside worker thread in an external file, then it worked just fine too

// thread.ts
import { Worker, isMainThread } from 'worker_threads';
const worker = new Worker('./src/worker.ts');

Here is my tsconfig.json

{
    "compilerOptions": {
        "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
        "module": "commonjs", /* Specify what module code is generated. */
         "rootDir": "./src", /* Specify the root folder within your source files. */
         "outDir": "./dist", /* Specify an output folder for all emitted files. */
         "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
        "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
        "strict": true, /* Enable all strict type-checking options. */
        "skipLibCheck": true /* Skip type checking all .d.ts files. */
    }
}

Can anybody enlighten me?

2

There are 2 best solutions below

0
On

Faced similar issu with launching .ts files as workers and here is how i managed to do this First of all, i use wrapper function to create a worker instance:

import { Worker } from 'worker_threads';
const workerTs = (file: string, wkOpts: WorkerOptions & any) => {


wkOpts.eval = true;
  if (!wkOpts.workerData) {
    wkOpts.workerData = {};
  }

  wkOpts.workerData.__filename = file;
  return new Worker(`
          const wk = require('worker_threads');
          require('tsconfig-paths/register');
          require('ts-node').register({
            "compilerOptions": {
              "target": "es2016",
              "esModuleInterop": true,
              "module": "commonjs",
              "rootDir": ".",
            }
          });
          let file = wk.workerData.__filename;
          delete wk.workerData.__filename;
          require(file);
      `,
    wkOpts
  );
};

const worker = workerTs('./worker.ts', { worderData: { /* */ } });

And my worker.ts looks like this:

import { workerData, parentPort } from "worker_threads";

doSmth(workerData);

parentPort.postMessage('done');

Hope it will help you!

0
On

For me what really helped was adding:

execArgv: ["--require", "ts-node/register"]

...to the worker options. But since in my case I transpile TS into JS for production and I don't want to have ts-node as a production dependency, so I wrote this function:

import { Worker, WorkerOptions } from "worker_threads";

function importWorker(path: string, options: WorkerOptions) {
  const resolvedPath = require.resolve(path);
  return new Worker(resolvedPath, {
    ...options,
    execArgv: /\.ts$/.test(resolvedPath) ? ["--require", "ts-node/register"] : undefined,
  });
}

And when I call this function I don't add an extension, just as we do in regular imports.