Ein Blog mit Jekyll und Gulp

veröf­fent­licht am 03 November 2015

Beste Webent­wick­lungs­um­ge­bung die ich je auf die Beine gestellt hab, doch war das Einrichten (mit meinen Ansprü­chen) nicht so einfach wie ich dachte.

Der ursprüngliche Plan

Eigent­lich wollte ich nur einen Server aufsetzen um kleinere Witz-­Seiten wie is it friday yet zu erstel­len, da ich so kleine php Seite ziemlich gerne mal zusammen hacke wenn mir die Idee dafür kommt. Da ich bereits eine nicht genutzte VM bei DigitalOcean hatte, dachte ich mir das ich diese einfach mal auf vorder­mann bringe. Ich hätte zwar auch einen Share­d-Hoster nehmen können aber wo wäre da der Spaß gewesen. ;)

Da ich nginx lange nicht mehr verwendet hab, hab ich diesen mal schnell instal­liert. Bevor ich aber php-fpm instal­liert hab, kam mir der gedanke mal zu forschen, welche Möglich­keiten man hat stati­sche Seiten zu generie­ren. Dabei stieß ich schnell über StaticGen auf Jekyll. Das Build-­System ist mir schon mal unter die Augen gekom­men, da es von GitHub Pages verwendet wird, daher war ich nun interessiert.

Erste Einrichtung

Die erste Einrich­tung ist eigent­lich ein Kinder­spiel und ist direkt auf der Start­seite von Jekyll beschrie­ben, auch die ersten Seiten zu bauen war ziemlich leicht. Mit jekyll new wird ein Beispiel-Pro­jekt generiert an dem man sich recht gut entlang hangeln kann.

Eigent­lich kopiert Jekyll einfach nur alles was in dem Projek­t-Order ist in den Build-Or­der, jedoch mit ein paar wichtigen Ausnah­men. Es gibt ein paar Ordner welche von Jekyll beson­ders behan­delt werden. Außerdem kann man jede Datei mit einem sogenannten “Front Matter” ausstat­ten, welches eine kleine Yaml Datei am anfang der Datei ist. Das Konzept ist recht einfach und gut dokumentiert. Die Probleme entstehen erst, wenn man Abhän­gig­keiten mit bower einbringt und diese am besten dann auch noch kompri­mieren will.

Assets mit Gulp und Bower

Ich bin nicht neu in der Web-Ent­wick­lung und habe nunmal etwas hohe Ansprü­che. Jekyll bringt einen sass und Coffee­S­cript Kompiler mit sich. Mir war jedoch sofort klar dass das nicht ausrei­chen wird wenn ich bootstrap und 1000 weitere libra­ries verwenden will. Außerdem will ich natür­lich dass das css/js kompri­miert wird, also musste ein fortge­schrit­tenes Build-Tool her.

Nun die Entschei­dung für Gulp war für mich ganz klar. Ich kenne es bereits und hab gute Erfah­rungen damit. Um Gulp verwenden zu können waren aller­dings erst mal einige Anpas­sungen an der _config.yml von Jekyll nötig.

exclude:
  - bower_components
  - bower.json
  - node_modules
  - package.json
  - gulpfile.js

keep_files:
  - assets

exclude sorgt dafür, dass bestimmte Dateien nicht von Jekyll mit in den Build-­Ordner kopiert werden, damit nicht alle rohen Dateien und die Build-­Kon­fi­gu­ra­tion sich nachher im Documen­tRoot befin­den. keep_files verhin­dert, dass Die erwähnten Dateien aus dem Build Ordner gelöscht werden, in dem Beispiel werde ich also alle Dateien in den assets Ordner werfen.

Nun kann in dem Projekt, wie bei jedem anderen bower Projekt, mit bower install --save bootstrap#^4.0 Bootstrap instal­liert werden. Danach noch den Schwall gulp Abhän­gig­keiten mit npm install --save-dev gulp gulp-sass und schon geht es weiter mit dem gulpfile.js im Root-­Ord­ner. Ich werde nicht erklären wie Gulp funktio­niert aber Grund­le­gend ist nun folgendes möglich:

gulp.task('sass', function () {
    var sass = require('gulp-sass');
    gulp.src('bower_components/bootstrap/scss/bootstrap.scss')
        .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
        .pipe(gulp.dest('web/assets'))
});
gulp.task('default', ['sass']);

Und nun können wir einfach die styles im layout einbinden einbinden.

<link rel="stylesheet" href="{{ '/assets/bootstrap.css' | prepend: site.baseurl }}">

Nach dem Prinzip ist alles möglich, was mit Gulp möglich ist. Ich würde für css noch gulp-sourcemap und gulp-autoprefixer empfehlen.

Jekyll build mit Gulp ausführen

Dies ist noch ein netter bonus. Jekyll selbst kann über Gulp auf gerufen werden. Die Idee für den Jekyll Build in Gulp habe ich hier Gefunden.

gulp.task('jekyll', function (gulpCallBack) {
    var spawn = require('child_process').spawn;
    var jekyll = spawn('jekyll', [
        'build',
        '--no-watch',
        '--destination', 'web'
    ], {stdio: 'inherit'});
    jekyll.on('exit', function (code) {
        gulpCallBack(code === 0 ? null : 'ERROR: Jekyll process exited with code: ' + code);
    });
});

Dadurch verliert man zwar jekyll serve aber das kann Gulp auch. Zum Auslie­fern der Seite kann BrowserSync verwendet werden. Das Geile daran ist: Browser­Sync kann auch automa­tisch geänderte Dateien auslie­fern und mehrere Browsers gleich­zeitig belie­fern um das Ergebnis auf möglichst vielen Platt­formen gleich­zeitig zu betrach­ten. Hier jetzt eine Gulp-­Kon­fi­gu­ra­tion für sass und jekyll welches mit Browser­Sync ausge­lie­fert wird:

var browserSync = require('browser-sync').create();

var deployPath = 'web';
var sourceFiles = {
    scss: '_sources/*.scss'
};
var targetDirectories = {
    assets: deployPath + '/assets'
};

gulp.task('jekyll', function (gulpCallBack) {
    var spawn = require('child_process').spawn;
    var jekyll = spawn('jekyll', [
        'build', '--no-watch',
        '--destination', deployPath
    ], {stdio: 'inherit'});

    jekyll.on('exit', function (code) {
        gulpCallBack(code === 0 ? null : 'ERROR: Jekyll process exited with code: ' + code);
    });
});

gulp.task('html', ['jekyll'], function () {
    var htmlmin = require('gulp-htmlmin');
    var path = require('path');

    gulp.src([path.join(deployPath, '*.html'), path.join(deployPath, '**/*.html')])
        .pipe(htmlmin({ collapseWhitespace: true }))
        .pipe(gulp.dest(deployPath))
        .pipe(browserSync.stream());
});

gulp.task('sass', function () {
    var sass = require('gulp-sass');
    var autoprefixer = require('gulp-autoprefixer');
    var sourcemaps = require('gulp-sourcemaps');

    gulp.src(sourceFiles.scss)
        .pipe(sourcemaps.init())
        .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
        .pipe(autoprefixer({ browsers: ['> 1% in DE, last 2 versions, Firefox ESR'] }))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest(targetDirectories.assets))
        .pipe(browserSync.stream())
});

gulp.task('css', ['sass']);

gulp.task('default', ['clean', 'html', 'css']);

gulp.task('watch', ['default'], function () {
    gulp.watch([
        '*.html',
        '_*/*.html',
        'pages/*.html',
        '*.markdown',
        '*/*.markdown',
        '*'
    ], ['html']);
    gulp.watch([
        '_sources/*.scss',
        '_sources/**/*.scss'
    ], ['css']);
});

gulp.task('serve', ['watch'], function () {
    browserSync.init({
        server: {
            baseDir: deployPath
        }
    });
});

Schlußwort

Dieser Eintrag ist viel größer geworden als ich vorge­sehen hab und eigent­lich wollte ich noch viel mehr auf Jekyll layouts und Struk­tu­rie­rung ein gehen. Statt­dessen ist es nur die Einrich­tung der Umgebung geworden und selbst dort hab ich nur die Oberfläche beleuchten können.

Ich werde noch weiter meine Erfah­rungen mit Jekyll sammeln und später bestimmt noch etwas dazu zu sagen haben.