Yarn Workspacesを利用したMonorepo環境の構築

こんにちは、フロントエンドエンジニアのやなぎ(@apple_yagi)です。

今年(2022年)の4月頃に、PR TIMESのフロントエンド開発基盤の構築を行い、各プロジェクトのリポジトリに散らばっていたReactで実装しなおした Frontend のコードベースを一つのリポジトリに集約することができました。

あわせて読みたい
PR TIMESにおけるフロントエンド開発基盤の構築
PR TIMESにおけるフロントエンド開発基盤の構築こんにちは、21新卒エンジニアの柳(@apple_yagi)です。今月から新卒2年目となり、一年早かったなとしみじみしています。昨年PR TIMESでは企業ページをフルスクラッチでR...

しかし、この時点では Frontend のコードベースを一つにまとめただけで、各プロジェクトで共通のコンポーネントやスタイルを一元管理するまでには至っていませんでした。

今回、Yarn Workspacesを利用してMonorepoを構築し、共通コンポーネント・スタイルの共有を行ったのでご紹介します。

本エントリーでは、Yarn Workspacesの機能を使用して、1つのリポジトリで複数のプロジェクトを管理している構成のことをMonorepoと指します。

目次

Monorepoで達成したいこと

現状のフロントエンドのディレクトリ構成は以下のようになっており、appsディレクトリ配下に2つのReactで構築されているWebアプリケーションが存在しています。

.
├── .githooks
├── .github
├── apps
│   ├── app1
│   │   ├── package.json
│   │   ├── src/
│   │   ├── tsconfig.json
│   │   ├── webpack.config.js
│   │   └── yarn.lock
│   └── app2
│       ├── package.json
│       ├── src/
│       ├── tsconfig.json
│       ├── webpack.config.js
│       └── yarn.lock
├── .eslintrc.json
├── .prettierrc.json
├── Makefile
├── package.json
├── README.md
└── yarn.lock

※ 例としてapps配下のディレクトリ名はapp1, app2としています

現状のディレクトリ構造は一見Monorepoのような構成になっていますが、app1とapp2で共通のコンポーネントやスタイルは各アプリケーションごとに別々で作成されています。

PR TIMESでは昨年からデザインシステムを構築し、サービス全体で使用するコンポーネントやスタイルの統一を図っていましたが、それらのコンポーネントやスタイルは各アプリケーションで別々に実装されており、二重管理されていました。

あわせて読みたい
PR TIMESのデザインシステム構築のステップを振り返り
PR TIMESのデザインシステム構築のステップを振り返り長年の運営でルールがばらばらの状況から、一歩ずつデザインシステムを整備した『PR TIMES流デザインシステムのつくりかた』をご紹介いたします。プロセスを公開することで同じような状況のチームのお役に立てれば嬉しいです。

さらに、各アプリケーションで保持しているデザインシステムのコンポーネントは、若干実装がズレており、デザインシステムの運用方法として良くない方向に進んでいました。

そこで、デザインシステムのコンポーネントとスタイルを共通実装にまとめ、各アプリケーションに配布することを目標に、Monorepoの構築に取り組みました。

Yarn Workspacesを選んだ理由

タイトルの通り、今回Monorepoを構築するためにYarn Workspacesを使用しました。理由は以下の通りです。

  • 元々パッケージマネージャーにYarnを使用しており導入が簡単だった
  • 現状の達成したいことがデザインシステムで定義されているコンポーネント・スタイルの共有であり、Yarn Workspacesでも十分に達成することが可能だった

Monorepoを構築するためのビルドツールとして、NxTurborepoなどがありますが、今回達成したいことに対してToo Machと思ったため導入を見送りました。

今後、Yarn Workspacesで満たせない要件が起きたとき、またそのときの要件と時代にあったツールを選定したいと思います。

Monorepoの構築

以下のステップで、Yarn Workspacesを利用してMonorepoを構築を行いました。

Step1. ディレクトリ構成の変更

現状のディレクトリ構成では各アプリケーション内にデザインシステムのリソースが埋まってしまっているため、まずは独立したパッケージとしてデザインシステムのリソースを配置するようなディレクトリ構成に変更しました。

変更後のディレクトリ構成は以下の通りです。

.
├── .githooks
├── .github
├── apps
│   ├── app1
│   │   ├── package.json
│   │   ├── src/
│   │   ├── tsconfig.json
│   │   ├── webpack.config.js
│   │   └── yarn.lock
│   └── app2
│       ├── package.json
│       ├── src/
│       ├── tsconfig.json
│       ├── webpack.config.js
│       └── yarn.lock
├── packages
|   ├── design-system    <-- デザインシステム用のコンポーネント、スタイルをまとめたパッケージ
│   │   ├── package.json
│   │   ├── src/
│   │   |   ├── Button.tsx
│   │   |   └── styles
│   │   |       └── global.css
│   │   ├── tsconfig.json
│   │   └── yarn.lock
|   └── tsconfig
|       ├── package.json
|       └── base.json
├── .eslintrc.json
├── .prettierrc.json
├── Makefile
├── package.json
├── README.md
└── yarn.lock

変更後のディレクトリ構成ではapps配下にアプリケーションのnpmパッケージを、packages配下にデザインシステムのリソースや、各パッケージである程度共通化しておきたいtsconfigの設定を入れるようにしています。

本構成は、vercel/turborepoのexamplesで紹介されている構成を参考にしています。

GitHub
turborepo/examples/basic at main · vercel/turborepo
turborepo/examples/basic at main · vercel/turborepoThe High-performance Build System for JavaScript & TypeScript Codebases - turborepo/examples/basic at main · vercel/turborepo

Step2. 各package.jsonにworkspacesのための設定を追加する

Yarn Workspacesを利用するには、リポジトリのルート直下にある package.json にworkspacesを追加する必要があるので、そちらの設定を行います。

今回のディレクトリ構成では以下のような設定を追加します。

{
	"workspaces": [
    "apps/*",
    "packages/*"
  ]
}

次にpackages配下のdesign-systemとtsconfigのディレクトリ内にpackage.jsonを作成し、nameとversionを指定します。

{
	"name": "@prtimes/design-system",
	"version": "1.0.0"
}
{
	"name": "@prtimes/tsconfig",
	"version": "1.0.0"
}

その後、apps配下にある各アプリケーションのpackage.jsonのdependenciesに@prtimes/design-systemや@prtimes/tsconfigを追加します。

{
	"dependencies": {
		// versionにワイルドカードを指定し、バージョンを意識せずに管理する
		"@prtimes/design-system": "*"
	},
	"devDependencies": {
		"@prtimes/tsconfig": "*"
	}
}

これにより、@prtimes/design-systemを参照することでデザインシステムのコンポーネントやスタイルを各アプリケーションで利用することが可能になります。

import { Button } from "@prtimes/design-system";
import "@prtimes/design-system/styles/global.css"

export const App1Button = () => {
  return <Button>App1 Button</Button>;
}

また、共通のtsconfigの設定も以下のように利用できます。

{
	// ベースとなるtsconfigの設定をextendする
  "extends": "@prtimes/tsconfig/base.json",
	// 各アプリケーション内のtsconfigでさらに設定を追加することもできる
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "exclude": ["./dist"]
}

まとめ

今回、Monorepo環境を構築したことにより、各アプリケーション上で別々に管理されていたデザインシステムのリソースを一箇所にまとめることができ、二重でデザインシステムを管理する手間や、実装のズレなどを改善することができました。

PR TIMESにReactが導入されてからまだ一年も経過していませんが、今回の取り組みや、数日前に投稿された PR TIMES フロントエンドの React 18 バージョンアップの取り組み のように、PR TIMESのフロントエンドチームでは先手先手で負債の解消に取り組んでいます。

また、アドバイザーとして入って頂いている 1000chさん からアドバイスをもらいながら、今後のPR TIMESのフロントエンドの展望についても活発に議論しています。

今後もさらなる発展を遂げるPR TIMESのフロントエンドに乞うご期待ください。

この記事を書いた人

株式会社PR TIMES 開発本部 フロントエンドエンジニア

目次
閉じる