Zach Smith
Zach Smith

Reputation: 8961

How to get babel-node to convert files referenced by Workers to commonjs? (like babel does)

In an Express.js app, I'm using Babel to precompile to commonjs before starting it. The compilation step looks like this:

babel ./src --out-dir dist
node ./dist/bin

As part of the project I have a file called my-worker.js where I use import syntax:

# my-worker.js

import { parentPort, workerData } from 'worker_threads'
import axios from 'axios'
...

And that is used by other-file.js:

#other-file.js

...
const worker = new Worker(__dirname + '/my-worker.js', { workerData: ... })
...

This works fine. Babel converts all the files to commonjs, and loading the worker script works.

BUT

When I use @babel/node, this doesn't work:

babel-node ./src/bin

I get the warning:

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

Along with the error:

Cannot use import statement outside a module

I don't want to use "type": "module", since then I have to explicitly name file extensions, and also I'm not sure that import X, { y } from ... syntax is supported (which I like).

If I change my worker file to be my-worker.mjs, and change the new Worker statement accordingly, then that works with @babel/node, but not with my production build since filenames are changed back to .js.

How can I get @babel/node to load and cache (I guess this is what it needs to do?) files loaded by a Worker? Why does this work with @babel and not with @babel/node?


My .babelrc file looks like this:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": 3,
        "targets": {
          "node": "13"
        },
        "modules": "commonjs"
      }
    ]
  ]
}

Upvotes: 4

Views: 978

Answers (1)

joshuanapoli
joshuanapoli

Reputation: 2509

The @babel/register API can help dynamically transpile a script source, as pointed out in https://github.com/babel/babel/issues/10972#issuecomment-572608142

You can use this approach with eval mode to make a single-file script. This might be useful if you use babel-node to run a command-line utility script.

import { isMainThread, Worker, workerData } from "worker_threads";

function createTranspiledWorker(filename, options) {
  const transpile = `
    require('@babel/register');
    require(${JSON.stringify(filename)});
  `;
  return new Worker(transpile, { ...options, eval: true });
}

async function main() {
  const w = createTranspiledWorker(__filename, { workerData: { hello: "world" } });
  const exit = new Promise(resolve => w.on("exit", resolve));
  await exit;
}

function worker() {
  console.log("worker", workerData);
}

if (isMainThread) {
  main();
} else {
  worker();
}

Upvotes: 4

Related Questions