2021/09/28
はじめに
先日,初めて npm に package を公開した(リンク).
苦戦した点もいくつか合ったので,同じ問題に直面した方のために,記録を残す.
package 内容は README にあるとおり,React Hooks の State で配列の操作を簡単にするカスタムフックである.
ソースコードは TypeScript を用いて開発し,完成物となる package には rollup により es/umd それぞれ作りつつ,もちろん型定義 d.ts も詰め込み,npm に publish した.
方法
以下の順に紹介していく.
- ディレクトリ構成
- 手順
- 各ファイルの内容や意味
ディレクトリ構成
普段の React 開発は./src/
にソースコードを入れるが,今回は最終的に以下のようなディレクトリ構成にした.
.
├── demo
├── dist
│ ├── index.d.ts
│ ├── index.es.js
│ └── index.umd.min.js
├── .gitignore
├── jest.config.js
├── lib
│ ├── index.ts
│ ├── useArrayAction.test.ts
│ ├── useArrayAction.ts
│ ├── useArray.test.ts
│ ├── useArray.ts
│ ├── useArray.types.ts
│ ├── useRecoilArray.test.ts
│ └── useRecoilArray.ts
├── LICENSE
├── package.json
├── README.md
├── rollup.config.js
├── tsconfig.json
└── webpack.config.js
実際に publish する手順
npm への publish 自体は,npm publish
をするだけで簡単に可能である.
publish するための設定はpackage.json
に記載する.
しかし,私の場合そのまま publish してもうまくインポート出来なかった.(TypeScript だから?)
そのため,下で紹介する rollup を実行した後に publish する.
publish するためのコマンドは以下.
$ npm login # if you need
$ npm test # if you need
$ rollup -c
$ npm publish # (--access=public)
ファイルたち
demo
package が動作するデモのソースコードを配置した(普通の ReactApp).
本来は依存をできるだけ少なくして package 使用者が転用できるようにするべきとも思ったが,今回は横着して material-ui を使ってしまった.
デモアプリの公開は Firebase Hosting を使い,リンクをREADME.md
に記載した.
dist
.gitignore
した.
これは,パッケージングされるディレクトリであり,package 使用時にインポートする場所となる.
ちなみに,私の場合はindex.umd.min.js
/index.es.js
/index.d.ts
の 3 ファイルが生成される.
ここをパッケージングするというのは npm のコマンドで指定されているわけではなく,package.json
で指定するため,好みの名前にしても良い.
.gitignore
.gitignore
である.
jest.config.js
テストの設定.
書くと長くなりそうで,かつ,publish するために必須ではないため今回は割愛.
lib
*.test.ts
はテスト.それ以外がライブラリである.
テストは書くと長くなりそうで,かつ,(以下略).
import
や export
は結構好みが分かれると思うが,私の場合は「すべてを持つ Object を default export,それぞれも namede import 可能」という形式をとった.
lib
├── index.ts # 他のファイルから関数をimportしexport,defaultで全部をexport
├── useArrayAction.ts # 再利用性のあるコードなのでファイル分割した
├── useArray.ts # react hooks
├── useArray.types.ts # 好みの問題でファイル分割した
└── useRecoilArray.ts # react hooks
LICENSE
LICENSE
である.
package.json
依存や開発環境依存などが記録されるおなじみのヤツ.
package 用に記載した項目のみ以下に記す.
<your-xxx>
のようになっている部分を好みで書き換えると良い.(テストのための設定は,混乱を避けるために手動で削除した.)
{
"name": "<package-name>",
"description": "<your-description>",
"version": "<your-version(ex: 0.1.0)>",
"author": {
"name": "<your-name>",
"url": "<your-url>"
},
"repository": {
"type": "git",
"url": "<your-repository>"
},
"license": "<your-license>",
"keywords": ["<your-keywords>"],
"main": "dist/index.umd.min.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"files": ["dist"],
"private": false,
"dependencies": {
...
},
"scripts": {
"test": "jest --env=jsdom --verbose",
"build": "rollup -c",
"prepublish": "npm run build",
"publish": "npm publish",
},
"devDependencies": {
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-typescript": "^8.2.5",
"rollup": "^2.57.0",
"rollup-plugin-dts": "^4.0.0",
"rollup-plugin-terser": "^7.0.2",
"tslib": "^2.3.1",
}
}
README.md
package の特徴や使い方,API 一覧などを記入する.
これは,npm の package のページにそのままドキュメントとして扱われつつ,ユーザがインストールした時にライブラリとともにダウンロードされる.
rollup.config.js
開発したライブラリをいい感じにまとめてくれる rollup の設定ファイル.
内容は以下(js で開発したソースのためには,typescript
/dts
を消すと良い).
ちなみに,es_config
/dts_config
/umd_config
については,変数にせずに一つの配列としてdefault export
してもよい.
import typescript from '@rollup/plugin-typescript';
import dts from 'rollup-plugin-dts';
import babel from '@rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser';
const entry = 'lib/index.ts';
const es_config = {
input: entry,
output: {
file: 'dist/index.es.js',
format: 'es',
},
external: ['react', 'recoil'],
plugins: [
typescript(),
babel({
extensions: ['.ts'],
}),
],
};
const dts_config = {
input: entry,
output: {
file: 'dist/index.d.ts',
format: 'es',
},
plugins: [dts()],
};
const umd_config = {
input: entry,
output: {
file: 'dist/index.umd.min.js',
format: 'umd',
name: 'StructuredState',
exports: 'named',
indent: false,
globals: {
react: 'react',
recoil: 'recoil',
},
},
external: ['react', 'recoil'],
plugins: [
typescript(),
babel({
extensions: ['.ts'],
exclude: 'node_modules/**',
}),
terser(),
],
};
export default [es_config, dts_config, umd_config];
tsconfig.json
cra で生成されるやつを活用. 以下の項目を変更. ただし,以下の項目は変更した.
"declaration": false,
webpack.config.js
無くてもいける.
おわりに
最初は TypeScript ファイルをそのまま publish してしまい,謎のエラーを吐いて,焦って unpublish したりと,非常に苦戦した.
unpublish したら 24 時間程度 publish できなくなる点に注意.
参考
開発中にタブを消してしまったので,若干不足しているページもあると思いますが...
- https://dev.to/loonywizardhow-to-publish-typescript-package-to-npm-2k54
- https://github.com/npm/cli/issues958
- https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry
- https://zenn.dev/yuki0410/articles74f80c4243919ea2a247-2
- https://github.com/rollup/rollup-plugin-babel/issues162
- https://github.com/Swatinemrollup-plugin-dts
以上の参考は publish に際しての参考で,本記事の内容は自分のリポジトリを参考にしました.
ちなみに,ライセンス表示の形式は,いい感じ書き方が無いかと模索した結果,react・angular のリポジトリをミックスして書きました.
世界中の素晴らしいコードを自由に閲覧し勉強できるオープンソースは素晴らしいですね.感謝感謝です.