自動フォーマット駆動の JavaScript 開発環境
Python や Go では、コードの編集時には標準のコードスタイルで自動チェックし、保存時にもその同じコードスタイルで自動フォーマットするといったことができる。言語で決められた標準のコードスタイルのない言語では、自動チェックと自動フォーマットで同じコードスタイルが適用されるようにしないと意味がない。
Closure Linter はまさにそのためのツールだけど Google JavaScript Style Guide に限られる。
jsfmt は esformatter を自動フォーマットエンジンとして利用する。esformatter では diff を出力することはできるけどコードスタイルエラーの詳細を出力できない。
Linter である JSHint や ESLint にはコードスタイルに関するオプションもある。それらを esformatter における同等の設定に変換して jsfmt で自動フォーマットすればいいように思ったけど、自動変換してくれるようなツールは見つからなかった。手動で変換するにはオプションが多すぎる。
また JSHint ではコードスタイルに関するオプションは deprecated になっていて、次のメジャーリリースで削除されるらしい。JSHint のドキュメントではコードスタイルのチェックとして代わりに JSCS が参照されている。JSCS には自動フォーマット機能が最近実装された。
以上の状況をふまえると、自動チェックと自動フォーマットを一致させるには今のところ Closure Linter か JSCS しかないけど Closure Linter は使わない。なぜなら自分のコードだけならそれでもいいけど、いろいろなスタイルの既存のコードを触るなら最初から JSCS で設定しておけばいいと思うから。でも JSCS の自動フォーマット機能は、設定が緩いせいかもしれないけど、気が利かないと感じることが多い。
コードスタイルの自動チェックとともに、もちろん Linter による自動チェックも行う。
ESLint 2.0.0 Wish List で ESLint に autofix オプションを提供するか話されていて将来的には ESLint だけで済むかもしれない。でも同時に ESLint からコードスタイルに関するオプションを削除して JSCS に任せるという意見も出ている。
コードスタイルの設定のうちインデントの設定については、自動チェックと自動フォーマットに対応していれば EditorConfig に任せて、JSCS では指定しないのもいいかと思ったけど、Emacs の EditorConfig プラグインが対応してない。
Expand
JSHint/ESLint と JSCS で自動チェック
JSCS の設定を JavaScript のメジャーモードに反映
JSHint/ESLint と JSCS で自動チェック
これは Flycheck でできる。すべての linter をインストールしている場合は、JSHint > ESLint > Closure Linter の順で優先される。たとえば JSHint をインストールした状態で常に ESLint を使うようにするには .emacs に
(setq-default flycheck-disabled-checkers '(javascript-jshint))
と書く。あるいはファイルローカル変数として設定する。
JSCS をインストールしている場合は、linter のチェックの後で結果が warning 以下なら JSCS でコードスタイルもチェックされる。
JSCS の設定を JavaScript のメジャーモードに反映
これは以前やっていた。このうち global ディレクティブを反映させる機能は今では js2-mode が実現する。以前のときより JSHint の環境オプションも増えているし、ESLint に対応しながら、JSHint の vars.js をパースしたり、ESLint の globals.json を読んだり、ESLint の設定ファイルの継承をサポートしたりした。
これって、JSHint を Flymake で走らせている場合は
(setq js2-mode-show-parse-errors nil) (setq js2-mode-show-strict-warnings nil)
のように js2-mode の lint (の警告とエラーの表示)をオフにすれば、「js2-additional-externs に追加」の処理をしないでいい気がしてきた。つまり単に js2-basic-offset を適用するだけっていう。
って書いてあって馬鹿すぎた。これでだいたい合ってる。Flycheck から呼び出す JSHint や ESLint が /*global ... */ や /*jshint ... */ や /*eslint-env ... */ を見てくれるから Emacs Lisp でこれらを見て js2-additional-externs に追加したりする必要はない。
JSCS のインデントルールを js2-basic-offset などに適用する elisp は emacs-jscs に書いた。
以下は js2-mode のオプションを見直してみた結果で、JSHint を使う場合は
(setq js2-include-browser-externs nil) (setq js2-mode-show-parse-errors nil) (setq js2-mode-show-strict-warnings t) (setq js2-strict-trailing-comma-warning t) (setq js2-strict-missing-semi-warning nil) (setq js2-strict-inconsistent-return-warning t) (setq js2-strict-cond-assign-warning nil) (setq js2-strict-var-redeclaration-warning nil) (setq js2-strict-var-hides-function-arg-warning nil) (setq js2-highlight-external-variables nil) (setq js2-include-jslint-globals nil)
で値が t の変数は初期値のままだけどオプションを一覧表示するために書いておく。nil に変更した部分は対応する JSHint のオプションで警告させる。ESLint を使う場合は
(setq js2-strict-trailing-comma-warning nil) (setq js2-strict-missing-semi-warning t) (setq js2-missing-semi-one-line-override t) (setq js2-strict-inconsistent-return-warning nil)
を追加・変更する。ただしこれらの設定はセミコロンで終わらない文は警告するが、function(x) {return x} のようなワンライン関数内のセミコロンで終わらない文は警告しないようにするためのものになっている。もし常に警告するか警告しないようにするなら JSHint や ESLint で設定できるから、ESLint の設定は js2-mode-show-strict-warnings を含めて上記の変数はすべて nil にする。JSHint の設定は変わらない。
js2-include-browser-externs
初期値は t で、window や document などの識別子が定義済みとなる。JSHint で browser オプションを、ESLint では browser 環境を true にすればいい。ただしこれらで定義される識別子はそれぞれ微妙に異なる。
js2-mode-show-parse-errors
初期値は t で、JavaScript のパースエラーがハイライトされる。パースエラーは JSHint と ESLint で検出されるから js2-mode でハイライトする必要はない。
js2-mode-show-strict-warnings
初期値は t で、js2-strict-* 変数の設定を有効にする。nil の場合は js2-strict-* 変数の設定が一括で nil になる。
js2-strict-trailing-comma-warning
初期値は t で、{foo: 2,} や [3,] のカンマを警告する。JSHint でこれを警告するには es3 オプションを true にする必要がある。この変数を t のままにしておくと es3 オプションが false の場合でも js2-mode で警告させることができる。ESLint では comma-dangle ルールで制御できるから ESLint を使う場合は nil にすればいい。ESLint はデフォルトで警告する。
js2-strict-missing-semi-warning
初期値は t で、セミコロンで終わらない文を警告する。JSHint では asi オプションが、ESLint では semi ルールが提供されていてどちらもデフォルトで警告する。
js2-missing-semi-one-line-override
初期値は nil で、function(x) {return x} のようなワンライン関数内のセミコロンで終わらない文を警告する。t にすれば警告しない。ただし js2-strict-missing-semi-warning が nil の場合は、この変数の値に関係なくセミコロンで終わらない文を常に警告しない。JSHint は lastsemic オプションによってデフォルトで警告する。ESLint には対応するルールがないっぽい。
js2-strict-inconsistent-return-warning
初期値は t で、同じ関数内の return と return value の混在を警告する。JSHint では警告できないようだから js2-mode で警告するようにしておく。ESLint は consistent-return ルールによってデフォルトで警告する。
js2-strict-cond-assign-warning
初期値は t で、if (a = b) ... のような条件式としての代入を警告する。JSHint では expr オプションが、ESLint では no-cond-assign ルールが提供されていてどちらもデフォルトで警告する。
js2-strict-var-redeclaration-warning
初期値は t で、変数の再宣言を警告する。よくわからないけど JSHint はデフォルトで警告する。ESLint は no-redeclare ルールによってデフォルトで警告する。
js2-strict-var-hides-function-arg-warning
初期値は t で、関数引数と同じ名前の変数定義を警告する。JSHint はデフォルトで警告しないが、shadow オプションで警告するようにできる。ESLint は no-shadow ルールによってデフォルトで警告する。
js2-highlight-external-variables
初期値は t で、未定義の変数をハイライトする。JSHint はデフォルトで警告しないが、undef オプションで警告するようにできる。ESLint は no-undef ルールによってデフォルトで警告する。
js2-include-jslint-globals
初期値は t で、/*global .. */ ディレクティブで指定した識別子を定義済みとする。JSHint と ESLint がディレクティブを見るから js2-mode で見る必要はない。
JSCS で自動フォーマットするための関数 jscs-fix を emacs-jscs に書いた。go-mode.el の gofmt 関数の大部分を再利用するため langfmt パッケージを抽出してそれに依存するようにした。