はじめに
たとえば、下記のような相互依存関係のあるプロジェクト群をJavaScriptで作りたい場合が普通にあるかと思います。
start-lerna/
packages/
common/ (Moment.jsに依存)
childA/ (../common に依存)
childB/ (../common, lodash に依存)
本記事で、このようなプロジェクトを lerna を使って構築・管理してみます。あまり高度な使い方には触れません。というか、私自身も初心者なので、触れられません。 (^^;)
なお、パッケージ管理に yarn を使います。別に使わなくてもできます。
この記事で使用しているソースコードは https://github.com/knjname/2018-07_start-lerna にて利用可能です。
lerna プロジェクト(親プロジェクト) を作る
# プロジェクトフォルダを作る
$ mkdir start-lerna
$ cd start-lerna
# lernaの初期化
$ yarn add -D lerna
$ yarn lerna init
...
lerna success Initialized Lerna files
# 中身の確認 (勝手に .git フォルダも作られている)
$ ls -a
.git
lerna.json
package.json
packages
パッケージ管理にyarn を使うので、 lerna.json
の npmClient
キーに "yarn"
を指定しましょう。
"npmClient": "yarn",
子プロジェクトを作る
lerna のルールとしては、 親プロジェクト/packages/子プロジェクトたち
という場所に子プロジェクトを置くのがデフォルトなので、それに反しないように作っていきます。
common
プロジェクト
$ cd packages
$ mkdir common
$ cd common
# 下記コマンドで version=1.0.0, main: index.js な package.json ができるはず
$ yarn init
# Moment.js への依存性追加
$ yarn add moment
const moment = require("moment");
module.exports = function (name) {
return "Hello, " + name + ". I am common module you're depending on. And it's " + moment(new Date()).format("h:mm") + " now.";
}
childA
プロジェクト (兄弟プロジェクトへの依存性を持っているプロジェクト)
$ cd packages
$ mkdir childA
$ cd childA
$ yarn init
手作業で package.json
に common
への依存性を追加しましょう。
"dependencies": {
"common": "^1.0.0"
}
common
のモジュールを使ったスクリプトも追加しましょう。
const common = require("common");
console.log(common("knjname"));
上記の common
への依存性がある(そして推移的に Moment.js に依存性がある) childA
を実際に動かしてみましょう。
# 子プロジェクトの依存性解決
$ yarn lerna bootstrap
$ cd packages/childA
$ node index.js
Hello, knjname. I am common module you're depending on. And it's 8:41 now.
ちゃんと common
への依存性も、 common
自体の依存性(= moment
) も解決した状態で実行されましたね。
ちょっと common/index.js
の内容を変更してみましょう。
const moment = require("moment");
module.exports = function (name) {
return "Good bye, " + name + ". I am common module you had depended on. And it was " + moment(new Date()).format("h:mm") + " now.";
}
そうすると、特に何もしなくても変更に追従してくれますね。
$ node index.js
Hello, knjname. I am common module you're depending on. And it's 8:41 now.
packages/childA/node_modules/common
が ../../common
へのシンボリックリンクになっているからです。
$ ls -al packages/childA/node_modules
common -> ../../common
childB
プロジェクト (兄弟プロジェクトと lodash
への依存性を持っているプロジェクト)
今度は兄弟プロジェクトへも、独自 lodash
にも依存性を持っているプロジェクトを作成してみます。
$ cd packages
$ mkdir childB
$ cd childB
$ yarn init
common
への依存性をまず足そうと思うのですが、前回のように手で package.json
に追加することはせず、 lerna
コマンドでやってみます。
$ yarn lerna add common --scope=childB
...
lerna success Bootstrapped 1 packages
✨ Done in 0.84s.
このコマンドで childB/package.json
に依存性エントリが追加されます。
"dependencies": {
"common": "^1.0.0"
}
更に lodash
への依存性も追加しましょう。
$ yarn lerna add lodash --scope=childB
:
:
lerna success Bootstrapped 1 packages
✨ Done in 0.84s.
同じように childB/package.json
に依存性エントリが追加されるのですが、 childB/yarn.lock
ファイルも自動生成されます。ありがたい。
"dependencies": {
"common": "^1.0.0",
"lodash": "^4.17.10"
}
lodash@^4.17.10:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
もちろんこれら依存関係を用いたスクリプトを作らないと意味がありませんね。
const common = require("common");
const _ = require("lodash")
console.log(_.map(["foo", "bar", "baz"], function (elem) {
return common(elem)
}))
$ node index.js
[ 'Good bye, foo. I am common module you had depended on. And it was 8:59 now.',
'Good bye, bar. I am common module you had depended on. And it was 8:59 now.',
'Good bye, baz. I am common module you had depended on. And it was 8:59 now.' ]
ちゃんと動きました!
プロジェクトを初期からインストールさせてみる
上記で簡単なプロジェクト構成を作成しましたが、当然 node_modules
などはバージョン管理には入れないので、他人がプロジェクトをチェックアウトした際に数コマンドでセットアップできないと困ったことになります。
きちんとできるか、検証してみましょう。
# まずは node_modules を全消ししてみる
$ rm -rf node_modules packages/*/node_modules
# lerna 自体(親プロジェクト自体)のインストール
$ yarn
# lerna による全プロジェクトの依存性インストール
$ yarn lerna bootstrap
...
lerna success Bootstrapped 3 packages
✨ Done in 2.21s.
ちゃんとできました!
おわりに
以上のやり方で小規模なプロジェクトは運用できると思います。たとえば、ソースコード生成系のプロジェクトを lerna を使って分離するなどの手法を取ることができます。
もっと高度な使い方は 公式ドキュメント を見ましょう。