はじめに
自作のnpmパッケージを参照するときにエントリポイントを複数公開して、別のパッケージでimport
したかった。しかし、モジュール '***/***/***' またはそれに対応する型宣言が見つかりません。
となり、importに失敗したため、調査結果をメモする。
前提条件
フォルダ構成
- モノレポ構成
- 関係するもののみ表示
. ├── apps │ └── webapp │ └── (省略) ├── package.json ├── packages │ ├── package1 │ │ ├── package.json │ │ ├── src │ │ │ ├── Domain │ │ │ │ ├── Model │ │ │ │ │ ├── Activity.ts │ │ │ │ │ └── index.ts │ │ │ │ └── (省略) │ │ │ └── Util │ │ │ ├── DateUtil.ts │ │ │ └── NumberUtil.ts │ │ └── tsconfig.json │ └── package2 │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── pnpm-lock.yaml └── pnpm-workspace.yaml
実現したいこと
package1
- エントリポイントを公開するnpmパッケージ
// Domain/Model/Activity.ts export class Activity { } // Domain/Model/index.ts(Modelフォルダ内のファイルをまとめてexport) export { Activity } from "./Activity" // Util/NumberUtil.ts export function parse(value: string): number {}
package2
package1
を参照するパッケージ
import { Activity } from "@my-project/pakcage1/model" import { parse } from "@my-project/pakcage1/util/NumberUtil"
エントリポイントの設定
- 複数のエントリポイントを設定するには、package.jsonの
exports
フィールドを使用する - (利用可能なするnode.jsとtypescriptのバージョンは省略)
package1
- エントリポイントを公開するpackage1のpackage.json
- 必要な設定のみ抜粋
- 今回はtypescriptのファイルを直接指定
// package.json { "name": "@my-project/package1", "exports": { "./model": "./src/Domain/Model/index.ts", "./util/*": "./src/Util/*.ts" }, "devDependencies": { "typescript": "^5.0.4" } }
package2
- 参照するpackage2のpackage.jsonは以下
- monorepoのworkspaceを利用
// package.json { "name": "@my-project/package2", "dependencies": { "@my-project/package1": "workspace:*" } }
Typescriptの設定
- 当初、設定時に以下のようなエラーが表示され、うまく
import
できなかった
// ERROR: `モジュール '@my-project/pakcage1/model' またはそれに対応する型宣言が見つかりません。` import { Activity } from "@my-project/pakcage1/model"
- 結論を言うと、参照側(package2)のtsconfig.jsonの設定が問題だった
- 原因
- 変更前に
moduleResolution
の設定をnode
していたため node
はpackage.jsonのexportsをサポートしていない- https://github.com/microsoft/TypeScript/issues/51862#issuecomment-1358049778
- 変更前に
- 修正
node
からnodenext
に変更
package1
- 修正後の状態
- (動作確認のため、ts-nodeを設定してある)
// tsconfig.json { "compilerOptions": { "target": "esnext", "module": "CommonJS", // ts-nodeの実行 "composite": false, "moduleResolution": "nodenext", "paths": { "@/*": ["./src/*"] }, "rootDirs": ["./src", "./test"], "declaration": true, "outDir": "./build", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "noImplicitAny": true, "removeComments": true, "noEmit": false, }, "exclude": [ "test", "node_modules", "build" ], "ts-node": { "esm": true, "experimentalSpecifierResolution": "node" } }
package2
- 修正後の状態
// tsconfig.json { "compilerOptions": { "target": "ESNext", "module": "ESNext", "moduleResolution": "nodenext", // (package.jsonのexportsフィールド) "paths": { "@/*": ["./src/*"] }, "rootDirs": ["./src", "./test"], "declaration": true, "outDir": "./build", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true }, "exclude": [ "node_modules" ] }