20.12. JSHint – das bessere JSLint?

JSHint ist ein JSLint Fork von Anton Kovalyov. Dieser hat zwar nicht das Standardwerk der JavaScript-Welt „JavaScript – The Good Parts“ geschrieben, dennoch kritisiert er Crockford’s jslint, das in dem Buch ein eigenes Kapitel bekommen hat, pointiert mit:

JSHint is a fork of Douglas Crockford’s JSLint that does not tyrannize your code. It is
designed to detect errors that actually break your code while skipping things that, according
to Crockford, “are known to contribute mistakes in projects”. In other words, JSHint is a
fork of JSLint for the real world. The most important difference is that JSHint is developed
and supported by the JavaScript developer community and not by one very opinionated person.

Damit behält er vielleicht Recht. Die schlechten schlechten Teile, der sich sehr schnell
entwickelten „Toy Language“ müssen, im Gegensatz zu dem was Crockford sagt, nicht
unbedingt „schlecht“ sein. Werfen wir einen Blick darauf, was Kovlyov damit meint.

 

Hier ein Code Beispiel, das zum ersten Release von jshint in Anton Kovalyov Blog erschien:

/*global jQuery */ 
// Example taken from jQuery 1.4.2 source jQuery.extend({
	/* ... */
	isEmptyObject: function( obj ) {
		for ( var name in obj ) {
			return false;
		} 
		return true;
	}
	/* ... */
});

jslint zwingt den Developer hier die 'var' Deklaration ganz nach oben zu
setzen, was durch Hoisting
auch seinen
guten Grund hat.
Dennoch ist es eine Situation, die im Alltag öfters auftritt.

Ein weiteres Beispiel: jquery benutzt gerne undefinedals Variable
und implementiert damit ebenfalls ein sehr unbeliebtes Pattern:
(function (window, document, undefined) {
	// undefined will be undefined here even if there is
	// var undefined = 'hello'; 
	// somewhere in the global scope 
}(this, this.document));

Es ist zwar „Bad Style“, jslint meldet hier das reserviertes Wort als Fehler, während sich der Fehler mit jshint übergehen lässt. Das ist vor allem sinnvoll, wenn man jshint als z.B. Pre-Commit Hook verwendet.

Was hingegen in JSLint ein No-Go ist, aber doch vielseitig Praxis ist, wird hier mit JSHint möglich:

if (cond) statement(); //  JSLint: It expects all blocks to be enclosed in braces ({}):

if (cond) { // This would be the correct way JSLint wants
  statement();
}

Ein Bild von beiden Tools kann man sich effektiv machen, wenn man seinen JavaScript Code am Besten mit den Ergebnissen der Lints unter jshint.com und jslint.com vergleicht.

Was macht JSHint nun besser als JSLint? Oder doch nicht?

JSLint hat nun seit dem JSHint Fork im Januar 2011 nicht geschlafen und toleriert nicht nur ES5 Syntax sondern auch Node.js, Rhino und Yahoo-Widget Code sowie weitere Kleinigkeiten. Deutlich wird aber anhand der Commit History, dass hier nur Crockford himself den Hut darauf hat. Bei jslint gilt höchsten falls die Devise „Tolerate bad style“ und „Safe Subset“ während jshint hier auf Konfigurationen für Globalen setzt und damit mit HTML5/DOM5, verschiedenen Environments und JavaScript Bibliotheken klar kommt. Letzteres ist klar Community Driven und kommt damit dem Einsatz von JSHint im auf der CI Plattform oder als Pre-Commit Hook sehr entgegen.

  • JSHint bringt Wrapper für Rhino, JavaScriptCore (Apple) und den Windows Scripting Host
  • JSHint wird als NPM Package angeboten
  • JSHint unterstützt jQuery, DOJO, Mootools, Protoype, CouchDB
  • JSHint unterstützt ES5, ES.Next
  • JSHint erlaubt mehr Freiheit in der Festlegung eines eigenes CodeStyles, während JSLint nur Fehler und Bad Style nur „toleriert“
  • JSHint bietet Config-Files, so z.B. auch ein Development-Profil (z.B. mit console.log)
  • JSHint ist deutlich mehr community-driven (siehe Bug Tracker auf GitHubContributers)
  • Plugins für VIM, Emacs, TextMate usw.
  • Kompatibilität mit JSLint
  • JSHint besitzt im Gegensatz zu jslint Tests ;)

Installation von JSHint

JSHint lässt sich mittlerweile sehr einfach, nodejs vorausgesetzt, direkt über den Packetmanager NPM oder auch Ruby Gems  installieren. Alternativ sind natürlich auch noch Rhino oder WSH möglich.

$ sudo npm install -g jshint # install jshint via NPM globally
/usr/local/bin/jshint -> /usr/local/lib/node_modules/jshint/bin/hint                                                                                                                                                                                
jshint@0.5.5 /usr/local/lib/node_modules/jshint                                                                                                                                                                                                     
├──argsparser@0.0.6                                                                                                                                                                                                                                
└── minimatch@0.0.5
Usage: jshint path path2 [options]                                                                                                                                                                                                                  

Options:                                                                                                                                                                                                                                            

   --version            display package version                                                                                                                                                                                                     
   --config             custom config file                                                                                                                                                                                                          
   --reporter           custom reporter                                                                                                                                                                                                             
   --jslint-reporter    use a jslint compatible xml reporter                                                                                                                                                                                        
   --show-non-errors    show additional data generated by jshint

JSHint Config Files

Die Optionen von JSHint sind reichhaltig und lassen sich in vier Bereiche einteilen:

  • Settings für strikten Code (Enforcing Options)
  • Settings für tolerante Checks (Relaxing Options)
  • Settings für Environemnts (JS-Libraries, Environments)
  • JSLint Legacy Options (not maintained anymore, soon deprecated)

Die Optionen werden in der Web-Oberfläche von jshint.com im Übrigen auch angezeigt, und lassen sich daraus komfortabel kopieren, nachdem man die passende Konfiguration gefunden hat. Alternativ findet sich im node-jshint Repository hier auch eine Beispielkonfiguration.
Diese kann man in der Shell jslint einfach mitgeben:

$ jshint my-file-to-be-linted.js --config config.js

JSHint in der CI Plattform

JSHint generiert Violations XML-Ausgaben, die in CI Plattformen wie z.B. Jenkins mit gleichnamigen Plugin gelesen werden können.

$ jshint my-file-to-be-lint.js --jslint-reporter --config jslint-myproject-settings.js
<?xml version="1.0" encoding="utf-8">
<jslint>
        <file name="Exception.js">
                <issue line="15" char="2" reason="Missing semicolon." evidence="}" />
        </file>
</jslint>

namespace :jshint do
  task :require do
    sh "which jshint" do |ok, res|
      fail 'Cannot find jshint on $PATH' unless ok
    end
  end

  task :check => 'jshint:require' do
    project_root = File.expand_path('../../', File.dirname(__FILE__))
    config_file = File.join(project_root, 'config', 'jshint.json')
    js_root_dir = File.join(project_root, 'public', 'console', 'javascripts')

    files = Rake::FileList.new
    files.include File.join(js_root_dir, '**', '*.js')
    files.exclude File.join(js_root_dir, 'vendor', '**', '*.js')

    sh "jshint #{files.join(' ')} --config #{config_file}" do |ok, res|
      fail 'JSHint found errors.' unless ok
    end
  end
end

desc 'Run JSHint checks against Javascript source'
task :jshint => 'jshint:check'

Auch der PHP_CodeSniffer bietet mittlerweile die Option, JS Dateien mit jshint zu sniffen.

Für neue Blogupdates anmelden:


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.