feat: deno desktop subcommand#33441
Merged
Merged
Conversation
# Conflicts: # Cargo.lock # Cargo.toml
# Conflicts: # cli/rt/run.rs
Picks up the webview bindings fix (littledivy/laufey#11): macOS/Windows binding calls now resolve with the real return value instead of null. Bumps the crate dep + Cargo.lock, and refreshes cli/laufey_sums.lock with the v0.3.2 release digests (verified against GitHub's per-asset SHA-256).
|
@crowlKats after your talk today can't wait for this to be merged! super excited! |
@kucukkanat Can you give me a link to the talk show? |
littledivy
marked this pull request as draft
June 13, 2026 10:42
littledivy
marked this pull request as ready for review
June 13, 2026 10:42
crowlbot
force-pushed
the
desktop-framework-hmr
branch
from
June 16, 2026 08:53
935b5c2 to
15b9efb
Compare
denort is the `deno compile` base binary. libsui rewrites its ELF when embedding user code, and that rewrite drops `.relr.dyn` relative relocations when the symbol table has been stripped. With the v8 149.4.0 bump (more relative relocations) this became fatal: the compiled binary's C++ static-init guards get an un-relocated mutex pointer and deadlock at startup (`libc++abi: __cxa_guard_acquire failed to acquire mutex`), which made compile_watch_test fail only on release linux-x86_64. Strip only debug info from denort (keep .symtab) so the relocations survive the libsui rewrite. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
crowlbot
force-pushed
the
desktop-framework-hmr
branch
from
June 16, 2026 09:13
15b9efb to
90d62f4
Compare
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
littledivy
added a commit
that referenced
this pull request
Jun 21, 2026
## Summary Adds `.msi` as a `deno desktop` output format, alongside the existing `.dmg`/`.AppImage` and the `.deb`/`.rpm` formats added in #35296. As with those, the format is chosen by the `--output` extension (or the per-platform `output` key in the `desktop` block of `deno.json` — whose Windows description already documented `MyApp.msi`); the driver strips the extension, builds the intermediate Windows app dir, then wraps it in an MSI. The MSI database is authored entirely in pure Rust via the [`msi`](https://crates.io/crates/msi) crate, with the file payload stored in an embedded MSZIP cabinet ([`cab`](https://crates.io/crates/cab) crate), so it **cross-compiles from any host** — only the *target* must be Windows (gated like `.deb`/`.rpm` are gated on a Linux target). The app installs per-machine under `ProgramFiles64Folder\<AppName>\`, mirroring the staged app-dir tree exactly (nested directories like CEF's `locales/` are preserved); uninstall removes it. ``` %ProgramFiles%\<AppName>\ <AppName>.bat (launcher) laufey.exe (CEF backend) libcef.dll, ... (CEF support files) denort.dll (compiled runtime + user code) ... (nested dirs preserved) ``` ### Notable choices - **Deterministic GUIDs** — `ProductCode`, `UpgradeCode`, the package code, and per-component GUIDs are derived via UUIDv5 from the app identity (`--identifier`), so identical inputs produce an identical installer (matching the `source_date(0)` / `mtime: 0` reproducibility of the `.rpm`/`.deb`/AppImage paths) and the `UpgradeCode` is stable across versions. - **Directory/Component model** — one `Component` per install directory (a file's directory is carried by its component), `KeyPath` = the first file in that directory, components flagged 64-bit (`msidbComponentAttributes64bit`). A single `Feature` ties them together. - **Embedded cabinet** — one MSZIP folder holding every file named by its MSI `File` key, referenced by `Media.Cabinet = #appcab`. Summary stream marks the source compressed with long file names (`x64;1033` / `Arm64;1033` template). - **No new config fields** — metadata is derived from the staged app-dir name, `--identifier`, and the default `1.0.0` version that matches the macOS bundle. ### Metadata Derived from existing config (app-dir name → `ProductName`, `--identifier` → GUID seed, default `1.0.0` version). Help text for `--output` now lists `MyApp.msi`. ## Why This rounds out the desktop installer story for Windows — `.msi` was explicitly left out of the original desktop PR (#33441). `.msi` is what most Windows users/IT deployments install through. ## Tests Pure-Rust unit tests (cross-platform): triple→arch mapping, deterministic+valid GUID derivation, 8.3 short-name minting, and a full `create_windows_msi` round-trip that parses the produced MSI back and asserts the `Property`/`Directory`/`File`/`Component`/`Media` tables, the 1..N file sequence, the two-component layout for a nested dir, and that a file's bytes round-trip out of the embedded cabinet. All pass; clippy clean. As with `.deb`/`.rpm`, the matching-OS integration check (install / launch / upgrade / uninstall on a real Windows box) can't run in the cross-platform unit suite. Major-upgrade automation (an `Upgrade` table + `RemoveExistingProducts`) is intentionally left as a follow-up. ## New dependencies `msi = "0.10.0"` and `cab = "0.6.0"` (both pure Rust). Also enables the `v5` feature on the existing `uuid` dep. Closes #35377 Ref mdsteele/rust-msi#59 Closes denoland/divybot#582 --------- Co-authored-by: divybot <[email protected]> Co-authored-by: Divy Srivastava <[email protected]>
|
This is beyond awesome, thank you so much <3 |
This was referenced Jul 18, 2026
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.
Summary
Adds the
deno desktopsubcommand for building desktop apps from Deno projects.Core:
deno desktop <entry>compiles a project into a self-contained desktop app, built on WEF (prebuilt backends downloaded fromgithub.com/denoland/wef/releases, pinned via Cargo.lock, SHA256-verified, cached under<deno_dir>/wef/<version>/).WEF_DEV_DIRpoints at a local wef checkout for development.cef(default, bundled Chromium),webview(OS webview),raw(winit, no engine).Deno.serve()in the entry auto-binds to the port the webview navigates to (viaDENO_SERVE_ADDRESS).Framework auto-detection (
cli/tools/framework.rs): Next.js, Astro, Fresh, Remix, Nuxt, SvelteKit, SolidStart, TanStack Start, Vite SSR. Prod server runs by default; dev server runs under--hmr.HMR (
--hmr): framework projects use the framework's dev server; non-framework apps use file-watch +Debugger.setScriptSourcehot-swap with the runtime and CEF staying alive.Deno.BrowserWindowAPI (cli/tsc/dts/lib.deno.desktop.d.ts): window lifecycle (show/hide/focus/close/reload), size/position, always-on-top, navigation,bind/unbindRPC to webview JS viabindings.<name>(),executeJs, app/context menus, native window handle, keyboard/mouse/wheel/resize/focus events.Runtime integration:
prompt()/alert()/confirm()become native popups; uncaught errors show a native alert and optionallyPOSTtodesktop.errorReporting.url.Auto-updater:
Deno.desktopVersion+Deno.autoUpdate({ url, interval, onUpdateReady, onRollback }). Polls<url>/latest.json, applies bsdiff patches (qbsdiff) to the dylib, stages for next launch, rolls back on failed launch.Unified DevTools (
--inspect/--inspect-brk/--inspect-wait): single DevTools session showing both the Deno runtime V8 and the CEF renderer V8 as attached targets — one Console dropdown (Renderer / Deno), one Sources panel with both threads. Implemented as a CDP multiplexer incli/tools/desktop_devtools.rs.--inspect-brkpauses both isolates (Deno via its own mechanism, CEF via injectedDebugger.enable+Debugger.pausebefore navigation).Dock / Tray:
Deno.dock(macOS) andDeno.Tray(cross-platform) for status-area icons with tooltips, dark-mode icons, and context menus.Distribution / cross-compile:
--targetand--all-targetsdownload prebuilt denort + WEF backends for the target triple. Outputs:.appbundle (framework underContents/Frameworks/),.dmgviahdiutil.exe+ DLLs directory.AppImageviaappimagetoolOS-specific limitations
Several features are currently wired up for a subset of platforms. They degrade gracefully (the rest of the app still works) but are worth calling out:
apply_pending_update,get_dylib_path, andAutoUpdateStateare all#[cfg(unix)]. On non-unix (Windows)update_rolled_backis hardcoded tofalseandauto_update_stateisNone, so the staged-update / patch-apply / rollback path is a no-op there. Bringing it to Windows means implementing the dylib-swap + pending-update application for that platform (likely behind a small platform module so the#[cfg]branches inrun_desktop/laufey::main!collapse).promote_dylib_symbols_to_global(re-dlopens the runtime dylib withRTLD_GLOBALso NAPI symbols are visible to addons like next-swc) is#[cfg(unix)]. On Windows, frameworks that pull native addons may fail to load those addons.disclaim_spawn(posix_spawn with TCC responsibility disclaimed) so it becomes its own permission principal; without itUNUserNotificationCenter.requestAuthorizationfails. The non-macOS path is a plaintokiospawn withkill_on_drop.--ozone-platform=x11+GDK_BACKEND=x11, because the LAUFEY mouse/focus/resize event monitor uses XI2 on X11. On Wayland sessions it runs through XWayland; native Wayland is unsupported.Deno.dockis macOS-only (no-op / unavailable elsewhere).--hmrmode (any platform).IconConfig::Seterrors with "icon sets are not supported in --hmr mode yet"; only a single icon path works under--hmr.Not yet implemented
macos.codesignIdentityindeno.json, with an ad-hoc signature fallback for unsigned builds), but there's nonotarytoolsubmission /staplerstep yet..deb/.rpminstallersCloses #3234