starmelon/README.md
nobody ac97ac37db
chore: upgrade deno_* crates to Mar 2024
The way extensions are injected into the Isolate changed dramatically
between Sept 2021 and Jan 2024. Now ops are available in the virtual
module ext:core/ops.
2025-11-23 19:34:17 -08:00

2.7 KiB

When I first created starmelon in September 2021 there was a hacky workaround for setTimeout I had to inject into the generated javascript. Now in Nov 2023 after upgrading deno to 0.127 I find that globalThis.__boostrap.timers is no longer defined. Now the elm code seems to work without requring this hack. However I don't have any correctness tests for starmelon. To reduce the risk of forgetting how it was working I have included the hacks from src/exec/scripting.rs:168

// I think that when I set this script to be the main module, I am skipping the
// deno/runtime/js/99_main.js script that sets up a bunch of global variables. If I
// manually add the timer related code below then setTimeout works again.
// NB. there are 706 lines of setup code that add a bunch of apis to the global window
// scope. Figure out if I need to include all of them. For example, starmelon does not need
// to perform http calls right now, but I eventually want to.
final_script.push_str("const { setTimeout } = globalThis.__bootstrap.timers;\n");
final_script.push_str(
    "Deno.core.setMacrotaskCallback(globalThis.__bootstrap.timers.handleTimerMacrotask);\n",
);
final_script.push_str("globalThis.setTimeout = setTimeout;\n");

Somewhere between deno_runtime version 0.127 and 0.147 they decided to remove deno_core::op macro and replace it with deno_core::op2. As far as I can tell, the op2 macro offers greater control of how values are passed between Rust and JavaScript.

In Jan 2024 they moved operations to a virtual module import { op_example } from "ext:core/ops. You can only access this virtual module in the bootstrap ESM scripts of an deno_core::Extension. Consequently what I have done is write a bootstrap script that imports the ops and reassigns them as properties of globalThis.Extension object. All of my extensions are merged onto the same object. It appears the Deno.core is deleted by the time the deno_runtime::worker::MainWorker runs the main module. Deno[Deno.internal].core.ops still exists but does not contain the ops our Extensions defined.

An aside is that if you construct a JsRuntime directly and add Extensions then those ops will show up on Deno.core.ops. But they will not be enumerable properties, so you will have to use Object.getOwnPropertyNames(Deno.core.ops) to visually confirm the ops are there. My current understanding in April 2024 is that MainWorker includes a JsRuntime, but then also applies all of the deno_runtime extensions that make the JS enviroment feature comparable with Node. For example setTimeout does not exist in a new JsRuntime but does exist in a new MainWorker.

You can find some of the extensions that Deno provides in [https://github.com/denoland/deno/tree/main/runtime/js]