🧰 ES モジュール
前章までで Node.js 環境で利用できるライブラリを作成できるようになったわけですが、Node.js モジュールシステム (= CommonJS
) からだけではなく、TypeScript の ES Modules システム(以下、ESM
)からも利用できるようにするには、ESM 形式のライブラリも用意する必要があります。
また、序章で述べたように CLI
としての実行ファイルも作成しなければなりません。
tsconfig.json
を作成する
ESM 出力用の プロジェクトフォルダ直下へ tsconfig.esm.json
を作成します。
{
"extends": ".",
"compilerOptions": {
"module": "ESNext",
"outDir": "dist/esm",
"declaration": true,
"declarationMap": true
},
"include": ["src"]
}
"extends": "."
で基本的には ./tsconfig.json
での設定を継承します。また、ESNext
形式のモジュールを dist/esm
ディレクトリへ出力し、併せて型定義ファイルとそのソースマップも出力します。
便利ツールのインストール
ESM 向けのビルド工程が増えるため、これらを整理するためのユーティリティをインストールしましょう。
% npm i -D npm-run-all
npm-run-all
では、複数の NPM スクリプトのタスクをコントロールできます。
-
run-s
: NPM スクリプトを 順番に 実行します。 -
run-p
: NPM スクリプトを 並列に 実行します。
NPM スクリプトのアップデート
この tsconfig.esm.json
を読み込んだビルドができるように NPM スクリプトを更新します。
"scripts": {
"prebuild": "rimraf dist",
"build": "run-p build:*",
"build:common": "tsc",
"build:esm": "tsc -p tsconfig.esm.json"
},
package.json
へ ESM 向けエントリを追加
ESM として読み込まれた場合に、呼出先へモジュールとその型定義ファイルの場所を提示するようにエントリを追加します。
"main": "dist/index.js",
"module": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
ビルドふたたび
では、さっそく再ビルドしてみましょう。
% npm run build
> my-hello-lib@1.0.0 prebuild
> rimraf dist
> my-hello-lib@1.0.0 build
> run-p build:*
> my-hello-lib@1.0.0 build:esm
> tsc -p tsconfig.esm.json
> my-hello-lib@1.0.0 build:common
> tsc
dist
ディレクトリへ Node.js 向けライブラリと ESM 向けライブラリの両方が出力されたことを確認しましょう。
% tree -a -I 'node_modules'
.
├── dist
│ ├── esm
│ │ ├── index.d.ts
│ │ ├── index.d.ts.map
│ │ └── index.js
│ └── index.js
├── package-lock.json
├── package.json
├── src
│ └── index.ts
├── tsconfig.esm.json
└── tsconfig.json
3 directories, 9 files
🧨 bin
CLI から下のように実行すると Hello.
と出力されるコマンドライン向けインターフェイスも用意します。
% npx my-hello-lib
Hello.
実行ファイルの作成
src
へ bin
ディレクトリを作成し、index.ts
を配置します。
% mkdir -p src/bin
% touch src/bin/index.ts
index.ts
は以下のような Node.js 向けスクリプトとします。
#!/usr/bin/env node
import { hello } from '..';
hello();
package.json
のアップデート
CLI
として呼び出されたときに実行するファイルの場所を bin
エントリに指定します。
"bin": {
"my-hello-lib": "dist/bin/index.js"
},
tsconfig.esm.json
のアップデート
ESM 向けのビルドでこの bin
フォルダを無視するよう tsconfig.esm.json
へ exclude
を指定します。
{
"extends": ".",
"compilerOptions": {
"module": "ESNext",
"outDir": "dist/esm",
"declaration": true,
"declarationMap": true
},
"include": ["src"],
"exclude": ["src/bin", "node_modules", "dist"]
}
exclude
指定の注意点
tsconfig.json
では、デフォルトの状態、つまり exclude
を指定しない場合には、自動的に以下のファイル・フォルダを無視します。
node_modules
-
outDir
で指定されたフォルダ内のファイル
しかし exclude
に 1 つでもフォルダ/ファイルを指定すると、このデフォルト設定は上書きされてしまいます。
そこで、ここでは src/bin
を除外するために node_modules
と dist
も付け加えています。
ビルド三たび
再ビルドしてみましょう。
% npm run build
> my-hello-lib@1.0.0 prebuild
> rimraf dist
> my-hello-lib@1.0.0 build
> run-p build:*
> my-hello-lib@1.0.0 build:common
> tsc
> my-hello-lib@1.0.0 build:esm
> tsc -p tsconfig.esm.json
以下のようなファイル/フォルダ構成となります。
% tree -I 'node_modules'
.
├── dist
│ ├── bin
│ │ └── index.js
│ ├── esm
│ │ ├── index.d.ts
│ │ ├── index.d.ts.map
│ │ └── index.js
│ └── index.js
├── package-lock.json
├── package.json
├── src
│ ├── bin
│ │ └── index.ts
│ └── index.ts
├── tsconfig.esm.json
└── tsconfig.json
5 directories, 11 files
📦 配布物(NPM パッケージ)の内容を確認する
パッケージングするコマンドを --dry-run
オプション(実際には何も出力しない)付きで実行しましょう。
% npm pack --dry-run
npm notice
npm notice 📦 my-hello-lib@1.0.0
npm notice === Tarball Contents ===
npm notice 137B dist/bin/index.js
npm notice 75B dist/esm/index.d.ts
npm notice 142B dist/esm/index.d.ts.map
npm notice 59B dist/esm/index.js
npm notice 176B dist/index.js
npm notice 510B package.json
npm notice === Tarball Details ===
npm notice name: my-hello-lib
npm notice version: 1.0.0
npm notice filename: my-hello-lib-1.0.0.tgz
npm notice package size: 748 B
npm notice unpacked size: 1.1 kB
npm notice shasum: dd0d4c477346e0d8657399d6dc79d296721613d8
npm notice integrity: sha512-IwSgzMz7xLSV0[...]d8l4+SBihGBVw==
npm notice total files: 6
npm notice
my-hello-lib-1.0.0.tgz
ここまでで my-hello-lib パッケージ
の作成はいったん完了です。