4

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"
      }
    ]
  ]
}
Zach Smith
  • 8,458
  • 13
  • 59
  • 133
  • The solution I am currently using is that I'm getting the name of the files that I want to run via `fs.readFileSync` on app startup. Then in dev I can use `.mjs`, which is fine for Node 13, and in production, the commonjs output is `.js`, and `fs.readFileSync` finds the `.js` files. – Zach Smith Jan 10 '20 at 11:28
  • For a more babel oriented approach: https://github.com/babel/babel/issues/10972 – Zach Smith Jan 10 '20 at 11:29

1 Answers1

4

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();
}
joshuanapoli
  • 2,509
  • 3
  • 25
  • 34