TypeScriptで簡単にChrome Extensionを作る方法

Githubで草を生やす活動は楽しいですね!
増えてきて芝みたいになると達成感があって、モチベーションも上がります。
ところで、この前の10/30に草がハロウィン仕様になっていたのを見て、
草を自分の好みの色を変更できればもっと楽しくなるのでは?と思い
[草の色が変更できる（ついでにお絵描きもできる）Chrome Extension](https://chrome.google.com/webstore/detail/github-contributions-pain/bdibnecenojddgmjplnfehpjdmkiddok?hl=ja)を作りました。
**その時に[generator-chrome-extension-kickstart-typescript](https://github.com/mazamachi/generator-chrome-extension-kickstart-typescript)を使って、TypeScriptで簡単にChrome Extensionが作れたので、アプリ作成から公開までの手順をまとめます。**

## 1. プロジェクトの雛形を生成する
yomanと[generator-chrome-extension-kickstart-typescript](https://github.com/mazamachi/generator-chrome-extension-kickstart-typescript)で簡単に雛形が作れるのでコレらを使っていきます。

* `yoman`と`generator-chrome-extension-kickstart-typescript`をグローバルインストールします

```
$ npm install -g yo generator-chrome-extension-kickstart-typescript 
```
* プロジェクトのルートフォルダを作成後、ルートフォルダに移動して`yo`コマンドで雛形を生成します

```
$ mkdir project
$ cd project
$ yo chrome-extension-kickstart-typescript 
```

* いくつか質問されるので適当に答えます
    * **What would you like to call this extension?**
        * プロジェクト名を入力します。デフォルトはプロジェクトのフォルダ名
    * **And how would you call it if you only had 12 characters (short_name)?**
        * プロジェクトの略称を12文字以内で入力します
    * **How would you like to describe this extension?**
        * プロジェクト概要を入力します
    * **Would you like to use UI Action?**
        * 作りたいUI Actionに応じて、`No`,`Browser`,`Page`のいずれかを選択します
            * `No` ・・・ UI Actionを使わない場合
            * `Browser` ・・・ ツールバーの右にアイコンが常駐するタイプのChrome拡張を作成する場合
            * `Page` ・・・ 特定のWebサイト観覧時のみにアドレスバー右にアイコンが表示されるタイプのChrome拡張を作成する場合
    * **Would you like to override a chrome page?**
        * Chromeの内部ページを書き換えるかどうかで、`No`,`Bookmarks Page`,`History Page`,`Newtab Page`のいずれかを選択します
    * **Would you like more UI Features?**
        * 作りたいUIを複数選択します
            * `Options Page` ・・・ オプション画面を作りたい場合
            * `Devtools Page` ・・・ 開発者ツールを拡張したい場合
            * `Content Scripts` ・・・ Webで表示している画面にJavaScriptを差し込んで何か処理をしたい場合
            * `Omnibox` ・・・ アドレスバーを拡張したい時に選択
    * **Would you like to use permissions?**
        * 使用するパーミッションを複数選択します
    * **Would you like to install promo images for the Chrome Web Store?**
        * Chromeストアで使う宣伝用画像を自動で生成するかを選択します


※自分は下記のように答えました
**What would you like to call this extension?** => github-contributions-paint
**And how would you call it if you only had 12 characters (short_name)?** =>  github-paint
**How would you like to describe this extension?** =>  You can paint github contributions!
**Would you like to use UI Action?** => Browser
**Would you like to override a chrome page?** =>  No
**Would you like more UI Features?** =>  Content Scripts
**Would you like to use permissions?** =>  Tabs
**Would you like to install promo images for the Chrome Web Store?** =>  Yes

* すると雛形が生成されます

```:生成された雛形のフォルダ構成
github-contributions-paint
├── README.md
├── app
│   ├── images       　　　    　・・・Chromeウェブストア公開時に必要なアイコン格納フォルダ
│   │   ├── icon-128.png
│   │   ├── icon-16.png
│   │   ├── icon-19.png
│   │   └── icon-38.png
│   ├── manifest.json          ・・・Chrome拡張の設定を定義するファイル
│   ├── pages                  ・・・HTMLファイル格納フォルダ
│   │   └── popup.html         ・・・Chrome拡張のアイコンをクリックした時に表示するポップアップ画面用資産
│   ├── scripts                ・・・TSファイル格納フォルダ
│   │   ├── background.ts      ・・・バックグランド処理を記述するファイル
│   │   └── popup.ts           ・・・Chrome拡張のアイコンをクリックした時に表示するポップアップ画面用資産
│   ├── styles                 ・・・CSSファイル格納フォルダ
│   │   └── popup.scss         ・・・Chrome拡張のアイコンをクリックした時に表示するポップアップ画面用資産
│   ├── _locales
│   │   └── en
│   │       └── messages.json ・・・アプリの設定ファイル
│   └── fonts
├── promo                     ・・・Chromeウェブストア公開時に必要な宣伝用画像の格納フォルダ
│   ├── Chrome-Webstore-Icon_128x128.png
│   ├── Promo-Image-Large_920x680.png
│   ├── Promo-Image-Marquee_1400x560.png
│   ├── Promo-Image-Small_440x280.png
│   ├── Screenshot_1280x800.png
│   └── Screenshot_640x400.png
├── dist
├── gulpfile.babel.js
├── node_modules
├── package-lock.json
├── package.json
├── tasks
├── tsconfig.json
└── tslint.json

```

* 最後にTypeScriptをグローバルインストールしていない場合は、グローバルインストールまたはプロジェクトにインストールします

```
$ npm install typescript --save-dev
```

==2018/11/24 追記(ここから)==

* このまま次手順にいくと警告がでるのでそれを防ぐためにgulpのバージョンを下げます。

```
$ npm install gulp@3.9.0 --save-dev
```

<small>※関連する[issue](https://github.com/gulpjs/gulp/issues/1631)、[stackoverflow](https://stackoverflow.com/questions/48568942/failed-to-load-external-module-babel-register-while-gulp)</small>

==2018/11/24 追記(ここまで)==
## 2. 作ったChrome Extensionを動かしてみる
雛形を作成したら早速動かすことができます

* ビルドコマンドを実行します

```
$ npm run dev:chrome
```

* Chromeで`chrome://extensions`を開いて`パッケージ化されていない拡張機能を読み込む`ボタンをクリック
![手順_1.png](https://qiita-image-store.s3.amazonaws.com/0/49915/5a549a45-8ec4-8955-b733-875549f8ad31.png)

* プロジェクトルート/dist配下の`chrome`フォルダを選択
![操作_2.png](https://qiita-image-store.s3.amazonaws.com/0/49915/f91a166e-6e2e-3f37-710e-ee7520c93ec2.png)

* するとプロジェクトが[Chrome Extension管理画面](chrome://extensions/)に追加されて、ブラウザ右上にアイコンが表示されます
![動作確認1_1.png](https://qiita-image-store.s3.amazonaws.com/0/49915/61b2e113-e520-7ef8-c47e-426be93aa84b.png)


* アイコンをクリックして動作検証ができます
![動作確認_11.png](https://qiita-image-store.s3.amazonaws.com/0/49915/3ff11e73-81b3-cd5a-8e70-9a754a6a9965.png)


## 3. 開発中にChrome Extensionの動作確認する
雛形の動作検証ができたらTypeScriptファイルを編集して処理を追加していきます。
`npm run dev:chrome`を実行した後、下記手順繰り返すことで、ソースコードを修正しながら随時動作検証ができます。

* 資産（TypeScriptやHTML,CSS）を修正する（勝手にビルドが走ります）
* 最新のビルド資産を取り込むため[Chrome Extension管理画面](chrome://extensions/)でリロードをクリックします
![開発手順_1.png](https://qiita-image-store.s3.amazonaws.com/0/49915/5f9651c6-737f-d6db-80bf-44a7166b2005.png)
* Chrome Extensionの動作確認をする

## 4. アイコンと宣伝用画像を作る
アプリを公開する前に、アイコンと宣伝用画像を作成し、雛形生成時のデフォルト資産と差し替えます。
デザイナーさんなら苦労しないでしょうが、自分はこれが一番大変でした。
3つのアイコンと3つの宣伝用画像が必要です。

### アイコン
* 128x128
    * 必須。インストール時やChromeウェブストアで表示する用
* 48x48
    * 拡張機能の管理ページで表示する用
* 16x16
    * ブラウザ右上に表示する用
  
### 宣伝用画像
* 440x280
    * 必須
* 920x680
    * 大きめの画像
* 1400x560
    * ChromeExtensionをChrome Storeで選択した時に表示される画像

## 5. Chromeウェブストアに公開する
* 雛形生成時バージョンが0.0.0になっており、バージョンが低すぎて公開できない状態なので、`manifest.json`と`package.json`のバージョン指定を0.0.1にあげます

```json:manifest.json
{
  ・・・
  "version": "0.0.1", <- 0.0.0から0.0.1にあげる
  ・・・
}
```

```json:package.json
{
  ・・・
  "version": "0.0.1", <- 0.0.0から0.0.1にあげる
  ・・・
}
```

* 下記コマンドで公開用のzipファイルを作成します

```
$ npm run build:chrome
```

* [Chromeウェブストア](https://chrome.google.com/webstore/category/extensions)のデベロッパーダッシュボードを開きます。Chromeアカウントでログインします
![アプリ登録_1.png](https://qiita-image-store.s3.amazonaws.com/0/49915/edb48ac9-0181-1d78-c7ad-a082712af01c.png)

* ダッシュボードで`新しいアイテムを追加する`を選択します
![アプリ登録_2.png](https://qiita-image-store.s3.amazonaws.com/0/49915/787a3a25-2e93-6186-17ed-6b19bbb3bab5.png)

* `ファイルを追加`で作成したzipファイル(プロジェクトルート/packages配下にあります)を選択後、`アップロード`を選択します
![アプリ登録_3.png](https://qiita-image-store.s3.amazonaws.com/0/49915/223b4624-d892-3eb8-5ea8-73c4aaf2b5b8.png)

* 説明、アイコン、宣伝用画像、カテゴリを入力して`変更を公開`をクリックします
![登録_5.png](https://qiita-image-store.s3.amazonaws.com/0/49915/724f0355-fe7b-7918-2d75-09c33cce5e00.png)

* これで公開作業は完了です。しばらくするとChromeウェブストアに公開されます


## 最後に
今回は[generator-chrome-extension-kickstart-typescript](https://github.com/mazamachi/generator-chrome-extension-kickstart-typescript)を使って、TypeScriptでChrome Extensionを作成、公開する手順をまとめました。
最初はTypeScriptでChrome Extensionを作るのは、ビルドの設定など色々めんどくさそうだぁと思っていましたが、
[generator-chrome-extension-kickstart-typescript](https://github.com/mazamachi/generator-chrome-extension-kickstart-typescript)を使うことで、簡単に作成から公開まで実現できました。
ブラウザでのちょっとした作業を効率化するのにChrome Extentionは非常に強力なツールなので、今後なにかアイディアがあればまた作ってみたいと思います。