Mein neues Build-Tool: Grunt

Die Beschreibung des Build-Skriptes in diesem Artikel ist nicht mehr aktuell. Die Änderungen finden sich im Artikel Build-Skript: Errata.

Seit einigen Wochen verwende ich nicht mehr das Build-Skript des HTML5 Boilerplates sondern ein eigenes, das auf Grunt basiert. Garvin hatte mich dazu um einen Blogartikel gebeten, der erklärt „wofür ich Grunt verwende“ – ich musste ihn jedoch vertrösten, da ich bereits wusste, dass es im Adventskalender der Webkrauts einen Grundlagen-Artikel zu Grunt von Frederic Hemberger geben würde, dem ich nicht vorgreifen wollte. Ich überspringe daher hier die Grundlagen zu Grunt und verweise auf Frederics Artikel.

Warum ich Grunt vorziehe

Das Build-Skript des Boilerplates basiert auf ant, einem Build-Skript auf Java-Basis. Ich meide Java wie der Teufel das Weihwasser. Es ist eine wandelnde Sicherheitslücke, die ständig nach Updates brüllt. Zudem ist das auf Node.js basierende Grunt erheblich schneller, durch seinen modularen Aufbau flexibler einsetzbar und leichter anzupassen.

„Meine“ Grunt-Plugins

Ich bilde mit meinem Grunt-Skript quasi die Funktionalität des H5BP-Skriptes ab. Einiges davon kann Gruntout of the box“, ergänzend verwende ich die Plugins

z.T. auch, um zusätzliche Funktionen bereit zu stellen. Diese Auswahl ist natürlich höchst subjektiv und individuell – es gibt noch weit mehr Grunt-Plugins für andere Einsatzgebiete.

Ich empfehle, Plugins zu wählen, die schon länger existieren und regelmäßig gewartet werden. Ansonsten kann es durchaus mal vorkommen, dass ein Plugin von heute auf morgen den Dienst versagt – wobei es für relativ viele Plugins Alternativen gibt, die dann möglicherweise „einspringen“ können.

Es scheint (mir) zudem sinnvoll, eine Grunt-Konfiguration in eine generelle Projektvorlage zu integrieren, damit man verschiedene Einstellungen in Grunt direkt an die eigenen Vorlieben anpassen kann, ohne Grunt für jedes Projekt neu konfigurieren zu müssen.

Aufgaben des Build-Skripts

In meiner Grunt-Konfiguration eines typischen Projektes sind folgende „tasks“ (Aufgaben) definiert:

  • lint: prüft JS-Dateien auf (Syntax-)Fehler
  • clean: bereinigt das Build-Verzeichnis
  • server: stellt einen lokalen Webserver bereit
  • modernizr: erzeugt eine an das Projekt angepasste Version von Modernizr
  • concat: kombiniert JS-Dateien
  • min: minifiziert JS-Dateien
  • compass: kompiliert (und minifiziert) Sass- bzw. Compass-Dateien nach CSS
  • pgmin/pngnq/jpgmin: optimieren PNG- und JPG-Grafiken
  • copy: kopiert Dateien in das Build-Verzeichnis
  • reload: lädt den zuvor erzeugten Webserver und damit im Browser geöffnete Dateien automatisch neu, sobald Projektdateien verändert und gespeichert werden
  • hashres: benennt CSS- und JS-Dateien sowie deren Referenzen in HTML-Dateien (oder PHP-/Smarty-Templates) nach einem zufällig erzeugten Hash um, um Browsercaches ausztricksen

Dazu gibt es noch die sogenannten watch-Tasks, die verschiedene Kombinationen dieser Aufgaben im Hintergrund ausführen.

Warum das Ganze?

Build-Skripte führen automatisiert eher langweilige Routine-Aufgaben aus, die jedoch „best practises“ in der Frontend-Entwicklung entsprechen. Natürlich gibt es für alle oben aufgeführten Aufgaben auch manuelle Wege – aber warum sollte man diese Aufgaben von Hand ausführen, wenn es auch automatisiert geht? Warum riskieren, dass man einen der Schritte vergisst und sich somit doppelte Arbeit macht?

Ich verwende das Build-Skript im Prinzip derzeit auf zwei Arten:

  1. Während ich an einem Projekt lokal arbeite, starte ich über grunt watch im Projektverzeichnis (bzw. einfach grunt, da das Starten der Tasks server und watch in meiner Konfiguration den Standardtask bildet) einen Webserver, der automatisch im Browser neu geladen wird, wenn ich HTML-, SCSS oder JS-Dateien verändere und speichere. Letztere werden dabei automagisch kombiniert und minifiziert.

  2. Um einen auslieferungsfähigen Build des Projektes zu erzeugen, starte ich grunt deploy im Projektverzeichnis. Vereinfacht gesagt packt der Task mir alle Dateien in optimierter Form in ein Verzeichnis, aus dem sie dann direkt per FTP oder ssh auf den Webserver wandern können (was man übrigens auch Grunt erledigen lassen kann, aber die entsprechenden Plugins muss ich mir erst noch genauer ansehen).

Gerade diese Flexibilität gefällt mir sehr, sehr gut – man möchte im Grunde ständig neue Grunt-Plugins ausprobieren. Leider sind die Plugins z.T. spärlich bis gar nicht dokumentiert, weshalb man entweder den JS-Quellcode der Plugins durchschauen oder ausprobieren muss. Ich habe z.B. ein paar Anläufe gebraucht, um grunt-modernizr und grunt-reload zum laufen zu bringen.

Natürlich braucht es eine gewisse Zeit, um Grunt nebst Plugins (einmalig) so einzurichten, wie man es haben möchte – ich habe dabei Einiges aus dem init-Projekt von Hans-Christian Reinl abgeschaut, aber im Grunde ist auch die Konfiguration von Grunt recht simpel, weil eben alles in Javascript geschieht. (An dieser Stelle auch nochmal ein Dank an Hans, der mich überhaupt erst auf Grunt gebracht hat.)

Hat man diese Einrichtung jedoch einmal in einer universell verwendbaren Projektvorlage vorgenommen, erledigt Grunt im Hintergrund oder „auf Kommando“ zuverlässig und schnell all die kleinen Aufgaben, die uns sonst in Projekten Zeit und Nerven kosten, ganz automatisch.