SwiftFormat + husky + lint-stagedでSwiftコードをコミット時に自動整形する

# はじめに

本稿は、**Swiftで書かれたソースコードを自動で整形する仕組みづくり**を目的にしています。具体的には、 `git commit` する際に、gitのステージに上がっているソースファイルに対してコード整形を実行し、整形後のソースファイルがコミットされるようにします。

:::note info
コード整形を自動化することで、常に 改行やインデントなどを定めたルール(コーディング規約)を適用し、ソースコードの品質を保つことができます。
:::

【参考】Objective-C版は[こちら](https://qiita.com/hituziando/items/630777463877d2d45bcf)

https://qiita.com/hituziando/items/630777463877d2d45bcf

# 実行環境

macOSのバージョン

```shell
sw_vers
ProductName:		macOS
ProductVersion:		13.2.1
BuildVersion:		22D68
```

Swiftのバージョン

```shell
swift -version
swift-driver version: 1.62.15 Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: arm64-apple-macosx13.0
```

# 自動コード整形環境の構築
## 3つのツール

自動コード整形環境は、以下の3つのツールを組み合わせて構築します。

1. **[SwiftFormat](https://github.com/nicklockwood/SwiftFormat)** ... Swift用コード整形ツール。執筆時点でStarが6.5kあり、メンテナンスも継続中。また、設定できるルールも豊富なため採用しました

2. **[husky](https://typicode.github.io/husky/#/)** ... 任意のコマンドを `git commit` 時などに実行するツール

3. **[lint-staged](https://github.com/okonet/lint-staged)** ... 任意のコマンドをgitのステージに上がっているファイルに対して実行するツール

## SwiftFormatのインストール

SwiftFormatのインストール方法はいくつか提供されていますが、本稿ではソースコードからビルドする方法を採用します。

```shell
git clone https://github.com/nicklockwood/SwiftFormat
cd SwiftFormat
swift build -c release
```

ビルドに成功すると、 `/path/to/SwiftFormat/.build/release/` に `swiftformat` コマンドが生成されます。この `swiftformat` コマンドを適当なところに格納し、PATHを通します。正しくPATHが通っていれば、下記のコマンドでバージョン番号を確認できるはずです。

```shell
swiftformat -version
0.51.2
```

## SwiftFormatの設定

プロジェクトのルートディレクトリ直下に、下記のような.swiftformatファイルを配置します。

```.swiftformat
--swiftversion 5.7

--disable andOperator
--disable enumNamespaces
--disable numberFormatting

--ifdef noindent
--indentcase true
--stripunusedargs closure-only
```

.swiftformatファイルとは、コードスタイルなどコンフィグレーションを記述したファイルです。 *.swiftformat* というファイル名で保存しておくことで、 `swiftformat` コマンドが自動で読み込んでくれます。
このファイルの中身は、あなたのプロジェクトに合わせて編集してください。

## node + npmのインストール

huskyおよびlint-stagedはどちらもnpmで配布されているパッケージです。そのため、まずは `node` と `npm` コマンドをインストールします。既にインストールされている方は読み飛ばしてください。

本稿では、Node.jsのバージョン管理ツール [volta](https://volta.sh/) を使います。

```shell
volta install node@18.14.2
node --version
volta install npm@9.5.1
npm --version
```

## husky + lint-stagedのインストール

:::note warn
これ以降のコマンドはプロジェクトのルートディレクトリで実行してください。
:::

下記のpackage.jsonをプロジェクトのルートディレクトリ直下に配置します。

```package.json
{
  "name": "your project name",
  "private": true,
  "scripts": {
  }
}
```

huskyとlint-stagedをインストールします。

```shell
npm install -D husky lint-staged
```

## huskyの設定

package.jsonに下記の `"prepare"` を追記します。

```package.json
{
  "name": "your project name",
  "private": true,
  "scripts": {
    "prepare": "npx husky install && npx husky add .husky/pre-commit \"npx --no-install lint-staged\""
  },
  "devDependencies": {
    "husky": "^8.0.3",
    "lint-staged": "^13.2.0"
  }
}
```

以下のコマンドを実行します。

```shell
npm run prepare
```

コマンドが成功すると `.husky` ディレクトリが生成されます。
本稿の設定では、コミット前(pre-commit)に `npx --no-install lint-staged` コマンドが実行されます。

## lint-stagedの設定

package.jsonに下記の `"lint-staged"` を追記します。

```package.json
{
  "name": "your project name",
  "private": true,
  "scripts": {
    "prepare": "npx husky install && npx husky add .husky/pre-commit \"npx --no-install lint-staged\""
  },
  "devDependencies": {
    "husky": "^8.0.3",
    "lint-staged": "^13.2.0"
  },
  "lint-staged": {
    "./**/*.swift": [
      "swiftformat"
    ]
  }
}
```

`"./**/*.swift"` はプロジェクトのルートディレクトリ配下にあるすべての.swiftファイルを対象にしています。このパスはあなたのプロジェクトに合わせて調整してください。
gitステージに上げたファイルのうち、このパスの条件に合致するファイルに対して、 `swiftformat` コマンドを実行します。

以上でセットアップは完了です。

ここまでの操作で下記のファイルとディレクトリが生成されているはずです。

```:Your project root directory
- .husky/
- .swiftformat
- node_modules/
- package-lock.json
- package.json
```

# 使い方

gitステージに上げたSwiftソースファイルに対して、コミット時にコード整形を自動的に実行します。

```shell
git add [Swiftソースファイル]

git commit -m "コミットメッセージ"
```

コード整形されたソースファイルがコミットに反映されます。


以上でコード整形を自動で実行する仕組みができました。