typescriptの共有コードを綺麗に保つ方法

# はじめに

Q: なにするの？
A: あらゆるエディタから編集されても、共有コードの見た目を綺麗に保つようにします。[エディタ戦争](https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%87%E3%82%A3%E3%82%BF%E6%88%A6%E4%BA%89)の抑制、コードレビューの単純化などを期待します

Q: どうやって？
A: 以下のモジュールを利用します

- eslint : linter(構文チェックをする)
- prettier : コードフォーマッタ(コード整形)
- husky : git hooksを楽にする
- lint-staged : stagingエリアのファイルのみにhuskyを適用
- commitlint : commit規約に従わせる
- editorconfig : エディタごとの設定(文字コード・インデントなど)を統一する

~~Q: 多すぎでは？~~
それでは環境を作っていきましょう。

# Step1: gtsで楽々環境構築

Q: [gts](https://github.com/google/gts)?
A: Google様によるtypescriptのスタイルガイド。環境構築のツールを提供してくださっています

Q: どこまでの環境構築ができる?
A: package.json, typescript, eslint, prettier, スタイルガイドの設定までやってくれます


というわけで、プロジェクトフォルダを作り、gtsの初期化を実行します。

```
mkdir [プロジェクト名]
cd [プロジェクト名]
npx gts init
```

package.jsonを作成するか聞かれます。

```shell
package.json does not exist.
? Generate (Y/n)
```

`y`を入力します。

はい、もうtypescriptの環境が整い、linterやformatterも使えるようになりました。`npm run fix`でコードを綺麗にできます。VS Codeでは、prettierとeslintの拡張を入れて、「Editor: Format On Save」をONにすると快適です。

注意: `package.json`の"name"の項目だけでも設定しておかないと後でエラーになります

# Step2: gitのセットアップ

後のステップで使うので、いつも通り初期化します。

```
git init
```

ついでに、.gitignoreファイルを作成。node_modulesとbuild(typescriptのコンパイル先)をgitの管理から外します。


```:.gitignore
node_modules
build
```

ちなみに`(echo node_modules; echo build) > .gitignore`で作成できます。

# Step3: git hooksに秩序の維持をさせる

Q: git hooks?
A: gitコマンドの前後でスクリプトを実行できます

Q: なぜ？
A: コミットやプッシュのタイミングでコード整形などを実行することで、~~グロテスクな~~整っていないコードの混入を防ぎます

[husky](https://github.com/typicode/husky)を使うことで、hooksを楽に設定できます。
ではhuskyをインストールしましょう(devDependenciesに)。


```shell
npm i -D husky
```

`package.json`に以下の設定を追記します。

```json:package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "npm run fix",
      "pre-push": "npm test"
    }
  }
}
```

はい、これである程度コードの秩序が保たれます。（ただ、次のステップのlint-stagedとセットで使った方がよいです）。

Q: `"pre-commit": "npm run fix"`?
A: コミット前にコード整形&修正します（fixスクリプトはgtsが登録したもの。gts fixと同義）。ただし整形&修正内容はコミットに含まれません。次のステップでやります

Q: `"pre-push": "npm test"`?
A: プッシュ前にテスト(現在未設定)を行うため

注意: `npm test`のスクリプトはエラーが出ないように設定しておかないと、push時に弾かれます

# Step4: 確実で高速な治安維持

[lint-staged](https://github.com/okonet/lint-staged)で編集したファイルだけにコード整形などを実行させます。

Q: なぜ？
A: 現状だと全てのファイルに対してスクリプトを実行しています。これだとコミット時間が長くなるので、無駄なチェックを減らします。また、修正を自動でコミットに含めることができます

```shell
npm i -D lint-staged
```

`package.json`に以下の設定を追記します。
ステージングエリアにあり、拡張子がtsのファイルにのみfixコマンドをかけています。

```json:package.json
{
  "lint-staged": {
    "*.ts": [
      "npm run fix"
    ]
  }
}
```

huskyにあるコミット前処理は、lint-stagedに置き換えます。

```diff:package.json
{
  "husky": {
    "hooks": {
-     "pre-commit": "npm run fix",
+     "pre-commit": "lint-staged", 
      "pre-push": "npm test"
    }
  }
```

これで完了です。今後のコミットファイルにしか`fix`は適用されないので、先に全てのファイルに`fix`スクリプトをかけておいた方がいいかもですね。

# Step5: コミット規約に従わせる

Q: なぜ？
A: ↓こんなカオスなコミット履歴、読みにくいですよね。せめて統一されたprefixが欲しいので、[commitlint](https://github.com/conventional-changelog/commitlint)でコミットメッセージのチェックを行います

>Fix piyopiyo
>[update] hogehoge
>foobarをリファクタ
>feat(meow): create nyan cat
>アソコをちょっと修正

ではcommitlintをインストールしましょう(devDependenciesに)。

```shell
npm i -D @commitlint/cli @commitlint/config-conventional
```

Q: [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/@commitlint/config-conventional#commitlintconfig-conventional)？
A: よく使われているコミット規約の設定です。有名なAngularのガイドラインがベースになっています。[別の共有設定](https://github.com/conventional-changelog/commitlint#shared-configuration)や[個別設定](https://github.com/conventional-changelog/commitlint#config)を使ってもよいです

commitlintの設定ファイルを作り、config-conventionalの設定を継承します。

```javascript:commitlint.config.js
module.exports = {extends: ['@commitlint/config-conventional']};
```

ちなみに`echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js`で作成できます。

あとは、huskyにcommitlintを設定して、完了です。

```diff:package.json
{
  "husky": {
    "hooks": {
+     "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
      "pre-commit": "lint-staged", 
      "pre-push": "npm test"
    }
  }
```

コミット規約は周知しておきましょう。

# Step6: EditorConfigでエディタごと違いを吸収する

Q: [EditorConfig](https://editorconfig.org/)？
A: コーディングスタイルを定義して、異なるエディタ間で同じようにフォーマットしようという試みです。多くのエディタで対応しています

Q: eslint・prettierでよいのでは？
A: 守備範囲外のファイルのフォーマットに使います

というわけで設定をします。と言っても、.editorconfigファイルを用意するだけです。

以下のようなファイルを用意します。（設定はプロジェクトにあった形に書き換えてください）

```:.editorconfig
root = true

[*]
end_of_line = lf
insert_final_newline = true

[*.ts]
charset = utf-8

[package.json]
indent_style = space
indent_size = 2
```

あとはエディタで対応させれば完了です。

デフォルトで対応しているか、拡張機能が対応しているかなど、エディタごとに違います（例えばVS Codeでは拡張機能）。それぞれのエディタごとに[調べて](https://qiita.com/naru0504/items/82f09881abaf3f4dc171)使いましょう。

# 完了

これで誰もが好きな開発環境でコミットしても、共有コードに秩序がもたらされます。~~もし規約に突っ込まれても、Google様を盾にできます。~~プロジェクトに適した規約を選択しましょう。