In this article, we will learn how to create simple static website generator using Nunjucks and Gulp. Our implementation does not require any specific knowledge, just some basic JavaScript, and HTML. Of course, complexity depends on website functionality, but in our scenario, we will focus on the main idea of static website generation.
There are 3 files in the root of the project.
.editorconfig is the interesting one. EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs. More information about EditorConfig could be found using the following link
http://editorconfig.org/.
Building common layout
Nunjucks is templating engine with block inheritance, auto-escaping, macros, asynchronous control, and more. To build our website we will start with defining master page layout using nunjucks way of templating. Link to nunjucks templating documentation:
https://mozilla.github.io/nunjucks/templating.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="/css/style.css" />
{% block head %}
<title>My site</title>
<meta name="keywords" content="">
<meta name="description" content="">
{% endblock %}
</head>
<body>
{% block header %} {% endblock %}
<div class="main">
{% block content %} {% endblock %}
</div>
{% include "parts/_footer.html" %}
{% block scripts %}
{% endblock %}
</body>
</html>
As you can see layout contains the reference to _footer.html. Nunjucks allows you to extract several parts of HTML markup into reusable components.
_footer.html
{% import '_globals.html' as globals %}
<footer class="footer">
Check out website <a href="{{globals.website_url}}">{{globals.website_url}}</a>
</footer>
Website shared settings
It is useful to have one common place where you can store global settings for your website. For such purposes, we will create the_globals.html file, where will store shared configuration. On previous code snippet, you can find an example how to use global settings.
_globals.html
{% set website_url = "http://www.maniuk.net" %}
Creating page
All pages go to pages directory. The page might extend any of specified base layouts. Following code example shows markup for a simple index page.
index.html
{% extends "_layout.html" %}
{% import 'macros/_header.html' as header %}
{% block head %}
<title>My site</title>
<meta name="keywords" content="">
<meta name="description" content="">
{% endblock %}
{% block header %}
{{ header.renderHeader('index') }}
{% endblock %}
{% block content %}
Hello world!
{% endblock %}
We want current page to be highlighted in navigation menu so we will create
nunjucks macro to achieve that.
_header.html
{% macro renderHeader(activePage='index') %}
<header class="header">
<div class="logo">
<a href="/">
<img src="/img/logo.png" alt="logo" />
</a>
</div>
<nav class="navigation">
<ul class="navigation-list">
<li class="navigation-list-item {%if activePage == 'index' %}current{% endif %}">
<a href="/" class="navigation-list-link">Index</a>
</li>
<li class="navigation-list-item {%if activePage == '404' %}current{% endif %}">
<a href="/404.html" class="navigation-list-link">404 Page</a>
</li>
</ul>
</nav>
</header>
{% endmacro %}
Building project with gulp
First of all, we need to specify the list of dependencies we need for a successful build.
"devDependencies": {
"browser-sync": "^2.18.13",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^4.0.0",
"gulp-clean-css": "^3.9.0",
"gulp-concat": "^2.6.1",
"gulp-htmlmin": "^3.0.0",
"gulp-imagemin": "^3.3.0",
"gulp-nunjucks-render": "^2.2.1",
"gulp-sass": "^3.1.0",
"gulp-uglify": "^3.0.0"
},
End now it is time for a gulpfile.js file with build configuration.
gulpfile.js
var gulp = require('gulp'),
browserSync = require('browser-sync').create(),
sass = require('gulp-sass'),
uglify = require('gulp-uglify'),
autoprefixer = require('gulp-autoprefixer'),
cleanCSS = require('gulp-clean-css'),
imagemin = require('gulp-imagemin'),
nunjucksRender = require('gulp-nunjucks-render'),
concat = require('gulp-concat'),
htmlmin = require('gulp-htmlmin');
gulp.task('serve', ['sass', 'nunjucks-html-watch'], function() {
browserSync.init({
server: './build'
});
gulp.watch('css/dev/*.scss', ['sass']);
gulp.watch('./**/*.html', ['nunjucks-html-watch'])
});
gulp.task('sass', function() {
return gulp.src('css/style.scss')
.pipe(sass())
.pipe(autoprefixer({
browsers: ['last 2 versions'],
cascade: false
}))
.pipe(cleanCSS())
.pipe(gulp.dest('./build/css'))
.pipe(browserSync.stream());
});
gulp.task('compressJs', function () {
return gulp.src('js/*.js')
.pipe(uglify())
.pipe(gulp.dest('build/js'))
});
gulp.task('compressImage', function () {
return gulp.src('img/**')
.pipe(imagemin({
progressive: true,
optimizationLevel: 3
}))
.pipe(gulp.dest('build/img'))
});
gulp.task('nunjucks', function() {
return gulp.src('pages/**/*.+(html|nunjucks)')
.pipe(nunjucksRender({
path: ['templates']
}))
.pipe(htmlmin(
{
collapseWhitespace: true,
removeComments: true
}))
.pipe(gulp.dest('build'))
});
gulp.task('nunjucks-html-watch', ['nunjucks'], function () {
browserSync.reload();
});
gulp.task('vendors-scripts', function() {
return gulp.src([
'./node_modules/jquery/dist/jquery.min.js'])
.pipe(concat('vendors.js'))
.pipe(gulp.dest('build/js/'));
});
gulp.task('copy-files', function() {
gulp.src([
'config/web.config'
])
.pipe(gulp.dest('build'));
});
gulp.task('build-project',
['sass', 'compressImage', 'compressJs', 'nunjucks', 'vendors-scripts', 'copy-files']);
gulp.task('default', ['build-project', 'serve']);
Local development
I find it handy to use gulp build-in HTTP server, developers can easily emulate a server on their machine. Following bash script runs simple HTTP server for local development.
run.sh
echo "Installing dependencies..."
npm install
echo "Running gulp"
./node_modules/.bin/gulp
Conclusion