npm workspaceを用いて共通処理をpackageとして切り出す
以下のような構成でモノレポではあるものの、それぞれ独立したnodeプロジェクトで運用していた状態から、npm workspaceを用いて共通処理置き場(sharedプロジェクト)を作成したのでその方法を紹介します。
. ├── backend │ ├── package.json │ └── package-lock.json ├── frontend │ ├── next.config.ts │ ├── package.json │ └── package-lock.json └── supabase ├── migrations └── schema.sql
workspace移行後のプロジェクト構成
. ├── backend │ └── package.json ├── frontend │ ├── next.config.ts │ └── package.json ├── node_modules ├── package-lock.json ├── package.json ├── shared │ └── package.json └── supabase ├── migrations └── schema.sql
そもそもnpm workspaceとはという方は以下の記事がすごくわかりやすかったのでおすすめです。
npm workspacesとモノレポ探検記
suinさんのスクラップ
やったこと
プロジェクトルートにworkspace用のpackage.json作成
{ "name": "your-app", "private": true, "workspaces": [ "frontend", "backend", "shared" ], "scripts": { "build": "npm run build --workspaces" }, "devDependencies": { "@types/node": "^22.13.1", "typescript": "^5.7.3" } }
全てのパッケージで利用するようなdependenciesはルートに指定しておくことができます。
nodeのモジュール解決ではホイスティング(hoisting, 巻き上げ)という機能があり、自分のnode_modulesに該当モジュールがない場合は親のnode_modulesに該当モジュールを探しに行くという機能による恩恵です。
sharedプロジェクト作成
プロジェクト構成
. ├── dist ├── package.json ├── src │ ├── date.ts │ └── index.ts └── tsconfig.json
shared/package.json
{ "name": "@your-app/shared", "private": true, "version": "1.0.0", "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "build": "tsc", "watch": "tsc -w" } }
shared/tsconfig.json
{ "compilerOptions": { "target": "es2018", "module": "commonjs", "declaration": true, "outDir": "./dist", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src"], "exclude": ["node_modules", "dist", "**/*.test.ts"] }
shared/index.ts
export * from "./date";
shared/date.ts
export const getDateStringBeforeN = (n: number) => { const today = new Date(); const targetDate = new Date(today); targetDate.setDate(today.getDate() - n); const year = targetDate.getFullYear(); const month = String(targetDate.getMonth() + 1).padStart(2, "0"); const day = String(targetDate.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; };
プロジェクトをbuildしておく
npm run build -w shared
frontend側の修正
sharedをfrontend側の依存に追加する
npm i @your-app/shared -w @you-app/frontend
sharedの機能を利用する
import { getDateStringBeforeN } from "@your-app/shared";
注意点
元々存在していたnode_moduleとpackage-lock.jsonを手動で削除してからルートにて npm install
を実行する必要があります。これを実施しないと以下のようなエラーで共通処理を正しくimportできないと思います。
Module not found: Can't resolve '@your-app/shared'
最後にbuildコマンドでsharedプロジェクトを一緒にbuildされるように修正します。
"scripts": { "build": "npm --workspace=@you-app/shared run build && next build" },
最後に
全てのメソッドがshared/index.tsにまとまって一括でimportする構成はなんか微妙な気がする場合はexportを利用する手もあると思います。
package.jsonのexportsフィールドについて
