【30分以内で】新しいReact Hooksをnpmパッケージとして公開した話

npmパッケージの作成と公開/更新に便利（なはずの）boilerplateを作ったので
以前作ったuseStackRefをnpmパッケージとして公開してみる

最終結果
https://github.com/nariakiiwatani/react-use-stack-ref
https://www.npmjs.com/package/react-use-stack-ref


この記事にしたがって進めていく
https://qiita.com/nariakiiwatani/items/ac8acb1822ead4dfc429

## ダウンロード
[dev-npm-package-react-typescript-boilerplate](https://github.com/nariakiiwatani/dev-npm-package-react-typescript-boilerplate)をZIPでダウンロードして適当なフォルダに解凍する。

## `package.json`を編集

```json:package.json(編集部分のみ記載)
  "name": "react-use-stack-ref",
  "author": "nariakiiwatani <nariakiiwatani@annolab.com> (https://github.com/nariakiiwatani)",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/nariakiiwatani/react-use-stack-ref"
  },

```

## 開発環境のセットアップ

あとで`yarn run setup:demo`をやることになるのでここは単に`yarn run setup`でも良かった

```
yarn run setup:all

yarn run v1.22.4
$ yarn run setup && yarn setup:demo
$ yarn install
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
$ cd demo && yarn install
warning package.json: No license field
warning No license field
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
✨  Done in 15.39s.
```

## `src`フォルダ内を編集

1. `ModuleFile.ts`削除
1. `useStackRef.ts`を作成
1. `index.js`と`index.d.ts`を修正

```javascript:index.js(index.d.tsも同じ)
import { useStackRef } from './dist/useStackRef'
export { useStackRef }
```

## ビルドが通るか確認

```
yarn run build

yarn run v1.22.4
$ tsc
✨  Done in 1.74s.
```
`dist`フォルダにビルド結果が出力されていることを確認

<img width="396" alt="スクリーンショット 2020-08-12 16.19.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/9099e87b-ea0c-4ebe-92f1-d93ff30d09e2.png">

## コミットしておく

ここで、編集を始めるより前に初期状態をコミットしておいた方が良かったと後悔する

```shell
git init
git add .
git commit -m "initial commit"

[master (root-commit) 5ab56bf] initial commit including new module 'useStackRef'
 19 files changed, 8973 insertions(+)
 create mode 100644 .github/workflows/pages.yml
 create mode 100644 .github/workflows/release.yml
 create mode 100644 .gitignore
 create mode 100644 LICENSE.md
 create mode 100644 README.md
 create mode 100644 demo/.gitignore
 create mode 100644 demo/dist/index.html
 create mode 100644 demo/package.json
 create mode 100644 demo/src/components/App.tsx
 create mode 100644 demo/src/index.tsx
 create mode 100644 demo/tsconfig.json
 create mode 100644 demo/webpack.config.js
 create mode 100644 demo/yarn.lock
 create mode 100644 index.d.ts
 create mode 100644 index.js
 create mode 100644 package.json
 create mode 100644 src/useStackRef.ts
 create mode 100644 tsconfig.json
 create mode 100644 yarn.lock
```

## デモページのセットアップ

```json:demo/package.json(変更箇所のみ記載)
  "dependencies": {
	"react-use-stack-ref": "link:../"
  },
```

```tsx:demo/src/components/App.tsx(変更箇所のみ記載)
import { useStackRef } from 'react-use-stack-ref'
```

```
yarn run setup:demo

yarn run v1.22.4
$ cd demo && yarn install
warning package.json: No license field
warning No license field
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...
success Saved lockfile.
✨  Done in 9.92s.
```

## デモページのビルドを確認

```
yarn run build:demo

yarn run v1.22.4
$ cd demo && yarn run build
warning package.json: No license field
$ webpack
Hash: 60733be7b91f6c1ca0c1
Version: webpack 4.44.1
Time: 2151ms
Built at: 2020-08-12 16:28:08
        Asset      Size  Chunks                   Chunk Names
    bundle.js   947 KiB    main  [emitted]        main
bundle.js.map  1.08 MiB    main  [emitted] [dev]  main
Entrypoint main = bundle.js bundle.js.map
[0] multi ./src/index.tsx 28 bytes {main} [built]
[./src/index.tsx] 179 bytes {main} [built]
    + 12 hidden modules
✨  Done in 3.35s.
```

`demo/dist`フォルダに結果が出力される

<img width="417" alt="スクリーンショット 2020-08-12 16.28.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/b48199ba-82e9-0cd3-0641-f047b187157d.png">

## 開発用サーバーでデモページが動作することを確認する

```
yarn run dev

yarn run v1.22.4
$ yarn run build
$ tsc
$ cd demo && yarn run start
warning package.json: No license field
$ webpack-dev-server --open
ℹ ｢wds｣: Project is running at http://localhost:8080/
ℹ ｢wds｣: webpack output is served from /
ℹ ｢wds｣: Content not from webpack is served from ./dist
ℹ ｢wdm｣: wait until bundle finished: /
ℹ ｢wdm｣: Hash: e51f0a781a90c74c170f
Version: webpack 4.44.1
Time: 2518ms
Built at: 2020-08-12 16:29:27
        Asset      Size  Chunks                   Chunk Names
    bundle.js  1.26 MiB    main  [emitted]        main
bundle.js.map  1.46 MiB    main  [emitted] [dev]  main
Entrypoint main = bundle.js bundle.js.map
[0] multi (webpack)-dev-server/client?http://localhost:8080 ./src/index.tsx 40 bytes {main} [built]
[../node_modules/react-dom/cjs/react-dom.development.js] 839 KiB {main} [built]
[../node_modules/react-dom/index.js] 1.33 KiB {main} [built]
[../node_modules/react/index.js] 190 bytes {main} [built]
[./node_modules/webpack-dev-server/client/index.js?http://localhost:8080] (webpack)-dev-server/client?http://localhost:8080 4.29 KiB {main} [built]
[./node_modules/webpack-dev-server/client/overlay.js] (webpack)-dev-server/client/overlay.js 3.51 KiB {main} [built]
[./node_modules/webpack-dev-server/client/socket.js] (webpack)-dev-server/client/socket.js 1.53 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/createSocketUrl.js] (webpack)-dev-server/client/utils/createSocketUrl.js 2.91 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/log.js] (webpack)-dev-server/client/utils/log.js 964 bytes {main} [built]
[./node_modules/webpack-dev-server/client/utils/reloadApp.js] (webpack)-dev-server/client/utils/reloadApp.js 1.59 KiB {main} [built]
[./node_modules/webpack-dev-server/client/utils/sendMessage.js] (webpack)-dev-server/client/utils/sendMessage.js 402 bytes {main} [built]
[./node_modules/webpack-dev-server/node_modules/strip-ansi/index.js] (webpack)-dev-server/node_modules/strip-ansi/index.js 161 bytes {main} [built]
[./node_modules/webpack/hot sync ^\.\/log$] (webpack)/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built]
[./src/components/App.tsx] 135 bytes {main} [built]
[./src/index.tsx] 179 bytes {main} [built]
    + 31 hidden modules
ℹ ｢wdm｣: Compiled successfully.
```

空っぽのページがブラウザで表示される。コンソールにエラーもなし。

<img width="639" alt="スクリーンショット 2020-08-12 16.30.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/c120bf23-8332-fe62-0127-b94733500ea7.png">


## デモページを作成

```tsx:demo/src/component/App.tsx

import React, { useState, useCallback, ChangeEvent } from 'react'
import { useStackRef } from 'react-use-stack-ref'

const App = (props: {}) => {
	const stack = useStackRef()
	const [input, setInput] = useState("")
	const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
		setInput(e.target.value)
	}
	const handlePush = useCallback(e => {
		stack.push(input)
		setInput("")
	}, [stack])
	const handlePop = useCallback(e => {
		const value = stack.pop()
		value && setInput(value as string)
	}, [stack])
	return (<>
		<h1>live demo for useStackRef</h1>
		<input type="text" value={input} onChange={handleChange} />
		<button onClick={handlePush} disabled={input.length === 0}>push</button>
		<button onClick={handlePop} disabled={stack.size === 0} >pop</button>
		<div>current value: {stack.value}</div>
		<div>stack depth: {stack.size}</div>
	</>)
}

export default App;
```

動作確認

<img width="482" alt="スクリーンショット 2020-08-12 16.44.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/e2709148-8999-5cec-33c9-489d940c2ade.png">


## コミットしておく

```
git add .
git commit -m "create demo"

[master 5d27f8f] create demo
 3 files changed, 34 insertions(+), 14 deletions(-)
 rewrite demo/src/components/App.tsx (65%)
```

## GitHubリポジトリを設定

react-use-stack-refという名前のリポジトリを作成

npmのサイトでnpmトークンを`Read and Publish`の権限で作成
クリップボードにコピーしておく

<img width="1140" alt="スクリーンショット 2020-08-12 16.48.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/1c95a2cd-c7ef-5245-2577-581f0fbe2f67.png">

GitHubリポジトリの`Settings -> Secrets`に新たな変数を追加
`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`の部分はコピーしたトークン文字列

<img width="486" alt="スクリーンショット 2020-08-12 16.47.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/639a255d-56ec-d940-216b-a062ccc857de.png">

追加後の画面

<img width="1400" alt="スクリーンショット 2020-08-12 16.47.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/21b2950a-6236-e941-e930-559ac9ab5c60.png">

##  `README.md`を編集してコミット

```
git add .
git commit -m "update readme"

[master 5c9508f] update readme
 1 file changed, 37 insertions(+), 95 deletions(-)
 rewrite README.md (99%)
```

## `git push`

```
git remote add origin git@github.com:nariakiiwatani/react-use-stack-ref.git
git push -u origin master

Enumerating objects: 38, done.
Counting objects: 100% (38/38), done.
Delta compression using up to 8 threads
Compressing objects: 100% (31/31), done.
Writing objects: 100% (38/38), 148.32 KiB | 898.00 KiB/s, done.
Total 38 (delta 5), reused 0 (delta 0)
remote: Resolving deltas: 100% (5/5), done.
To github.com:nariakiiwatani/react-use-stack-ref.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
```

## GitHub Pagesの設定

`Publish demo page`というGitHub Actionが自動で実行されて、デモページのビルド結果が`gh-pages`ブランチのpushされる。
するとリポジトリの`Settings -> Options -> GitHub Pages`で`gh-pages`が選べるようになるので選択して、ディレクトリは`/(root)`のままで`Save`ボタンを押す

<img width="929" alt="スクリーンショット 2020-08-12 16.57.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/f6fbddee-5c68-8e06-686b-9168cd42195e.png">

## 動作確認

公開用URL( https://nariakiiwatani.github.io/react-use-stack-ref/ )でデモページが動いていることを確認

<img width="508" alt="スクリーンショット 2020-08-12 16.59.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/ee905eb8-c283-a63b-e250-71f9de4ebf56.png">

## デモのページをREADMEに追記しておく

`[live demo](https://nariakiiwatani.github.io/react-use-stack-ref)`

```
git add .
git commit -m "add live demo url in readme"

[master f156883] add live demo url in readme
 1 file changed, 2 insertions(+)
```

```
git push

Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 361 bytes | 361.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:nariakiiwatani/react-use-stack-ref.git
   5c9508f..f156883  master -> master
```

## npmのサイトに行ってパッケージが公開されていることを確認

...ない！

<img width="1144" alt="スクリーンショット 2020-08-12 17.05.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/b005ed77-a59a-ab5c-f753-b0aef4584453.png">

### GitHub Actionのログを確認

semactic-releaseが新しいバージョンがないと判断してpublishしなかったらしい

```
[8:02:52 AM] [semantic-release] › ✔  Completed step "analyzeCommits" of plugin "@semantic-release/commit-analyzer"
[8:02:52 AM] [semantic-release] › ℹ  There are no relevant changes, so no new version is released.
```

### semantic-releaseのために何かコミットしてpush

READMEにnpmパッケージページへのリンクを記載した。
コミットメッセージを"fix:"で始めるのを忘れない。

```
git add .
git commit -m "fix: add npm site in readme"

[master fbb5d05] fix: add npm site in readme
 1 file changed, 2 insertions(+)
```

```
git push

Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 336 bytes | 336.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:nariakiiwatani/react-use-stack-ref.git
   f156883..fbb5d05  master -> master
```

公開確認！

<img width="1135" alt="スクリーンショット 2020-08-12 17.08.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16556/10778885-b2ab-8494-6a86-3cc97ec2f369.png">

# 終わりに

ということで、`semantic-release`の仕様を忘れていた以外は特につまづくことなく公開できました。
今回はデモページの作成やこの記事のための記録があってトータルで30〜40分くらいかかりましたが、新しいパッケージの開発環境構築と公開に限って言えば余裕で30分を切るのではないでしょうか。

みなさんもどうぞ自分のライブラリを公開する際は使ってみてください。

[dev-npm-package-react-typescript-boilerplate](https://github.com/nariakiiwatani/dev-npm-package-react-typescript-boilerplate)