I use an embedded V8 engine in my (Windows Desktop) C++ application. I understand that V8 has support for ES6 modules. How would I go about activating and using this feature in my application?
I would not expect anyone to have a complete worked example of how this works, but a high level answer pointing me (and future readers) in the right direction would entirely fulfil my hopes and aspirations for this question.
In leiu of actual examples from V8 (I was actually planning to write some at some point), I will write one here. For some examples of use in the wild I recommend Node.js's implementation, or my own, both using very similar layouts (having been written by the same people). There is also an implementation in D8, V8's CLI debugger.
Local<String> source_text = String::NewFromUtf8(
isolate, "import 'some thing'; 1 + 1");
ScriptOrigin origin(String::NewFromUtf8("main.mjs"), // specifier
Integer::New(isolate, 0), // line offset
Integer::New(isolate, 0), // column offset
False(isolate), // is cross origin
Local<Integer>(), // script id
Local<Value>(), // source map URL
False(isolate), // is opaque
False(isolate), // is WASM
True(isolate)); // is ES6 module
Context::Scope context_scope(context);
ScriptCompiler::Source source(source_text, origin);
Local<Module> module;
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
// if you have a v8::TryCatch, you should check it here.
return;
}
// You can resolve import requests ahead of time (useful for async)
for (int i = 0; i < module->GetModuleRequestsLength(); i++) {
Local<String> specifier = module->GetModuleRequest(i); // "some thing"
}
// or you can resolve them sync in the InstantiateModule callback
module->InstantiateModule(context, [](Local<Context> context, // "main.mjs"
Local<String> specifier, // "some thing"
Local<Module> referrer) {
return Local<Module>();
});
// setting this callback enables dynamic import
isolate->SetImportModuleDynamicallyCallback([](Local<Context> context,
Local<ScriptOrModule> referrer,
Local<String> specifier) {
return MaybeLocal<Promise>();
});
// setting this callback enables import.meta
isolate->SetHostInitializeImportMetaObjectCallback([](Local<Context> context,
Local<Module> module,
Local<Object> meta) {
// meta->Set(key, value); you could set import.meta.url here
});
Local<Value> result;
if (module->Evaluate(context).ToLocal(&result)) {
String::Utf8Value utf8(isolate, result);
printf("module eval result: %s\n", *utf8);
} else {
// once again, if you have a v8::TryCatch, use it here.
}