Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

- Start auxiliary WebAssembly workers through a generated module entry that
initializes their wasm-bindgen bindings.

## 0.1.13 - 2026-08-27

- Build and publish configured auxiliary WebAssembly worker binaries in the
Expand Down
21 changes: 18 additions & 3 deletions lib/rekindle/web/builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ defmodule Rekindle.Web.Builder do
with {:ok, cargo} <-
Rekindle.Cargo.build_worker(project, target, worker, profile, cargo_options(options)),
:ok <- create_worker_directory(directory),
:ok <- bindgen(wasm_bindgen, cargo.artifact, directory, "worker", options),
:ok <- bindgen(wasm_bindgen, cargo.artifact, directory, "bindings", options),
:ok <- write_worker_entry(directory),
:ok <- ensure_worker(directory) do
{:ok,
%{
Expand All @@ -147,12 +148,26 @@ defmodule Rekindle.Web.Builder do
end
end

defp write_worker_entry(directory) do
path = Path.join(directory, "worker.js")
source = "import init from \"./bindings.js\";\n\nawait init();\n"

case File.write(path, source) do
:ok -> :ok
{:error, reason} -> file_error(:worker_entry, path, reason)
end
end

defp ensure_worker(directory) do
if File.regular?(Path.join(directory, "worker.js")) and
File.regular?(Path.join(directory, "worker_bg.wasm")) do
File.regular?(Path.join(directory, "bindings.js")) and
File.regular?(Path.join(directory, "bindings_bg.wasm")) do
:ok
else
error(:missing_worker, "wasm-bindgen did not produce worker.js and worker_bg.wasm")
error(
:missing_worker,
"the worker bootstrap or wasm-bindgen bindings are missing"
)
end
end

Expand Down
44 changes: 41 additions & 3 deletions test/integration/development_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -321,13 +321,14 @@ defmodule Rekindle.DevelopmentTest do

worker = request("/@rekindle/web/#{generation}/workers/storage/worker.js", options)
assert worker.status == 200
assert worker.resp_body == "import init from \"./bindings.js\";\n\nawait init();\n"

assert Plug.Conn.get_resp_header(worker, "content-type") == [
"text/javascript; charset=utf-8"
]

worker_wasm =
request("/@rekindle/web/#{generation}/workers/storage/worker_bg.wasm", options)
request("/@rekindle/web/#{generation}/workers/storage/bindings_bg.wasm", options)

assert worker_wasm.status == 200
assert Plug.Conn.get_resp_header(worker_wasm, "content-type") == ["application/wasm"]
Expand Down Expand Up @@ -427,6 +428,38 @@ defmodule Rekindle.DevelopmentTest do
refute Process.alive?(browser_process)
end

test "starts a published auxiliary module worker in the browser", %{root: root} do
Application.put_env(:rekindle_development_test, Rekindle,
plugin: Rekindle.Plugin.Egui,
targets: [web: [], desktop: []]
)

publish_web(root, """
export default async function init() {
const worker = new Worker(
new URL("./workers/storage/worker.js", import.meta.url),
{type: "module"}
);
worker.addEventListener("message", async ({data}) => {
if (data === "worker-ready") {
await fetch("/__test/loaded/worker", {method: "POST"});
}
});
}
""")

{browser, url, browser_directory} = start_browser_server(root, :reload)
on_exit(fn -> File.rm_rf(browser_directory) end)

{browser_id, browser_process} = run_browser(browser, url, browser_directory)

assert_receive :browser_document_requested, @browser_start_timeout
assert_receive {:browser_loaded, "worker"}, 10_000
assert Process.alive?(browser_process)
assert :ok = stop_supervised(browser_id)
refute Process.alive?(browser_process)
end

test "backs off repeated WebSocket connections that close before state arrives", %{root: root} do
Application.put_env(:rekindle_development_test, Rekindle,
plugin: Rekindle.Plugin.Egui,
Expand Down Expand Up @@ -642,11 +675,16 @@ defmodule Rekindle.DevelopmentTest do

File.write!(
Path.join(temporary, "workers/storage/worker.js"),
"export default async function init() {}"
"import init from \"./bindings.js\";\n\nawait init();\n"
)

File.write!(
Path.join(temporary, "workers/storage/bindings.js"),
"export default async function init() { postMessage(\"worker-ready\"); }"
)

File.write!(
Path.join(temporary, "workers/storage/worker_bg.wasm"),
Path.join(temporary, "workers/storage/bindings_bg.wasm"),
<<0, 97, 115, 109>>
)

Expand Down
11 changes: 8 additions & 3 deletions test/integration/web_build_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ defmodule Rekindle.WebBuildTest do
assert File.regular?(Path.join(generation_root, "main.js"))
assert File.regular?(Path.join(generation_root, "main_bg.wasm"))
assert File.regular?(Path.join(generation_root, "workers/storage/worker.js"))
assert File.regular?(Path.join(generation_root, "workers/storage/worker_bg.wasm"))
assert File.regular?(Path.join(generation_root, "workers/storage/bindings.js"))
assert File.regular?(Path.join(generation_root, "workers/storage/bindings_bg.wasm"))

worker = File.read!(Path.join(generation_root, "workers/storage/worker.js"))
assert worker == "import init from \"./bindings.js\";\n\nawait init();\n"

wrapper = File.read!(result.artifact)
assert wrapper =~ ~s(import init from "./main.js";)
Expand All @@ -180,7 +184,8 @@ defmodule Rekindle.WebBuildTest do
assert File.regular?(Path.join(generation_root, "main.js"))
assert File.regular?(Path.join(generation_root, "main_bg.wasm"))
assert File.regular?(Path.join(generation_root, "workers/storage/worker.js"))
assert File.regular?(Path.join(generation_root, "workers/storage/worker_bg.wasm"))
assert File.regular?(Path.join(generation_root, "workers/storage/bindings.js"))
assert File.regular?(Path.join(generation_root, "workers/storage/bindings_bg.wasm"))

entry = File.read!(Path.join(root, "priv/static/rekindle/entry.js"))
assert entry =~ "// Rekindle generation: #{result.metadata.generation}"
Expand Down Expand Up @@ -388,7 +393,7 @@ defmodule Rekindle.WebBuildTest do
printf 'wasm' > "$output/${out_name}_bg.wasm"
exit 0
fi
if [ "$mode" = "missing-worker" ] && [ "$out_name" = "worker" ]; then
if [ "$mode" = "missing-worker" ] && [ "$out_name" = "bindings" ]; then
exit 0
fi
printf "const wasm = new URL('${out_name}_bg.wasm', import.meta.url);\\n/* %s */\\nexport default async function init() { return wasm; }\\n" "$mode" > "$output/${out_name}.js"
Expand Down
Loading