Elm Analyse でコード品質を担保する

Elm2 アドベントカレンダー 2017 の３日目です。

[Elm Analyse](https://stil4m.github.io/elm-analyse/)  は Elm コードの良くない匂いを嗅ぎつけて指摘してくれるツールです。


## 使い方

npm （または yarn）でインストールします。

```
npm install -g elm-analyse
```

プロジェクトのルートディレクトリでコマンドを打つと警告が一覧で出力されます。

```
elm-analyse
```
（場合によっては多少待ちます）


## チェック事項

以下の項目をチェックします。


**CoreArrayUsage**
Elm 0.18 の Array にはバグがあるので、今は [Skinney/elm-array-exploration](http://package.elm-lang.org/packages/Skinney/elm-array-exploration/latest) を使った方がいい（0.19 でコアにマージされます）。

**DebugCrash**
`Debug.crash` が使われている。

**DebugLog**
`Debug.log` が使われている。

**DropConcatOfLists**
リストを無駄な結合 `[1,2,3] ++ [4,5]` は `[1,2,3,4,5]` のようにまとめた方が良い。

**DropConsOfItemAndList**
リストを無駄な結合 `1::[2,3,4]` は `[1,2,3,4]` のようにまとめた方が良い。

**DuplicateImport**
同じモジュールを２回インポートしている。

```haskell
-- BAD
import Html exposing (text)
import Html exposing (Html)
```

**DuplicateImportedVariable**
同じ値を２度インポートしている。

```haskell
-- BAD
import Html exposing (Html, text, Html)
```

**DuplicateRecordFieldUpdate**
レコードの同じフィールドを重複して更新している。

```haskell
-- BAD
{ person | name = "John", name = "Jane" }
```

**ExposeAll**
できる限りモジュールが公開するインターフェイスを絞った方が良い。

```haskell
-- BAD
module Foo exposing (..)
```

<!--**FileLoadFailed**-->

**NoUncurriedPrefix**
`(++) "Hello " "World"` のように中置演算子を無駄に関数として使わず、素直に `"Hello " ++ "World"` とした方が良い。

**FunctionInLet**
`let ~ in` 中に関数を宣言する必要はあまりないので、トップレベルに置けるなら置いた方が良い。

```haskell
-- BAD
foo : Int -> Int
foo x =
    let
        somethingIShouldDefineOnTopLevel : Int -> Int
        somethingIShouldDefineOnTopLevel y =
            y + 1
    in
        somethingIShouldDefineOnTopLevel x
```

**ImportAll**
ワイルドカードで import すると他の人が読んだときにどのモジュールの関数かわかりにくくなるので、避けた方がいい。

```haskell
-- BAD
import Html exposing (..)
```

**LineLengthExceeded**
一行の文字数が長すぎる（デフォルトの閾値は 150 文字）

**MultiLineRecordFormatting**
レコードは複数行で宣言した方が読みやすい。

```haskell
-- BAD
type alias Person =
    { name : String , age : string , address : Address }

-- GOOD
type alias Person =
    { name : String
    , age : string
    , address : Address
    }
```

**NoTopLevelSignature**
トップレベルの関数や値には型をつけた方がいい。

```haskell
-- BAD
foo =
    1
```

**NonStaticRegex**
動的に正規表現を作ると実行時エラーを起こす可能性があるため、トップレベルに宣言した方がいい。

```haskell
-- BAD
foo x =
    let
        myInvalidRegex = Regex.regex "["
    in
        (myInvalidRegex, x)
```

**RedefineVariable**
同じ変数名で外の変数を上書きしない方が混乱が少ない。

```haskell
-- BAD
foo : Maybe Int -> Int
foo x =
    case x of
        Just x ->
            x
        Nothing ->
            1
```

**SingleFieldRecord**
1つのフィールドしかないレコードは obsolete （廃れた習慣？）だ。（意図が良くわからないが Haskell でそういう書き方をするから？）

```haskell
-- BAD
type Model =
    Model { input : String }
```

**TriggerWords**
`TODO` や `FIXME` のようなコメントを残したままにしない方が良い（単語は設定で変えられる）。

**UnformattedFile**
[elm-format](https://github.com/avh4/elm-format) されていない。

**UnnecessaryListConcat**
不必要に `List.concat` している。単に `++` で繋げた方が良い。

```haskell
-- BAD
foo : List Int
foo =
    List.concat [ [ 1, 2 ,3 ], [ a, b, c] ]
```

**UnnecessaryParens**
不必要な括弧は取り除いた方が良い。（それでも括弧が好きな人は Lisp をやろうと書いてある）

```haskell
-- BAD
someCall =
    (foo 1) 2
```

**UnnecessaryPortModule**
`port module` と不必要に宣言されている。

```haskell
-- BAD
port module Foo exposing (notAPort)

notAPort : Int
notAPort = 1
```

**UnusedImport**
インポートされているが使われていないモジュールがある。

```haskell
-- BAD
import Unused
```

**UnusedImportAlias**
`import ... as ...` でエイリアスをつけたモジュール名が使われていない。

```haskell
module Foo exposing (main)

-- BAD（ H が使われていない）
import Html as H exposing (Html, text)

main : Html a
main =
    text "Hello"
```

**UnusedImportedVariable**

```haskell
module Foo exposing (thing)

-- BAD （ div が使われていない）
import Html exposing (Html, div, text)

main : Html a
main =
    text "Hello World!"
```

**UnusedPatternVariable**
パターンマッチで束縛した変数が使われていない。

```haskell
-- BAD （ age が使われていない）
sayHello {name, age} = "Hello " ++ name
```

**UnusedTopLevel**
トップレベルの関数が使われていない。

**UnusedTypeAlias**
type alias を宣言しているが使われていない。

**UnusedVariable**
変数が使われていない。

```haskell
-- BAD
foo : String -> Int
foo x =
    1
```

**UseConsOverConcat**
`[ a ] ++ foo` ではなく `a :: foo` と書いた方がいい。

## チェック項目の設定

**異議あり！**という項目が多々あったと思うので、プロジェクトのルートディレクトリに `elm-anayse.json` を置いてください。`true` のものだけがチェックされます。

```json
{
  "checks": {
    "DebugCrash": false,
    "DebugLog": false,
    "DropConcatOfLists": false,
    "DropConsOfItemAndList": false,
    "DuplicateImport": true,
    "DuplicateImportedVariable": true,
    "ExposeAll": false,
    "ImportAll": false,
    "LineLengthExceeded": false,
    "MultiLineRecordFormatting": false,
    "NoTopLevelSignature": false,
    "NoUncurriedPrefix": true,
    "NonStaticRegex": true,
    "RedefineVariable": false,
    "SingleFieldRecord": false,
    "UnnecessaryListConcat": false,
    "UnnecessaryParens": true,
    "UnusedImport": true,
    "UnusedImportAlias": true,
    "UnusedImportedVariable": true,
    "UnusedPatternVariable": false,
    "UnusedTopLevel": true,
    "UnusedTypeAlias": true,
    "UnusedVariable": false,
    "UseConsOverConcat": false,
    "UnformattedFile": true
  },
  "TriggerWords": {
    "words": ["todo", "fixme"]
  }
}
```

## レポートをブラウザで表示

`--serve` オプションで結果をブラウザで表示します。

```
elm-analyse --serve
```

localhost:3000 にアクセスすると、警告の一覧やモジュールの依存関係が表示されます。

![Screen Shot 2017-12-03 at 1.10.26.png](https://qiita-image-store.s3.amazonaws.com/0/109182/9678bcdd-77b9-e3c4-361f-b3bf1c77c70b.png)

問題のコードの位置を教えてくれるのが嬉しかったりします。

![Screen Shot 2017-12-03 at 1.07.46.png](https://qiita-image-store.s3.amazonaws.com/0/109182/3f0c5705-5cfa-d0d2-1d2a-3f05b4d5e7c4.png)

## まとめ

チーム開発のときにプロジェクトでルールを決めて使うと、自動でチェックできて便利かもしれませんよ！