denoでクリップボードの画像を扱うモジュールを作った
初めに
最近denoで少し遊んでいて、画像をクリップボードで扱うモジュールがほしいなと思って探したが、見つからなかったのでdeno-clipboard-imageを作りました。
もともとGoで作ったclipboard-imageをdenoに移植したものです。
このモジュールは外部コマンドに依存していますが、抽象化して関数で使いやすくしたものです。
本当は直接クリップボードを扱いたいんですが、denoはそのような機能を提供していないので外部コマンドに頼りました。
使い方
クリップボードに画像をコピーしたい場合は次のように、write()にDeno.Readerを渡します。
const image = await Deno.open("test.png");
await write(image);
image.close();
画像データを読み取りたいときはread()を使うとDeno.readerが返ってくるので、ファイルに書き出すしたりバッファリングしたりできます。
src = await read();
const out = await Deno.create("image.png");
await copy(src, out);
out.close();
ハマったこと
テストの際に、AssertionError: Test case is leaking resources.エラーが出ていて困っていました。
deno-clipboard-imageは外部コマンドを実行していて、一時的にファイルを作ったり外部コマンドの入出力を扱ったりしています。
write()が実行されると、次の関数で中身がファイルに書き込まれてから外部コマンドに渡されます。
async function writeTmp(src: Deno.Reader): Promise<string> {
const tmp = await Deno.makeTempFile();
const dst = await Deno.open(tmp, { write: true });
await io.copy(src, dst);
dst.close();
return tmp;
}
このように、リソースを使っている箇所のclose漏れが原因と思ってひたすら探したんですが、結局ファイルではなくプロセスの入出力をcloseしていないことが原因でした。
Deno.runしたあとp.close()すれば問題ないと思っていましたが、どうやらp.stdinなどをclose()しないと駄目のようです。
実際入出力をcloseしたら上記のエラーが消えました。
const p = Deno.run(opts);
...
p.stdin?.close();
p.stderr?.close();
p.stdout?.close();
p.close();
一応こちらの記事ではtest関数のsanitizeResourcesオプションをfalseにすれば、
リソース開放漏れのチェックをスキップしてくれますが、実装ミスを防げなくなってしまうので基本しないほうがよいと思っています。
既知の問題
Windowsではpowershellを使って画像のコピーと読み取りをしていますが、どうやらバージョンによってはうまく動作しない様です。
とりあえずissueに起こしたのですが、Windows機を持っていないので検証ができない状態です。
きっといつか治ると思います。..w
最後に
クリップボードの画像を扱いたいのは、もともとdenops.vimを使ってVimでソースコードを画像化するプラグインを作る際に、生成された画像を直接クリップボードに渡したいからでした。
しかし、denoでは画像モジュールが皆無だったので実現できなかったんです。
ただこれからGoのimageパッケージみたいなモジュール作られるかもしれないので、できた際はすぐに実装できるよう、今のうちにモジュール用意しておきました。
Discussion