モノレポでいこう!~yarn workspacesを使ってみた~


モノレポとは

概要

単一のリポジトリで複数のプロジェクトを管理することをモノレポという。
その分リポジトリの内容は複雑になるので、そこそこ運用知識が必要となる。

メリット、デメリット

モノレポのメリット、デメリットは下記のようなものが挙げられる。

メリット

依存パッケージの管理をまとめてできる
共有リソースを有効に利用する
npm scriptの一括操作

デメリット

Issue や PR をすべて 1 つのリポジトリで管理する必要がある
Monorepo での開発ワークフローを理解している必要がある

モノレポリポジトリ作成

今回の背景

  • Next.jsでHeadressCMSなプロジェクトを作成する
  • 検証環境(SSR)、本番環境(SSG)とする
  • リポジトリ1つに検証環境と本番環境をまとめて管理したい(リポジトリは分けない)
  • ホステインング環境はAWS Amplifyを使用する

パッケージ管理ツール選定

パッケージ管理ツールとしてNx、Lernaなど機能がリッチなものもあるが、今回は検証用であり、そこまで高度なことは求めていないので、より低レベルな機能を持つyarn workspacesを使用する。

yarn workspaces

Yarn1.0以降でデフォルトで使用できるパッケージアーキテクチャを設定するための新しい手法。
下記のようなことが可能。

  • 複数 npm パッケージを単一リポジトリで一元管理
  • yarn install コマンドを使った Monorepo すべての依存パッケージを一括インストール
  • yarn workspaces コマンドを使った Monorepo で管理しているパッケージが持っている npm-scripts の一括実行
  • 単一のyarn.lockファイルですべての依存関係を管理

参考:https://classic.yarnpkg.com/lang/en/docs/workspaces/

手順

  1. リポジトリのルートにpackage.json(プライベート・パッケージ)を作成
    yarn init -p

 1-1. 下記のようなpackage.jsonが作成される

package.json
{
  "name": "monorepo",
  "version": "1.0.0",
  "license": "UNLICENSED",
  "private": true
}

 1-2. 作成するワークスペースを定義する

/packages配下をglobパターンで指定する

package.json
{
  "name": "monorepo",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "packages/*"
  ]
}

2.リポジトリのルートにpackagesディレクトリを作成

├── /packages
└── package.json

3.ワークスペースを作成
 3-1. packagesディレクトリ配下にcommon(共通ライブラリ)を作成

├── /packages
│   └── /common
└── package.json

 3-2. commonにpackage.jsonを作成
cd packages/common && yarn init

├── /packages
│   └── /common
│     └── package.json
└── package.json

nameがワークスペース名となる

common/package.json
{
  "name": "@projects/common",
  "version": "1.0.0",
  "main": "index.ts"
}

 3-3. packagesディレクトリ配下にprd(Next.js本番環境)を作成
yarn create next-app /packages/prd

├── /packages
│   └── /common
│   │  └── package.json
│   └── /prd
│      └── package.json
└── package.json

 3-4. ワークスペース名を編集する

prd/package.json
{
  "name": "@projects/prd",
  "version": "1.0.0"
  ## 以下省略
}

 3-5. packagesディレクトリ配下にstg(Next.js検証環境)を作成
yarn create next-app /packages/stg

├── /packages
│   ├── /common
│   │  └── package.json
│   ├── /prd
│   │  └── package.json
│   ├── /stg
│      └── package.json
└── package.json

 3-6. ワークスペース名を編集する

stg/package.json
{
  "name": "@projects/stg",
  "version": "1.0.0"
  ## 以下省略
}

4.common(共通ライブラリ)をprd、stgから参照できるようにする

package.json
{
 ## 以上省略
  "dependencies": {
    "@projects/common": "*"
  }
  ## 以下省略
}

5.各ワークスペースのpackage.jsonのパッケージをルートのpackage.jsonに転記する

※Amplifyの仕様上、dependeciesnextは各Next.jsプロジェクトのルート直下のpackage.jsonに記述されている必要がある。(Amplify側がNext.jsプロジェクトかの判別に使っていると思われる)

package.json
{
 ## 以上省略
  "dependencies": {
    "@projects/common": "*",
    ## 各ワークスペースのパッケージを転記する
  },
 "devDependencies": {
    ## 各ワークスペースのパッケージを転記する
  }
  ## 以下省略
}

6.各scriptを記述する
リポジトリのルートから各ワークスペースのscriptの実行、またはワークスペース全てにscript実行できるようにpackage.jsonにscriptを記述する。

package.json
{
  "scripts": {
    "build-all": "yarn workspace @project/prd build && yarn workspace @project/stg build",
    "build:prd": "yarn workspace @project/prd build",
    "build:stg": "yarn workspace @project/stg build",
    "dev-all": "run-p dev:**",
    "dev:prd": "yarn workspace @project/prd dev",
    "dev:stg": "yarn workspace @project/stg dev",
    "start-all": "run-p start:**",
    "start:prd": "yarn workspace @project/prd start:local",
    "start:stg": "yarn workspace @project/stg start:local",
    "lint-all": "yarn workspaces run lint",
    "lint:prd": "yarn workspace @project/prd lint",
    "lint:stg": "yarn workspace @project/stg lint",
    "lint:common": "yarn workspace @project/common lint"
  }
}

Amplifyでのモノレポ運用

TODO 後ほど記述予定です