Introducing Project Orca - Part 2

July 28, 2018

In Introducing Project Orca - Part 1 I wrote about why we should be using a static website and provided a short overview of what a Metalsmith static site generator is. In this post we'll look at how Metalsmith transforms a file using plugins.

A simple build script

We will be using a very simple Metalsmith build script build.js to:

  • Transform a file into a javascript object
  • Convert markdown content into html
  • Determine the permalink
  • Apply a page template
  • Transform the final javascript object back into a file
  • Place the file into a destination directory

build.js


Metalsmith(__dirname)            
    .source('sourcepath')      
    .destination('destpath')   
    .use(markdown())
    .use(permalinks())          
    .use(layouts())
    .build(function(err) {         
        if (err) throw err;          
    });

The above Metalsmith build script defines the source path and the destination path for all files that are processed.

Transforming example-file.md

We are starting with a simple markdown file example-file.md. First Metalsmith transformes this file into a javascript object and then plugins will change the appropriate javascript properties. At the end the object will be transformed back into a file by Metalsmith and placed into the destination directory.

The initial markdown file includes a section at the top that is called frontmatter. Frontmatter is formatted in YAML and set between triple-dashed lines.

Below the frontmatter is the content, in our case it is just a simple paragraph with a h2 header

example-file.md


---
layout: blog-post.html

# meta data
title: Etiam Mollis Risus
draft: true
description: "Vestibulum id ligula porta felis euismod semper."

# page properties
blog_title: Etiam Mollis Risus
categories: category1
tags: [tag1, tagA, tag3]
author: [author2, author1]
featured_blog_post: true
featured_blog_post_order: 2

image:
  feature: default.jpg
  thumbnail: default_tn.jpg
date: 2017-03-12
body_classes: blog-post has-sidebar
---

## Nibh Fringilla Cursus
Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla. Integer posuere erat a ante venenatis dapibus posuere velit aliquet.

Note the variable layout that specifies the template file to be used. In our example the template looks like this:

blog-post.html



<!doctype html>
<html>
    <head>
        <title>{{ title }}</title>
    </head>
    <body>
        {{ contents | safe }}
    </body>
</html>

File to javascript object transformation

Metalsmith starts the build process by transforming all files in the source directory into objects. In our example we only have one file, example-file.md.


{
  relative_to_source_path/example-file.md: {
    layout: blog-post.html,
    title: Etiam Mollis Risus,
    description: ‘Vestibulum id ligula porta…’,
    blog_title: Etiam Mollis Risus,
    categories: category1,
    tags: [tag1, tagA, tag3],
    author: [author2, author1],
    featured_blog_post: true,
    featured_blog_post_order: 2,
    image: {
      feature: default.jpg,
      thumbnail: default_tn.jpg
    },
    date: 2017-03-12,
    body_classes: blog-post has-sidebar,
    contents:‘## Nibh Fringilla Cursus…’,
    mode:0777,
    stats: {}
}

The result of the transformation shows an object of objects. Each file object is identified by its source path as the key and the value with all file properties.

Markdown to HTML transformation

The first plugin that is invoked by Metalsmith is the markdown plugin to convert the file contents into html


{
  relative_to_source_path/example-file.html: {
    layout: blog-post.html,
    title: Etiam Mollis Risus,
    description: ‘Vestibulum id ligula porta…’,
    blog_title: Etiam Mollis Risus,
    categories: category1,
    tags: [tag1, tagA, tag3],
    author: [author2, author1],
    featured_blog_post: true,
    featured_blog_post_order: 2,
    image: {
      feature: default.jpg,
      thumbnail: default_tn.jpg
    },
    date: 2017-03-12,
    body_classes: blog-post has-sidebar,
    contents:<h2>Nibh Fringilla Cursus…</h2>,
    mode:0777,
    stats: {}
}

The results of this conversion show in two places. The object key now shows a .html file extension and the contents variable now shows an html string.

Determine the permalink


{
  relative_to_source_path/example-file.html: {
    layout: blog-post.html,
    title: Etiam Mollis Risus,
    description: ‘Vestibulum id ligula porta…’,
    blog_title: Etiam Mollis Risus,
    categories: category1,
    tags: [tag1, tagA, tag3],
    author: [author2, author1],
    featured_blog_post: true,
    featured_blog_post_order: 2,
    image: {
      feature: default.jpg,
      thumbnail: default_tn.jpg
    },
    date: 2017-03-12,
    body_classes: blog-post has-sidebar,
    contents:<h2>Nibh Fringilla Cursus…</h2>,
    path: 'example-file',
    mode:0777,
    stats: {}
}

The permalinks plugin creates a new directory for each file and names it with the file name sans the extension. Then it places the file into the new directory and renames the file to index.html. For example after the plugin is done, the file example-file.html has become example-file/index.html. It also added a new variable path to the file object.

Applying the page template


{
  relative_to_source_path/example-file.html: {
    layout: blog-post.html,
    title: Etiam Mollis Risus,
    description: ‘Vestibulum id ligula porta…’,
    blog_title: Etiam Mollis Risus,
    categories: category1,
    tags: [tag1, tagA, tag3],
    author: [author2, author1],
    featured_blog_post: true,
    featured_blog_post_order: 2,
    image: {
      feature: default.jpg,
      thumbnail: default_tn.jpg
    },
    date: 2017-03-12,
    body_classes: blog-post has-sidebar,
    contents:<!doctype html>
		  <html>
			    <head>
  			    <title>Etiam Mollis Risus</title>
			    </head>
			    <body>
  			    <h2>Nibh Fringilla Cursus</h2></body>
		  </html>,
    path: 'example-file',
    mode:0777,
    stats: {}
}

The layout plugin applies the template file blog-post.html. This transforms the variable contents into a full fledges html page. The object is now ready to be converted back into a file by Metalsmith and put into the destination directory. Because we used the permalinks plugin our file name is now example-file/index.html

example-file/index.html


<!doctype html>
<html>
  <head>
    <title>Etiam Mollis Risus</title>
  </head>
  <body>
    <h2>Nibh Fringilla Cursus</h2>
    <p>Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam id dolor id nibh ultricies vehicula ut id elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla. Integer posuere erat a ante venenatis dapibus posuere velit aliquet.</p>
  </body>
</html>

As you can see, we have complete control over the files object. In fact, we do not even need a source file, we could just build a new js object and add that to the Metalsmith pages object in a plugin and at the end that object will be converted into a real file. The data for such a dynamically generated file may come from a data file or from an API call that will fetch data from an external source.

The result is always the same, a good old static html file.

If you want to learn more about adding dynamically generated files read my post Building Job pages with Metalsmith with the Lever Postings API.

A very crucial part of generating html pages with a static site generator is the templating engine. In a future post, Introducing Project ORCA Part3 we will have a look at Nunjucks, "a rich and powerful templating language for JavaScript" that is maintained by Mozilla.