このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

JavaScriptの構文チェッカーJSLintをEmacsから使う

先日、Emacsのgrepモードの説明をしました。これの応用として、JavaScriptの構文チェッカーであるJSLintをEmacsから使ってみます。

Windows上のMeadow3での話ですが、Windows特有の部分は、他のOSでは問題にならないゴタゴタのところ(例:危険な^Z)だけです。

内容:

  1. Emacsjavascriptモード
  2. JSLint
  3. Rhino
  4. とりあえず動かしてみる
  5. JSLintソースの修正
  6. EmacsからJSLintを使う
  7. MakefileからJSLintを使う

Emacsjavascriptモード

[追記]
javascriptモードは推奨できません。「EmacsでJavaScriptソースを快適に読むために:js2-modeとエグズーベラントCtags」 をご覧ください。
[/追記]


;; Author: Karl Landstrom
;; Maintainer: Karl Landstrom
;; Version: 2.0 Beta 8
;; Date: 2006-12-26
.emacsの設定例:

;; javascript-mode
(add-to-list 'auto-mode-alist
(cons "\\.\\(js\\|json\\)\\'" 'javascript-mode))
(autoload 'javascript-mode "javascript" nil t)
(setq js-indent-level 2) ; 値はお好みにより

●JSLint

JSLintには、Konfabulator版、WSH版、Rhino版があります。ここでは、Rhino版について説明します。

Rhino版jslint.jsをhttp://www.jslint.com/rhino/index.htmlからダウンロードできますが、これは改行や空白を取り除いたもので、人間が見るには辛すぎます。後で少し変更する都合があるので、JSLintページ一番下のImplementationのところから普通のソースコードをダウンロードします。fulljslint.jsとrhino.jsの2つのファイルが必要です。

(コメントにjslint.jsと書いてありますが、ファイル名はfulljslint.jsです。)


// jslint.js
// 2008-04-09
/*
Copyright (c) 2002 Douglas Crockford (www.JSLint.com)


// rhino.js
// 2007-02-19
/*
Copyright (c) 2002 Douglas Crockford (www.JSLint.com) Rhino Edition

Rhino

現時点(2008-04)での最新版は Rhino 1.7R1 2008-03-06 rhino1_7R1.zip です。実行に必要なのはjs.jarだけですから、js.jarをどっか適当な場所に置きます。

次は、僕が使っている起動用バッチファイルです*1


@echo off
REM rhino-c.bat -- コマンドラインRhino

if "%RHINO_HOME%" == "" set RHINO_HOME=C:\installed\rhinolib
if "%JS_JAR%" == "" set JS_JAR=%RHINO_HOME%\js.jar

java -cp %JS_JAR% org.mozilla.javascript.tools.shell.Main %1 %2 %3 %4 %5 %6 %7 %8 %9

●とりあえず動かしてみる

次のようにして自分用のjslint.jsを作ります。


cat fulljslint.js rhino.js > jslint.js
または、

copy /b fulljslint.js + rhino.js jslint.js
ここで、/bを付けないと、最近の若い人はたぶん知らない^Zが末尾に付きハマリます。

チェックしたいJavaScriptファイルを準備して、JSLintで調べてみます。ここでは、以前に掲載したRingBuffer.jsにツッコミドコロを後から入れたソースを使います。


>rhino-c jslint.js RingBuffer.js
Lint at line 7 character 32: Missing semicolon.
throw "Too small buffer size"

Lint at line 10 character 21: Use the array literal notation [].
this.buffer = new Array(size);

Lint at line 16 character 2: Missing semicolon.
}

Lint at line 19 character 24: Line breaking error ')'.
return (this.next + 1)

この出力形式では、「grepコマンドとEmacs grepモードって、やっぱり便利だよな」で紹介したEmacsの便利な機能を使えないので、jslint.jsに手を加えます。


>copy jslint.js jslint.js.orig

(この後、jslint.jsを編集)

JSLintでもう1つ困ったことは、JavaScriptソースコードに日本語の文字(UTF-8です)を使うと、それがコメント内であっても"Unsafe character."と文句を言うことです。 この点も修正します。

●JSLintソースの修正

出力形式に関しては、もともとrhino.js(rhino.jsはわずか36行)に含まれていた出力部分を書き換えればOKです。

Unsafe characterの判定は、cxという変数に含まれる正規表現に基づいてなされます。


// unsafe character
cx = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,

このcsを書き換えればいいかなと思ったのですが、どう書き換えればいいか僕は分からなかったので、(望ましくはないけど)次のチェック部分を削ってしまいました。


s = lines[line].replace(/\t/g, ' ');
at = s.search(cx);
if (at >= 0) {
warningAt("Unsafe character.", line, at);
}

以前決めたパッチの作り方に従って、次のコマンドを実行します。


diff -u jslint.js.orig jslint.js > jslint.js.20080411.diff

パッチファイルは次のとおりです。このオリジナルjslint.jsは、fulljslint.jsとrhino.jsをアペンドしたファイルであることに注意してください。


--- jslint.js.orig Fri Apr 11 10:44:31 2008
+++ jslint.js Fri Apr 11 11:42:39 2008
@@ -644,17 +644,19 @@
// Private lex methods

function nextLine() {
- var at;
+// var at; //omitted by M.Hiyama
line += 1;
if (line >= lines.length) {
return false;
}
character = 0;
s = lines[line].replace(/\t/g, ' ');
+/* //omitted by M.Hiyama
at = s.search(cx);
if (at >= 0) {
warningAt("Unsafe character.", line, at);
}
+*/
return true;
}

@@ -3908,7 +3910,8 @@
for (var i = 0; i < JSLINT.errors.length; i += 1) {
var e = JSLINT.errors[i];
if (e) {
- print('Lint at line ' + (e.line + 1) + ' character ' +
+/* modified by M.Hiyama */
+ print(a[0] + ':' + (e.line + 1) + ': character ' +
(e.character + 1) + ': ' + e.reason);
print((e.evidence || '').
replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"));
@@ -3916,7 +3919,9 @@
}
}
} else {
+/* //omitted by M.Hiyama
print("jslint: No problems found in " + a[0]);
+*/
quit();
}
})(arguments);

EmacsからJSLintを使う

パッチ済みのjslint.jsを適当な場所にコピーして、例えば次のようなバッチファイルを作っておきます。


@echo off
REM jslint-r.bat -- JSLint Rhino

if "%JSLINT_JS%" == "" set JSLINT_JS=%HOME%\lib\jslint.js
call rhino-c "%JSLINT_JS%" %1

Emacsから M-x compile として、Compile command にjslint-r RingBuffer.jsを指定すると、次のようになります。


原寸大

Meadowの場合、シェルとしてCygwinやMSYSのシェルを指定していると、バッチファイルをうまく実行できない(command not found)ときがあるので注意してください。次の式を評価すれば、Windowsバッチファイルを実行できる状況になります。


(setq explicit-shell-file-name "cmdproxy.exe")
(setq shell-file-name "cmdproxy.exe")
(setq shell-command-switch "/c")

MakefileからJSLintを使う

僕は、シェルから直接ではなくてMakefileからJSLintを起動しています。


OK_FILES:=$(patsubst $(SRC_DIR)/%.js, $(BIN_DIR)/%.ok,$(SOURCES))
# ...

lint: $(OK_FILES)

$(OK_FILES): $(BIN_DIR)/%.ok : $(SRC_DIR)/%.js
@echo "$< --> $@"
$(JSLINT_CMD) $< | tee $(SRC_DIR)/$(JSLINT_PREFIX)$<.lint
@if [ ! -s $(SRC_DIR)/$(JSLINT_PREFIX)$<.lint ]; then \
rm $(SRC_DIR)/$(JSLINT_PREFIX)$<.lint ; \
touch $@ ; \
fi

ウーム、Makefileに凝っていた頃書いたやつだから、無駄に複雑だ。

わかんねぇっ。

*1:僕は、Rhinoを常に -strict オプションで起動するようにしているのですが、そうするとjslint.jsも警告だらけになります :-)