【Node.js】lernaとnpmのworkspacesでMonorepoな環境を構築する
Node.jsでlernaとnpmのworkspacesを使ってMonorepoな環境を構築したい!
概要
今回の記事では、Node.jsでlernaとnpmのworkspacesを使ってMonorepoな環境を構築する手順を掲載する。
Monorepoとは、複数のプロジェクトを単一のリポジトリで管理する方法。共通の処理があったり、複数のアプリを合わせて、1つのサービスとするような感じのプロジェクト郡の管理に便利だったり。
lernaはバージョン管理やパッケージの公開に強い。以前は、Monorepoなパッケージ管理機能を持ってたんだけども。後述するnpmのworkspacesなどの登場でそれらを使うことを推奨してて現在のバージョンでは関連するコマンドが使えなくなってる。ちなみにReactやJestにも採用されてるらしい。
npmのworkspacesではMonorepoなパッケージ管理機能(共通の外部パッケージの効率的な管理や管理してるプロジェクト同士の依存関係の解決など)やプロジェクト管理を行うことができる。
仕様書
環境
- Node.js 20.15.0
- npm 10.8.1
- lerna 8.1.8
手順書
lernaのプロジェクトを作るディレクトリーの中でlernaをインストールする。
npm install -d lerna
lernaでプロジェクトを初期化する。
npx lerna init
my_workspaceがプロジェクトだとして下記のような感じでディレクトリーとファイルが作られる。
my_workspace
node_module
.gitgnore
lerna.json
package-lock.json
package.json
lerna.jsonの中身を確認する。
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "0.0.0",
"packages": [
"packages/*"
]
}
packagesのpackages/*がlernaで管理するプロジェクトの保管場所になるのでディレクトリーpackagesを作って、この中にプロジェクトを作ったり、移動したりする。
package.jsonにもworkspacesを追加する。書き方はlerna.jsonと同じ感じ。
{
"name": "my_workspace"
"private": true,
"workspaces": [
"packages/*"
],
"description": ""
}
公開する予定もないので"private": trueも追記しておく。
管理してるプロジェクト同士の依存関係の簡単な設定の例
構成がこんな感じになってるとして
my_workspace
packages
main_app
admin_app
main_appとadmin_appがあって、admin_appがmain_appに依存してるとする場合、はadmin_appのpackage.jsonのdependenciesの中に"main_app": "^1.0.0"を追加する。
{
"name": "admin_app",
"version": "1.0.0",
"private": true,
...
"dependencies": {
...
"main_app": "*"
},
...
}
もしくはコマンドを使う。
npm install main_app --workspace=admin_app
admin_appからmain_app/src/share/my_compornent.tsxとmain_app/src/share/logo.pngをインポートする例。
main_app
src
share
index.ts
logo.png
MyCompornent.tsx
main_app/src/share/MyCompornent.tsxがこんな感じになってるとする。
const MyCompornent = () => {
return (<h1>MyCompornent</h1>);
};
export default MyCompornent;
main_app/src/share/index.tsがエクスポートに使うファイルになる。
export * from './MyCompornent';
import logo from './logo.png';
export const Logo = logo;
export { default as MyCompornent } from './MyCompornent';
main_app/package.jsonにexportsを設定する。
{
"name": "main_app",
...
"exports": {
"./share": "./src/share/index.ts",
},
...
}
インポートする側admin_appでは下記のような感じで使えるようになる。
import { MyCompornent, Logo } from 'main_app/share';
トラブルシューティング
lernaでプロジェクトを初期化する際に下記のようなエラーが発生する場合
npx lerna init
lerna ERR! Cannot initialize lerna because your package manager has not been configured to use `workspaces`, and you have not explicitly specified any packages to operate on
lerna ERR! See https://lerna.js.org/docs/getting-started#adding-lerna-to-an-existing-repo for how to resolve this
既に作成されたプロジェクトを含んでる状態で初期化しようとすると発生することがある。その場合は、管理したいプロジェクトがあるディレクトリーをオプションで指定して初期化する。
ディレクトリーがひとつの場合。
npx lerna init --packages="packages/*"
ディレクトリーが複数ある場合。
npx lerna init --packages="packages1/*" --packages="packages2/*"
まとめ(感想文)
メインのアプリがあって、管理アプリなどを別のプロジェクトで作る場合や共通のコードがある場合に使えるかもね!
参考文献・引用
下記の記事を参考にさせて頂きました。ありがとうございました。
