たかぎとねこの忘備録

プログラミングに関する忘備録を自分用に残しときます。マサカリ怖い。

Lernaを使ってバラバラのExpoのプロジェクトとFirebaseのプロジェクトをmonorepoにしてみる

僕の場合、Expoでアプリを開発する時は、Firebaseを使うことがほとんどである。認証周りとかFirestoreとか便利だし。

ただ、それぞれ別々のリポジトリとして管理していると、いちいちフォルダ間の移動が大変だったりどこにどのファイルを作ったかを忘れたりと面倒なことが多々起こる。ファイルを探すのに指を使うのはエネルギーを消費するので、極力疲れることは避けたい。

なので、monorepoとして管理できたら、たびたび発生するめんどうくさい問題は解決するのではないかと考えた。

いろいろ調べた結果Lernaを使うと良いらしいことがわかったので、今回はLernaを用いてExpoとFirebaseのプロジェクトをひとつのmonorepoとしてまとめて管理する方法を、将来の自分のために残しておこうと思う。

作業環境

MacBook Air (13-inch, Mid 2012)

macOS Catalina 10.15.7

注意事項

expo-yarn-workspaceを使ってexpoをmonorepoに対応させているため、Windowsだとうまく動かない可能性が出てくると思う。ご注意を。

セットアップ

まずはじめに、monorepo用のディレクトリを作成して中に移動する。 そのディレクトリ内でlernaをインストールする。 そして、Independent modeでinitを実行する。

$ mkdir lerna-repo && cd $_

# /
$ yarn add --dev lerna
$ yarn lerna init --independent

ルートディレクトリにあるpackage.jsonを編集する。

# /package.json
{
    "name": "lerna-repo",
    "private": true,
    "workspaces": [
        "packages/*"
    ],
    "devDependencies": {
        "lerna": "^4.0.0"
    }
}

次に、ルートディレクトリにあるlerna.jsonを編集する。 Indepdendentモードでinitを実行したので、versionがindependentという値になっている。 デフォルトのモードだと0.0.0になっている。

# /lerna.json
{
    "packages": [
        "packages/*"
    ],
    "version": "independent",
        "npmClient": "yarn",
        "useWorkspaces": true,
        "command": {
            "publish": {
                "ignoreChanges": ["*.md"],
                "message": "chore: publish"
            }
        }
}

.gitignoreファイルをここいらで作成しておく。

# /
$ touch .gitignore
# /.gitignore
node_modules/
commands/*
.env

# macOS
.DS_Store

Expoプロジェクト

続いてexpoプロジェクトをpackages配下に置くために、packagesフォルダに移動する。

expo initコマンドを実行してexpoプロジェクトを作成する。 もしくは、すでにリモートリポジトリにexpoプロジェクトがある場合は、それをクローンする。 終わったら、expoプロジェクト内に移動して、.gitフォルダを削除する。

# /
$ cd packages

# /packages/
$ expo init

# もしくは

$ git clone git@github.com:username/expo-project.git
$ cd ./expo-project

# /packages/expo-project/
$ rm -rf .git

package.jsonに、nameversionフィールドを追加する。

# packages/expo-project/package.json
{
    "name": "expo-project",
    "version": "0.0.0",
    ...
}

expoのプロジェクトをmonorepoに対応させるために、expo-yarn-workspacesパッケージをインストールする。

# /packages/expo-project
$ yarn install
$ yarn add --dev expo-yarn-workspaces

package.jsonpostinstallスクリプトを追記する。 同時に、mainの内容を書き換え、expo-yarn-workspacesの設定も追記する。

# /packages/expo-project/package.json
{
    ...
    "main": "__generated__/AppEntry.js",
    "scripts": {
            "start": "expo start --clear",
            ...
            "postinstall": "expo-yarn-workspaces postinstall"
         },
    ...
    "expo-yarn-workspaces": {
            "symlinks": ["expo-modules-core"]
         }
}

metro.config.jsを作成する。

# /packages/expo-project/metro.config.js
const { createMetroConfiguration } = require('expo-yarn-workspaces');

module.exports = createMetroConfiguration(__dirname);

実際にyarn run startを実行してみて動くことを確認する。 動かない場合は、metro.config.jsがあるか、package.json内のmainが、___generated___/AppEntry.jsを指しているか等を確認する。

Firebase プロジェクト

ルートディレクトリ内で、Firebaseプロジェクトを作成する。 すでにリモートリポジトリがある場合は、それをクローンする。

# /
npm install -g firebase-tools
firebase login
firebase init

## もしくは

git clone git@github.com:username/firebase-project.git
cd firebase-project

# /firebase-project
rm -rf .git
cd ../

packages配下にfunctionsフォルダを移動する。 リモートリポジトリをクローンした場合は、設定ファイルをルートディレクトリに移動させる。

# /
mv functions packages

## もしくは

mv firebase-project/functions packages

### 設定ファイルをルートディレクトリに移動させる。
mv firebase-project/.firebaserc .firebaserc
mv firebase-project/firebase.json firebase.json
mv firebase-project/firestore.indexes.json firestore.indexes.json
mv firebase-project/firestore.rules firestore.rules
mv firebase-project/storage.rules storage.rules

firebase.jsonを編集する。 functionsの設定にsourceフィールドを追加して、現在のfunctionsのパスを指定する。

# /firebase.json
{
  "functions": {
    "source": "packages/functions"
  }
}

packages/functions内に移動して、tsconfig.jsonを編集する。 具体的には、typeRootsの中身を修正してルートディレクトリにあるnode_modules/@typesを参照するに設定する。

/packages/functions/tsconfig.json
{
    "compilerOptions": {
      ...
    "typeRoots": ["./types", "../../node_modules/@types"],
  },
}

functionspackage.jsonに、nameフィールドとversionフィールドを加える。

# /packages/functions/package.json
{
  "name": "functions",
  "version": "0.0.0",
    ...
}

packages/functions内で、yarn installを実行する。そしてビルドを実行する。

# /
$ cd packages/functions
# /packages/functions/
$ yarn install
$ yarn build

最後に、デプロイできることを確認する

# /packages/functions/
$ firebase deploy --only functions

おわりに

いままでも、Lernaを使ってぐちゃぐちゃになっているexpo関連のプロジェクトをまとめてしまって、monorepoとして管理しようと試みたことはあった。 ただそのたびに、忙しい等の言い訳をして避けてきていたので、今回心にゆとりがあったこともあり、挑戦してみた。

実際にやってみると、思っていたほどむずかしいことはほとんどなかった。 煩雑だった複数プロジェクトの管理は楽になるし、GitHub Actionsの設定も特段変更する必要はほとんどなかったので、いいことづくめだということがわかった。

よかったよかった。

参考にした記事

expo/packages/expo-yarn-workspaces at master · expo/expo · GitHub

GitHub - lerna/lerna: A tool for managing JavaScript projects with multiple packages.

LernaとYarn WorkspacesでMonorepo管理 - Cybozu Inside Out | サイボウズエンジニアのブログ

git - Can you have additional .gitignore per directory within a single repo? - Stack Overflow

Structuring a Firebase web project with Lerna | by João Teixeira | Firelayer | Medium