Jak automatizovat statickou analýzu javascriptu pomocí JSHint (jslint) tak, aby šla spouštět z příkazové řádky. Validační funkce je definována v javascriptu, je tedy nutné spouštět javascriptový kód z příkazové řádky.

UPDATE: obdobná funkčnost té popsané v článku je již v základu zahrnuta v projektu JSHint, více viz https://github.com/jshint/jshint/blob/master/env/rhino.js

JSHint je javascriptová knihovna pro statickou analýzu zdrojových kódů v JavaScriptu. V následujícím článku se pokusím vysvětlit, jak tuto knihovnu používat z příkazové řádky k vlastní analýze souborů.

Základní problém který je nutné vyřešit je jak spustit z příkazové řádky javascriptový kód, když validace sama je javascriptová funkce.

Stáhneme si knihovnu JSHint, například zde: http://jshint.com/jshint.js, neměl by být problém pro analýzu použít i standardní jslint, pro ten však existují přímo hotové command line nástroje, například jslint4java . V článku budeme dále pracovat jen s JSHint .

Javascript umí spouštět například Rhino, project mozilly, implementace javascriptu v Javě (http://www.mozilla.org/rhino/scriptjava.html). Konzoli javascriptu pak dovedeme pustit například příkazem

java -cp .:js.jar org.mozilla.javascript.tools.shell.Main
rhino js console

js.jar je samotná implementace Rhina, stáhnout lze ze stránek projektu (http://www.mozilla.org/rhino/download.html).

To by byl první krok, dovedeme spustit z příkazové řádky javascriptovou konzoli. Teď je nutné přesvědčit ji, aby v dávce spustila náš validační kód a vytiskla výstup. Když předáme jako parametr Rhinu cestu k souboru, začne automaticky vykonávat jeho kód. Vytvoříme si tedy javascript, který spustí validaci a vytiskne výstup.

na stránce https://developer.mozilla.org/en/Rhino_Shell je popsáno, jaké parametry Rhino akceptuje a zároveň jaké funkce nám poskytuje. Využijeme tyto funkce:

load([filename,…])

Load JavaScript source files named by string arguments. If multiple
arguments are given, each file is read in and executed in turn.

print([expr…])

Evaluate and print expressions. Evaluates each expression, converts the
result to a string, and prints it.

readFile(path [, characterCoding])

Read given file and convert its bytes to a string using the specified character
coding or default character coding if explicit coding argument is not given.

Kód samotného scriptu pro testování by mohl vypadat nějak tahle:

// dodefinujeme funci trim pro stringy, abychom mohli orezavat evidence hodnoty
String.prototype.trim = function () {
    return this.replace(/^\s*/, "").replace(/\s*$/, "");
};

// funckce pro zkraceni nazvu souboru, odstraneni cesty
function onlyFilename(path) {
    parts = path.split("/");
    return parts[parts.length - 1];
}

// natahneme si z bin knihovnu jshint.js
load("bin/jshint.js");

// a iterujeme pres vsechny predane parametry, cesty k validovanym souborum
for (i in arguments) {
    // pocitadlo chyb, pro kazdy soubor zvlast
    var counter = 0;
    // samotna validace, vraci true/false pokud soubor obsahuje chyby
    var result = JSHINT(readFile(arguments[i]), "");
    if (!result) { // pokud neprosla validace

        // iteruji pres vsechny chyby a budu je pocitat a vypisovat
        for (var j in JSHINT.errors) {
            counter++;
            // aktualni chyba
            var error = JSHINT.errors[j];
            // muze byt null, pokud je nejaka fatalni, pole konci null
            if (error != null) {
                // nekdy je chyba bez dukazu, napriklad kdyz uz kontrola dal neprobiha, jen hlasi
                // ze chyb je moc a dal nepokracuje
                if ((typeof(error.evidence) != "undefined")) {
                    // naformatujeme vystup chyby, print je funkce rhina
                    print(onlyFilename(arguments[i]) + ":" + error.line + ":" + error.character
                            + ": " + error.evidence.trim() + "\n  " + error.reason);
                } else {
                    print(error.reason);
                }
                print("-----------");
            }
        }
        // result souboru, vytiskneme ze nalezl chyby a kolik jich bylo
        print("Error, file '" + arguments[i] + "' contains " + counter + " errors!")
    }
}

Na začatku jsou definovány nějaké pomocné funce, jejich význam je jen hezčí formátování výstupu. To důležité je níže

load(„bin/jshint.js“); – naimportuje z adresáře bin soubor jshint.js, to je naše testovací knihovna, tu vložíme a tu pak budeme volat.

JSHINT(readFile(arguments[i]), "") – spustí test, funkce readFile() je funkce z Rhina, která umí načíst externí sobor (zdrojový kód k validaci). Návratová hodnota globální funkce JSHINT je logická proměnná, zda test prošel bez chyb či nikoli.

arguments – pole z Rhina, které drží informace o argumentech předaných z příkazové řádky. Tak můžeme jako další parametr při spouštění Rhina předat cestu k souboru pro zvalidování.

JSHINT.errors – pole chyb jako výsledek validace, přes něj budeme iterovat v případě že test neprošel a obsahuje chyby.

Teď máme již vše připraveno, můžeme zkusit spustit test

Vzorový javascript, který budeme validovat

function timeMsg() {
    var t = setTimeout("alertMsg()", 3000);
}
function alertMsg() {
    alert("Hello");
}

Příkaz

java -cp .:bin/js.jar org.mozilla.javascript.tools.shell.Main bin/runner.js example.js

Výstup validace

java -cp .:bin/js.jar org.mozilla.javascript.tools.shell.Main bin/runner.js bin/example.js
example.js:2:13: var t = setTimeout("alertMsg()", 3000);
  Implied eval is evil. Pass a function instead of a string.
-----------
Error, file 'bin/example.js' contains 1 errors!
js validace - vysledky

Ve vzorovém javascriptu tedy byla nalezena chyba, kde se nachází a jaké je její řešení je standardním výstupem validátoru.

Zdrojové kódy k tomuto článku včetně příkladu jsou ke stažení: jsvalidation.zip

Odkazy
http://www.mozilla.org/rhino/ : Rhino (JavaScript for Java)
http://jshint.com/ : JSHint
http://www.jslint.com/ : JSLint
http://code.google.com/p/jslint4java/ : jslint4java (command line tool)