Denoでversion manager cretを作ってみた話
新しくDenoのversion managerを作りました
version managerというと他の言語にはいろいろあったりします(特にnode.jsの世界においては顕著ですね)
Denoにもご多分に洩れずdvmというのがあります
始めは自分もそれを使おうとしていたのですが、最初の使用感としての何かが自分を困惑させました(今ではもうすっかり忘れてしまいましたが 😂)
なのでDenoの勉強のためにも(ほとんどはTypeScriptの再学習になりましたが)作ってみようと思いました
今回の記事は「どういう事があったかなどの開発編」と「このpackageの機能編」の2部構成で行こうと思います
開発編
Denoに関してはこれを探してきた時点で特に説明は必要ないと思うので省きます
さて、Deno自体はとても便利で軽量ですし、デフォルトでtscを内蔵しているので(今後swcに移行していくらしいですが)node.jsのようにボイラーテンプレートから用意しないといけない、ということは特にないです
実際にshell自体のコマンドを使いたい場合は Deno.run
というコマンドを使います
Deno.run({
cmd: ["ls", "-l", "."]
})
最も簡単なパターンです
しかし大抵何かの返り値が欲しい場合が多いのでこうして使うケースはそうそう無いです
const p: Deno.Process = Deno.run({
cmd: ["ls", "-l", "."]
})
さて、こうする事で受け取れるかというと残念ながら違います
これを実行すると分かると思うのですが、 ls -l .
の実行結果がstdoutから流れてしまいます
では、受け取るためにはどうするかと言うと cmd
のpropertyの他に stdout
, stderr
, stdin
があります
// こうすることによって、実行されず一旦実行計画だけが p の中に保存されている状態になります
const p: Deno.Process = Deno.run({
cmd: ["ls", "-l", "."],
stdout: "piped",
})
/**
* それからoutputすることによって、返り値をUint8Arrayで得ることができます
*/
p.output();
// =>
// Promise {
// Uint8Array(1024) [
// 116, 111, ...
// ]
// }
始めこれが全てこのメソッドで完結していけばいいのかと勘違いしていました
まぁそんなわけないですよね、というのをリファレンス見ていたら知ったんですが、この手のを探す時は基本的にその名称の英単語の類義語などを知っておく必要があるので(例えば symbolic link
は symlink
, delete
は remove
など)(他の人はどういう風に探しているんですかね?)知っていれば知っているほど探しやすいってやつですね
完全にこの時間ロスがあってしまったので簡易な仕様書を書くべきだと判断したのでsubcommandを下記の5つに制定しました
- install
- Github API download(Deno.fetch)
- mkdir(Deno.mkdirSync)
- unzip(Deno.run)
- rm(Deno.removeSync)
- uninstall
- ls(Deno.readDirSync)
- rm(Deno.removeSync)
- use
- ln(Deno.symlinkSync)
- ls
- readlink(Deno.realPathSync)
- ls(Deno.readDirSync)
- ls-remote
- Github API download(Deno.fetch)
もちろんこの通りにできてる部分もありますが、その大部分は余計な処理は入ってるのでこの通りではないです(ユーザからのinputを受け取りますしね)
他にちょっとしたハマりどころは removeSync
, lstat
です
removeSync
でdirectoryを削除しようとした時、 Deno.removeSync(dirPath, { recursive: true })
が必要です
シンボリックリンクの情報を得ようとした時に Deno.stat
に最初たどり着くかもしれませんが、こちらはfile system上で必ず isSymlink: false
となりますが仕様です
Deno.lstat
を活用します
しかし、例えばfilepathを取得したい場合、どちらも使えません
lstat
は FileInfo
を返してくれるのですが、残念ながらfilepathのpropertyは削除されてしまいました
なのでfilepathを得たい場合は Deno.realPathSync
を使う必要があります
それとは別に悩んでしまったこととして *Sync
系のmethodをTS上だけで自作できないか、どうかというところを捻っていたのですが(どうしてもこちらで async で包む必要があるのでシンプルでなくなってしまう)作ることができなくて断念しました
Deno
自体で苦戦することは昨今のTypeScript事情などを鑑みるに詰まるところは特にあるという感じはないですね、むしろTypeScriptをそんなに書いたことない人の方がハマる可能性はありますね(例えば default parameter and optional parameter with object destructuring
のような)
あとはcli toolなどを作ろうとした時にshellのcommandの類などを知っているのと知らないのでは詰まるところが違う、といった感じです(optionで指定できることをdeno上からはどうするか、など)
Deno以外で詰まってしまった部分として gh release
commandや brew create
でbinaryを配布する時に使う bottle
などはどういう風に使うかなどがよく分からなかった部分でした
機能編
このpackageですが、 brew
でinstallできるようにしてあります(windowsも配布しているのですがテストはしてません、すみません)
brew install lion-man44/cret/cret
正直自分自身がversion managerに期待することは多くないのでOSSとしても作ろうと思った経緯があります
大抵この手のツールはshell scriptで作られていることが多かったりするのですが、正直それだとテストされてない部分もあったりするのでそこが自分の中でネックでした(shell scriptが便利とは言え、やはりあの構文に対するモチベはそこまで高いものでもないので)
Deno
にはデフォルトで test
commandがあるので テストを書いてみたかったというのもあります(デフォルトで書いていくのはしんどいのでassertion系やtest系のツール入れていかないとcallback地獄にすぐ陥ります)
subcommandは5つです
- install
- uninstall
- use
- ls
- ls-remote
簡単な作りにしてあるので誰でも読めますし、誰でも使えると思います
cret install
を実行するとversionが聞かれるので入力してください
installが終わったら export PATH=$HOME/.cret/bin:$PATH
を .bash_profile
, .zprofile
などに追加していただければPATHが通るようになります
まとめ
最後に、Denoを今回初めて触ってみていろいろとJS界隈でもまた時代の流れがありそうでワクワクしています
今回作ったのはToyみたいなものですが、もし使ってくだされば嬉しいです(あとdebugしてくれてもいいんじゃよ?🤗)
Discussion