9月も半ばを過ぎましたが、なんとも動きづらい天気が続いてます。もうちょいシトシトとした雨が降ってはくれないものか。いや外出してるときに降られたくはないのだけども。

最近TypeScriptをひたすら触っていますが、pnpmでworkspaceにしたらなんか解決できないかなぁ…と思うことがあったので考えてみました。

TypeScriptでの困りごと

元々のJavaScriptにおける言語機能から考えれば当たり前といえば当たり前なんですが、Rust/Java/Kotlin/OCamlとか触ってると(OCamlはめんどくさいことも多いけど)、次のようなことができないのが結構ストレスです。

pacakge privateのようなものができない

例えばRustであればcrate privateだったりmodule privateだったり、この観点ではかなり柔軟だと思います。というかそこまでやるか?と思うときもまれにあります。 Javaであればpackage private/private/protectedとか、Kotlinであればinternalですね。JVM系列はJava9からmodule systemが入ったのは把握してるんですが、moduleでどういうことができるのかがよくわかってないもので…。

OCamlは公開範囲を規定とかできませんが、まぁJavaScriptよりはマシです。ホントに?

さて、ではこれができないことで何が困るんでしょうか?具体的に言うと、↓のようなことをやろうとした場合にめんどくさいな、というところです。

  1. importできるmoduleのlayerを制約したい
  2. 本来見えないはずのmoduleが見えてしまうのを避けたい
    • これはindex作ればまぁなんとかなりますが
  3. 共通で利用する部分を整理しやすくしたい

大体、一個のファイルが大きくなってもいいんなら解決できるんですが、1000行とかいったりするのもそれはそれでやだよね、という思考からきてます。

そこまで大きくならないのであれば、exportしなけりゃいいだけなんで、二つめは問題にならないですね。

お仕事で使ってるJVMというかGradleならば、Gradleで適切にmulti projectで分割してやれば強制することができます。Rustはまぁ強烈すぎるんでやりかたは沢山あるでしょうが。

pnpmのworkspace

最初にnodeのパッケージマネージャでこれを作ったのはyarnじゃないのかな?という浅学っぷりなのですが、pnpmにはworkspaceという機能があります。

https://pnpm.io/ja/workspaces

これはモノレポ構成を上手く扱うための機能の一つで、workspaceを利用することで、workspace内のpackageは workspace:* のような形で、package間の依存関係を作成することができます。また、ルートにあるpackage.jsonと上手く利用することで、開発時に利用する同じ依存関係、たとえばviteやtypescriptといった開発用パッケージのバージョン管理をシンプルにすることができます。

Bitとは?

ところで、↑のページを見ていたら、pnpm本体から bit というツールの存在を示唆されました。なんじゃこれ?というところでちょっと試してみたのですが、以下のようなものでした。

開発元はbit.cloudという、このbitを利用したコラボレーションやらなんやらを行うことができるプラットフォームを開発しているようです。お、これでいけるんじゃね…?と試してみましたが、ちょっと思っていたものと違いました。

  1. bitは別のバージョン管理の元に生きている
    1. 例えばビルドパイプラインで利用するtypescriptですが、 bitに依存を明示的に追加しないといけないです 。それって管理するものがただ増えただけなのでは…
  2. bitコマンドが非常に大きい
    1. なんかえらい時間かかるなーと思ったら、展開した結果が1.5GBくらいありました。大分ビックリ
  3. bitの世界でのビルドパイプラインを構築しなければならない
    1. 色々用意はされているようですが、どちらにせよbitという世界の中で作成する必要がありました

構成全部をここに全振りできるんならいいのだと思いますが、ちょっと個人でこれを全振りする勇気は出ませんでした。

pnpmのworkspaceでのpackage分割を考察してみる

さて、戻ってpnpmのworkspaceでpackage分割をしてみます。構成としては、

- packages  // workspace
  - lib-a
    - package.json
  - lib-b
    - package.json
  - app
    - package.json

のような超シンプルなもので考えてみます。一旦考察だけなので。しかしちょっと考察するだけで色々としんどさが見えます。

private packageかつtypescriptの場合のショートカット

真面目にやると超めんどくさいのですが、実はショートカットがあります。

https://turbo.build/blog/you-might-not-need-typescript-project-references

package.jsonは次のように書くことができます。

{
  ...,
  "main": "./src/index.ts",
  "types": "./src/index.ts",
  ...
}

What?って感じですが、これできちんと動きます。Language Serverも問題なく動作します。こうすることで、declarationを生成したりビルドしたり、を各パッケージで実行する必要がなくなります。が、当然エントリポイントではこれらをtranspileしないといけません。これについてはviteがハンドリングしてくれるため、viteを利用している場合には特に問題はありません。

だがしかし、禍福はあざなえる縄のごとし。これをやった場合一個欠点があり、 node_modulesからsrc/配下が全部見えるようになります 。これはnode_modulesがそういう構造になっちゃってるからなので、制約は大変難しいです。

結論

ということで、個人的なプロジェクト程度では、これを利用するのは過剰だね、という気分でした。めんどくさくてもきちんとindex.tsとかで整理していくとか、なんでもかんでもexportしないとかが重要ですね。

後はファイルサイズに対するアレルギー的なものですが、実際にはある責務をmoduleに担当させているのであれば、それが一定のサイズになるのはままあることなので、まぁ気にしすぎない方がいいかな、と思ったり。特にReactでコンポーネント作ってるとめっちゃ細かく割りがちなのですが、後から考えるとそこ割らないでまとめた方が結局テストとか楽だよな、ってこともあります。

なんもまとまっていませんがこのへんで。