📦

備忘録: npm workspace によるモノレポ設定 と sharedパッケージ

に公開

0. ディレクトリ構造

記事内では、以下のディレクトリ構造を想定しています。

project-root
  ├─ packages
   ├─ frontend
   ├─ package.json
   └─ tsconfig.json
   ├─ backend
   ├─ package.json
   └─ tsconfig.json
   └─ shared
       ├─ package.json
       └─ tsconfig.json
  └─ package.json
  

1. プロジェクトルートの初期化

ルート直下の package.json で workspaces を定義し、プロジェクト全体の依存関係を管理します。

  • コマンド
cd [your-root-dir]
npm init
  • package.json (ルートディレクトリ)
{
  "name": "your-app-name",
  // ...
  "workspaces": [
    "packages/*"
  ],
  // ...
}

※ packages以外のディレクトリ名でも可


パッケージがネストしている場合、こうなります

"workspaces": [
  "packages/*/*"
]

2. 各パッケージの作成

一例として、frontend, backend, shared の3つを作成します。

  • コマンド
cd [path-to-target-package]
# 例: cd ./packages/shared

npm初期化

npm init

3. 参照されるパッケージの設定(ここではsharedのこと)

  • package.json (sharedディレクトリ)
{
  "name": "@pkg/shared",
  "version": "0.0.1"
  "main": "./dist/index.js",  // ビルド成果物のエントリーポイント
  "types": "./src/index.ts",  // コーディング時のエントリーポイント
  // ...
}

4. 依存関係の定義

frontend や backend の package.json で、shared を依存先に指定します。

  • package.json (依存する側の各パッケージ)
{
  "dependencies": {
    "@pkg/shared": "*"  // sharedをパッケージとして利用する
  }
}
  • コマンド (プロジェクトルート)
npm install
# これによって、@pkg/shared がインストールされる

5. tsc初期化

  • コマンド (各パッケージ)
npm install typescript

npx tsc init
#または
npx tsc --init

6. tsconfigを編集設定

  • tsconfig.json (shared)
{
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "composite": true,   // "declaration": true を設定する際に必要になることがある
    "declaration": true, // .d.ts を出力する場合trueにする
    // ...
  },
  "include": ["src"]
}

各項目の意図

"outDir" ビルド成果物の出力先
"rootDir" ソースディレクトリ
"include" tscがコンパイルするファイルを指定
  • tsconfig.json (FE/BE)
{
  "compilerOptions": {
    "paths": {
      "@pkg/shared": ["<path-to-shared>/src/index.ts"]
      // 例: ../shared/src/index.ts
      // ↑これがないとtscがパス解決できません
    }
  },
  "references": [
    { "path": "../shared/" }
  ]
}

7. npmコマンド例

  • package.json (プロジェクトルート)
{
  // ...
  "scripts": {
    "syncDb": "npm run syncDb -w packages/backend",
    "build:shared":   "npm run build -w packages/shared",
    "build:frontend": "npm run build -w packages/frontend",
    "build:backend":  "npm run build -w packages/backend",
    "start:backend":  "npm run start -w packages/backend",
    "dev:frontend":   "npm run dev -w packages/frontend",
    // ...
    "tsc:frontend":   "npm run tsc -w packages/frontend",
    "tsc:backend":    "npm run tsc -w packages/backend"
  },
  • コマンドを使用
$project-root> npm run tsc:frontend
  # → "npm run tsc -w packages/frontend" を呼び出し

  # → frontendの "npm run tsc" が実行される

  :. npx tsc -b --noemit

※ 実行されているのは以下の部分です。

// packages/frontend/package.json より抜粋
{
  "scripts": {
    "tsc": "npx tsc -b --noemit"
  }
}

想定されるエラーと対処法

エラー

tsconfig.json
ファイル 'c:/project-root/app/packages/backend/index.ts' が 'rootDir' 'c:/project-root/packages/backend/src' の下にありません。'rootDir' にすべてにソース ファイルが含まれている必要があります。
  ファイルがプログラム内に存在します。理由:
    既定で一致するインクルード パターン '**/*'
  'c:/Users/Fujishu/Desktop/Document/portfolios/ai/sdd-tutorial-todo-app/packages/backend/package.json' には値 "module" のフィールド "type" があるため、ファイルは ECMAScript モジュールです。

対処

このエラーは、src内にすべてのソースコードが収まっていないということを意味しています。

  1. tsconfig

最初に疑うのは tsconfig の設定ミスです。
依存元と依存先の両方に設定漏れがないかを確認します。

一番疑わしいものとして、依存関係の指定ミスがあります。

tsconfig.json
{
  "compilerOptions": {
    // ...
    "paths": {
      "@pkg/shared": ["../shared/src/index.ts"]
    },
  },
  // ↓references を追加する
  "references": [
    { "path": "../shared/" }
  ]
}

"paths: {...}"はパス解決をしてくれますが、「依存パッケージだからsrcには入っていない」 ということを伝えない限りエラーは消えません。

そこで、"references"を合わせて使うことで、sharedを外部パッケージとして認識させることができます。

  1. ディレクトリ構造

それでも一切問題が見つからない場合、ディレクトリ構成がおかしい可能性を疑います。

実際、私はディレクトリ構造を間違えてこのエラーが発生しました。

# 求める構造
backend
  - src
    - index.ts
# 今回の設定にそぐわない構造
backend
  - index.ts

Discussion