Webkrauts Logo

Webkrauts Webkrauts Schriftzug

- für mehr Qualität im Web

(Bessere) Projekt-Pakete schnüren mit Grunt

Build-Tool für Webworker

(Bessere) Projekt-Pakete schnüren mit Grunt

Mit Grunt gibt es ein neues Tool, das hilft, wiederkehrende Aufgaben in Frontend-Projekten zu automatisieren. Einmal konfiguriert ist das Testen und Ausliefern selbst bei umfangreichen Projekten problemlos möglich – und eignet sich so auch für die gemeinsame Entwicklung in Teams.

Die meisten Frontend-Entwickler setzen in der einen oder anderen Art Build-Prozesse für ihre Projekte ein, auch wenn sie vielleicht den Begriff nicht verwenden: Sie fassen einzelne Dateien für den Produktivbetrieb zusammen, minifizieren JavaScript-Code, um Übertragungszeit zu sparen, oder wandeln Sass- oder Less-Dateien in CSS für den Browser um.

Grunt hilft, viele dieser einzelnen Schritte – auch von Drittkomponenten – von zentraler Stelle aus zu steuern.

Hello Grunt!

»Cowboy« Ben Alman – vielen aus dem jQuery-Team bekannt – hat vor kurzem mit Grunt ein neues Tool vorgestellt, das dabei helfen soll, wiederkehrende Schritte in Build-Prozessen zu automatisieren.

Alle bisherigen Lösungen wurden aus anderen Programmier-Bereichen entliehen. Da diese nicht primär für Frontend-Projekte entwickelt wurden, haben sie alle ihre eigenen Probleme. Manche erfordern das Arbeiten in anderen Programmiersprachen (z.B. make, rake) oder setzen auf individuelle Beschreibungsformate (z.B. Ant, Maven), die erst erlernt werden müssen.

Daraus entstand der Wunsch, ein Tool explizit für den Web-Workflow in einer Sprache zu entwickeln, die Frontend-Entwickler beherrschen und es Ihnen ermöglicht, dieses Tool einfach Ihren Bedürfnissen anzupassen. Grunt basiert daher vollständig auf JavaScript und läuft dank Node.js auf allen gängigen Plattformen.

Es erlaubt eigenen JavaScript-Code auf Syntaxfehler und durch Unit-Tests zu überprüfen, einzelne Dateien zusammenzufassen und zu minifizieren und bietet darüber hinaus Plugins für viele der gängigen Webworker-Tätigkeiten.

Installation

Um Grunt verwenden zu können, muss zuerst Node.js auf dem eigenen Rechner installiert sein. Falls das noch nicht der Fall ist, kann man sich den entsprechenden Installer für seine Plattform einfach herunterladen und ausführen. Danach über die Konsole npm install grunt -g aufrufen, um Grunt selbst zu installieren. Ab jetzt steht auf der Konsole der Befehl grunt (bzw. unter Windows grunt.cmd) zu Verfügung.

Wie funktioniert nun Grunt?

Um Grunt in seinem Projekt nutzen zu können, muss im Hauptverzeichnis des Projektes eine Konfigurationsdatei abgelegt werden. In der aktuellen Version 0.3.9 heißt diese noch grunt.js. Da Windows allerdings beim Aufruf von grunt statt grunt.cmd versucht, die Datei per Windows Scripting Host auszuführen, wird sie künftig Gruntfile.js heißen, wie man es auch schon von anderen Build-Tools wie make oder rake kennt.

Eine Grundstruktur für eine Projektkonfiguration kann folgendermaßen aussehen:

  1. /*global module:false*/
  2. module.exports = function(grunt) {
  3.  
  4.   // Initialisiert Grunt mit den folgenden Projekteinstellungen
  5.   grunt.initConfig({
  6.  
  7.     // Liste der Dateien, deren Syntax mit JSHint geprüft werden soll
  8.     lint: {},
  9.  
  10.     // Liste der HTML-Dateien für JavaScript Unit-Tests mit QUnit
  11.     qunit: {},
  12.  
  13.     // Liste der Dateien, die zusammengefasst werden sollen
  14.     // (Quell- und Zieldateien)
  15.     concat: {},
  16.  
  17.     // Liste der Dateien, die minifiziert werden sollen
  18.     // (Quell- und Zieldateien)
  19.     min: {},
  20.  
  21.     // Tasks, die mit 'grunt watch' ausgeführt werden sollen
  22.     watch: {},
  23.  
  24.     // Konfigurationsoptionen für JSHint
  25.     // Siehe: http://www.jshint.com/docs/
  26.     jshint: {},
  27.  
  28.     // Konfigurationsoptionen für UglifyJS
  29.     // (wird zur Minifizierung verwendet)
  30.     // Siehe: https://github.com/mishoo/UglifyJS#usage
  31.     //
  32.     // Zusätzliche Einstellungen werden in der Regel nicht benötigt.
  33.     uglify: {}
  34.   });
  35.  
  36.   // Default task, der ausgeführt wird, wenn man Grunt
  37.   // ohne weitere Parameter aufruft.
  38.   grunt.registerTask('default', 'lint qunit concat min');
  39.  
  40. };

Hier sieht man schon, dass es sich dabei um ganz gewöhnliches JavaScript handelt. Relevant sind dabei nur die beiden Funktionen grunt.initConfig und grunt.registerTask.

Die erste Funktion definiert, welche Tasks es in unserem Projekt überhaupt geben soll. Die obige Auflistung ist dabei komplett optional: Tasks, die im Projekt nicht benötigt werden, können weggelassen werden oder zusätzliche über Plugins definiert werden.

Jeder einzelne dieser Tasks kann separat aufgerufen werden, wenn man ihn beim Aufruf von Grunt als Parameter angibt. Ein grunt lint beispielsweise führt nur die Syntax-Prüfung der angegebenen Dateien aus. Da man aber nicht alle Tasks einzeln von Hand aufrufen möchte, ist mit grunt.registerTask ein Standard-Task festgelegt, der ausgeführt wird, wenn Grunt ohne zusätzliche Parameter aufgerufen wird. In unserem Beispiel werden nacheinander die Tasks lint, qunit, concat und min aufgerufen.

Aufbau der einzelnen Tasks

Linting

Dieser Schritt bezeichnet die Prüfung der JavaScript-Dateien auf Syntaxfehler. Der Name leitet sich von »JS Lint« ab, dem ersten Tool dieser Art von Douglas Crockford. In diesem Schritt wird unter anderem geprüft, ob Semikolons korrekt gesetzt wurden, alle Klammern geschlossen oder ob Funktionen aufgerufen werden, die im Script nicht definiert sind. Hier lassen sich schnell auch Tippfehler entlarven, die oftmals beim Debugging im Browser schwerer zu finden sind.

Für diesen Task werden die zu prüfenden Dateien in einem Array aufgelistet:

  1. lint: {
  2.   files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js']
  3. },

Zuerst wird die Grunt-Konfiguration selbst geprüft, um Fehler im Buildprozess zu verhindern, danach folgen in diesem Beispiel alle JavaScript-Dateien in sämtlichen Unterordnern von »lib«, und schließlich alle Unit-Test im Test-Verzeichnis.

Unit-Tests

Wenn man noch einen Schritt weiter gehen will, sind Unit-Tests sehr sinnvoll, z.B. bei einem eigenen jQuery-Plugin oder einer größeren Applikation. Hier wird jeder Einzelbaustein im Code unabhängig auf korrekte Funktionsweise und Verhalten im Fehlerfall geprüft.

Als Standard für Unit-Tests verwendet Grunt QUnit aus dem jQuery-Projekt, es können aber auch andere Testing-Frameworks wie z.B. Jasmine über Plugins ergänzt werden. Auch hier werden die auszuführenden Tests in einem Array hintereinander aufgeführt:

  1. qunit: {
  2.   files: ['test/**/*.html']
  3. },

Zusammenfassen von Dateien

Bei diesem Task handelt es sich um einen sogenannten »Multi-Task«: Es können mehrere Gruppen von Dateien angegeben werden, die zusammengefasst werden sollen:

  1. concat: {
  2.   js: {
  3.     src: ['lib/module1.js', 'lib/module2.js', 'lib/plugin.js'],
  4.     dest: 'dist/script.js'
  5.   }
  6.   css: {
  7.     src: ['style/normalize.css', 'style/base.css', 'style/theme.css'],
  8.     dest: 'dist/screen.css'
  9.   }
  10. },

Wird der Task ohne weitere Parameter aufgerufen, werden alle Gruppen abgearbeitet. Wenn man nicht den ganzen Prozess starten möchte, weil sich z.B. nur CSS-Dateien geändert haben, reicht es in diesem Fall grunt concat:css aufzurufen.

Minifizieren von JavaScript

Ebenso verhält es sich beim Minifizieren von JavaScript-Dateien: Hier können ebenfalls eine oder mehrere Gruppen von Dateien angegeben werden. Um sich doppelte Tipparbeit zu sparen, können überall auch Werte andere Tasks referenziert werden. In diesem Beispiel ist die Quelldatei, die minifiziert werden soll, unsere Zieldatei aus dem vorherigen Concat-Task ('dist/script.js'):

  1. min: {
  2.   dist: {
  3.     src: ['<config:concat.js.dest>'],
  4.     dest: 'dist/script.min.js'
  5.   }
  6. },

Automatische Ausführung von Aufgaben

Alle wichtigen Schritte im Projekt sind nun konfiguriert, der Build-Prozess läuft fehlerfrei durch. Nichts würde aber den Arbeitsfluss mehr stören, als bei jeder Änderung die Tests erneut von Hand ausführen zu müssen.

Deswegen verfügt Grunt über die Möglichkeit, Tätigkeiten direkt auszuführen, sobald Dateien geändert wurden:

  1. watch: {
  2.   files: '<config:lint.files>',
  3.   tasks: 'lint qunit'
  4. },

Startet man Grunt mit grunt watch, werden in diesem Beispiel automatisch die Tasks »lint« und »qunit« ausgeführt, sobald sich eine der JavaScript-Dateien geändert hat, die im Lint-Task aufgeführt sind.

Diese Funktionalität lässt sich noch über Plugins erweitern, etwa für das automatische Kompilieren von Sass-Dateien zu CSS mit anschließendem Neuladen des Browserfensters. So muss man sich nicht um die Watch-Funktionen externer Tools zusätzlich kümmern, Grunt wird zur zentralen Schaltstelle.

Init-Tasks

Natürlich will man eine solche Konfiguration nicht immer für jedes Projekt von Hand neu schreiben oder aus bestehenden Projekten zusammenkopieren. Grunt bietet daher die Möglichkeit, gängige Projekttypen mit Hilfe sogenannter Init-Tasks anzulegen. Die geläufigsten sind »gruntfile« (Basiskonfiguration für Web-Projekte) und »jquery« (für Autoren von jQuery-Plugins). Die vollständige Liste erhält man mit grunt init.

Um die Konfiguration aus dem obigen Beispiel zu erhalten, ruft man grunt init:gruntfile auf. Mit ein paar wenigen Fragen wird die Grundstruktur vorab mit Leben gefüllt:

Screenshot der Ausgabe von grunt init:gruntfile
Screenshot der Ausgabe von grunt init:gruntfile

Plugins und praktische Helferlein

Trotz der noch recht jungen Version von Grunt gibt es bereits eine große Anzahl an Plugins: Sei es die Integration von CSS-Präprozessoren wie Sass/Compass, Less oder Stylus, CoffeeScript, Template-Sprachen wie Handlebars, Haml oder Jade oder automatische Größenoptimierung von Bildern – für viele der gängigen Frontend-Aufgaben finden sich vorgefertigte Lösungen.

Wer den aktuellen Build-Status nicht in der Konsole prüfen möchte, kann für Mac OS stattdessen zum Growl-Plugin greifen. Nutzer von Sublime Text können Grunt auch direkt aus dem Build-Menü heraus aufrufen. Dazu müssen nur zwei kleine Dateien im Sublime Text-Benutzerverzeichnis abgelegt werden:

Grunt - Build Project.sublime-build

  1. {
  2.   "cmd": ["grunt", "--no-color"],
  3.   "selector": ["source.js", "source.css", "source.sass", "source.less", "source.json"]
  4. }

Grunt - Watch Project Folder.sublime-build

  1. {
  2.   "cmd": ["grunt", "watch", "--no-color"],
  3.   "selector": ["source.js", "source.css", "source.sass", "source.less", "source.json"]
  4. }

Alternativ gibt es auch ein separates Grunt-Plugin für Sublime Text.

Fazit

Mit Grunt erhalten Frontend-Entwickler ein flexibles Werkzeug, mit dem sich wiederkehrende Aufgaben in Build-Prozessen automatisieren lassen. Dabei hilft das Prüfen und Testen des eigenen Codes im Hintergrund, die Qualität und Zuverlässigkeit zu verbessern, wovon nicht zuletzt Frontend-Teams bei der Arbeit mit größeren Projekten profitieren.

Update 21.12.2012:

Mit der Version 0.4, deren Veröffentlichung unmittelbar bevorsteht, werden sich bei Grunt ein paar Dinge verändern, die noch nicht in diesem Artikel berücksichtigt sind. Eine gute Übersicht der Änderungen findet ihr im Wiki des Projektes unter »Getting started« und »Upgrading from 0.3 to 0.4«.

Kommentare

Moritz Gießmann

Moritz Gießmann (Webkraut)
am 03.12.2012 - 10:45

Hat schon jemand HTML-Validierungs-Plugins getestet wie zum Beispiel das hier? https://github.com/jzaefferer/grunt-html
Mich würde interessieren, ob man das auch gut über Grunt abbilden kann?

Permanenter Link

Matthias Mees
am 04.12.2012 - 12:07

Gerade mal ausprobiert: Grundsätzlich funktioniert es, aber es ist ziemlich langsam (was man auch, wie Frederic bereits bemerkte, kompensieren kann, indem man es in einen separaten Task packt). Kommt auch nach ersten kurzen Tests auch nur mit reinen HTML-Dateien gut klar, bei PHP- oder Smarty-Templates ist es relativ empfindlich. Beides dürfte aber nicht am Grunt-Plugin selbst, sondern am eingebundenen VNU liegen.

Permanenter Link
Frederic Hemberger

Frederic Hemberger (Autor)
am 03.12.2012 - 11:00

Selber ausprobiert habe ich das HTML-Plugin noch nicht.

Ich persönlich würde sowas eher nur als separaten Task einbauen, der nicht vom Haupt-Task aufgerufen wird. Es kann ja durchaus sein, dass etwas im HTML enthalten ist, dass ein Validator irrtümlicherweise für einen Fehler hält (bestes Beispiel ist der "X-UA-Compatible"-Metatag), daraufhin würde dann der weitere Buildprozess abgebrochen und die Fehlermeldung des Validators ausgegeben.

So kann man dann den HTML-Code zwar einfach prüfen, behindert damit aber nicht den restlichen Buildprozess.

Permanenter Link

Die Kommentare sind geschlossen.