Static HTML generation for slashie.net website using MustacheJS

I did some back-end changes for the slashie.net website, with the idea of making it easier to include new projects and update their status. About one year ago I went for a single page layout, but was doing maintenance over the HTML file… which was a bit frustrating. With the new approach I can easily manipulate how the list of projects is shown. You can see the current results at slashie.net

I looked around a bit for a static site generator, but in the end decided to roll my own since what I was going for was extremely simple and didn’t want to meddle with the setup of any of these.

What I needed was generating a single HTML file based on a JSON file with the information of all my projects. I decided to go for nodejs using mustachejs for the template; the script is dead simple… I thought I’d share what I did, in case someone needs something similar. This approach gives you a lot of flexibility, as long as you don’t need to maintain a lot of different pages.

Mustache

First, the data, it’s basically a structured list of all my projects

{	
	"sections": [
		{
			"name": "Games",
			"id": "games",
			"items": [
				{
					"title": "Ananias",
					"image": "img/buttons/ananias.png",
					"text": "Dive to the bottom of the Tomb of Ananias using your smartphone or computer. Take adventure with you everywhere!.",
					"status": "published",
					"buttons": [
						{
							"title": "Play Online",
							"url": "http://ananiasgame.com/"
						},
						{
							"title": "Android Version",
							"url": "https://play.google.com/store/apps/details?id=co.slashland.ananias"
						},
						{
							"title": "Desktop Version",
							"url": "http://slash.itch.io/ananias"
						}
					],
					"tags": ["turn-based", "stable", "denzi", "roguelike", "pixel-art", "javascript", "android"]
				},
				{
					"title": "Rodney",
					"image": "img/buttons/rodney.png",
					"text": "Slash your way to the bottom of the Dungeons of Doom, learn new skills and use tactics to survive.",
					"status": "published",
					"buttons": [
						{
							"title": "Play Online",
							"url": "http://games.slashware.net/rodney"
						}
					],
					"tags": ["turn-based", "stable", "oryx", "roguelike", "pixel-art", "javascript"]
				}
			]
		}
	]
}

Now, the generator itself, it basically reads the data and the template, parses the JSON and then renders it using MustacheJS

function generateFile(){
	var file = fs.readFileSync('data.json').toString();
	var template = fs.readFileSync('template.html').toString();
	var data = JSON.parse(file);
	groupByStatus(data); // Groups by status and sorts items
	calculateNewRows(data); // Adds the newRow and endRow attributes
	var output = Mustache.render(template, data);
	fs.writeFile('index.html', output);
}

Now, I had to add the groupByStatus and calculateNewRows functions in order to do some processing over the raw project data (namely grouping the projects by status and grouping them by 3); Mustache declares itself as a logic-less template system, which means it lacks if/else statements or loops. All it does is replacing stuff. You may have to do something similar, depending on how complex you need your page to be.

Finally, this is my Mustache template, as you can see, it’s pretty simple

<!DOCTYPE html>
<html lang="en">
<head>
	<title>Slashie.net</title>
	<link rel="icon" type="image/png" href="img/icon.png">
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
	<link href="lib/bs3/css/bootstrap.css" rel="stylesheet">
	<link href="css/slashland.css" rel="stylesheet">
	<!--[if lt IE 9]>
		<script src="lib/bs3/js/html5shiv.js"></script>
		<script src="lib/bs3/js/respond.min.js"></script>
	<![endif]-->
	<script type="text/javascript" src="lib/jquery-1.10.2.min.js"></script>
	<script type="text/javascript" src="lib/bs3/js/bootstrap.js"></script>
</head>
<body>
<section class = "container">
	<section class = "page-header">
		<div class = "row">
			<div class="col-md-2">
				<div id="logoImage"></div>
			</div>
			<div class="col-md-5">
				<h3>Welcome...</h3>
				<p>I'm Slash. This is my collection of game projects, I hope you like them!.</p>
				<p>You might also want to check <a href = "http://slashwareint.com">Slashware Interactive</a>, which is my brand for finished products.</p>
				<ul class="nav nav-pills">
					<li><a href = "http://blog.slashie.net" target = "_blank">Blog</a></li>
					<li><a href = "https://twitter.com/slashie_" target = "_blank">Twitter</a></li>
				</ul>
			</div>
		</div>
	</section>
</section>
{{#sections}}
<section id = "{{id}}">
	<div class = "container">
		<h1>{{name}}</h1>
		{{#count}}<h2>{{count}} projects</h2>{{/count}}
		<p>{{text}}</p>
		<div class = "container">
			{{#items}}
			{{#newRow}}
			<div class = "row">
			{{/newRow}}
				<div class="col-md-4">
					<div class="thumbnail">
						{{#image}}
						<img src = "{{image}}"/>
						{{/image}}
						<div class="caption">
							<h3>{{title}}</h3>
							<p>
								{{#tags}}
								<span class = "label label-success">{{.}}</span>
								{{/tags}}
							</p>
							<p>{{{text}}}</p>
							{{#buttons}}
							<a href = "{{url}}" target = "_blank" class = "btn btn-primary" role = "button">{{title}}</a>
							{{/buttons}}
						</div>
					</div>
				</div>
			{{#endRow}}
			</div>
			{{/endRow}}
			{{/items}}
		</div>
	</div>
</section>
{{/sections}}

<div id = "footer" class = "text-right">
	<p>Copyright (c) 2004 - 2016 Santiago Zapata</p>
	<p>Generated using <a href = "https://github.com/janl/mustache.js/" target = "_blank">mustachejs</a></p>
</div>
</body>
</html>

You can find the mustache tags in the “sections” section, as you can see mustache interprets each attribute contextually from the source object.

The # mustache tags (for example {{#sections}}) represent a repeating block; the contents inside will be replicated for each one of the objects if the attribute is a list, or once if the attribute exists. (this is used for example on the {{#image}} block, to only show the image tag if the project has an image)

The double and triple mustache tags {{}} and {{{}}} are replaced with the value of the attribute of the given name on the context object. The triple mustache leaves HTML content unscaped, which may be useful if you want to include HTML content directly.

Finally, the {{.}} tag is used to include the value of an object directly, it’s useful for example to include the content of arrays of strings.

And that’s about it! now I can easily add new projects without having the worry about the page layout! My program sorts the projects in alphabetical order and groups them by type and status. What’s also important for me is it takes care of the grouping by 3 items which is required by the way I’m using bootstrap.

And of course, the main point of this is decoupling the data from the view, which in turns takes me to the following point: I may in the future work a bit in the frontend side, to maybe get rid of bootstrap and include some filters…

I hope this is useful for someone to create a simple portfolio or similar page.

One thought on “Static HTML generation for slashie.net website using MustacheJS

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s