JavaScript
Node.js
Chrome
puppeteer

CarloでNode.jsのツールにGUIをもたせる

先日、GoogleがCarloというライブラリをリリースしました。

https://github.com/GoogleChromeLabs/carlo

少し気になったので、軽くメモを残しておきます。

f4e15e2e22666a5bb4d1b7b0dd1b596f.png

Carlo is 何

CarloはChromiumを画面描画エンジンとして動作するNode.js アプリケーションフレームワークです。
Node.js、ChromiumともにV8が動作するため、JavaScriptのみでデスクトップアプリが作れるよ、というわけです。

公式のサンプルとほぼ一緒ですが、次のようにコードを書くと、index.htmlがデスクトップアプリ風に立ち上がります。

const carlo = require('carlo');

(async () => {
  const app = await carlo.launch();

  // html/CSS/JavaScriptを配置しているフォルダを教える
  app.serveFolder(__dirname);

  // エントリポイントとなるHTMLをopenする
  await app.load('index.html');
})();

URL欄を消したり、カメラや通知のアクセスが最初から有効化されるなど、デスクトップアプリとしての最低限部分はCarloが自動でやってくれます。

Electronとの違い

JavaScriptのデスクトップアプリケーションエンジンという意味で、CarloはElectronやNW.jsと似ています。
これらのフレームワークとCarloの一番の違いは「ローカルにインストールされたChromeが利用されること」です1
ElectronもNW.jsも、Node.jsとChromiumをビルドした独自のランタイムを必要とします。これらのランタイムは、アプリケーションにバンドルして配布するのが一般的なため、最終的な配布物のファイルサイズが数百MBがなってしまいます。
Carloはこのような独自のランタイムを必要としません。
ユーザーがインストールしているブラウザをそのまま利用するため、配布サイズを軽くできます。

一方で、ただのシンプルなChromeであるということは、ElectronやNW.jsが独自に拡張している箇所(ネイティブのダイアログやタスクトレイへのアクセス、カスタムURLスキーマの登録など)をCarloがサポートしていくとは考えにくく、これらの機能を期待する場合はCarloよりも他のフレームワークを使った方がよいでしょう。

Node.jsをバンドルするかどうか

Carloで開発したアプリケーションのターゲットユーザーがNode.jsをインストールしているケースであれば、要するに開発者ということですが、package.jsonのbinにでも登録しておけば、普通にNPMにpublishするだけで配布完了です。
Angular ConsoleとかVue CLI UIのような「開発支援ツールにGUIをつけて提供したい」みたいなケースであれば、Carloは結構向いてそうです。

ブラウザはともかくとして、Node.jsがインストールされていないユーザーに対してはアプリケーションにNode.jsのみバンドルする必要がでてきます。
Calroの開発チーム曰く、 pkg を利用すればいいんじゃね?とのことなので、Carloとしてのサポートは特になさそう。

CarloとPuppeteerの関係

Carloは、Node.jsとChromiumとの連携にPuppeteerを使っています。PuppeteerということはCDP(Chrome DevTool Protocol)ですね。
NPMのpuppeteerパッケージは、自動でChromiumのランタイムをpost installで持ってくるので結構重いのですが、この機能をオフにしただけのpuppeteer-coreというパッケージがあり、Carloはこちらを利用しています。
というか、コードを読んでみればわかるとおり、CarloはPuppeteerのラッパーでしかないです。それも相当薄い。
例えば、CarloのAPIとしてNode.js側の関数をChrome側へ公開するための exposeFunction とか生えていますが、完全にPuppeteerの同名の関数と一緒です。

  // ブラウザ側のコードでcwdという関数を呼び出すと、こいつが動作する
  await app.exposeFunction('cwd', () => process.cwd());

なんなら、 app.page_ でPuppeteerのPageインスタンスにアクセスできてしまうので2、現状ではCarloのAPIとしては利用できないようなメソッドをサクッと動かすことも簡単にできてしまいます。

基本的にCarloがやってくれているのは、Node.js + Chrome間でObjectを共有する機能と、https://domain/<ファイル名> というURLをhookして serveFolder したフォルダ内のファイルを提供する機能くらいです。その気になればPuppeteerだけで書くのも全然イケそう。

その他

Calroを触ってみてから気づいたのですが、生JavaScriptではなく、トランスパイラ付きでコードを書くのが地味に面倒です。

TypeScriptの型定義が無いとかは別にまぁ良いんですけど、Electronと違ってレンダラ側ではNode.jsが使えないため、 require が動作しないんですよね。
一方でNode.jsも必要なので、--module es2015 というわけにもいかず、UMDにしてみたら依存パッケージ側でtscに怒られ、、といった消耗をしてしまいました。

まとめ

所感として、ガッツリとしたデスクトップアプリを作るというよりも、CLIとして提供していたようなNode.jsのツールに対して、少しだけGUIを足したい、みたいなケースに一番向いてそうです。

パッと思いついたのは、webpackをローカルで回してstatsを可視化する、とかそういう系統のやつ。

皆さんも思いついたら何か作ってみてはいかがでしょうか。それでは、また。


  1. Chrome/Chromiumがローカルにインストールされていない場合、コンソールにエラーメッセージが寂しく表示されて終了するようです。 

  2. もちろんプライベートなフィールドなので、完全に自己責任の世界です。