Assemble – erste Schritte mit dem Static Site Generator für Grunt

Wie bereits im Artikel „Static Site Generators“ geschrieben, habe ich mich letztes Jahr projektbedingt mit Assemble beschäftigt. Assemble ist ein Static Site Generator für Grunt und nutzt Handlebars als Template-Engine.

tl;dr; „Warum ist mir egal, ich will Code sehen” => hier klicken

Mir persönlich nutzt ein Static Site Generator oft bei meinem täglichen Workflow, beim Erstellen von Klickdummys und Templates. Der Vorteil eines „Static Site Generator“ liegt auf der Hand: man nutzt die Dynamik einer Script-Sprache um wiederholende Elemente nicht jedes Mal neu schreiben zu müssen oder Markup mit dynamischen Daten zu vermischen. Man stelle sich vor, man muss bei einer statischen Seite einen Navigationspunkt ändern und das bei vielen Seiten, das ist aufwändig und fehleranfällig.

OK, jetzt könnte man meinen, dass man doch gleich PHP oder ähnliches nehmen könnte. Ja kann man auch. ;) Aber auch hier wäre es von Vorteil, wenn man das Markup von der Logik entkoppelt, also sowas wie ein MVC-Prinzip. Möchte/Soll man die Template-Schnipsel wieder verwenden können, also sollen die Schnippets möglichst modular wiederverwendbar sein, ist man schnell bei einer Template-Engine, die das unterstützt.

Ähnlich mit Javascript-Bausteinen. Immer mehr Teile der Website werden mit dynamischen Daten und Markup-Schnipseln in die Seite integriert, bzw. werden ja bereits viele JS-Anwendungen komplett im Browser zusammengebaut… buh! lang lebe semantisches Markup!

Auch hier gibt es Template-Engines, z.B. Handlebars, die eine klare Trennung von Markup und Logik handle’n ;). Und schwuppdiwupp ist man schon bei einem System, aus dem man auch statische Seiten exportieren könnte. Mit Grunt ein paar Prozesse definiert, fertig. Bonus: die Handlebars-Templates können auch im Javascript verwendet werden.

Assemble macht das alles und bringt noch ein paar Features mehr mit.

  • Handlebars (nutzt Mustache Syntax)
  • YAML Frontmatter
  • Datenverarbeitung aus JSON oder YAML

Installation von Assemble

npm install assemble --save-dev

Grunt Beispiel Config

assemble: {
    options: {
        flatten: true,
        assets: 'assets',
        helpers: ['templates/helpers/*.js', 'handlebars-helpers'],
        partials: ['templates/partials/**/*.hbs'],
        layout: ['templates/layouts/default.hbs'],
        data: ['templates/data/*.{json,yml}']
    },
    pages: {
        src: ['templates/pages/**/*.hbs'],
        dest: 'dist'
    },
    docs: {
        src: ['templates/docs/**/*.hbs'],
        dest: 'dist/docs'
    }
}

Ein einfaches Default-Layout mit Partials

Ein einfaches HTML5-Dokument als Beispiel für die Haupt-Layout-Datei. Über {{> Name}} können so genannte Partials integriert werden. In dem Beispiel, Teile des Dokuments, die immer auf der Seite sind (Header, Footer,…).

layout: default.hbs

<!DOCTYPE html>
<html lang="en" class="no-js">
  <head>
    <title>{{page-title}}</title>
    {{> head }}
  </head>
  <body {{#if site-class}} class="{{site-class}}"{{/if}}>
    {{> header}}
    {{> nav}}
    {{> body}}
    {{> footer}}
    {{> foot }}
  </body>
</html>

Das {{> body }} Partial ist hier etwas spezieller und wird als Platzhalter für die jeweiligen Seiten verwendet. Es kann im oberen Bereich der Seite eine YAML-Frontmatter-Konfiguration enthalten. Diese YAML-Daten können z.B. auch an das Haupt-Layout-Template (Default.hbs) übergeben werden.

pages: index.hbs (body partial)

---
page-title: Index Template
site-class: "index"

intro: 
  name: 
  - the name 1
  - the name 2
  - the name 3
---
<h1>here comes the hotstepper</h1>

<ul class="intro">
  {{#each intro }}
  <li class="intro_item
    {{#unless @index}}
    is-active
    {{/unless}}">
    <h3>{{ this.name }}</h3>
    <img src="{{ assets }}/img/{{hyphenate this.name }}.jpg" alt="{{ this.name }}">
    <p>{{ lorem ipsum.sentence }}</p>
  </li>
  {{/each}}
</ul>

Diese Intro-Section (vielleicht News-Teaser) kann man als Partial auslagern, wenn man dieses HTML-Konstrukt mehrfach nutzen möchte.

partials: _intro-section.hbs

<ul class="intro">
  {{#each intro }}
  <li class="intro_item
    {{#unless @index}}
    is-active
    {{/unless}}">
    <h3>{{ this.name }}</h3>
    <img src="{{ assets }}/img/{{hyphenate this.name }}.jpg" alt="{{ this.name }}">
    <p>{{ lorem ipsum.sentence }} </p>
  </li>
  {{/each}}
</ul>

Anderes Page-Layouts innerhalb „pages“ verwenden

Wenn man für bestimmte Seite ein anderes „Page-Layout“ verwenden möchte, kann man das einfach in Kopfbereich (YAML-Frontmatter-Bereich) der Seite angeben.

pages: test.hbs

---
page-title: pages
layout: pages.hbs
---

Die Datei ‘pages.hbs’ legt man in das Layout-Verzeichnis, das man in der Grunt-Config angegeben hat. Hier kann man auch das Layout-Verzeichnis mit layoutdir angeben:

assemble: {
    options: {
        layout: 'default.hbs',
        layoutdir: 'templates/layout/'
    }
}

Dann wird unter ‘layout’ nur noch der Name der Default-Datei angegeben.

Anders Dateiformat in der Ausgabe generieren

Standardmäßig wird aus den .hbs Dateien eine HTML-Datei generiert. Möchte man ein anderes Dateiformat generieren, kann man dafür eine gesonderte Konfiguration in Grunt angeben. In den ‘options’ kann die ’ext’ention festgelegt werden.

gruntconfig.js

assemble: {
  // Build sitemap from JSON and templates 
  sitemap: {
    options: { ext: '.xml' },
    files: {
      '.': ['path/to/sitemap.tmpl' ]
    }
  },
  // Build README from YAML and templates 
  readme: {
    options: { ext: '.md'  },
    files: {
      '.': ['path/to/readme.tmpl' ]
    }
  }
}

Dynamische Daten verwenden

Ein großer Vorteil von Assemble ist es, dass mit Assemble Daten im JSON- oder YAML-Format verarbeitet werden können. Das ist praktisch, wenn man z.B. wiederkehrende Daten hat. Diese Daten legt man das in den Grunt-Options festgelegte Verzeichnis, z.B. in ‘templates/data/’.

assemble: {
    options: {
        data: ['templates/data/*.{json,yml}']
    }
}

Wiederkehrende Daten kann man in ein JSON-Objekt oder in eine YAML-Notation auslagern.

Beispiel: site.json

{
    "sociallinks": [
        "facebook",
        "youtube",
        "xing"
    ]
}

sociallinks.hbs partial template

<div class="social-links">
    {{#each site.sociallinks}}
    <a href="#linkurl" title="Link to {{capitalizeEach this }}">
        {{.}}
    </a>
    {{/each}}
</div>

simple Daten übergeben

Mit dem Helper parseJSON kann man einfache Daten bei der Einbindung an ein Partial übergeben.

Einbindung des Partials in page-Template

{{#parseJSON '{"teasertext": false, "limit": 5}'}}
    {{> _newslist }}
{{/parseJSON}}
### partial code
<ul class="teaserlist">
{{#each limit==limit}}
    <li>
        <h3>Teaser Headline</h3>
        {{#is teasertext true}}
        <p>lorem ipsum</p>
        {{/is}}
    </li>
{{/each}}
</ul>

So kann man ein Partial an anderen Stellen in leicht veränderter Ausgabe verwenden, schön modular. In dem Beispiel wird eine Teaserliste mit 5 Teasern und ohne Teaser-Text ausgegeben.

Eine Übersicht der generierten Seiten erstellen (Sitemap)

Um eine Link-Liste mit den generierten Seiten als Übersicht zu erstellen, kann man den View-Helper ’{{#pages}}’ verwenden.

---
title: Overview
---
<ul>
{{#pages}}
    <li><a href="{{lowercase basename}}{{ext}}">{{data.title}}</a></li>
{{/pages}}
</ul>

Für die Linkbezeichner wird hier der Wert aus dem YML-Kopfbereich ‘title’ verwendet. Zugriff auf die Daten bekommt man mit ‘data’, also ‘data.title’.

Weitere nützliche Handlebars-Helpers

Mittlerweile gibt es eine Vielzahl an zusätzlichen Plugins bzw. Handlebars-Helpers.

Nach dem Installieren, die Helper in den Grunt-Options Grunt-Options hinzufügen.

assemble: {
    options: {
        helpers: ['templates/helpers/*.js', 'handlebars-helpers', 'handlebars-partials'],
    }
}

Soweit ein kleiner Einblick in Assemble. Letztlich hab ich festgestellt, dass der Artikel mehr zu einer Tipps & Tricks Liste geworden ist… und wieder mal länger als anfangs gedacht.

Habt ihr weitere Tipps zur Verwendung?

3 thoughts on “Assemble – erste Schritte mit dem Static Site Generator für Grunt”

  1. Ich benutze assemble auch schon eine ganze Zeit, um Prototypen oder statisches Zeug zu klöppeln. Klar, man überlegt alle paar Wochen mal, ob das wirklich lohnt (kostet ja auch ein wenig Ausführungszeit im Build), aber dann fällt einem wieder ein, dass man selbst in Projekten mit vielen Seiten nur an einer Stelle etwas ändern muss, wenn eine neue jQuery-Version eingebunden wird und so. Vor allem ist es für meinen Geschmack flexibler einsetzbar als seine „blog-aware“ Kollegen.

    Was ich cool finde und gerne nutze sind die „selbst definierten“ Variablen. So definiere ich z.B. in den Optionen im assemble-Task die Variablen production und remote, die man dann in Handlebars über z.B. #unless abfragen kann. Sehr nützlich z.B., um auf dem lokalen dev-Server noch kein Piwik-Snippet einzubinden, im deployment-Task dann aber schon.

Kommentare sind geschlossen.