Embedder API: Loading ES6 Modules
Details
I am trying to use the NodeJS Embedder API in a C++ application. My goal would be to load JS modules that might contain import statement. What would be the best way to go about that?
So far, I have created a v8::Module and I can instantiate it and run it without trouble. However, I have issue importing modules especially default ones such as path. I am not sure what would be the best way to resolve module recursively taking into account the internal modules nodejs exposes (path, http, etc...)
What would be the best way to achieve something like that?
Node.js version
v18.10.0
Example code
Example with custom module execution:
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate,
"import Path from 'path';console.log(Path);"
).ToLocalChecked();
v8::ScriptOrigin origin(isolate, v8::String::NewFromUtf8(isolate, "main.mjs").ToLocalChecked(),
0,
0,
false,
0,
v8::Local<v8::Value>(),
false,
false,
true
);
v8::ScriptCompiler::Source source(source, origin);
v8::Local<v8::Module> module;
if (!v8::ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
return;
}
module->InstantiateModule(context, [](
v8::Local<v8::Context> context, v8::Local<v8::String> specifier,
v8::Local<v8::FixedArray> import_assertions, v8::Local<v8::Module> referrer) {
// How should the module be resolved? Can we use the ModuleWrapper from NodeJS?
return v8::Local<v8::Module>();
});
Operating system
MacOS Monterey 12.6, but not that relevant for this particular question
Scope
Embedder API
Module and version
Not applicable.
I tried a lot of ways, none of them worked so far.
1. SourceTextModule
I tried to use vm.SourceTextModule, but it looks like I would need to link myself the modules dependencies...
2. vm.runInThisContext + dynamic import
Environment is loaded using a custom callback:
v8::MaybeLocal<v8::Value> loadRes = node::LoadEnvironment(env,
[this](const node::StartExecutionCallbackInfo& info) {
...
});
And then I use the vm.runInThisContext function obtained during the environment setup:
v8::Local<v8::Value> source = v8::String::NewFromUtf8Literal(isolate, "import('path').then(p => console.log(p))");
runInContextFunc->Get(isolate)->Call(context, v8::Null(solate), 1, &source).ToLocalChecked();
This doesn't work and keep throwing an error: ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING. Looking at the code, it makes sense why this error is thrown. However, I am not sure what I am supposed to do. Ideally, I would like to re-use the node resolution algorithm already there.
3. LoadEnvironment without any callback
I tried to set a startup script to process.argv[1], and then load the environment using:
node::LoadEnvironment(_setup->env(), node::StartExecutionCallback{});
The script is executed properly, but I still can't easily use import statements outside of that.
I don't know if it's the best idea, but I solved that using evalModule from the internals.
I tried to use
vm.SourceTextModule, but it looks like I would need to link myself the modules dependencies...
Related to https://github.com/nodejs/node/issues/31234 to provide built-in implementations of module linker, initializeImportMeta, importModuleDynamically
There has been no activity on this issue for 11 months. The help repository works best when sustained engagement moves conversation forward. The issue will be closed in 1 month. If you are still experiencing this issue on the latest supported versions of Node.js, please leave a comment.
Closing after no activity on this issue for 12 months.