---
title: "Deno accueille `deno desktop` : sous-commande pour applications de bureau auto-contenues, basée sur WEF, avec Deno.BrowserWindow, DevTools unifiés et compilation croisée macOS, Windows et Linux"
description: "Deno a fusionné `deno desktop` le 16 juin 2026 (PR #33441), une nouvelle sous-commande qui transforme un projet Deno en application de bureau auto-contenue. La fonctionnalité embarque le backend WEF (CEF par défaut, plus WebView et winit brut), l'API Deno.BrowserWindow pour le cycle de vie de la fenêtre et les événements natifs, la détection automatique des frameworks Next, Astro, Fresh, Remix, Nuxt, SvelteKit, SolidStart, TanStack Start et Vite SSR, un multiplexeur CDP qui expose les deux isolats V8 dans une seule session DevTools, un système de mise à jour automatique avec patches bsdiff, et des sorties compilées .app/.dmg/.exe/.AppImage. Trois PR Deno plus petites sont arrivées le même matin : `deno link`/`unlink`, `deno test --shard`, et un `request_builder_hook` fetch pour les en-têtes `x-deno-fetch-token`/`cdn-loop`."
date: 2026-06-16
image: "/images/heroes/2026-06-16--deno-desktop-subcommand-wef-cef-browserwindow.png"
author: lschvn
tags: ["runtimes", "frameworks", "tooling"]
tldr:
  - "Deno a fusionné la [PR #33441](https://github.com/denoland/deno/pull/33441) le 16 juin 2026, ajoutant la sous-commande `deno desktop`. Elle compile un projet Deno en application de bureau auto-contenue, adossée à WEF (WebView Engine Foundation), avec CEF (Chromium embarqué) comme moteur par défaut, plus les backends WebView (webview de l'OS) et raw (winit, sans moteur). La fonctionnalité est isolée dans une nouvelle sous-commande, donc le comportement CLI existant n'est pas touché."
  - "La release introduit l'API `Deno.BrowserWindow` pour le cycle de vie de la fenêtre, la taille, la position, les menus natifs, l'intégration dock/tray, et un canal RPC `bind`/`unbind` vers le JS du webview, ainsi que la détection automatique des frameworks Next.js, Astro, Fresh, Remix, Nuxt, SvelteKit, SolidStart, TanStack Start et Vite SSR. `Deno.serve()` dans l'entry s'auto-lie au port vers lequel le webview navigue via une nouvelle variable d'environnement `DENO_SERVE_ADDRESS`."
  - "Le mode HMR et `--inspect` / `--inspect-brk` passent par un multiplexeur CDP qui expose le V8 du runtime Deno et le V8 du renderer CEF comme deux cibles dans une seule session DevTools (un menu déroulant Console, un panneau Sources). Le même matin ont aussi atterri les sous-commandes `deno link`/`unlink` ([#34359](https://github.com/denoland/deno/pull/34359)), `deno test --shard=<i>/<n>` ([#35057](https://github.com/denoland/deno/pull/35057)), et un fetch `request_builder_hook` qui injecte les en-têtes `x-deno-fetch-token` et `cdn-loop` pour les déploiements cloud ([#35088](https://github.com/denoland/deno/pull/35088))."
faq:
  - question: "Qu'est-ce que `deno desktop` ?"
    answer: "`deno desktop` est une nouvelle sous-commande Deno qui compile un projet Deno en application de bureau auto-contenue. Elle utilise le backend WEF (WebView Engine Foundation) pour héberger un webview (CEF/Chromium embarqué par défaut, le WebView de l'OS, ou une fenêtre winit brute sans moteur web), une nouvelle API `Deno.BrowserWindow` pour le cycle de vie de la fenêtre et les menus natifs, la détection automatique des frameworks Next/Astro/Fresh/Remix/Nuxt/SvelteKit/SolidStart/TanStack Start/Vite SSR, un mode `--hmr`, une session DevTools `--inspect` unifiée couvrant à la fois le V8 du runtime Deno et le V8 du renderer CEF, un auto-updater avec patches bsdiff, et la compilation croisée vers macOS .app/.dmg, Windows .exe + DLLs, et Linux répertoire applicatif/.AppImage."
  - question: "Qu'est-ce que WEF et comment `deno desktop` l'utilise ?"
    answer: "WEF (WebView Engine Foundation) est le nouveau backend sur lequel `deno desktop` embarque son webview. La CLI Deno télécharge les backends WEF précompilés depuis `github.com/denoland/wef/releases`, épingle la version via le `Cargo.lock` du projet, les vérifie en SHA-256, et les met en cache sous `<deno_dir>/wef/<version>/`. Il existe trois backends : `cef` (le défaut, un Chromium embarqué), `webview` (le webview de l'OS hôte), et `raw` (une fenêtre winit sans moteur embarqué). Pour le développement contre un checkout WEF local, la variable d'environnement `WEF_DEV_DIR` pointe la CLI vers ce répertoire au lieu de télécharger."
  - question: "Peut-on construire une application de bureau depuis un projet Next.js, Nuxt, ou SvelteKit existant ?"
    answer: "Oui. `deno desktop` embarque la détection automatique des frameworks (l'implémentation vit dans `cli/tools/framework.rs`) pour Next.js, Astro, Fresh, Remix, Nuxt, SvelteKit, SolidStart, TanStack Start et Vite SSR. Le serveur de production du framework s'exécute par défaut, et le serveur de dev du framework s'exécute sous `--hmr`. `Deno.serve()` dans l'entry s'auto-lie au port vers lequel le webview navigue via la nouvelle variable d'environnement `DENO_SERVE_ADDRESS`, donc la config `port` normale d'un framework peut continuer à pointer sur le défaut du framework."
  - question: "Est-ce que `deno desktop` supporte le hot module reload ?"
    answer: "Oui, via le flag `--hmr`. Pour les projets framework détectés, `--hmr` exécute le serveur de dev du framework (serveur de dev Next pour Next, serveur de dev Nuxt pour Nuxt, serveur de dev Vite SSR pour Vite SSR, etc.). Pour les projets non-framework, la CLI utilise un file-watcher plus un hot-swap `Debugger.setScriptSource` CDP pour pousser les modifications dans l'isolat V8 en cours d'exécution pendant que le runtime et le processus CEF restent en vie. Les jeux d'icônes ne sont pas encore supportés sous `--hmr` ; seul un chemin d'icône unique fonctionne dans ce mode."
  - question: "Qu'est-ce qui a atterri dans le main de Deno le même matin ?"
    answer: "Trois autres fonctionnalités ont atterri dans la même branche `main` de Deno le 16 juin 2026, toutes dans la fenêtre 06h59 à 07h15 UTC : `deno link <path>` et `deno unlink <path-or-name>` ([PR #34359](https://github.com/denoland/deno/pull/34359)) donnent un accès CLI de première classe au tableau `links` dans `deno.json` (le workflow npm-link-style pour les paquets JSR locaux), avec une suggestion de `deno add ./local-dir` qui pointe vers `deno link` quand le spec résout vers un répertoire linkable. `deno test --shard=<i>/<n>` ([PR #35057](https://github.com/denoland/deno/pull/35057)) découpe une exécution de tests entre machines CI avec un tri stable. Un fetch `request_builder_hook` ([PR #35088](https://github.com/denoland/deno/pull/35088)) injecte les en-têtes `x-deno-fetch-token` et `cdn-loop` pour les déploiements cloud et assainit les copies fournies par l'utilisateur de `x-deno-fetch-token` pour que le code utilisateur ne puisse pas usurper la valeur du runtime."
  - question: "Quelles plateformes `deno desktop` ne supporte-t-il pas encore ?"
    answer: "Plusieurs fonctionnalités se dégradent gracieusement sur un sous-ensemble de plateformes : l'auto-update (swap dylib bsdiff et rollback) est unix-only et no-op sur Windows ; le support des addons natifs NAPI est unix-only (le re-`dlopen` `promote_dylib_symbols_to_global` est `#[cfg(unix)]`) ; les permissions de notification sont spécifiques à macOS ; Linux est X11-only (le lanceur généré force `--ozone-platform=x11`, Wayland passe par XWayland) ; `Deno.dock` est macOS-only. La signature de code est implémentée (macOS, fallback ad-hoc pour les builds non signés), mais la notarisation et le stapling via `notarytool` ne sont pas encore câblés. Les installeurs Windows MSI, Linux .deb/.rpm, le clipboard, et secureStorage sont aussi encore en TODO."
---

[Deno a fusionné `deno desktop`](https://github.com/denoland/deno/pull/33441) le 16 juin 2026 (PR #33441, [ouvert à l'origine en avril 2026 comme ticket #3234](https://github.com/denoland/deno/issues/3234)), ajoutant une nouvelle sous-commande qui compile un projet Deno en application de bureau auto-contenue. Le même matin ont aussi atterri trois PR plus petites : [`deno link` / `deno unlink`](https://github.com/denoland/deno/pull/34359) pour le tableau `links` dans `deno.json`, [`deno test --shard`](https://github.com/denoland/deno/pull/35057) pour découper une exécution de tests entre machines, et un fetch [`request_builder_hook`](https://github.com/denoland/deno/pull/35088) pour les déploiements cloud. Le gros morceau est la sous-commande desktop : elle donne à Deno un chemin de première classe pour livrer des applications de bureau multiplateformes avec une seule invocation `deno desktop <entry>`, et c'est le plus gros ajout à la CLI Deno depuis que [Deno 2.7 a stabilisé l'API Temporal et les builds Windows-on-Arm](/articles/2026-04-07-deno-2-7-stabilizes-temporal-api-windows-arm-npm-overrides).

## Ce que `deno desktop` fait réellement

`deno desktop <entry>` compile un projet Deno en application de bureau auto-contenue, construite sur WEF (WebView Engine Foundation). La CLI télécharge les backends WEF précompilés depuis `github.com/denoland/wef/releases`, épingle la version via le `Cargo.lock` du projet, les vérifie en SHA-256, et les met en cache sous `<deno_dir>/wef/<version>/`. Pour le développement contre un checkout WEF local, `WEF_DEV_DIR` pointe la CLI vers un répertoire local au lieu de télécharger.

Il existe trois backends, sélectionnables par projet :

- `cef`, le défaut, un Chromium embarqué (CEF est la même famille de moteurs que celle sur laquelle le renderer d'Electron est construit).
- `webview`, le webview de l'OS hôte (Edge WebView2 sur Windows, WKWebView sur macOS, WebKitGTK sur Linux).
- `raw`, une fenêtre winit sans moteur embarqué, pour les projets qui veulent dessiner dans la fenêtre eux-mêmes.

`Deno.serve()` dans l'entry s'auto-lie au port vers lequel le webview navigue, via une nouvelle variable d'environnement `DENO_SERVE_ADDRESS` que le runtime desktop injecte. Un projet framework peut garder sa config `port` normale et laisser le runtime desktop faire le binding.

## Détection automatique des frameworks et HMR

La CLI détecte automatiquement les projets framework dans [`cli/tools/framework.rs`](https://github.com/denoland/deno/pull/33441/files), avec un support de première classe pour Next.js, Astro, Fresh, Remix, Nuxt, SvelteKit, SolidStart, TanStack Start et Vite SSR. Un projet framework détecté obtient le serveur de production du framework par défaut ; sous `--hmr`, c'est le serveur de dev du framework qui s'exécute (serveur de dev Next pour Next, serveur de dev Nuxt pour Nuxt, serveur de dev Vite pour Vite SSR, etc.).

Les projets non-framework ont quand même du hot-reload sous `--hmr`, mais via un mécanisme différent. La CLI utilise un file-watcher plus un hot-swap CDP `Debugger.setScriptSource` pour pousser les modifications dans l'isolat V8 en cours d'exécution pendant que le runtime Deno et le processus CEF restent en vie. L'effet pratique est le même : on édite un fichier, on voit le changement, la fenêtre ne se recharge pas. L'asymétrie est que les projets framework obtiennent le pipeline HMR du framework, qui est généralement plus granulaire (mises à jour scope composant, frontières fast-refresh, préservation d'état au niveau framework), tandis que les projets non-framework obtiennent un hot-swap au niveau script V8. Le compromis est documenté dans la PR : les jeux d'icônes ne sont pas encore supportés en mode `--hmr`, seul un chemin d'icône unique fonctionne dans ce mode.

## Deno.BrowserWindow et la surface API native

La nouvelle API `Deno.BrowserWindow` (déclarée dans `cli/tsc/dts/lib.deno.desktop.d.ts`) est ce que le code utilisateur appelle pour piloter une fenêtre. La surface couvre le cycle de vie de la fenêtre (`show` / `hide` / `focus` / `close` / `reload`), la taille et la position, `alwaysOnTop`, la navigation, les menus d'app et contextuels, les handles de fenêtre natifs, et les événements clavier, souris, molette, resize et focus. Deux modules natifs-only s'insèrent à côté : `Deno.dock` (macOS-only) pour l'intégration du dock, et `Deno.Tray` (multiplateforme) pour les icônes de status-area avec tooltips, variantes dark-mode, et menus contextuels.

Un canal RPC `bind` / `unbind` expose le JS du webview côté Deno en tant que `bindings.<name>()`, et la direction inverse est `executeJs` depuis Deno vers le webview. L'intégration runtime est la partie qui a le plus de levier sur le code JS existant : `prompt()`, `alert()`, et `confirm()` dans le webview deviennent des popups natifs, et les erreurs non catchées affichent une alerte native avec un `POST` optionnel vers `desktop.errorReporting.url` pour le crash reporting. L'auto-updater expose `Deno.desktopVersion` et un `Deno.autoUpdate({ url, interval, onUpdateReady, onRollback })` qui poll `<url>/latest.json`, applique des patches bsdiff au dylib (via `qbsdiff`), stage le résultat pour le prochain lancement, et fait un rollback en cas d'échec de lancement.

## Une session DevTools, deux isolats V8

Les flags `--inspect`, `--inspect-brk` et `--inspect-wait` passent par un nouveau [multiplexeur CDP](https://github.com/denoland/deno/pull/33441/files) dans `cli/tools/desktop_devtools.rs` qui expose le V8 du runtime Deno et le V8 du renderer CEF comme deux cibles attachées dans une seule session DevTools. L'effet côté utilisateur est un menu déroulant Console unique (Renderer / Deno) et un panneau Sources unique avec les deux threads. Le multiplexeur parle Chrome DevTools Protocol au client inspector et le multiplexe sur deux backends, une connexion CDP au V8 du runtime et une au V8 du renderer.

`--inspect-brk` met en pause les deux isolats au démarrage (Deno via son propre mécanisme, CEF via un `Debugger.enable` + `Debugger.pause` injecté avant la navigation). Pour la plupart des sessions de debug desktop, c'est le workflow : on pause les deux, on met un breakpoint dans le renderer, on met un breakpoint dans le runtime, on unpause, on conduit l'app, et on step entre threads dans le même panneau Sources. La cible CEF est la même cible DevTools qu'utilise Chromium, avec les mêmes panneaux Performance, Memory et Network ; la cible Deno est la cible V8 Deno standard avec l'introspection `deno_core`.

## Compilation croisée et distribution

Les flags `--target` et `--all-targets` téléchargent les binaires `denort` précompilés et les backends WEF pour le triple cible. Les sorties sont :

- macOS : un bundle `.app` (framework sous `Contents/Frameworks/`), avec `.dmg` produit via `hdiutil`.
- Windows : un `.exe` plus un répertoire de DLLs.
- Linux : un répertoire applicatif et un `.AppImage` produit via `appimagetool`.

La signature de code est implémentée sur macOS via la clé `macos.codesignIdentity` dans `deno.json` (avec un fallback ad-hoc pour les builds non signés), mais la notarisation et le stapling via `notarytool` ne sont pas encore câblés. Les installeurs Windows MSI, Linux `.deb` / `.rpm`, le clipboard, et secureStorage sont tous encore en TODO dans la description de la PR.

## Limites OS-spécifiques à connaître en amont

La description de la PR est inhabituellement explicite sur les manques. Six limitations sont signalées comme « se dégradant gracieusement » mais à connaître :

- L'auto-update est unix-only. `apply_pending_update`, `get_dylib_path`, et `AutoUpdateState` sont `#[cfg(unix)]`, donc sur Windows le chemin staged-update, bsdiff et rollback est un no-op.
- Le support des addons natifs NAPI est unix-only. `promote_dylib_symbols_to_global` (le re-`dlopen` avec `RTLD_GLOBAL` qui rend les symboles NAPI visibles aux addons comme `next-swc`) est `#[cfg(unix)]` ; sur Windows, les frameworks qui chargent des addons natifs peuvent échouer à les charger.
- Les permissions de notification sont macOS-spécifiques. Le backend LAUFEY est lancé via `disclaim_spawn` (posix_spawn avec responsabilité TCC disclaimée) pour qu'il devienne son propre principal de permission ; sans cela, `UNUserNotificationCenter.requestAuthorization` échoue.
- Linux est X11-only. Le lanceur généré force `--ozone-platform=x11` et `GDK_BACKEND=x11` parce que le moniteur d'événements LAUFEY (mouse, focus, resize) utilise XI2 sur X11. Sur les sessions Wayland, ça passe par XWayland ; le Wayland natif n'est pas supporté.
- `Deno.dock` est macOS-only.
- Les jeux d'icônes ne sont pas supportés en mode `--hmr` (toute plateforme).

La motivation pour livrer avec ces manques est la même que pour chaque release « on livre la première coupe, on consigne les limites » : la sous-commande desktop est la première fois que Deno livre un runtime non-serveur, et obtenir l'intégration WEF, l'API `Deno.BrowserWindow`, l'auto-détection de frameworks, les DevTools unifiés, l'auto-updater, et la compilation croisée qui fonctionnent sur au moins une plateforme (macOS) est le prérequis pour tout le reste. Les manques cross-plateforme sont réels, mais ce sont des manques de la première coupe, pas des manques d'architecture.

## Les trois autres PR du même matin

La même branche `main` de Deno a vu trois autres fonctionnalités atterrir entre 06h59 et 07h15 UTC, avant `deno desktop` à 10h41 UTC. Elles sont plus petites en scope mais elles sont toutes vérifiées à la source primaire sur la même release et elles complètent l'histoire « Deno ajoute pas mal de plomberie » du matin.

[`deno link` et `deno unlink`](https://github.com/denoland/deno/pull/34359) donnent un accès CLI de première classe au tableau `links` dans `deno.json`, qui était la façon user-facing d'utiliser un paquet JSR local à la place d'une version du registre, mais qui jusqu'ici n'avait pas de CLI pour la manipuler. `deno link <path>` valide le path, ajoute le path relatif à `links` (en créant le tableau s'il manque, en préservant le formatage via la machinerie CST existante), et lance install. `deno unlink <path-or-name>` retire une entrée par path littéral, par path résolu, ou par le nom JSR du paquet lié lu depuis le `deno.json` de la cible. Quand aucun argument ne correspond, `unlink` sort en code non-zéro, donc une typo est détectable depuis les scripts. Comme fix de découvrabilité, `deno add ./local-dir` et `deno install ./local-dir` sortent maintenant avec un hint « Did you mean `deno link ./local-dir` ? » quand le spec résout vers un répertoire linkable.

[`deno test --shard=<i>/<n>`](https://github.com/denoland/deno/pull/35057) apporte le sharding CI style Vitest / Jest / Playwright à `deno test`. Les fichiers de test découverts sont triés pour un ordre stable, puis découpés en `<n>` groupes consécutifs et équilibrés, et seuls les fichiers du groupe `<i>` s'exécutent. Chaque fichier atterrit dans exactement un shard, les tailles de groupe diffèrent d'au plus un, et la sélection se passe avant tout `--shuffle`, donc un shard donné exécute les mêmes fichiers sur chaque machine quelle que soit la seed de shuffle. La PR est explicite sur le compromis : le shard est sélectionné au runtime, après que le module graph du suite complète a été construit et type-checked, donc chaque machine paie encore le coût de build du graph et de type-check pour toute la suite. Déplacer le pré-filtre de shard avant le type-checking, pour que chaque machine ne charge et ne check que son propre sous-ensemble, est un follow-up naturel.

Le [fetch `request_builder_hook`](https://github.com/denoland/deno/pull/35088) lit les variables d'environnement `X_DENO_FETCH_TOKEN` et `CDN_LOOP` une fois et, quand elles sont présentes, les injecte en tant qu'en-têtes `x-deno-fetch-token` et `cdn-loop` sur chaque `fetch()` sortant (à la fois le main worker et les web workers). L'en-tête `cdn-loop` permet à l'infrastructure upstream de détecter les boucles de requêtes. Pour garder le fetch token trustworthy, toute copie de `x-deno-fetch-token` fournie par l'utilisateur est assainie avant que la valeur du runtime soit appliquée, donc le code utilisateur ne peut pas l'usurper. Quand aucune des deux env vars n'est set, aucun en-tête supplémentaire n'est ajouté. C'est le côté runtime de la même plomberie de déploiement cloud que le produit Deno Deploy utilise pour identifier quel déploiement a fait un appel sortant.

## Ce qui suit

Le cluster de quatre PR est un signal fort de la direction prise par Deno : un `main` qui traite le desktop comme une cible de première classe, ajoute la parité de sharding CI avec Vitest / Jest / Playwright, finalise le workflow local-package style npm-link, et expose les en-têtes fetch de niveau runtime dont les déploiements cloud ont besoin. La prochaine release Deno (probablement 2.8.4, vu que les [release notes Deno 2.8](/articles/2026-06-01-deno-2-8-audit-fix-ci-pack-subcommands) sont sorties le 1er juin) est la première chance que ces fonctionnalités arrivent dans un build tagué. La PR `deno desktop` est la dernière des quatre à avoir atterri, et c'est celle avec le plus de surface area à valider, donc le premier build tagué qui livre les quatre est aussi celui contre lequel déposer du feedback.
