🧊

npm workspacesで、TSファイルを共通モジュールとして使い回す

ということをやりたくて、こうやったらできたという覚書。

やりたいこと

こういうモノレポ構成とする。

- package.json
- package-lock.json
- packages
  - shared
  - app1
  - app2

で、app1にあるコードベースを@myapp/app1とした場合、@myapp/sharedに置いたTSファイルを、そのままモジュールとして利用したいとする。

import { foo } from "@myapp/shared";
import { bar } from "@myapp/shared/bar";

// This is @myapp/app1 or @myapp/app2 code

@myapp/app1@myapp/app2は、それぞれ独自のコードベースになってて、それぞれtscではなくviteesbuildなどのバンドラーを使う前提。

共通モジュール側

package.jsonの抜粋。

{
  "name": "@myapp/shared",
  "version": "1.0.0",
  "type": "module",
  "exports": {
    ".": "./src/index.ts",
    "./bar": "./src/foo.ts"
  },
}

exportsmainでもいいけど、こうしておくと後からネームスペースが切れるので便利。type: moduleはお好みで。

正直、特別な指定というものはない。

モジュール利用側

兎にも角にも、npm i @myapp/shared -w @myapp/app1でインストールする。(npm、どこかのバージョンから、モノレポのルートじゃなくても、その個別パッケージ内でnpm iしたらいい感じに処理してくれるようになった?)

{
  "name": "@myapp/app1",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "@myapp/shared": "^1.0.0"
  }
}

あとはこのtsconfig.jsonの抜粋。

{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "bundler"
  }
}

このmoduleResolution: bundlerがすべてをいい感じに解決してくれる。

TypeScript: TSConfig Reference - Docs on every TSConfig option https://www.typescriptlang.org/tsconfig#moduleResolution

まとめ

モノレポ構成なおかげでnode_modulesはサクッと解決できるし、こういうのが本当にカジュアルにできちゃう時代になったんだなあ。