12k
All articles

deno desktop でデスクトップアプリを構築する

Deno desktopはTypeScriptアプリをネイティブデスクトップバイナリに変換し、webviewまたはCEF、フレームワーク対応、クロスコンパイル、制限を解説します。

OpenReplay Team
OpenReplay Team
deno desktop でデスクトップアプリを構築する

deno desktop は Deno プロジェクトを自己完結型のデスクトップアプリへとコンパイルします。あなたのコード、Deno ランタイム、そしてレンダリングエンジンが、プラットフォームごとに 1 つのバイナリにまとめられます。Deno 2.9 で登場しました。Electron や Tauri と異なるのは、レンダリングエンジンがビルド時の選択肢になっている点です。OS の WebView か、バンドルされた Chromium のどちらかを選べます。こうした切り替えを提供しているものは、Electron、Electrobun、Tauri、Dioxus のいずれにもありません。

Electron アプリをリリースした経験のある人なら、100 MB のインストーラをめぐるあの議論を知っているでしょうし、Tauri アプリをリリースした経験のある人なら、WebKitGTK でしか再現しないバグを知っているでしょう。この 2 つのフレームワークは 1 つの軸の両端に位置しており、これまではフレームワークを選ぶことがレンダリングエンジンを永久に決めることを意味していました。

本記事は、Electron と Tauri のトレードオフをすでに理解している読者に向けた、第 3 の選択肢のファーストルックです。このコマンドが何を生成するのか、モデルを理解できる最小のプログラム、各バックエンドがメガバイト単位でどれだけのコストになるのか、どのフレームワークプロジェクトを無修正で受け入れるのか、クロスコンパイルはどう機能するのか、そして何がまだ足りないのかを扱います。Electron 対 Tauri の議論そのものについては、別記事の Electron と Tauri の比較 を参照してください。

要点

  • deno desktop は 2026 年 6 月 25 日の安定版 Deno 2.9.0 リリースで登場しましたが、リリース記事では依然として実験的(experimental)とされ、一部のプラットフォーム機能は未実装です。
  • デフォルトの webview バックエンドは、Windows では WebView2、macOS では WKWebView、Linux では WebKitGTK を通じてレンダリングします。オプションの cef バックエンドは Chromium をバンドルし、どのプラットフォームでも同一のレンダリングを実現します。
  • Deno の比較ページによると、WebView ベースのアプリは約 40 MB、CEF ベースのアプリは約 150 MB で、Electron は約 100 MB 以上、Tauri は約 2〜10 MB です。
  • デスクトップのエントリポイント内の Deno.serve() ハンドラはポートもホスト名も受け取りません。ランタイムがウィンドウの読み込み先アドレスにバインドします。
  • --target--all-targets により、Rust ツールチェーンなしで任意のホストから 5 つのプラットフォームトリプルへクロスコンパイルできます。ただし macOS の .dmg だけはホスト依存の例外です。

deno desktop とは何か

deno desktop に Deno のエントリポイントかウェブフレームワークのプロジェクトを指定すると、インターフェースを webview で描画し、ロジックを Deno で実行するデスクトップアプリケーションが返ってきます。2.9 のリリース記事 ではこれを目玉機能として紹介しつつ、同じセクションで実験的であり API 表面はまだ安定化途上であると記しています。canary ではなく安定版 2.9.0 に着地したことは、v2.9.0 リリースノートfeat: deno desktop subcommand に記録されています。

各プラットフォームのバイナリには 3 つのものが入ります。あなたのコード、Deno ランタイム、そしてレンダリングバックエンドです。Deno 側と webview 間の通信は、ソケットベースの IPC ではなくプロセス内チャネルを経由します。これは Electron のメインプロセスとレンダラー間のメッセージパッシングとは異なるモデルです。Deno.BrowserWindowDeno.Tray といったネイティブなサーフェスはランタイムに組み込まれているため、ウィンドウ制御やシステムトレイアイコンにサードパーティのパッケージは不要です。

最小の deno desktop プログラム

最小構成のデスクトップアプリは、HTML を返す単一の Deno.serve() ハンドラと、コマンド 1 つです。

// main.ts
Deno.serve(() =>
  new Response("<!DOCTYPE html><h1>Window one</h1>", {
    headers: { "content-type": "text/html" },
  })
);
deno desktop main.ts

引数がないことに注目してください。通常の Deno サーバーならポートを渡しますが、ここでは省略します。デスクトップのエントリポイントでは、ランタイムが ウィンドウがすでに指しているアドレス をハンドラに渡してくれるからです。コンパイルされたアプリは、そのローカルサーバーを表示するネイティブウィンドウを開きます。これがプログラミングモデルで唯一直感的でない部分です。HTTP サーバーが UI のトランスポートであり、ランタイムが両端を自動で結び付けてくれます。

webview と cef、どちらのバックエンドを使うべきか

レンダリングエンジンはビルド時の判断であり、これこそ deno desktop が競合のどちらも埋めていない枠を占めている理由です。Electron は Chromium だけをバンドルし、Tauri はシステムの WebView だけを使います。deno desktop はどちらでも可能で、--backend または deno.jsondesktop.backend フィールドで選択します。

deno desktop main.ts                  # webview (default)
deno desktop --backend cef main.ts    # bundled Chromium

バックエンドのページ には、デフォルトの背後にあるエンジンが挙げられています。macOS では WKWebView、Windows では WebView2、Linux では WebKitGTK です。同ページには、評価にあたって重要となるこのバックエンドの制約が 2 つ記載されています。DevTools は CEF 限定であること、そして Linux での WebGPU には CEF が必要であることです。cef バックエンドは Chromium Embedded Framework のコピーをアプリバンドル内に含めます。同ページはフレームワーク単体のサイズを約 150 MB としています。

Deno の 比較ページ は、結果として得られるアプリサイズを概算値として挙げています。

ツールエンジンアプリサイズ(Deno の数値)
deno desktop, webviewOS の WebView約 40 MB
deno desktop, cefバンドルされた Chromium約 150 MB
Electronバンドルされた Chromium約 100 MB 以上
TauriOS の WebView約 2〜10 MB

配布のページ では別のデータポイントが示されています。WebView バックエンドの hello-world は約 66 MB で、--compress を使うと 19 MB まで下がります。これらはいずれも Deno 自身が示した数値であり、あなたのアプリの実測値ではないと考えてください。

リリース記事の 一行ルール は、あらゆる環境で同一のエンジンが必要でない限り webview のままにしておく、というものです。これを文書化された制約で補うと、実用的な判断基準になります。デフォルトは webview。UI が Chromium 固有のレンダリングに依存している場合、3 つの OS エンジンすべてでテストできない場合、開発中に DevTools が必要な場合、あるいは Linux で WebGPU が必要な場合は cef に切り替えます。切り替えの代償は、上の表の 2 行の差分です。

deno desktop はどのフレームワークを検出するのか

既存のフレームワークプロジェクトで deno desktop . を実行すると、設定ファイルまたは package.json からどのフレームワークかを判別し、ビルド出力を埋め込み、フレームワークの本番サーバーを Deno.serve() ハンドラとして実行します。Next.js、Astro、Fresh、Nuxt については、フレームワーク別の注記 に、そのフレームワーク自身のビルドコマンドの後に deno desktop . を実行するだけと示されており、アダプタも追加設定も不要です。SvelteKit も検出されますが、Deno Deploy アダプタまたは Node アダプタの出力経由のみです。React Router には Deno 互換の app/entry.server.tsx が必要です。

まずフレームワークのビルドを実行してください。このコマンドはそのビルドが生成したものをパッケージ化するだけで、代わりに next buildastro build を起動してはくれません。

npx next build        # produce .next/
deno desktop .        # package the built server
deno desktop . --hmr  # development: framework dev server with hot reload

--hmr を使うと、アプリのウィンドウはフレームワーク自身の dev サーバーを直接指すため、開発体験はブラウザのタブで作業するのとほぼ同じになります。編集しても state は保持され、fast refresh が機能し、エラーは通常のオーバーレイ経由で表示されます。

deno desktop アプリをクロスコンパイルする方法

--target <triple> は別の 1 つのプラットフォーム向けにビルドし、--all-targets はサポートされているすべてのプラットフォーム向けにビルドします。どちらも任意のホストから、Rust ツールチェーンなしで実行できます。配布のページ には 5 つのターゲットが挙げられています。aarch64-apple-darwinx86_64-apple-darwinx86_64-pc-windows-msvcaarch64-unknown-linux-gnux86_64-unknown-linux-gnu です。

deno desktop --target x86_64-pc-windows-msvc main.ts
deno desktop --all-targets main.ts

仕組みはコンパイルではなくダウンロードです。指定したターゲットについて、CLI はビルド済みの denort とビルド済みのバックエンドアーカイブを取得し、使用前に両方を SHA-256 ハッシュと照合します。これが、Tauri や Dioxus では実現できないことが可能な理由です。これらの Rust ツールチェーンはターゲットプラットフォーム上でコンパイルする必要があるため、OS ごとに専用のビルドマシンが必要になります。Electron は electron-builder を通じてクロスビルドできるので、この対比は特に Rust ベースのツールに対するものです。Deno 側の唯一の例外は macOS の .dmg で、これは hdiutil を呼び出すため Mac 上で生成する必要があります。

成熟度について正直に言うと

Electron は Slack、Visual Studio Code、Notion を動かしています。Tauri 2 は数年にわたるリリースとモバイルターゲットの実績があります。deno desktop は 2.9.0 とともに登場し、それ以降の 2.9 系パッチリリース はすべて、2.9.6 を含めてデスクトップ関連の修正を含んでいます。ドキュメントは、まだ揃っていないものについて率直です。

出力フォーマットは、比較ページの未対応リストが示すよりも進んでいます。配布ページには、macOS の .app.dmg、Windows のアプリディレクトリまたは .msi、Linux のアプリディレクトリ、.AppImage.deb.rpm が文書化されており、--output の拡張子で選択されます。リリースノートによると、.msi.deb.rpm インストーラは 2.9.0 そのもので登場しました。

確認されている未対応項目は以下です。

  • Windows の自動更新。 実際に更新を完了できるのは macOS と Linux のみです。これらはステージングされたパッチを適用し、新バージョンの起動に失敗した場合は旧バージョンへフォールバックします。Windows はパッチをダウンロードしてステージングはしますが、差し替えは行わないため、次回起動時に何も変わりません。自動更新のページ は、Windows の自動更新は現時点では未サポートとして扱うよう述べています。
  • Notarization(公証)。 macOS ホスト上では署名が自動的に実行されますが、コード署名のセクション は、公証は依然として手作業で行う必要があり、xcrun notarytool submit を独立した手順として実施する必要があると指摘しています。
  • モバイル。 iOS も Android もターゲットにありません。Tauri 2 は両方を備えています。
  • セキュアストレージ、実行時のパーミッションプロンプト、共有 CEF ランタイム。 比較ページはこれら 3 つすべてを未実装またはロードマップ上と記載しています。共有ランタイムは CEF アプリを小さくできる項目ですが、これは約束であってリリースではありません。

いま deno desktop を試すべきなのは誰か

すでに Deno を運用しており、チームが Rust ではなく TypeScript を書いていて、対象アプリが社内ツールか既存の Next.js、Astro、Fresh、Nuxt コードベースをデスクトップにラップしたもので、40 MB の WebView ビルドが許容でき、ユーザー層が macOS か Linux で自動更新をカバーできるなら、今すぐ試す価値があります。インプレース更新が必要な Windows ユーザーに向けて出荷する場合、同一コードベースからモバイルも必要な場合、あるいは署名とインストーラのツーリングに数年の実績がある配布パイプラインが必要な場合は、待つべきです。リリース記事の実験的というラベルは正確です。モデルは筋が通っており、コマンドは文書どおりに動作しますが、API 表面はまだパッチリリース間で変わり得ます。

まとめ

deno desktop は、レンダリングエンジンの選択をフレームワークの選択から切り離し、その選択の各側面に対して文書化された代価を課すという点で、本物の第 3 の選択肢です。もっとも手軽な評価方法は、すでに手元にあるフレームワークプロジェクトの中で deno desktop . をデフォルトバックエンドと --backend cef の 2 回実行し、生成された 2 つの成果物を上に挙げた未対応項目と照らし合わせて比較することです。

FAQ

deno desktop アプリで webview から Deno のコードを呼び出すにはどうしますか?

バインディングを通じて行います。Deno 側では win.bind(name, handler) でウィンドウにハンドラを紐付け、ページの JavaScript から bindings.name(args) を呼び出すと、ハンドラが返した値を運ぶ promise が返ってきます。バインディングはウィンドウごとに独立して保持されるため、ある Deno.BrowserWindow に登録したハンドラは別のウィンドウからは見えません。この呼び出しはソケット IPC ではなくプロセス内チャネルを経由し、ハンドラが throw した場合に webview に届くのは実際の Error ではなく、name、message、stack を持つプレーンなオブジェクトです。

deno desktop の raw バックエンドとは何で、どんなときに使うべきですか?

ウェブエンジンをまったく持たないデスクトップアプリを実行します。ウィンドウ、入力イベント、ネイティブ API サーフェスは利用できますが、HTML をレンダリングする先がありません。webview もなく、Deno.serve() の自動バインドもなく、bindings プロキシもありません。WebGPU、Skia、あるいは独自の描画コードで自前のインターフェースを描くアプリに向いています。これを選択する唯一の方法は deno.json の desktop.backend フィールドです。--backend フラグは cef と webview のみを受け付けます。

deno desktop アプリを、コードを変更せずに webview と cef バックエンド間で切り替えられますか?

はい。Deno のバックエンドのページは CEF と WebView を相互に置き換え可能なものとして扱っています。ウィンドウ、バインディング、イベント、ナビゲーション、JS 実行はいずれでも同じように振る舞い、その可搬性が崩れるのは raw バックエンドだけです。まだ使ったことのないバックエンドやターゲット向けにビルドすると、まずビルド済みのアーカイブがダウンロードされます。CEF の場合は数百メガバイトになり、チェックサムが検証され、Deno のキャッシュディレクトリに保持されるため、以降のビルドではダウンロードがスキップされます。

deno desktop アプリの内部でも Deno のパーミッションは適用されますか?

はい。ただしパーミッションはプロンプトからではなく、バイナリにコンパイルされたものに由来します。バインディングはプロセスに付与された権限のもとで Deno ランタイム内で実行されるため、ファイルを読むハンドラには起動時に付与された読み取り権限が必要で、webview 側の操作でそれを広げることはできません。ドキュメントは、実行時に別途パーミッションプロンプトが表示されることはないと明記しています。したがって、バインディングがページから受け取るものはすべて信頼できない入力として扱い、検証してください。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.