🔥

tsdxでサックリTypeScriptのライブラリプロジェクトを作成する

3 min read

TypeScript の Web プロジェクトを作るのは楽だけど、ライブラリプロジェクト作るのは意外に面倒な件

Next.jsCRA (create-react-app) などがある今、TypeScript な Web のプロジェクトを作るのは簡単ですが、

# CRA
$ yarn create react-app --template typescript foobar

# Next.js
$ yarn create next-app --example with-typescript foobar

一方で、単純に TypeScript なライブラリのプロジェクトを作るのは意外に面倒です。

$ yarn init -y
$ yarn add -D typescript rollup jest ts-jest # あと何入れりゃいいかな 何設定すりゃいいかな

依存性も面倒ですが、設定ファイル用意したり、メンテするのに骨が折れます。大きめの monorepo だと結構な数のライブラリが自然に生えるので、辛いところです。

tsdx を使えばこんなに TypeScript ライブラリプロジェクトを作るのが簡単

そういうことで、 tsdx の出番です。

tsdx を使えばめちゃくちゃ簡単に TypeScript のライブラリプロジェクトが作成できます。

$ npx tsdx create foobar

# 以下のオプションから初期構成を選べる
❯ basic
  react
  react-with-storybook

basic で、ざっと以下が用意されます。

  • npm-scripts として一連のコマンド
    • rollup によるビルド (yarn build / yarn prepare), ウォッチ(yarn start)
    • ts-jest によるテスト (yarn test)
    • ESLint + Prettier による lint (yarn lint)
    • size-limit によるバンドルサイズチェック (yarn size / yarn analyze)
  • ゼロ config な依存性設定
    • tsdx が rollup や babel などに依存性をもってくれます
  • プロジェクトの各種メインエントリの定義
    • CommonJS エントリ: "main": "dist/index.js"
    • ESM エントリ: "module": "dist/tsdxtryout.esm.js"
    • TypeScript 型定義エントリ: "typings": "dist/index.d.ts",
    • エントリ中で process.env.NODE_ENV === "production" かどうかで *.min.js に分岐するかなども定義してくれているようです
  • いくつかのプリセット babel 定義
    • .babelrc を置くことでデフォルトのものとマージされて利用されるらしい
      • ほか jest なども設定を上書き可能らしい

とにかく ゼロコンフィグでそこそこの TypeScript プロジェクトが用意できる のが嬉しいです。

ちゃんとライブラリプロジェクトを作れているか monorepo で検証してみる

きちんとライブラリプロジェクトとして機能するか気になるので、下記のような構成の monorepo のプロジェクトを用意してみます。

packages/
    lib/
    web/     ( => lib/ に依存)
    nodepj/  ( => lib/ に依存)

web プロジェクトと nodepj プロジェクトから lib プロジェクトを参照しているような monorepo です。

web プロジェクトは CRA による SPA のプロジェクト、 nodepj プロジェクトは素朴な NodeJS アプリ、lib については tsdx で作成済みのプロジェクトという形で構成します。

lib 側は下記の src/index.ts を含み外部に公開します。

export const sum = (a: number, b: number) => {
  console.log("This function SHOULD BE included by treeshaking.");
  return a + b;
};

export const notUsedSum = (a: number, b: number) => {
  console.log("This function SHOULD NOT BE included by treeshaking.");
  return a + b;
};

nodepj => lib (CommonJS)

まず、 nodepj => lib 間については CommonJS 経由で lib の CommonJS 定義を参照できなければいけませんが、下記がうまくいくのできちんと参照できているようです。

// src/index.ts
import { sum } from "@my/lib";

console.log(`Sum of 3 + 4 = ${sum(3, 4)}.`);
$ cd packages/nodepj
$ ts-node src/index.ts
This function SHOULD BE included by treeshaking.
Sum of 3 + 4 = 7.
✨  Done in 1.88s.

web => lib 間 (ESM)

次に、 web => lib 間については Webpack による ESM 参照経由で lib の ESM 定義を参照できなければならないのと、きちんと参照しなくていい関数を Tree Shaking で削除できているかが気になります。

$ cd packages/web
$ yarn build

$ grep 'SHOULD BE included' -r build | grep -v '.map' | wc -l
1
$ grep 'SHOULD NOT BE included' -r build | grep -v '.map' | wc -l
0

参照していない関数定義を削除できているので、きちんと ESM による Tree Shaking ができているようです。画面も問題なく出力されます。

まとめ

tsdx を使えば、ほとんど労力なしに、TypeScript によるライブラリプロジェクトをセットアップすることが可能です。

これを手動でやるとかなり大変なので、ありがたいですね。