JavaScript で自作のパッケージに依存したプログラムを開発する場合、それらを一つのリポジトリにまとめる Monorepo が便利です。Monorepo にすることで開発ワークフローがシンプルになり、 色々と楽になります。そんな Monorepo において、npm 7 より導入された workspaces の機能を用いると、さらにワークフローがシンプルになります。
今回は npm workspaces を使った Monorepo 開発を簡単に説明します。
サンプルリポジトリ:
ワーキングディレクトリ
npm workspaces を利用せず、ディレクトリごとに独立したパッケージを用意する場合には、npm install
などを実行する際はそれぞれのディレクトリに移動して実行する必要がありました。npm workspaces では、基本的にはターミナル上ではパッケージをまとめたルートディレクトリから移動せずに作業を行います。
これだけ覚えておけば OK
npm workspaces を使うときはルートディレクトリから移動しない
プロジェクト構成
今回は packages ディレクトリに各パッケージを配置することにします。
パッケージ構成
名称などは適当です。
- app: メインのパッケージ
- lib: app が利用するパッケージ
環境構築
npm init # /package.json が生成されます npm init -w packages/app # packages/app/package.json が生成され、/package.json の workspace に追記されます npm init -w packages/lib
既存プロジェクトに導入する場合
- 各パッケージのディレクトリの node_modules ディレクトリと package-lock.json を削除します
- ルートディレクトリに package.json を追加し、workspaces にそれぞれのパッケージの場所を列挙します
- ルートディレクトリで npm install を実行します
npm install (全パッケージ共通)
npm install lodash --workspaces
app, lib それぞれの package.json の dependencies が更新されます。(ルートの package.json ではないことに注意) また、node_modules ディレクトリはルートにだけ生成されます。
webpack や rollup など、共通した devDependencies をインストールする際によく使います。その場合、npm install @types/node --save-dev --workspaces
のようになります。
npm install (個別)
npm install react react-dom --workspace=app
app の package.json だけが更新されます。
npm-run-script
package.json の scripts は通常通りそれぞれのパッケージに記載しますが、起動方法が異なります。 npm install と同様のマナーでルートディレクトリで下記を実行します。
npm start --workspace=app
毎回 --workspace=app を付けるのも面倒なので、下記のようにルートの package.json に script を用意すると便利です。
"scripts": { "start": "npm start --workspace=app", "build": "npm run build --workspace=lib" },
import
workspaces 内のパッケージは npm install しないで使うことができます。 たとえば、lib パッケージが次のようなコードだとします。
export const createGreetingMessage = () => "hello, world"
もちろん、通常のパッケージと同様に、package.json の main
や types
が正しくファイルを指している必要があります。
そうすると、app パッケージから import することができます。
import { createGreetingMessage } from "lib" const message = createGreetingMessage() alert(message)
ワークフロー
今回の例では、環境構築以降は次のような流れで開発を進めます。
app
パッケージの webpack-dev-server を起動- 適宜
lib
パッケージを修正・ビルド (webpack-dev-server が自動的にリロードされ反映される)
lib パッケージの更新が頻繁なら、lib パッケージに watch
のようなスクリプトを追加して、concurrently などで webpack-dev-server と同時に起動するのも良いでしょう。
まとめ
もうルートディレクトリから動かなくて良い!