09

jshint について色々調べた。

jshint は jslint がいろいろ頑固であれなのでフォークされてできた類似のものらしい。 gruntのサンプルファイルに最初からgrunt-contrib-jshintが追加されていたので、設定をいろいろいじっていた。

いま開発環境として使っているのは Sublime text 2で、こちらもjsファイルに対してはsublimelinterがjshintを使用して自動的にチェックしてエディタ上にマーカー表示してくれるようになっている。sublimelinterから実行するjshintの設定は .jshintrcに書くのだが、配置先はソースツリーのどこかで良いらしい。配下のディレクトリにあるソースに対して再起的にその設定でチェックするということになっている。

なのでソースフォルダのトップに設定ファイルを作成しておいて、基本的にはその設定でsublimelinterが動くようにしておいて、gruntのjshintでも同じファイルを見て実行するように設定した。

module.exports = function(grunt){

    grunt.initConfig({

                ......

        jshint : {
            ssjs : {
                files : [{ expand:true, src:'src/main/jssp/**/*.js'}],
                options : {
                    jshintrc : 'src/main/jssp/.jshintrc'
                }
            }
        }
    });
          ......
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.registerTask('default', [ 'jshint']);
};

sublimelinterは開いているファイルに対してしかチェックできないので、通常はsublimelinterに任せておき、一括で問題のある箇所を把握したい場合はgruntタスクを動かす、というような使い方をしている。

ただ今回の開発は intra-mart Accel platform 上のものなので、それ固有の問題がある。

固有API

iAPが提供するAPIは当然jshintは知らないため、エラーになる。予め設定ファイルに書いて教えてやる必要がある。

{
  "curly": true,
  "eqnull": true,
  "eqeqeq": true,
  "evil": false,
  "forin": true,
  "immed": true,
  "latedef": false,
  "maxdepth": 5,
  "maxparams": 5,
  "maxlen": 160,
  "trailing" : true,
  "moz" : true,
  "rhino" : true,
  "quotmark" : "single",
  "undef" : true,
  "unused" : true,
  "strict" : false,
  "maxcomplexity" : 22,
  "esnext" : true,

  "globals": {
    "Debug" : true,
    "ImJson" : true,
    "Transaction" : true,
    "SharedDatabase" : true,
    "TenantDatabase" : true,
    "Web" :  true,
    "Contexts" : true,
    "Content" : true,
    "SystemLocale" : true,
    "DateTimeFormatter" : true,
    "AccountDateTimeFormatter" : true,
    "TimeZone" : true,
    "PluginManager" : true,
    "DbParameter" : true,
    "SessionScopeStorage" : true,
    "SystemStorage" : true,
    "Logger" : true,
    "include" : true,
    "isNull" : true,
    "isDate" : true,
    "isObject" : true,
    "isArray" : true,
    "isNumber" : true,
    "isBlank"  : true,
    "isUndefined" : true,
    "secureRedirect" : true, 
    "Identifier" : true,
    "Module" : true
  }
}

固有のエントリーポイント

iAPではページの実行ロジックを含むjsファイルのエントリポイントは必ずinit関数と決まっている。これは固定のもので、iAPの実行環境からこの関数が直接固定的に呼び出される。jshintでは未使用の変数や関数は警告を出すように設定しているので、すべてのinitは使用されていないという事で警告を出される。

うまい設定があるのか分からないが、インライン設定というのがあってそれで回避するようにした。こんなような記述になる:

/**
 * エントリーポイント
 * @param request 
 * @validate foo/bar.js
 * @onerror handleErrors
 **/ /*exported init*/
function init(request){
   ...
}

exportを書いておくことで、外部から参照されるオブジェクトであるという事をjshintに知らせる事ができる。 記述位置を最初迷ったのだが、jssp validator の動きに影響無いようだったので、上記のようにしている。

慣習的な記述

上記に関連するが、init関数はrequestオブジェクトを受け取る。 このため、init関数ので出だしは以下のようになる。

function init(request){
    ...

が、これでコードの方でrequestを使用しなかったら警告を出力される。仕方ないのでこれは単純に削除した。

function init(){
    ...

トランザクション

iAPではトラザクションを張っておきたい処理を関数化して特定のAPIに渡せばトランザクションをコントロールしてくれる。 http://www.intra-mart.jp/apilist/iap/apilist-ssjs/doc/platform/Transaction/index.html

Transaction.begin(function(){
  
  // トランザクション中で行いたい処理 
  
});

正常に終われば commit してくれるし、例外が発生していればrollbackされる。

ただこれをループ内で使おうとすると loopfunc(ループ内で関数を作るな)というチェックに引っかかってしまう。とりあえず以下のように書いておけば見逃してくれるようにはなるが

/* jshint -W083 */
Transaction.begin(function(){
  
  // トランザクション中で行いたい処理 
  
});

このコメントは同一スコープ内で有効になってしまうようなので、ちょっと微妙。 別にTransaction.beginに渡す関数を別途ちゃんと定義すれば回避できるのだが、それはそれでなんかかっこわるいなあという・・・。

大きな問題

これでだいぶコードの改善を進めることができるようになってきたが、一つ問題があって、それはjshintはE4Xを解釈できない事だ。特定のファイルのみE4Xを扱っていて、そこで報告されているエラーが尋常ではないので正しく動作していない模様。

E4Xを使用している箇所は1カ所なので、このファイルのみスキップさせようと考えているのだが、うまい方法が見つからない。

インライン設定で特定のファイルのみチェックをOFFにできると理想的だと思っている。 上位に位置する設定ファイルに個別にスキップするファイルを記述するのはタスクの独立性が低くなりそうなのがちょっと抵抗ある。