Run CommonJS code in ESM Module using runInThisContext

49 Views Asked by At

A third party dependency returns a javascript code which I would like to execute using vm.runInThisContext.

Unfortunately the javascript module systems missmatch as my script has to be EcmaScriptModule and the generated javascript code can only be generated as CommonJs.

I tried different options of runInThisContext but wasn't able to make it work.

Here is a simplified version of my problem:

example.mjs

import vm from "node:vm";

// Code to be executed
const code = `
  require("path").resolve("test.mjs");
`;

// Execute code in the current context
vm.runInThisContext(code);
node test.mjs

evalmachine.<anonymous>:2
  require("path").resolve("test.mjs");
  ^

ReferenceError: require is not defined
    at evalmachine.<anonymous>:2:3
    at Script.runInThisContext (node:vm:129:12)
    at Object.runInThisContext (node:vm:307:38)
    at test.mjs:9:4
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)

Is there any solution for this cross module problem?

2

There are 2 best solutions below

0
jantimon On BEST ANSWER

I found a solution which works in cjs and esm thanks to the great answer from @sujeet

const getRequire = async () => {
  if (typeof require !== "undefined") return require;
  const { createRequire } = await import("module");
  return createRequire(import.meta.url);
};

const code = `
  (require) =>
    require("path").resolve("test.mjs");
`;

const result = vm.runInThisContext(code)(await getRequire());
0
sujeet On

I don't think you can use runInThisContext as it will be still tied to your global esm module loader, instead, you can use runInNewContext and provide a separate sandboxed version of the CommonJS module loader.

   
import vm from 'node:vm';
import { createRequire } from 'module';
import { fileURLToPath } from 'url';
import path from 'path';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);

const code = `
  const path = require("path");
  path.resolve(__dirname, "test.mjs");
`;

const sandbox = {
  require,
  __dirname,
  console,
};

vm.runInNewContext(code, sandbox);

runInNewContext

working example