lernaでmonorepo管理

lernaはnpmベースのJavascriptプロジェクトでモノリシックなレポジトリを構築するツールです。

一括ビルド・一括テストなどの共通ワークフローやdevDependenciesに入るビルドツール群の共通化が可能になります。

インストール

パッケージはnpm install lernaでインストールします。管理に用いるlernaコマンドが入るため、必要に応じて-gオプションを付けます。

空のディレクトリでlerna initすると初期ファイルを作成します。
lernaはバージョン管理にgitを利用しており、lerna init実行によりプロジェクトディレクトリはgitリポジトリになります。

空のpackages/ディレクトリに個別のパッケージディレクトリを作成していきます。
lernaは後述のとおり、サブディレクトリ以下のpackage.jsonを参照して動作します。

npmパッケージのdependencies管理

lernaの運用では、プロジェクトトップとpackages以下の個別パッケージごとにpackage.jsonを持つ2層構造になります。

個別のパッケージには従来どおり、必要なnpmライブラリをdependenciesに指定する必要があります。
プロジェクトトップでは、webpackやeslint、puppeteerといったdevDependencies関連ツールを指定すると役割が明確になります。

package.jsonを更新したらnpm installの代わりにlerna bootstrapを実行することでプロジェクト全体のパッケージインストールが実行されます。

また、package.jsonの直接編集以外に、npm install --saveに相当するコマンドとしてlerna addコマンドを利用すればパッケージ横断の一括インストールも可能です。
--devオプションはnpm iの--save-devオプションと同等です。また、一部のパッケージにのみ追加したい場合には、--scopeオプションにターゲットを指定します(ターゲットが複数ある場合は–scopeオプションを羅列)。

パッケージ間参照の初期設定

lerna bootstrapは、package.jsonのnameversion(トップレベル設定項目)を参照してローカルレポジトリの依存性を解決します。

例として、利用する側で以下のような指定のケースを考えます。

"dependencies": {
  "@some-local-repos/common-lib": "^0.0.1",
}

この場合、package.jsonで以下のように宣言しているパッケージをインポートする挙動になります。

{
  "name": "@some-local-repos/common-lib",
  "version": "0.0.1",
}

手動で名前とバージョンを合わせれば、lerna publish未実行の時点でも依性解決が可能です。
lerna bootstrapで呼び出し側のnode_moules/にsymlinkが作成され、以下のようにコードから呼び出せます(ES Modulesの例)。

import {SomeClass} from '@some-local-repos/common-lib/some-class.js'

パッケージ間の依存解決については、 How to connect sibling packages? #200に議論があります。

packages以下の個別npmスクリプトを実行

lerna runnpm runの巡回コマンドです。
プロジェクトトップでlerna run ARGを実行するとpackages/以下の個別パッケージのpackage.jsonを探索し、ARGにマッチしたnpmスクリプトを実行します。

”build”, “install”といった共通ラベルを持つnpmスクリプトを各パッケージで定義しておけば、lerna runで一括ビルドなどが可能になります。

lerna publishとプライベート管理

パッケージのバージョン管理は、lerna publishで実行します。
この過程でgitのリモートリポジトリへのpushとnpm publishが実行され、npmパッケージとして公開する挙動となります。

pakege.jsonのprivate設定や、プライベートレジストリがセットアップされていれば個別の設定が優先されるようです。
また、lerna publishには、--skip-git--skip-npmオプションがあり、各プロセスを回避することも可能です。

プライベートレジストリについては、 Is there a way to use lerna packages without publishing it to npm ? #536に議論があり、参考になります。

まとめ

lernaのセットアップによりmonorepoを構築できます。
セットアップには相応の手間がかかりますが、WebComponentsなどの細かい部品を開発するようなプロジェクトでは、断片化を避け、管理コストを下げられるケースがあります。

公式PRサイトに掲載されているとおり、babelほか実績は多く、大規模オープンソースプロジェクトの実例も参考になります。

中馬崇尋
Chuma Takahiro