GitHubのIssueコメントをGitHub上にストックするためのChrome拡張作りました！

<p align="center">
  <a href="https://chrome.google.com/webstore/detail/github-stocked-comments/hgnmlljbpcdmllcmelmaadkkcknloclf?hl=ja&gl=JP">
    <img alt="github-stocked-comments" src="https://user-images.githubusercontent.com/11146767/54084647-b0a02d00-4376-11e9-831c-acd093aa4bd2.png" width="546">
  </a>
</p>

## 作ったもの

ナビバーに追加した `Stocked Comments` というリンクを押すとストックしたIssueコメントが以下のように表示されるものを作りました。

これGitHubの標準に備わっている画面じゃないんですよ(笑)



![Kobito.vE4vBQ.png](https://qiita-image-store.s3.amazonaws.com/0/113181/7e300967-2923-7d16-650e-826a9c79551a.png "Kobito.vE4vBQ.png")


ストックはIssueに表示されている `Stocked` ボタンからできるようになってます。


![gsc_screen1.png](https://qiita-image-store.s3.amazonaws.com/0/113181/d4ed1a9e-be02-13e8-1507-191a6e64ce54.png "gsc_screen1.png")


## 作った経緯

普段プログラミングをしていてgemとかnpmパッケージとか使用していてエラーに遭遇した時、問題解決につながる事はIssueに書いてあることが多いですよね。これを作る前は見つけたIssueのリンクをSlackとかに貼って次困った時に思い出せるように管理していたんですが、Slackは検索性能が悪いし、ログはどんどん流れていきあまり効率の良い方法ではないんですよね。そこでGitHubのIssueをストックするSlackに変わる何かいい方法はないだろうか？と考えたところ、「 `GitHubのものはGitHub上で管理すればいいのでは？` 」となってこのChrome拡張を作ろうとなったわけです。

## インストール

[Chrome Web Storeからダウンロード](https://chrome.google.com/webstore/detail/github-stocked-comments/hgnmlljbpcdmllcmelmaadkkcknloclf?hl=ja&gl=JP)

[GitHubからダウンロード](https://github.com/yukihirop/github-stocked-comments)

## 機能紹介

実際に使ってもらうと分かっていただけると思うが機能はざっくりこんな感じです。

|機能|説明|
|---|----|
|クライアントサイド検索機能|検索したい文字(大小の区別なし)にマッチしたIssueコメントだけ残るようになってます。<br>マッチした文字列は黄色のマーカー表示されます。|
|ソート機能|・最も最近ストックしたIssueコメント順<br>・最も投稿が古いIssueコメント順<br>・最も内容が長いIssueコメント順|
|ページネート機能|ストックしたIssueコメントの数が多くなると1ページあたり5個の表示になるようになってます。|
|フィルタリング機能(一般)|現在は、自分のIssueか他の人のIssueかをフィルタリングするようになってます。|
|フィルタリング機能(メイン言語別)|Issueコメントのリポジトリに使われているメイン言語でフィルタリングするようになってます。|
|Jump to a Friend機能|自分がフォローしているユーザーのページに飛ぶことができます。<br>30人まで写真付きで表示されます。|

## 使用しているライブラリとか(主要なものだけ)

使っているライブラリなどに関して簡単な説明をするとこんな感じです。

|項目|説明|
|---|----|
|vue|`Stocked Comments` をクリックした後にレンダリングされる画面を作成するのに使用してます。|
|vuex|`機能紹介` で挙げた機能を実現するために使ってます。<br>ページネート機能に関してはvuexのpluginを自分で作ってそれを使って実現してます。<br>(まだパッケージ化してないですが近々パッケージ化してnpmで公開しようかと思います。)|
|mark.js|クライアントサイド検索機能で検索結果にマッチした文字を黄色マーカー表示する際に使用してます。|
|rest.js|issueコメントIDからIssueの詳細を取ってくるためのGitHubのAPIのラッパーライブラリです。|
|jquery|`Stocked Comments` というリンクやIssueコメント上の `Stoked` ボタンを追加するために使用してます。|
|chrome.storage.local|chrome拡張で使えるローカルストレージです。`5MB` までデータを保存できます。|

## 知れてよかったライブラリ

このchrome拡張を作る上で使用してよかった・見つけれてよかったと思ったライブラリに関してです。

|項目|説明|
|----|---|
|[ALiangLiang/vue-webpack-chrome-extension-template]()|vueでchrome拡張を作る時に便利なboilerplateです。<br>ソースコードに変更が生じたら自動で再ビルド・chrome拡張の再読み込みが走り開発効率を上げてくれます。<br> バンドルする時に `node_modules` 以下も一緒にまとめてバンドルするのではなく、`node_modules` 以下はそれだけでバンドルして読み込むように設定されていて、ビルド時間の短縮につながるような工夫もされてます。<br> chrome公開時のzipファイルの生成も自動でやってくれます。|
|github-injection|有名なChrome拡張である `OctoLinker` の中で使用されていたGitHubにインジェクションする時に便利な小さなライブラリです。 `pjax:end` のイベントをラップしてあります。GitHubではページ遷移を軽くするためにpjaxでの遷移になっている箇所が多数あるらしく、Issueページもpjaxで表示されているのでIssueページが表示されたのをこのライブラリを使って検知して `Stocked` というボタンを付与するようにしました。(Issueページの表示がpjaxで行われていることをこのライブラリのおかげで知ることができたというのも使用した理由です。)|


## やってよかったこと

このchrome拡張を作る上でやってよかったことは以下のような感じです。

- 本格的に作る前に既にある比較的簡単なChrome拡張をリファクタしながら作ってみたこと。
	- ES6・babel・webpack・jestを使ったモダンな開発の練習になった。
	- chrome.storage.localをPromise化する方法が学べた。
	- manifestの書き方のイメージがついた。
	- 「chrome拡張、俺でも作れるんじゃね？」って自信がついた。
	

- vuexのソースコードを読んだこと。
	- `mapAction` とか `mapGetter` とかの仕組みが理解できた。
		- vuexのプラグインを作る時に役に立った。
	- `store` の実装は難しくて分からなかった....。
		- 高階関数？になっていて難しい...。

## 得られた知識

このchrome拡張ベースで説明すると

#### GitHubに追加したリンク `Stocked Comments` をクリックした後に画面をレンダリングする方法

chrome拡張では現在のタブでスクリプトを実行したい時は、manifestのbackgroudに設定したスクリプトファイル(`background/index.js`)で行う必要があるということ。こんな感じで。

これは現在のタブで `inject/index.js` を実行するというコードです。
`inject/index.js` はメインコンテンツとフッターをレンダーするというコードです。


[background/index.js]

```
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
  chrome.tabs.executeScript(tabs[0].id, {
        file: 'inject/index.js'
  })
})

```


[inject/index.js]

```
import Vue from 'vue'
import App from './App.vue'
import Footer from './Footer.vue'
import store from '@/inject/store'

Vue.config.productionTip = false

new Vue({ // eslint-disable-line no-new
  el: '#github-stocked-comments',
  store,
  render: h => h(App)
})

new Vue({ // eslint-disable-line no-new
  el: '#github-stocked-comments-footer',
  render: h => h(Footer)
})

```

簡単に書いたらこんな感じなのですが、実際は `background/index.js` からcontent_script側 (`content/event.js`) ni `js/inject.js` を呼び出すように命令を出して実現してます。

1. background => 2. content_script => 3. background => `js/inject.js` の実行

になってます。

[1. background/index.js#18-L34](https://github.com/yukihirop/github-stocked-comments/tree/v1.0.0/src/background/index.js#L18-L34)

[2. content/event.js#L28-L50](https://github.com/yukihirop/github-stocked-comments/tree/v1.0.0/src/content/event.js#L28-L50)

[3. background/index.js#L2-L15](https://github.com/yukihirop/github-stocked-comments/tree/v1.0.0/src/background/index.js#L2-L15)


#### GitHubのIssueに `Stocked` というボタンを追加する方法

追加する方法はjqueryで一般的なやり方で追加しているだけなので簡単ですが、追加するタイミングが意外と難しかったです。

Issueのページがpjaxで表示されている事が分かったので以下のような感じでできます。

```
document.addEventListener('pjax:end', () => {
	# Issueに「Stocked」というボタンを追加する処理
});
```

[github-injection](https://www.npmjs.com/package/github-injection)を使うともっといい感じ(エラー処理とかあり)にかけます。

```
import githubInjection from 'github-injection'

gitHubInjection(() => {
   # Issueに「Stocked」というボタンを追加する処理
})
```

#### node_modules以下のファイルだけでバンドルする方法

`optimize.CommonsChunkPlugin` を使って以下のように書くとできるそうです。
webpack4で `optimization.splitChunks` というものに変わったそうです。

こちらの記事がめちゃくちゃ詳しくて勉強になりました。
[webpack 4 の optimization.splitChunks の使い方、使い所 〜廃止された CommonsChunkPlugin から移行する〜](https://qiita.com/soarflat/items/1b5aa7163c087a91877d)

```
new webpack.optimize.CommonsChunkPlugin({
      name: 'vendor',
      minChunks: function (module) {
        return (
          module.resource &&
          /\.js$/.test(module.resource) &&
          module.resource.indexOf(
            path.join(__dirname, '../node_modules')
          ) === 0
        )
      }
    })
```



## 今後の課題

使用していて必要と感じたらですが、機能的には以下の機能を実装したいかなって思ってます。

|項目|内容|
|---|---|
|PR(discussion)保存|PR自体やdisscussionも保存できるようにする|
|フィルタリング機能(種類)|表示したいものを<br>・Issue<br>・Issueコメント<br>・PR<br>・PRのdiscussion<br>の種類でフィルターできるようにする|
|お気に入り機能|フィルタリング機能(一般)にお気に入りにしたリストを表示して絞れるようにする|
|メモ追加機能|保存したIssueコメントなどにメモを追加できるようにする|

その他

- Googleアナリティクスを導入して利用頻度を調べる
- jestでテストを書く
- CIを導入する

## まとめ

普段よく使っている物を便利にするChrome拡張を作るのは楽しいです。
Webサービスを個人開発するより簡単で割と使えるものになりやすい気がします。
是非、何か作ってみたらいかがでしょうか。