Vom Build-Tool zur Projektvorlage
Bereits seit zwei Jahren verwende ich Grunt als Build-Tool in Webprojekten. Seit ich hier zuletzt darüber geschrieben habe, hat sich das „kleine Build-Skript“ eher zu einer Projektvorlage entwickelt – Zeit für ein Update.
Was enthält die Projektvorlage?
Vereinfacht gesagt: Alles, was ich – normalerweise – in einem Webprojekt brauche oder brauchen könnte. HTML, SCSS, jQuery samt einigen nützlichen Plugins, ein paar weitere sinnvolle Dateien und eben ein auf Grunt basierendes Build-Skript, um das alles zusammen zu bringen und Abläufe zu automatisieren.
- HTML wird mittels assemble aus Handlebars-Templates erzeugt. Das ermöglicht Includes und zumindest zum Teil von Variablen gesteuerte Generierung von Markup (um z.B. auf Testservern kein Piwik-Snippet einzubinden).
- CSS wird aus Sass (genauer: SCSS) kompiliert, mittlerweile mit dem schnelleren libsass. Vendor-Prefixes, soweit nötig, erzeugt autoprefixer. Da ich ausschließlich mobile-first SCSS schreibe, wird für IE < 9, die keine Media-Queries unterstützen, ein separates Stylesheet erzeugt.
- Javascript- und jQuery-Plugins hole ich über Bower ins Projekt. Die in der Vorlage referenzierten Plugins stellen Funktionalitäten bereit, die ich regelmäßig in Projekten benötige. (Bonuspunkte, wenn sie zudem einfach anzupassen sind.)
- Dazu enthält das Projekt die Server-Konfiguration für Apache (in Form einer .htaccess-Datei) des H5BP-Projektes.
Ich verwende ausdrücklich und bewusst kein CSS-Framework und keine Sass-Bibliothek wie Compass. Natürlich schreibe ich nicht jede Zeile SCSS in jedem Projekt komplett neu. Ausgehend von normalize habe ich ein Basis-SCSS sowie eine kleine Bibliothek aus (zum Teil selbst geschriebenen, zum Teil von anderen übernommenen) Mixins. Die SCSS-Partials sind dabei so stukturiert, dass man nur in bestimmten (anfänglich leeren) Partials Code schreibt oder !default
-Variablen überschreibt, damit man die Projektvorlage bei Bedarf einfach aktualisieren kann.
Und was machen nun all diese Grunt-Plugins?
Mittlerweile hat sich im Build-Skript so viel verändert oder ist neu hinzu gekommen, dass ich es noch einmal komplett neu dokumentieren wollte.
Generieren
Mit einer Ausnahme bereits oben beschrieben.
- assemble: Erzeugt aus Handlebars-Templates HTML
- grunt-sass: Kompiliert SCSS zu CSS (minifiziert diese auch)
- grunt-autoprefixer: Ergänzt das CSS um die notwendigen Vendor-Prefixes
- grunt-fontello: Generiert ausgehend von einer JSON-Datei über Fontello einen individuellen Iconfont, der nur die benötigten Icons enthält – samt dafür notwendigem SCSS
Servieren
Der lokale Webserver, den Grunt hier startet, ist sehr einfach gehalten, also nicht mit einem lokalen Apache zu vergleichen. Nötig ist er dennoch, da bestimmte Dinge (AJAX) ohne einen lokalen Server nicht funktionieren. Zudem bietet er Livereload-Funktionalität, schont also die Tastatur. ;-)
- grunt-contrib-connect: Startet einen lokalen Entwicklungsserver
- grunt-localtunnel-me: Legt den lokalen Webserver über localtunnel offen, damit dieser von außen (z.B. externe Testgeräte) zu erreichen ist
- grunt-contrib-watch: Läuft während der lokalen Entwicklung im Hintergrund und führt bestimmte Tasks aus, wenn sich bestimmte Dateien ändern
Optimieren
- grunt-contrib-concat: Kombiniert alle JS-Dateien zu einer
- grunt-contrib-uglify: Minifiziert die kombinierte JS-Datei
- grunt-modernizr: Erzeugt basierend auf den im CSS und JS verwendeten Selektoren einen optimierten Build von Modernizr
- grunt-contrib-imagemin: Optimiert Rastergrafiken
- grunt-svgmin: Optimiert Vektorgrafiken
- grunt-hashres: Versioniert sowohl CSS-/JS-Dateien als auch deren Referenzierungen im Markup (Stichwort: Cache-Busting)
Organisieren
Ich hab’s gern ordentlich, deshalb landet alles, was auf einen Staging- oder Live-Server soll, in einem eigenen Verzeichnis.
- grunt-contrib-clean: Leert das Build-Verzeichnis
- grunt-contrib-copy: Kopiert Dateien ins Build-Verzeichnis, die nicht von anderen Tasks dorthin verschoben werden
Testen
Diese Plugins bzw. die dazugehörigen Tasks machen alle dasselbe – sie überprüfen den betreffenden Code auf syntaktische Korrektheit.
- grunt-htmlhint: HTML
- grunt-scss-lint: SCSS
- grunt-contrib-jshint: JS
Aus diesen Plugins habe ich ein paar Tasks definiert:
- grunt dev startet den lokalen Webserver, leitet ihn nach außen weiter und wirft den im Hintergrund laufenden watch-Task an, der auf Änderungen im Projekt reagiert.
- grunt deploy erzeugt einen zum Deployment auf einem Live-Server gedachten, optimierten Build des Projektes im Unterverzeichnis dist des Projektverzeichnisses. grunt stage tut im Prinzip dasselbe, aber eben für einen Staging-Server. Basierend auf ein paar Variablen kann man dabei unterschiedliches Markup erzeugen, um z.B. dort keine Tracking-Snippets einzubinden.
- grunt test führt die oben gelisteten Test-Tasks aus.
Ist das also nur für statisches HTML gedacht?
Jein. Alle Grunt-Tasks können grundsätzlich auch z.B. PHP-Dateien für ein CMS wie ProcessWire oder Smarty-Templates für Serendipity verarbeiten. Allerdings müssen dafür Stand heute einige Tasks von Hand angepasst werden. (Deshalb liegen übrigens alle Javascripte in scripts und alle Stylesheets in styles – so erwartet es ProcessWire standardmäßig.)
Ich fange zwar grundsätzlich jedes Webprojekt mit einem HTML-Prototypen an, aber das wäre für mich derzeit das letzte Feature, um das man diese Projektvorlage noch erweitern könnte: Projektstruktur und Build-Skript so anzupassen, dass man nahtlos vom HTML-Prototypen zum CMS-Template wechseln kann (was zumindest in ProcessWire ansonsten problemlos möglich ist). Allein, fehlt mir noch ein wenig die richtig Idee, wie man das sinnvoll umsetzen könnte …