deno_core
deno_core copied to clipboard
Offer an easy way to embed full Deno functionality into a Rust tool
Hi, we are writing a Rust based tool that needs to support calling out to JavaScript plugins.
Deno, being written in Rust and having sufficient Node.js support for our use case, is a great candidate for an embedded JavaScript execution context for these plugins.
I have been playing around with embedding Deno into our tool via the deno_core crate however - while it's perfect in terms of performance, latency, memory consumption, access - it's really designed for consumers to build a custom JavaScript runtime rather than embedding existing Deno functionality into a Rust based tool.
The deno_core crate supplies a basic JavaScript execution context, however it lacks a "batteries included" bootstrap function (including things like module resolution, TypeScript support and the Node.js standard library, etc).
It appears those aspects live in the main Deno repo and are unexported from the deno crate (which makes sense as that crate is just a binary).
One solution I have investigated is to add the denoland/deno repo as a git submodule in the tool we are building and pulling out / reconstructing the Deno execution context used by the Deno executable within our tool.
This could work but it's quite cumbersome, adds a large maintenance burden, increases the potential for mistakes in the integration and introduces friction when updating the embedded Deno.
While I'd love to take advantage of Deno as a JavaScript plugin execution context in Rust applications, at this stage I find the friction of integration is prohibitive.
Would the Deno team consider adding the ability to bootstrap a complete "batteries included" Deno execution context for a Rust consumer?
I'm not an expert on the API so ignore the suggestion below, it is just an example of an API with minimal integration friction and the ability to update easily:
use deno_core::full_deno_context;
fn main() {
let code = "globalThis.plugin_callback = (await import('path_to_plugin.js')).callback"
let deno = full_deno_context::bootstrap(full_deno_context::Config{
entrypoint: full_deno_context::Entrypoint::String(code),
entry_type: full_deno_context::ModuleType::Commonjs,
cwd: env::cwd().unwrap(),
});
let deno_ctx = deno.exec().unwrap();
// Just a demonstration, you'd probably use the existing approach in deno_core
// for calling plugin callback functions and passing in args rather than string evaluation
deno_ctx.eval("globalThis.plugin_callback('hello world')").unwrap();
}
Where the plugin located at path_to_plugin.js could either be a js|cjs|mjs|ts file, with or without node_modules using either Deno patterns/stdlib or Node.js patterns/stdlib.
I'm looking at the possibility of putting Deno into my project as a submodule and attempting to reuse or recreate this entry-point https://github.com/denoland/deno/blob/main/cli/worker.rs#L470
I'm not sure how I'll be able to obtain function pointers from the JavaScript content and inject custom functionality - but it's a start!