🫥

モノリポ(monorepo)での開発について 備忘録

2024/02/23に公開

モノリポとは

chatgpt曰く。。。。

モノリポ monorepoとは?
モノリポは複数のプロジェクトを1つのリポジトリで管理するアプローチです。
この方法は、コードの再利用性を高め、依存関係を一元管理しやすくするなど、多くのメリットがあります。

言葉で説明してもわかりづらいので、このディレクトリ構造図を見てみましょう。

これは僕のモノリポから作ったサンプルのツリーです。

ディレクトリ構造🌲

your-monorepo/
├─ @your-org/ //共有ライブラリ
│  ├─ assets/
│  │  ├─ src/
│  │  ├─ package.json
│  │  └─ README.md
│  ├─ next-app/
│  │  ├─ src/
│  │  ├─ package.json
│  │  └─ README.md
│  └─ .....
├─ apps/ //個別アプリ
│  ├─ project-a/ //個別プロジェクト
│  │  ├─ category-a/ //プロジェクト内のカテゴリ
│  │  │  ├─ app-a/ //個別アプリ
│  │  │  │  ├─ public/
│  │  │  │  ├─ src/
│  │  │  │  ├─ .eslintrc.json
│  │  │  │  ├─ .gitignore
│  │  │  │  ├─ next-env.d.ts
│  │  │  │  ├─ next.config.mjs
│  │  │  │  ├─ package.json
│  │  │  │  ├─ README.md
│  │  │  │  └─ tsconfig.json
│  │  │  └─ app-b/
│  │  │     ├─ public/
│  │  │     ├─ src/
│  │  │     ├─ .eslintrc.json
│  │  │     ├─ .gitignore
│  │  │     ├─ next-env.d.ts
│  │  │     ├─ next.config.mjs
│  │  │     ├─ package.json
│  │  │     ├─ README.md
│  │  │     └─ tsconfig.json
│  │  └─ category-b/
│  │     └─ [同様のアプリケーション構造]
│  └─ project-b/
│     ├─ category-a/
│     │  └─ [同様のアプリケーション構造]
│     └─ category-b/
│        └─ [同様のアプリケーション構造]
├─ node_modules/
├─ .gitignore
├─ package-lock.json
├─ package.json
├─ README.md
└─ tsconfig.json

つまりyour-monorepoという単一のディレクトリの中に複数のnextjsアプリを作っていくことが可能なのです。

良い点

良い点、というかこういう開発環境を求めていて、全然知らなくて損したな、という感じなんですが。

上の例では、your-monorepo の中に@your-appappsというフォルダを作っています。

@your-app には、apps内の複数のnextjsアプリで共有して使えるライブラリを作っていくことができます。現状では@your-app/next-app があって、これは自分のnextjsアプリで共通して使う雛形を入れています。

これまでは、自分でライブラリをまとめても、コードを使い回すためにnotionにおいておくか、過去に作ったプロジェクトから持ってこないとダメだったのが、モノリポ内で共有することができるのです。そして、別のアプリを作っているときに独自のライブラリを変更しても、常に全てのアプリで同じ状態のライブラリが使えるのです。

初期設定方法

まずは、ローカルにディレクトリを作ります。

例えばyour-monorepo

これが、モノリポのルートになるのです。

●npm init

your-monorepo をvscodeで開きます。

そして以下のコマンドを実行します。単純に、npm initです。

npm init -y

●package.jsonの設定

次にpackage.jsonの設定です。

{
  "name": "your-monorepo",
  "version": "1.0.0",
  "private": true,
  "workspaces": [
    "apps/*",
    "apps/*/*",
    "apps/*/*/*",
    "@your-app/*"
  ],
  "scripts": {
    "build": "next build",
    "b": "next build",
    "start": "next start",
    "s": "next start",
    "lint": "next lint",
    "dev": "run-p dev:next dev:scss",
    "d": "run-p dev:next dev:scss",
    "dev:next": "next dev",
    "dev:scss": "typed-scss-modules src --watch",
    "typegen:scss": "typed-scss-modules src"
  },
  "dependencies": {
    "@vercel/edge": "^1.1.1",
    "next": "14.1.0",
    "react": "^18",
    "react-dom": "^18",
    "sass": "^1.71.0"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "eslint": "^8",
    "eslint-config-next": "14.1.0",
    "npm-run-all": "^4.1.5",
    "typed-css-modules": "^0.9.1",
    "typed-scss-modules": "^8.0.0",
    "typescript": "^5"
  }
}

scripts dependencies devDependencies は現状こんな感じ、ということなので、各々よしなに。

注目すべきはworkspaces

ここで、どのディレクトリを個別のアプリとして認識するかを決めます。 僕の場合は@youre-app 配下に独自ライブラリ、apps 配下に個別のアプリを作っていくことにしています。

この部分です。

"workspaces": [
    "apps/*",
    "apps/*/*",
    "apps/*/*/*",
    "@your-app/*"
  ],

  • ``というのは全て という意味で、apps/*というのはapps/以下の全てのディレクトリ という意味です。

また、

"apps/*/*", "apps/*/*/*",

も設定しています。

配下のアプリも階層グループ分けしていくことがあるので、apps/以下の全ての階層/以下の全ての階層/以下の全ての階層 ということになっています。

●新しい個別アプリapps/your-app-nameを作る方法

新しい個別アプリを作るには、そのアプリを配置するディレクトリに入る、もしくはそのディレクトリをvscodeで開いて、以下のコマンドを実行します。

cd apps/
npx create-next-app@latest your-app-name —use-npm —no-git

前半は普通のnextjsの作り方ですが、最後の—no-git は「.git を生成しない」という指定

ちなみに.gitignoreは必要っぽい。

.next/などを不要不適切なものをgitにアップしないために。

●新しいライブラリ@your-app/your-lib-name を作る方法

これに関しては、なんだか正確じゃない気もするのですが、

基本は個別アプリを作る方法と同じです。

cd @your-app/
npx create-next-app@latest your-lib-name —use-npm —no-git

ライブラリはnextアプリじゃないので、エントリポイントを自分でpackage.jsonに書きます。

package.json

{
  "name": "@your-app/your-lib-name",
  "version": "0.1.0",
  "private": true,
  "main": "index.ts",
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "react": "^18",
    "react-dom": "^18",
    "next": "14.1.0"
  },
  "devDependencies": {
    "typescript": "^5",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "eslint": "^8",
    "eslint-config-next": "14.1.0"
  }
}

このjsonの"main": "index.ts", この部分です。 "index.ts" 内では、以下のようにライブラリ内の全ての機能をインポート/エクスポートします。

import * as Components from "./components/Components";
import * as Utils from "./utils/Utils";
import * as Const from "./constants/Constants";
import * as Functions from "./utils/Functions/Functions";
import * as Styles from "./styles/Styles";

export { Components, Utils, Const, Functions, Styles };

@your-app/your-lib-nameのライブラリの読み出し方

独自ライブラリを個別のアプリにインストールする方法です。

ここまでの設定がきちんと済んでいれば、以下のように普通のnpm installの方法で可能です。

cd ./apps/your-app-name //インストールしたいアプリに移動する
npm i @your-app/your-lib-name //普通のインストール

@your-app/your-lib-name はライブラリのpackage.jsonnameで設定している名前です。


Tips & Trouble Shoot

node_modules/はルートのみに存在する

モノレポでは、node_modules/はルート(ここではyour-app/)にのみ存在します。

なので、個別アプリの中にnode_modules/が存在しちゃっている場合は、モノレポのpackage.json のworkspaces にその個別アプリのディレクトリが指定されていない可能性があります。その場合は、きちんとpackage.json のworkspaces に該当する個別アプリのディレクトリを指定しましょう。apps/*みたいな書き方がよくわからないのであればapps/your-app-nameというようにきちんと指定することも可能です。

vercelでのデプロイ🌐

ここまでの設定がうまくいっていれば、vercelでデプロイする際のプロジェクトを作る時の画面で、root directoryをどこにするか、自動でvercelが確認してくれます。

その他は、普通のvercelのデプロイ方法と同じです。

別のライブラリをライブラリから使用する

例えば@your-app/assetsにアセット類をまとめて、@your-app/next-appから使いたい場合。

●scssのfont-faceのurlとして使いたい。

@font-face {
  font-family: "Helvetica Neue LT W05 25 Ult Lt";
  src: url("~@your-app/assets/fonts/xxxxxx/ec6281a0-c9c4-4477-a360-156acd53093f.woff2")
      format("woff2"),
    url("~@your-app/assets/fonts/xxxxxx/11066b40-10f7-4123-ba58-d9cbf5e89ceb.woff")
      format("woff");
  unicode-range: U+0030-0039, U+0041-007A, U+0025-00FF, U+0021, U+2381, U+0332,
    U+FF3F, U+005F, U+0022, U+0027, U+2018, U+2019, U+201C, U+201D, U+0023,
    U+FF03, U+0024, U+FE69, U+FF04, U+1F4B2, U+00B7, U+0387, U+2022, U+2219,
    U+22C5, U+30FB, U+FF65, U+0080 –U + 00FF, U+2122;
}
// ちなみにこれは合成フォントを実現するためのscss

url部分で”@your-app/assets/*********”とするとパスが解決されない。

こういう場合は”~@your-app/assets/*********” みたいに~ を最初に持ってくる必要があるみたい。


sakamoto-appを作る上で便利だなと思ったもの。

npm i typed-scss-modules —save-dev

これは、*.module.scssの型定義*.d.tsを自動で生成してくれるもの

これによってscssモジュールの使い回しが楽になり、重複を減らせる。

設定ファイルtyped-scss-modules.config.ts はモノレポ./ に置いちゃっていいと思う。多分。

typed-scss-modules.config.ts

export const config = {
  exportType: "default",
  nameFormat: "none",
  implementation: "sass",
};


npm i npm-run-all —save-dev

これはnpmコマンドを複数自動で実行できるようにするもの。

npm run dev した後に 自動でtyped-scss-modules src --watchを実行する。みたいな

package.jsonではこんな感じに設定する。

Discussion