📝

vm.Script 環境であっても Worker の中では import() が使える

2023/06/04に公開

通常、vm.Script で作った環境の中では import() が使えません。TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified. になります。オプション指定すれば動くんですけど、自分が vm.Script しているわけではなく、対象のランタイムが勝手にやってる場合もあり、今回はそういうケースを想定しています。

スクリプトなので当然 import mod from "mod"; も使えません。これでは ESM を読み込めなくて困ってしまいます。

しかし、vm.Script 環境であっても worker_threadsWorker の中では import() が使えます。ためしにやってみましょう。

require を入れたコンテキストを作って vm.Script 環境で script.js を実行します。

index.js
const vm = require("node:vm");
const fs = require("node:fs");
const path = require("node:path");

const code = fs.readFileSync(path.join(process.cwd(), "script.js"));

const script = new vm.Script(code);
const context = vm.createContext({ require });
script.runInContext(context);

その中で worker_thrcadsWorker を作って、worker.js を実行します。

script.js
const { Worker } = require("node:worker_threads");

const worker = new Worker("./worker.js");
worker.postMessage("msg");
worker.once("message", ({ value }) => {
  console.log(value);
});

worker.js の中で module.mjsimport() します。

worker.js
const { parentPort } = require("worker_threads");

parentPort.on("message", () => {
  import("./module.mjs").then(({ addOne }) => {
    parentPort.postMessage({ value: addOne(1) });
  });
});

module.mjs には適当な関数が生えています。

module.mjs
export function addOne(value) {
  return value + 1;
}

なんと動くんですね。vm.Script 環境で動かさないといけない制約がある中で ESM を使いたいときのワークアラウンドですね。やりたくねえ。

Discussion