jcubic
jcubic

Reputation: 66590

How to create ES Module context with eval?

I have a Scheme interpreter written in JavaScript called LIPS. It's all ES Module. But as part of the standard library I've created require function that use code like this:

import { createRequire } from 'module';
const require = createRequire(import.meta.url);

But you can't import esm package with require. So I need to implement import.

The problem is that import is not global variable. For some reason it's accessible but not in window or global. So to get access it from scheme you need (using JavaScript syntax)

eval("import")("fs")

But eval don't create ES context and this throw an error:

Uncaught SyntaxError: Cannot use import statement outside a module

I want to know if you can create ES context with eval, so I can access dynamic import with the need to modify the library JavaScript code.

Upvotes: 0

Views: 66

Answers (2)

jcubic
jcubic

Reputation: 66590

Just found the way to use import with eval:

await global.eval("(function(x) { return import(x); })")("fs");

or shorter with arrow function:

await global.eval("(x) => import(x)")("fs");

So I can now use it in Scheme without the need to modify JavaScript code.

(define import (global.eval "(x) => import(x)"))

Upvotes: 0

Pavlo Sobchuk
Pavlo Sobchuk

Reputation: 1564

The eval function operates in the current execution context (which is either the global scope or a function scope), but it does not treat the code as an ES Module. This is a limitation of how eval works—it runs the code as if it were part of the script context in which it is called, and it doesn’t provide the necessary module context that import statements require.

As a workaround, you can use something like this:

const dynamicImport = (module) => {
  return import(module);
};

// Usage:
dynamicImport('fs').then((fs) => {
  console.log(fs);
}).catch((error) => {
  console.error(error);
});

Upvotes: 0

Related Questions