CLIのUI開発支援するためのNPMライブラリ: rich-cli をTSで実装&公開しました

### こんなものを作って公開しました

- リポジトリ: https://github.com/takaodaze/rich-cli
- インストール: `npm i rich-cli`
- サンプルプログラム:https://github.com/takaodaze/rich-cli#usage



#### CharRenderer
![char.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283322/ef0659bf-6739-0809-626d-d30d887db770.gif)

```ts
import { ANSIEscSeqHelper, CharRenderer, CharStyle, Color } from "rich-cli"

const charRederer = new CharRenderer()

charRederer.writeLine('aaa')
charRederer.addIndent()
charRederer.writeLine('bbb')
charRederer.addIndent()
ANSIEscSeqHelper.changeCharStyle(new CharStyle('underline'))
ANSIEscSeqHelper.changeColor(new Color('green'))
charRederer.writeLine('ccc')
charRederer.reduceIndent()
ANSIEscSeqHelper.changeColor(new Color('red'))
ANSIEscSeqHelper.changeCharStyle(new CharStyle('bold'))
charRederer.writeLine('ddd')
charRederer.reduceIndent()
ANSIEscSeqHelper.resetCharAllStyle()
charRederer.writeLine('eee')
```

#### Progress
![progress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283322/09342c8a-c5c4-8a45-a582-aa54f52a4248.gif)
```ts
import { Progress } from "rich-cli"

const progress = new Progress(2562)
let n = 0

while (!progress.isComplete()) {
  n++
  progress.incrementProgress()
  if (n % 100 === 0) progress.message(`${n} completed!⭐`)
  progress.render()
  await sleep(2)
}
```

#### Spin
![spin.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283322/9eb211ef-f036-c710-b9c5-f2fb01502d95.gif)
```ts
import { ANSIEscSeqHelper, Color } from "rich-cli"

const stopSpin = ANSIEscSeqHelper.spin(new Color('magenta'))
await sleep(3000)
stopSpin()
```

#### Confirm
![t-rec_5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283322/c9d4d889-f932-f211-4be1-fc4973c4c450.gif)
```ts
import { PromptHelper } from "rich-cli"

const yn = await PromptHelper.askConfirm('Are you sure?')
if (yn) {
  console.log('Yes')
} else {
  console.log('No')
}
```



# 実装の仕組み
ANSI Escape Sequences(Code?)を使って、リッチな描画を試みました。

参考資料
- https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
- https://en.wikipedia.org/wiki/ANSI_escape_code

ライブラリを公開するためのバンドラなどはこちらの記事を参考にさせてもらいました。
https://zenn.dev/dqn/articles/npm-publishing-2023
昨今の開発事情で、webpack等のツールは不慣れになってしまいました。
tsupというバンドラを使用しています。
zero-configでとても使いやすかったです。


# 今後の展望
**正直にまだまだ低品質なので磨きたい。**
- 抽象化
    - 低次元なAPIを使って制御する部分があるので、より高次元なAPIのみで実装ができるようなものにしたい
- より使いやすいインターフェイス
    - 他の類似ライブラリを参考にしてみよう
- 機能拡張
    - セレクトUI
    - トグルUI
    - クリッカブルウェブリンク
- 耐久性
    - ものすごく小さい画面（１行30文字未満）だとプログレスバーがバグる
    - 他にも、並行に文字を出力したりすると、きっとバグる
- テスト
    - 急いで作ったので省略した
    - どうやって実装するかはまだ分かってない
        - コンソールの状態をどうやってテストしよう？
        - 標準出力をリダイレクトして、テキストを検査する？
        - 他の類似ライブラリを参考にしてみよう
- ノウハウ公開
    - カーソル操作・コンソールの描画周り非常にわかりづらいので、しっかり勉強して情報をまとめて記事書きたい



# 困難だった点
- そもそも、この手の技術についてのドキュメントが少ない...
- カーソルの操作について、いまだによくわからない部分がある
    - 画面端の挙動
    - scroll, save,\n,cursor down/up など、コンソールの仕様がよく分かっていないことが明らかになった...
        - この辺、体系的に勉強するにはどうしたらいいんだろう。
            - たぶん、各コンソールの勉強をすることになるのかな？
    - カーソルがある位置までしか、コンソールに描画されないんだ...とか。
- ANSIエスケープシーケンス
    - 情報がまとまって落ちてない



# おまけ（感想）
CLIを開発する時って、なんだか無味乾燥なものしか出来上がらないので
自分用に開発をリッチにするものがあれば良いなぁ...と思い、作り始めました。

意外と詰まるポイントがあって、悶々としました。
未理解な点については整理して、またどこかで記事にしようかなと思ったり。

あとは、それぞれの機能でライブラリを分けようかなと思ったりもするが
オールインワンなライブラリの方がお手軽にインストールできるので、使いやすそうと思ったり。

後になって車輪の再開発だったかもなぁと思ったりしました。

_他の魅力的なCLI開発支援系ライブラリたち_
https://github.com/nathanpeck/clui
https://github.com/patorjk/figlet.js
https://github.com/yeoman/update-notifier
https://github.com/sindresorhus/terminal-link
https://github.com/sindresorhus/log-symbols

ただ、ライブラリの開発は少し憧れていたことだったので、達成できたのでヨシ。
今後も自分のためにライブラリ開発はしていくかも。


```ts
import { Progress } from "rich-cli"　// P.S: これが動くと感動する。
```