Ok so here is the problem. A client handed over to us a small website to manage. Guess what, they use php. Quite badly I will say.
Anyway we got to host and manage the website and the first step is to change hosting and make sure all the existing functionalities are maintained.
A cunning plan
First things first: we need to move away from php. This site doesn’t have to be dynamic, at least for now. We just want to do minimal necessary work to host the website, we will decide later what to do with it.
I have been playing around a few static site generators recently. One big problem with static site generators is that they generally require quite a bit of configuration and the project structure needs to adapt to it. This cost makes sense if you have content that is constantly added and maitained like a blog. But this is not our case. We need something even easier and quiker to setup. I think some kind of templating framework controlled by gulp could be the best solution in these conditions.
There are many javascript powered template engines:
- nunjucks that uses jinja2
- handlebarsjs that uses mustache
- mustachejs that uses mustache
- pugjs that uses haml
- donejs
- dustjs
- hogan
- … If you are wondering this is not a full list and is not arranged in order
After a long research (5 minutes) I picked the best templating engine (the one I felt was cool) Nunjucks. I decided to try out nunjucks in combination with gulp to replace the php templating part of the website.
What do you need to start
Ok we need a few things to start.
If you don’t know brew probably you are on the wrong website. With brew you can easily install node and yarn. So we can just jump to gulp.
gulp is a toolkit for automating painful or time-consuming tasks in your development workflow, so you can stop messing around and build something. source
Install gulp
npm install gulp-cli -g
npm install gulp -D
Project setup and gulp
Easy yarn init
…and follow instruction.
To explain quickly what yarn is, you can think of it as your dependencies manager. Stealing a quote from yarn website:
FAST, RELIABLE, AND SECURE DEPENDENCY MANAGEMENT. source
With it we can add and remove dependencies with this command:
yarn add name-your-dependecy
yarn remove name-your-dependecy
In our case we will need gulp as a dependecy.
yarn add gulp
To use gulp you need a file named gulpfile.js in your root directory. Something like this to start with:
var gulp = require('gulp');
gulp.task('default', function() {
console.log("Success!");
})
Ok let’s stop with gulp for now and see what nunjucks is and what we can do with it.
Nunjucks
Nunjucks is:
A powerful templating engine with inheritance, asynchronous control, and more (jinja2 inspired) source
In order to understand Nunjucks documentation we probably need to first understand Jinja2. I will say that the important things to use from templating are:
- includes
- variables
- control flow like ifs and loops
- extensions
- macros
Let starts with includes.
Includes
The first thing you are going to use in a template engine is defenetly the include. Includes are super easy and super useful.
{% include 'header.html' %}
Body
{% include 'footer.html' %}
Variables
With jinja2 you can pass a context. You can think at the context like an object with all the variables that you need for your website. An example can be:
{
"site" :{
"name":"Tengio"
"url":"https://www.tengio.com"
}
"links": [
{
url:"https://www.tengio.com"
title:"Home"
},
{
url:"https://www.tengio.com/mobile-apps.html"
title:"Native apps"
},
...
]
}
I will show you how to pass this object to nunjucks later, for now suppose it is loaded in the context of jinja2 then you can access it with:
{{ site.name }}
{{ site['name'] }}
Ifs and loops
Now that you have a context your ifs or loops are like:
{% if True %}
yay
{% endif %}
{% for link in site.links %}
<a href="{{ link.url }}">{{ link.title }}</a>
{% endfor %}
There is quite a bit more that you can do. But this is already covering lots of scenarios.
Extensions
One typical thing with template engines for website is that you have a common structure that you just want to reuse for all the pages.
For example:
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}{% endblock %}
{% include "partials/head.nj" %}
</head>
<body id="page-top">
{% include "partials/header.nj" %}
<!---->{% block content %}{% endblock %}<!---->
{% include "partials/footer.nj" %}
{% include "partials/analytics.nj" %}
<script src="/js/some.js"></script>
</body>
</html>
Ok, so if you define this to be your base layout then you can write pages in this way:
{% extends "layout.nj" %}
{% block head %}
<title>{{site.title}}</title>
{% endblock %}
{% block content %}
Page content
{% endblock %}
You can probably see for yourself that this is going to be quite useful eh?
Macros
Macros are like inclides but you can send data to them and have conditional logic. This can be very useful in reducing code duplications.
Macros are sligtly more complicated to use. But not that much. Let’s define one macro:
{% macro currentPage(page) %}
...
<li {%if page=='home' %}class="active" {% endif %}
...
{% endmacro %}
To use it in pages we need to first import it:
{% import 'toolbar.html' as toolbar %}
Then we can use it:
{{toolbar.currentPage('home')}}
… let’s move on and see how we can use all this with gulp.
Nunjucks and gulp
There are a few packages implementing nunjucks for gulp. gulp-nunjucks-render looks good.
Project structure
Structure of the project is not complicate. I generally prefer a good separation from the source to what is the deployable package. See here:
/
- src
- pages
- templates
- partials
- macros
...
- resources
- Pages: it is where we are going to add all our files that are rendered to final pages.
- templates/partials: contains all the included files.
- templates/macros: contains all the macros files.
- templates: the root folder is the perfect place to contain base layouts.
Note: There is no real need for it but I started to use .nj as extension for all the src files used by nunjucks.
build
In gulp we need to import the required plugin and create a task.
var gulp = require('gulp');
var render = require('gulp-nunjucks-render');
gulp.task('nunjucks', function() {
return gulp.src('src/pages/**/*.+(nj)')
.pipe(render({
path: ['src/templates']
}))
.pipe(gulp.dest('public'))
});
With this task we are rendering all the files in pages using all the files in the templates folder. The final result of this process is saved into the public folder.
Note: remember you need to add the package to yarn:
yarn add gulp-nunjucks-render
Finaly to build the website just run:
gulp nunjucks
Adding configuration
As I mentioned before it is possible to add a context in a form of a json object. Doing that is rather simple. We need to load a json and pass it to the render
var data = require('gulp-data');
...
...
.pipe(data(function () {
return require('./src/nunjucks/data.json')
}))
...
Conclusions
It took me more time to write this blog than to understad nunjucks and apply it to the project itself. So I’m very pleased with the results.