Replies: 10 comments 8 replies
-
In my case I need to embed a JavaScript engine with Node.js support within my Rust application and extend the standard library with my own packages - so my current plan is to surface then lean out the I am stripping away parts of the CLI that I don't need and replacing workspace dependencies with their corresponding This is where I am so far and this works: #[tokio::main]
async fn main() {
// Putting Deno in its own thread to allow for nested tokio runtime
let deno_thread = std::thread::spawn(move || {
let entry_script = PathBuf::from("/path/to/pkg/src/index.ts");
deno_current_thread(run_program(entry_script));
});
deno_thread.join().unwrap();
}
/// Partially extracted "deno run" command
async fn run_program(script_path: PathBuf) {
let run_flags = RunFlags {
script: script_path.to_str().unwrap().to_string(),
..RunFlags::default()
};
let flags = Flags {
unstable_config: UnstableConfig {
byonm: true,
..UnstableConfig::default()
},
subcommand: DenoSubcommand::Run(run_flags.clone()),
..Flags::default()
};
let cli_options = CliOptions::from_flags(flags).unwrap();
let factory = CliFactory {
watcher_communicator: None,
options: Arc::new(cli_options),
services: Default::default(),
};
let cli_options = factory.cli_options();
let main_module = cli_options.resolve_main_module().unwrap();
let permissions = PermissionsContainer::new(Permissions::allow_all());
let worker_factory = factory.create_cli_main_worker_factory().await.unwrap();
let mut worker = worker_factory
.create_main_worker(main_module, permissions)
.await
.unwrap();
let exit_code = worker.run().await.unwrap();
}
/// Spawns a tokio task on current thread for Deno to evaluate JavaScript within.
///
/// Use channels to communicate with Deno
#[inline(always)]
fn deno_current_thread<F, R>(future: F) -> R
where
F: std::future::Future<Output = R> + 'static,
R: Send + 'static,
{
let tokio_runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()
.enable_time()
.event_interval(61)
.global_queue_interval(31)
.max_io_events_per_tick(1024)
.max_blocking_threads(32)
.build()
.unwrap();
let future = async move {
deno_core::unsync::spawn(async move { future.await }.boxed_local())
.await
.unwrap()
};
#[cfg(debug_assertions)]
let future = Box::pin(unsafe { MaskFutureAsSend::new(future) });
#[cfg(not(debug_assertions))]
let future = unsafe { MaskFutureAsSend::new(future) };
let join_handle = tokio_runtime.spawn(future);
tokio_runtime.block_on(join_handle).unwrap().into_inner()
} |
Beta Was this translation helpful? Give feedback.
-
If you get a panic in |
Beta Was this translation helpful? Give feedback.
-
Thanks 🙏 (I read your posts on embedding Deno earlier) It appears that it's somehow related to v8 snapshots in the I tried commenting bits out - like
I see that supabase also create a snapshot however when I plug theirs into the Deno CLI it also panics the build. Haha, I feel like I lack so much context |
Beta Was this translation helpful? Give feedback.
-
If I have a new cargo project and install I'm hoping to get support for TypeScript and the Node.js standard lib |
Beta Was this translation helpful? Give feedback.
-
@bartlomieju is it possible to initialize the snapshot at runtime without compiling one at build time - where the build-time snapshot could be an optimization implemented at a later date to offer faster start up times? I have skipped the creation of a snapshot and am using the following code however it breaks with console.log(fetch)
use deno_core::futures::FutureExt;
use deno_core::unsync::MaskFutureAsSend;
use deno_core::PollEventLoopOptions;
use deno_runtime::deno_core::url::Url;
use deno_runtime::deno_core::FastString;
use deno_runtime::deno_http::DefaultHttpPropertyExtractor;
use deno_runtime::permissions::PermissionsContainer;
use std::rc::Rc;
use std::sync::Arc;
use deno_runtime::*;
const CODE: &str = r#"
console.log(42)
"#;
fn main() {
deno_current_thread(run_js());
}
pub async fn run_js() {
let deno_fs = Arc::new(deno_fs::RealFs);
PermissionsContainer::allow_all();
let extensions = vec![
deno_webidl::deno_webidl::init_ops(),
deno_console::deno_console::init_ops(),
deno_url::deno_url::init_ops(),
deno_web::deno_web::init_ops::<PermissionsContainer>(
Arc::new(deno_web::BlobStore::default()),
None,
),
deno_webgpu::deno_webgpu::init_ops(),
deno_canvas::deno_canvas::init_ops(),
deno_fetch::deno_fetch::init_ops::<PermissionsContainer>(deno_fetch::Options {
user_agent: "Mach/0.0.0".to_string(),
root_cert_store_provider: None,
..Default::default()
}),
deno_websocket::deno_websocket::init_ops::<PermissionsContainer>(
"Mach/0.0.0".to_string(),
None,
None,
),
deno_crypto::deno_crypto::init_ops(None),
deno_broadcast_channel::deno_broadcast_channel::init_ops(
deno_broadcast_channel::InMemoryBroadcastChannel::default(),
),
deno_net::deno_net::init_ops::<PermissionsContainer>(None, None),
deno_tls::deno_tls::init_ops(),
deno_http::deno_http::init_ops::<DefaultHttpPropertyExtractor>(),
deno_io::deno_io::init_ops(Some(Default::default())),
deno_fs::deno_fs::init_ops::<PermissionsContainer>(deno_fs.clone()),
deno_node::deno_node::init_ops::<PermissionsContainer>(None, deno_fs.clone()),
];
let runtime_options = deno_core::RuntimeOptions {
module_loader: Some(Rc::new(deno_core::FsModuleLoader)),
extensions,
..Default::default()
};
let mut js_runtime = deno_core::JsRuntime::new(runtime_options);
let exe_path = std::env::current_exe().unwrap().parent().unwrap().to_path_buf();
let main_module = Url::from_file_path(exe_path).unwrap();
let code: FastString = CODE.to_string().into();
let mod_id = js_runtime.load_main_es_module_from_code(&main_module, code)
.await
.unwrap();
let result = js_runtime.mod_evaluate(mod_id);
js_runtime
.run_event_loop(PollEventLoopOptions {
wait_for_inspector: false,
pump_v8_message_loop: false,
})
.await.unwrap();
result.await.unwrap();
}
#[inline(always)]
fn deno_current_thread<F, R>(future: F) -> R
where
F: std::future::Future<Output = R> + 'static,
R: Send + 'static,
{
let tokio_runtime = tokio::runtime::Builder::new_current_thread()
.enable_io()
.enable_time()
.event_interval(61)
.global_queue_interval(31)
.max_io_events_per_tick(1024)
.max_blocking_threads(32)
.build()
.unwrap();
let future = async move {
deno_core::unsync::spawn(async move { future.await }.boxed_local())
.await
.unwrap()
};
#[cfg(debug_assertions)]
let future = Box::pin(unsafe { MaskFutureAsSend::new(future) });
#[cfg(not(debug_assertions))]
let future = unsafe { MaskFutureAsSend::new(future) };
let join_handle = tokio_runtime.spawn(future);
tokio_runtime.block_on(join_handle).unwrap().into_inner()
} I have tried logging: console.log(42); // works
console.log(globalThis); // fails
console.log(fetch); // fails
console.log(Deno); // works
console.log(await import('node:path')); // fails I thought that these two extensions would initialize support for deno_fetch::deno_fetch::init_ops::<AppPermissions>(deno_fetch::Options {})
deno_node::deno_node::init_ops::<AppPermissions>(None, deno_fs.clone()) Are extensions only available if they are compiled into the snapshot or am I just missing something? |
Beta Was this translation helpful? Give feedback.
-
Ok so I have started putting my experiments into a repo so it's easier to share than dumping source into the discussion. The first thing I am trying to do is produce a basic https://github.com/alshdavid-labs/deno-experiments/blob/main/fail-snapshot-1 It throws with:
|
Beta Was this translation helpful? Give feedback.
-
Ok I have made progress. Got my own custom Deno build integrated into my project with most of the Deno functionality included. Things I've learned
Questions remaining: Because of how long it takes to generate a release snapshot, how many extra dependencies it requires and how manageable the Deno release schedule is, I am considering if I should have the snapshots generated externally from my main repo - perhaps in a separate Consumers (just me at this stage) who want to integrate Deno into their project could download the snapshots into their project. I could also publish a crate that bottles up the Deno dependencies (npm manager, web_worker initialization) to simplify integration. That should make it trivial to embed Deno into an existing Rust project |
Beta Was this translation helpful? Give feedback.
-
Ok, so I have created a repo that publishes prebuilt Deno v8 snapshots from the cli (I hope this doesn't violate and licenses? it's all MIT from what I can tell). I feel this is valuable because building the snapshot makes it tricky to embed Deno into projects where we cross-compile in CI/CD. Using prebuilt snapshot binaries means that's not a concern plus it speeds up compile time. |
Beta Was this translation helpful? Give feedback.
-
Ok well I've managed to embed Deno with the CLI functionality into my application by vendoring the deno_cli package into my project and replacing the starting point for the There's a lot of unused functionality, like linting, the test runner, docs, etc and it's quite difficult to untangle so I'll "tree shake" by hand later but for now I can run Deno like this: fn main() {
deno_embed::deno_current_thread(async move {
let exit_code = deno_embed::run_script(
deno_embed::DenoInitOptions{ // Combination of RunFlags and Flags
script: "/home/dalsh/Development/alshdavid/mach/pkg2/index.js".to_string(),
..Default::default()
})
.await;
println!("{:?}", exit_code);
});
} It's a bit hard to manage the dependencies of Deno because they are locked which causes collisions with my main application. This is my wrapper of the I had to vendor & rename SWC into my project to avoid collisions |
Beta Was this translation helpful? Give feedback.
-
Unfortunately it looks like custom_extensions are being dropped (or I don't know how to access them). #[op2]
pub fn op_hello_world(
#[global] f: v8::Global<v8::Function>,
) {
println!("hi");
}
deno_core::extension!(
my_extension_name,
ops = [op_hello_world],
);
fn main() {
// ....
let mut worker = worker_factory
.create_custom_worker(
main_module,
permissions,
vec![my_extension_name::init_ops_and_esm()],
options.stdio,
)
.await?;
} Where it's unavailable anywhere in the Perhaps it relates to how ops are removed here |
Beta Was this translation helpful? Give feedback.
-
I'm trying to bootstrap a MainWorker to embed Deno into my Rust application and getting
With my code looking like
and the JavaScript
My project looks like:

Where the folders are pre-created
It panics here, in the
v8
crateBeta Was this translation helpful? Give feedback.
All reactions