lernaを使ってmonorepoなリポジトリを作ってみた

## lerna とは？

[lerna/lerna: A tool for managing JavaScript projects with multiple packages.](https://github.com/lerna/lerna)

が何ものかというと、単一のリポジトリで複数の npm module 開発を可能にするツールです。

たとえば、

1. [babel/babel: ?? Babel is a compiler for writing next generation JavaScript.](https://github.com/babel/babel)
1. [facebook/create-react-app: Set up a modern web app by running one command.](https://github.com/facebook/create-react-app)
1. [nuxt/nuxt.js: The Vue.js Developers Framework](https://github.com/nuxt/nuxt.js)

このあたりの有名なリポジトリが lerna を採用していて、もう面影がないですが、

[facebook/react: A declarative, efficient, and flexible JavaScript library for building user interfaces.](https://github.com/facebook/react)

react も同じディレクトリ構成をしています。（脱 lerna でもしたのでしょうか）


## monorepo の長所と短所

[babel/monorepo.md at master ・ babel/babel](https://github.com/babel/babel/blob/master/doc/design/monorepo.md)

からの引用ですが、

    Why is Babel a monorepo?

    Pros:

        * Single lint, build, test and release process.
        * Easy to coordinate changes across modules.
        * Single place to report issues.
        * Easier to setup a development environment.
        * Tests across modules are run together which finds bugs that touch multiple modules easier.

    Cons:

        * Codebase looks more intimidating.
        * Repo is bigger in size.
        * Can't npm install modules directly from GitHub
        * ???

このあたりはひじょうに痛感していて、依存し合う2つの npm module が別々のリポジトリだとテストをどっちに書こうかなどいろいろと悩みがあります。

そういった部分をマージしましょうというお話ですね。ただし、 Cons もそこそこあると。


## lerna のベースとなる環境を作る

[lerna/commands/init at master ・ lerna/lerna](https://github.com/lerna/lerna/tree/master/commands/init#readme)

```bash
$ lerna init
```

これで以下のように必須のファイル・ディレクトリたちが作られる。
これは `npm init` や `yarn init` の **lerna 版** だと思います。

    lerna-repo/
      packages/
      package.json
      lerna.json

## publish したい npm module の雛形を create する


```bash
$ lerna create lemon-sour
```

```bash
$ lerna create cli
```

上記のように実行すると、 packages の下にこのようにディレクトリが配置される。

```
packages
├─cli
│  │  package.json
│  │  README.md
│  │
│  ├─lib
│  │      cli.js
│  │
│  └─__tests__
│          cli.test.js
│
└─lemon-sour
    │  package.json
    │  README.md
    │
    ├─lib
    │      lemon-sour.js
    │
    └─__tests__
            lemon-sour.test.js
```

ローカルパッケージ cli を lemon-sour から参照するよう追加する。

```bash
$ lerna add cli --scope lemon-sour
```

`lerna add` は yarn add と同じコマンドだが、 `--scope` を指定いないとすべての packages に追加されてしまう。

なので、たてば lemon-sour だけに lodash を入れたい場合は、

```bash
$ lerna add lodash --scope lemon-sour
```

となる、このあたりは慣れるしかないんでしょうね。

また、 `run test` を実行すると、すべての package で script:test を実行してくれる。

```bash
$ lerna run test
```

さらに lerna コマンドは、どの階層にいても実行できるので、 create は ROOT に行ってから実行しないと、みたいなものがない。

## lerna と yarn を一緒に使う場合

そもそも **lerna** を使うと `npm install` や `yarn add` のようなものを直接使うことはできない。

なので、 yarn を使うには、 lerna.json を編集する必要がる。

```json
{
  "npmClient": "yarn",
  "useWorkspaces": true,
  "packages": [
    "packages/*"
  ],
  "version": "0.0.0"
}
```

ROOT の package.json に workspaces の key を追加する。

```json
{
  "name": "root",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "devDependencies": {
    "lerna": "^3.8.5"
  }
}
```

これで yarn workspaces が使える状態が整いました。

[ワークスペース | Yarn](https://yarnpkg.com/lang/ja/docs/workspaces/)

では試しに packages 全体に typescript を入れて、 lerna で build してみましょう。

```bash
$ lerna add typescript --dev
```

これで packages 個別に typescript がインストールされましたが、面白いことに packages のほうの node_modules のほうには typescript の link しかありません。

これは、同じバージョンの typescript 2つの npm module にインストールされましたが、それは2つ別々に配置するのが無駄なので、 ROOT のほうの node_modules に引き上げられます。


## add した module を remove する方法

どうやら、これがまだないらしく、つまり `yarn remove` 相当のものはありません。

[npm uninstall throws 404 for unpublished siblings ・ Issue #1229 ・ lerna/lerna](https://github.com/lerna/lerna/issues/1229)

今後追加されていくようです。

なので、 editor で package.json を修正して、以下のコマンドを実行する必要があります。


```bash
$ lerna clean // node_modules たちを消す
$ lerna bootstrap // yarn 相当
```

## publish する

```bash
$ lerna publish
```

これで、 lerna.json に書かれてるバージョンが上がって各 npm module たちが push されていきます。

## 今回のサンプルコード

[hisasann/lerna-npm-sample](https://github.com/hisasann/lerna-npm-sample)

[hisasann/lerna-yarn-sample](https://github.com/hisasann/lerna-yarn-sample)


## 読んだ記事

[Lerna を使って、 Babel や React が採用している monorepo を試してみる - Qiita](https://qiita.com/kimamula/items/0b4dff363933bfe74506#monorepo-%E3%81%A8%E3%81%AF)

[Lerna はまりポイント - Qiita](https://qiita.com/karak/items/13830871cc22904d9aa8)

[Lernaを使ってFirebase環境のためのモノレポ環境一式をカッコよく構築する - Qiita](https://qiita.com/kyasbal_1994/items/e9c5a918f4d2ddf10a28)

[lernaを使ってnpmプロジェクトをモノレポ化する - つくりおき](https://92thunder.hatenablog.com/entry/2018/05/30/222440)

[橋本商会 ≫ lernaでmonorepoした](http://shokai.org/blog/archives/10574)

[単一リポジトリで複数package|projectを管理することをmonorepoというそう - なっく日報](https://yukidarake.hateblo.jp/entry/2016/01/06/200800)

[Support Yarn workspaces to replace bootstrap command by bestander ・ Pull Request #899 ・ lerna/lerna](https://github.com/lerna/lerna/pull/899)