Repository navigation
fix(desktop): run Vite-based HMR dev servers inside the desktop runtime - #36488
Merged
crowlKats merged 1 commit intoAug 8, 2026
Merged
Conversation
With `deno desktop --hmr` on a framework project, the dev server was spawned as a plain `deno task dev` subprocess, so server-side code (SvelteKit endpoints, Vite SSR) had no access to the desktop APIs (Deno.BrowserWindow / Deno.Tray / Deno.Dock) — inconsistent with the compiled app, where the server runs inside the desktop runtime. For frameworks whose dev command is plain `vite dev` (Vite, SvelteKit), compile a generated dev entrypoint that boots the project's own Vite dev server via createServer() inside the desktop runtime, bound to the DENO_SERVE_ADDRESS port the webview already polls and navigates to. The external-process path (and its dev-server stdout URL scraping with the 15s timeout) is no longer used for these frameworks; wrapper dev CLIs (nuxi/vinxi/react-router/Fresh) keep the existing behavior. Also fixes a latent crash this surfaced: laufey invokes the runtime on its RuntimeLoader thread, which only gets the platform-default secondary thread stack (512KB on macOS). Synchronous swc parsing of a real-world module graph overflows that and kills the process with SIGBUS. The runtime now runs on a dedicated 8MB-stack thread, and the tokio runtime is built via tokio_util::create_basic_runtime() so blocking-pool threads get proper stack sizes too. Fixes denoland#35899
littledivy
approved these changes
Aug 8, 2026
bartlomieju
pushed a commit
that referenced
this pull request
Aug 27, 2026
…me (#36488) Fixes #35899 ## Problem With `deno desktop --hmr` on a framework project, the dev server was spawned as a plain `deno task dev` subprocess. Server-side code (SvelteKit endpoints, Vite SSR handlers) therefore had no access to the desktop APIs (`Deno.BrowserWindow` / `Deno.Tray` / `Deno.Dock`) — inconsistent with the compiled app, where the server entrypoint runs inside the desktop runtime. Verified with a SvelteKit endpoint probing the globals: `{"hasBrowserWindow":false}` under `--hmr`, `true` in the built app. ## Fix For frameworks whose dev command is plain `vite dev` (Vite, SvelteKit), `--hmr` now compiles a generated dev entrypoint that boots the project's own Vite dev server through Vite's JS API (`createServer()`) **inside the desktop runtime**, bound to the `DENO_SERVE_ADDRESS` port the webview already polls and navigates to (`DENO_DESKTOP_FRAMEWORK_DEV=1` signals rt_desktop to restore CWD to the source dir and leave hot reloading to the framework). Side benefit: the external-process path — parsing the dev server's stdout for the `Local:` URL with a 15s timeout (#35910's failure mode) — is no longer used for these frameworks. The generated shim is compiled with `--no-check` semantics (nothing user-authored in it) and allow-all permissions, matching the previously-unsandboxed `deno task dev` subprocess. Wrapper dev CLIs that aren't plain Vite (`nuxi dev`, `vinxi dev`, `react-router dev`, Fresh) keep the existing external-process behavior for now. ## Latent crash this surfaced (fixed too) Booting a real dev server in-runtime made the runtime parse a much bigger module graph, which exposed that laufey invokes `laufey_runtime_start` on its `RuntimeLoader` C++ thread — which gets the platform-default *secondary*-thread stack, only **512KB on macOS**. Synchronous swc parsing of SvelteKit's graph overflows it and kills the process with SIGBUS (`KERN_PROTECTION_FAILURE` in the stack guard region, ~220 swc parser frames). This can plausibly also kill *production* desktop apps with big server bundles. The runtime (and headless forked workers) now run on a dedicated 8MB-stack thread, and the tokio runtime is built via `tokio_util::create_basic_runtime()` so blocking-pool threads get the established stack sizes as well. ## Verification (macOS, debug build + rebuilt `denort_desktop` dylib) - SvelteKit (adapter-node, `sv create` scaffold) with an `/api` endpoint probing globals: `{"hasDeno":true,"hasBrowserWindow":true,"hasTray":true,"hasDock":true}` under `--hmr`; page HTML serves; no SIGBUS. - Plain Vite React SPA with a `configureServer` middleware probe: same result, and editing `src/App.jsx` triggers `[vite] hmr update /src/App.jsx` (file watching from the restored CWD works). - `cargo test -p deno --lib framework::` — 71 passed. - `tools/format.js` clean; `tools/lint.js --rs` clean (JS lint fails only on untracked local scratch files unrelated to this PR).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #35899
Problem
With
deno desktop --hmron a framework project, the dev server was spawned as a plaindeno task devsubprocess. Server-side code (SvelteKit endpoints, Vite SSR handlers) therefore had no access to the desktop APIs (Deno.BrowserWindow/Deno.Tray/Deno.Dock) — inconsistent with the compiled app, where the server entrypoint runs inside the desktop runtime. Verified with a SvelteKit endpoint probing the globals:{"hasBrowserWindow":false}under--hmr,truein the built app.Fix
For frameworks whose dev command is plain
vite dev(Vite, SvelteKit),--hmrnow compiles a generated dev entrypoint that boots the project's own Vite dev server through Vite's JS API (createServer()) inside the desktop runtime, bound to theDENO_SERVE_ADDRESSport the webview already polls and navigates to (DENO_DESKTOP_FRAMEWORK_DEV=1signals rt_desktop to restore CWD to the source dir and leave hot reloading to the framework).Side benefit: the external-process path — parsing the dev server's stdout for the
Local:URL with a 15s timeout (#35910's failure mode) — is no longer used for these frameworks. The generated shim is compiled with--no-checksemantics (nothing user-authored in it) and allow-all permissions, matching the previously-unsandboxeddeno task devsubprocess.Wrapper dev CLIs that aren't plain Vite (
nuxi dev,vinxi dev,react-router dev, Fresh) keep the existing external-process behavior for now.Latent crash this surfaced (fixed too)
Booting a real dev server in-runtime made the runtime parse a much bigger module graph, which exposed that laufey invokes
laufey_runtime_starton itsRuntimeLoaderC++ thread — which gets the platform-default secondary-thread stack, only 512KB on macOS. Synchronous swc parsing of SvelteKit's graph overflows it and kills the process with SIGBUS (KERN_PROTECTION_FAILUREin the stack guard region, ~220 swc parser frames). This can plausibly also kill production desktop apps with big server bundles.The runtime (and headless forked workers) now run on a dedicated 8MB-stack thread, and the tokio runtime is built via
tokio_util::create_basic_runtime()so blocking-pool threads get the established stack sizes as well.Verification (macOS, debug build + rebuilt
denort_desktopdylib)sv createscaffold) with an/apiendpoint probing globals:{"hasDeno":true,"hasBrowserWindow":true,"hasTray":true,"hasDock":true}under--hmr; page HTML serves; no SIGBUS.configureServermiddleware probe: same result, and editingsrc/App.jsxtriggers[vite] hmr update /src/App.jsx(file watching from the restored CWD works).cargo test -p deno --lib framework::— 71 passed.tools/format.jsclean;tools/lint.js --rsclean (JS lint fails only on untracked local scratch files unrelated to this PR).