LoginSignup
2
2

More than 5 years have passed since last update.

JSLint for Visual Studioを改造する

Last updated at Posted at 2015-07-25

背景

JsLint for VisualStudioはJavaScriptの静的解析を行うjslintやjshintをVisualStudio上で動かすVisualStudio用のアドインである。

しかしながら、2012年が最後のリリースでメンテナンスをしていない。
そのため、以下のようなコードはエラーとなり、オプションで回避もできない。

switch (1) {
    case 1:
        x = 3;
        break;
    case 2:
        x = 4;
        break;
    case 3:
        x = 5;
        break;
    default:
        x = 3;
        break;
}

/* 改造前のただしい書き方
switch (1) {
case 1:
    x = 3;
    break;
case 2:
    x = 4;
    break;
case 3:
    x = 5;
    break;
default:
    x = 3;
    break;
}*/

このドキュメントではこれを何とかするものである。

JSLint.VS2012.vsixの中身

VSIXはVisualStudioの拡張配置である。このVSIXはzipファイルにすぎない。
拡張子をzipにしてみよう。

次のような内容が確認できる。

jslint1.png

JSLint.Framework.dllが実際のjslintなどの処理を行っているプログラムである。
このDLLはjavascriptファイルをリソースとしてもっており、そのJavaScriptをNoesis.Javascript.dllを利用して実行し、静的解析を行っている。

JSLint.Framework.dllの改造方法

JSLint.Framework.dllの使用方法

JSLint.Framework.dllを参照して以下のようなプログラムを実行する。

using System;
using System.Collections.Generic;
using JSLint.VS2010.LinterBridge;
using JSLint.VS2010.OptionClasses;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var jslint = new JSLint.VS2010.LinterBridge.JSLinter();
            var option = new JSLintOptions();
            option = JSLintOptions.Default;
            option.SelectedLinter = Linters.JSHint;
            string script = @"
var x = 9;
switch (1) {
    case 1:
        x = 3;
        break;
    case 2:
        x = 4;
        break;
    case 3:
        x = 5;
        break;
    default:
        x = 3;
        break;
}
/* 改造前のただしい書き方
switch (1) {
case 1:
    x = 3;
    break;
case 2:
    x = 4;
    break;
case 3:
    x = 5;
    break;
default:
    x = 3;
    break;
}*/
x = x * 2;
";
            List<JSLintError> list = jslint.Lint(script, option, true);
            foreach (var x in list)
            {
                Console.WriteLine(x.Line + ":" + x.Message);
            }
        }
    }
}

おそらく、jshintでエラーが発生するだろうが、これをなんとかすればよい。

.NETのDLLの埋め込みリソースを取得する

VisualStudioの開発者コマンド プロンプトから以下のコマンドを実行すると、埋め込みリソースが展開される。

ildasm ../JSLint.Framework.dll /out=JSLint.Framework.il

jslint2.png

jslintの比較方法を修正する。

JSLint.Framework.JS.jshint.jsを以下のように修正する。07-25-2015でコメントを打っている箇所が修正箇所だ。

    blockstmt("switch", function () {
        var t = nexttoken,
            g = false;
        funct["(breakage)"] += 1;
        advance("(");
        nonadjacent(this, t);
        nospace();
        this.condition = expression(20);
        advance(")", t);
        nospace(prevtoken, token);
        nonadjacent(token, nexttoken);
        t = nexttoken;
        advance("{");
        nonadjacent(token, nexttoken);
        indent += option.indent;
        this.cases = [];
        for (;;) {
            switch (nexttoken.id) {
            case "case":
                switch (funct["(verb)"]) {
                case "break":
                case "case":
                case "continue":
                case "return":
                case "switch":
                case "throw":
                    break;
                default:
                    // You can tell JSHint that you don't use break intentionally by
                    // adding a comment /* falls through */ on a line just before
                    // the next `case`.
                    if (!ft.test(lines[nexttoken.line - 2])) {
                        warning(
                            "Expected a 'break' statement before 'case'.",
                            token);
                    }
                }
                // indentation(-option.indent); m.ita 07-25-2015 for ignore switch indent.
                advance("case");
                this.cases.push(expression(20));
                increaseComplexityCount();
                g = true;
                advance(":");
                funct["(verb)"] = "case";
                break;
            case "default":
                switch (funct["(verb)"]) {
                case "break":
                case "continue":
                case "return":
                case "throw":
                    break;
                default:
                    if (!ft.test(lines[nexttoken.line - 2])) {
                        warning(
                            "Expected a 'break' statement before 'default'.",
                            token);
                    }
                }
                //indentation(-option.indent);  m.ita 07-25-2015 for ignore switch indent.
                advance("default");
                g = true;
                advance(":");
                break;
            case "}":
                indent -= option.indent;
                indentation();
                advance("}", t);
                if (this.cases.length === 1 || this.condition.id === "true" ||
                        this.condition.id === "false") {
                    if (!option.onecase)
                        warning("This 'switch' should be an 'if'.", this);
                }
                funct["(breakage)"] -= 1;
                funct["(verb)"] = undefined;
                return;
            case "(end)":
                error("Missing '{a}'.", nexttoken, "}");
                return;
            default:
                indent += option.indent;  //m.ita 07-25-2015 for ignore switch indent.
                if (g) {
                    switch (token.id) {
                    case ",":
                        error("Each value should have its own case label.");
                        return;
                    case ":":
                        g = false;
                        statements();
                        break;
                    default:
                        error("Missing ':' on a case clause.", token);
                        return;
                    }
                } else {
                    if (token.id === ":") {
                        advance(":");
                        error("Unexpected '{a}'.", token, ":");
                        statements();
                    } else {
                        error("Expected '{a}' and instead saw '{b}'.",
                            nexttoken, "case", nexttoken.value);
                        return;
                    }
                }
                indent -= option.indent;  //m.ita 07-25-2015 for ignore switch indent.
            }
        }
    }).labelled = true;

.NETの埋め込みリソースを変更してDLLを作り直す

JavaScriptを修正したら、以下のコマンドでDLLを作り直す。

ilasm JSLint.Framework.il /dll

動作確認

JSLint.Framework.dllを使用したプログラムを再実行すれば、switchのインデントでjslintが文句をいわなくなったことが確認できる。

参考

Is it possible to Add/Remove/Change an embedded resource in .NET DLL?
http://stackoverflow.com/questions/6545858/is-it-possible-to-add-remove-change-an-embedded-resource-in-net-dll

ひとこと

自分でメンテできなかったり、金でメンテさせることができないツールを安易に採用してはいけない。

なお、お助け料一億万円でいいぞ!ローンも可。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2