サブディレクトリから import する npm のパッケージを作る
February 3, 2022
import の from にある /(スラッシュ) の意味 #
npm のパッケージを import する時に from の中でスラッシュを記述するものがある。例えば Firebase SDK はこのパターン。
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
import { getFunctions } from 'firebase/functions';
これはサブディレクトリから import している。
- 参考
実際に node_modules
の中身を除いてみると、firebase
パッケージは次のような構造になっていることがわかる。
└ node_modules/
└ firebase/
├ app/
│ ├ dist/
│ └ package.json
├ auth/
│ ├ dist/
│ └ package.json
├ firestore/
│ ├ dist/
│ └ package.json
└ functions/
├ dist/
└ package.json
TypeScript で作ると dist や lib が入ってしまう #
dist
や lib
といったディレクトリにビルド結果を出力し、これを npm publish する。
そうすると、パッケージ利用者が install した際にも実際のコードが dist
や lib
配下に入ってしまう。
パッケージ開発者側 #
src/
└ src/
├ foo/
│ └ index.ts
└ bar/
└ index.ts
tsconfig.json
{
"compilerOptions": {
"outDir": "./dist"
}
}
package.json
{
"files": ["dist"]
}
パッケージ利用者側 #
node_modules
└ node_modules/
└ example-package/
├ package.json
└ dist/
├ foo/
│ ├ index.js
│ └ index.d.ts
└ bar/
├ index.js
└ index.d.ts
foo
や bar
のサブディレクトリから import するには、あいだに dist
を記載しないといけなくなる。
import foo from 'example-package/dist/foo';
import bar from 'example-package/dist/bar';
上記ではなく dist
は除いた形で import できるようにしたい。
dist をパッケージのルートとして npm publish したい #
同様の質問をしている人がいた。
How to npm publish specific folder but as package root - Stack Overflow
結局のところ、これを解決する方法を npm が提供してはいないため、皆それぞれの方法で実現している様子。
以下では自分なりの簡易的な解決方法を提示する。
対応方法 #
src
配下の foo
と bar
ディレクトリに資産があり、ビルド結果は dist
ディレクトリに出力される想定とする。
└ src/
├ foo/
│ └ index.ts
└ bar/
└ index.ts
1. .npmignore を作成 #
.npmignore
を以下の内容で作成する。
*
!*/
!/foo/**
!/bar/**
2. package.json を更新 #
package.json
から以下を削除する。
{
"files": ["dist"]
}
package.json
に以下を追加する。
{
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build && cp -r ./dist/* . && rm -rf ./dist",
"postpublish": "git clean -fd"
}
}
3. npm publish #
あとは publish するだけ。
npm publish
説明 #
まず prepublishOnly
で dist
配下に資産が作られ、続くコマンドでそれらをルートディレクトリに移動させる。
そして publish する。package.json
の files
を削除したことで、プロジェクトに含まれるすべてが publish 対象となっているが、ルートディレクトリにある設定ファイルなどは publish したくない。
そこで .npmignore
を利用する。まずすべての資産を ignore 対象に指定し、それを上書きするように foo
と bar
のみを ignore から外す。
そのため、最終的にはルートディレクトリに直下の foo
と bar
のみが publish される。
publish が終わった後は postpublish
が動き、ルートディレクトリ直下に一時的に作成された foo
と bar
が削除される。
結果 #
これによりパッケージ利用者が install した際は、以下のように install される。
node_modules
└ node_modules/
└ example-package/
├ package.json
├ foo/
│ ├ index.js
│ └ index.d.ts
└ bar/
├ index.js
└ index.d.ts
これで以下のように使用することができる。
import foo from 'example-package/foo';
import bar from 'example-package/bar';