僕たちは英語ができない

# はじめに
日の本の国のプログラマーにとって英単語のスペルミスというのは日常的になります。
最初のうちに検出されたスペルミスは「てへぺろ(・ω<)」ですんで簡単に修正できますが、終盤に見つかったスペルミスは安易に修正できず未来永劫、そのスペルミスが遺産として残ることになります。

いくつかのエディタにはスペルコードを検知する仕組みがあったりします。
例えば、VSCodeにおいては[Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)というプラグインが存在し、英語ができない僕たちのために間違った英単語を教えてくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47856/e3c95463-40a3-477b-190a-764b581015d5.png)

さて、新しいコードを作成するだけの場合、多くはこのプラグインの導入で解決するでしょう。
しかし残念ながら、僕たちは過去の遺産には縛られますし、警告を無視する勇気ある人間は必ず存在します。

このため、継続的インテグレーションの途中などで、フォルダを指定して一括でスペルチェックをする必要があります。
今回はその方法を考えてみます。

# cSpellによるスペルチェック
[cSpell](https://github.com/streetsidesoftware/cspell/
)はコマンドラインでスペルチェックを行ってくれます。いくつかのプログラミング言語固有の辞書のサポートや、キャメルケース、パスカルケースの考慮を行います。

cSpellは元々、VSCodeの[Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker)プラグインとして作成されたものになります。

なお、Node.jsが動作する環境で使用することが可能です。
今回は以下の環境で実験しています。

 - v12.20.1
 - macOS 10.15.7

## インストール方法

```
npm install -g cspell
```

あるいはNode.jsのプロジェクトに組み込む場合は以下の通り

```
npm install --save-dev cspell
```



## 使用方法

```
cspell ファイル名
or
cspell lint ファイル名
```

ファイル名にはワイルドカードを使用することができます。

```
cspell "src/**/*.js"
cspell "src/**/*.{js,vue}"
```

また、規定ではカレントディレクトリ以下に検査対象のソースコードにあることが前提になります。
別のディレクトリを選択する場合は -r, --rootを指定する必要があります。

```
cspell check -r ルートディレクトリ "src/**/*.js"
```

その他、使用方法についてはhelpで確認してください。

```
cspell --help

# 各コマンドのヘルプは以下で確認できる
cspell lint --help
cspell check --help
cspell trace --help
cspell link --help
```

## 出力結果
コマンドラインで実行すると以下のような結果を出力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47856/da1a58c7-b22b-7a6e-e479-35511df04f40.png)

HTMLでレポートするような機能は存在しないようです。
https://github.com/streetsidesoftware/cspell/issues/21

### jsonで警告の一覧を保存する方法
まず以下のような標準入力を変換するスクリプトを作成します。

```makejson.js
process.stdin.resume();
process.stdin.setEncoding('utf8');

let fragment = '';
const result = []
process.stdin.on('data', function(chunk){
  if (chunk == '') { return ;}
  var lines = chunk.split('\n');
  lines[0] = fragment + lines[0];
  fragment = lines.pop();
  lines.forEach(function(line){
    const item = {
      path: '',
      line: 0,
      column: 0,
      word: ''
    };
    item.path = line.substr(0, line.indexOf(':'));

    line = line.substr(line.indexOf(':')+1)
    const locations = line.substr(0, line.indexOf(' - ')).split(':')
    item.line = Number(locations[0]);
    item.column = Number(locations[1]);

    line = line.substr(line.indexOf(' - ')+1)
    item.word = line.match(/\((.+)\)/)[1]
    result.push(item)
  });
});
 
process.stdin.on('end', function(){
  console.log(JSON.stringify(result, null, 2))
});
```

次にパイプで渡せば結果をjsonファイルに保存できます。

```
cspell src/* | node makejson.js > ret.json
```

### 認識できない単語の一覧を取得する
認識できなかった単語の一覧を取得するには以下のようなオプションを指定して実行すると単語の一覧が標準出力されます。

```
cspell src/* --unique --wordsOnly --no-progress --no-summary
```

この例では以下のような挙動をします。

 - --wordsOnlyで認識できない単語のみを出力する
 - --uniqueで認識できない単語の重複を排除してます。
 - --no-progressで経過情報を出力しない
 - --no-summaryでサマリの出力をしない

## 警告の解決方法
使用しているとわかるのですが、警告として検出される単語が実際は正しい単語の場合もあります。
これを解決するには２通りの方法があります。

 - 設定ファイルに有効な単語として登録する方法
 - チェックをしないようにソースコードに書き込む

### 設定ファイルに有効な単語として登録する方法
英単語を登録するにはjsonファイルを記述する必要があります。

```js:cSpell.json
{
  // Version of the setting file.  Always 0.1
  "version": "0.1",
  // language - current active spelling language
  "language": "en",
  // words - list of words to be always considered correct
  "words": [
      "vuex",
      "Vetur"
  ],
  // flagWords - list of words to be always considered incorrect
  // This is useful for offensive words and common spelling errors.
  // For example "hte" should be "the"
  "flagWords": [
      "hte"
  ]
}
```

作成した設定ファイルを指定して実行するには-cを使用します。

```
cspell  -c ./cSpell.json "src/**/*.{js,vue}"
```

この例では「vuex」,「Vetur」を有効な単語とし、「hte」を誤った単語としてみなします。
なお、設定ファイルの詳細については以下に説明があります。
https://github.com/streetsidesoftware/cspell/tree/master/packages/cspell#customization

### チェックをしないようにソースコードに書き込む
コメントによりスペルチェックの有効・無効を制御できます。

```js
const strChack = '検知される'

// cSpell:disable スペルチェックをしない
strChack = '検知されない'
strChack = '検知されない'
/* cSpell:enable スペルチェックを有効に　*/

strChack = '検知される'

// cspell:disable-next-line 次の行を無視する
strChack = '検知されない'

strChack = '検知される'
```

あるいはコメントを使用して無視する単語を登録することもできます。

```js
// cSpell:ignore strChack
const strChack = '検知されない'

```

コメントを利用したcspellの制御については以下を参照してください。
https://github.com/streetsidesoftware/cspell/tree/master/packages/cspell#enable--disable-checking-sections-of-code


# まとめ
cspellを使用することで、英語ができない我々でも単純なスペルミスを検出して修正することが可能です。
これを継続的インテグレーションに組み込むことで、プロジェクトの初期段階においてスペルミスを検知して、謎の英単語をプロジェクトルールとして認めるということをしなくて済むようになります。