Paul Laffitte

First post with Metalsmith

23 May 2020

As I noticed that many developers have their own blog, I started to think that every good developer should have a blog. The fact that I sometimes feel the need to share my thoughts, discoveries and serendipitous moments in development pushed me even more into this idea. Well, here we go. This is the first post of what I hope will be a long series of posts.

Why Metalsmith

As a big fan-boy of markdown and statically-rendered websites, I wanted to write this blog with something that would allow me to use markdown, in a simple manner, and obviously render it into HTML, well, statically. I started wondering about using tools like Jekyll, or Gatsby - actually there is a real plethora of tools that achieve those goals - but both of them looked either too complex or not well fitted for what I wanted to build.

Jekyll could be a good option but, since I do not use Ruby, I thought that it would be probably shooting myself in the foot. If later I want to do something custom that Jekyll doesn't allow me to do, it would not be easy for me to write a plugin since I need to learn Ruby first.

Gatsby looks very nice and trendy, but it's based on React and, honestly, I feel that it would be way overkill to use react for a simple blog like this, plus the fact that I should implement server-side rendering if I want something SEO-friendly. You understood, Gatsby wasn't fitted for my needs.

Then I found Metalsmith, "An extremely simple, pluggable static site generator", the subtitle spoke for itself, and after a few minutes reading the docs, I chose to use it. It's fairly non-opiniated, pretty neat and tidy, and indeed very simple, I would even say basic. Without plugins it's actually just "an abstraction for manipulating a directory of files". Moreover, it's written in Javascript, a language that I like to think I'm mastering.

For the deployment, and since I build a static website, I will use Netlify. It's very easy to setup, it takes less 20 minutes to be in production, and the free plan is more generous than needed (100GB bandwidth/month, 300 build minutes/month...) I mean, it's basically just text right here!

How to bootstrap a blog with Metalsmith

First thing to do is obviously to init your javascript project, I like to use yarn, mostly because it's way faster than npm. Anyway, it use npm repositories so I have access to everything that I can get with npm. You project initialized, you can install all your dependencies, and there is a lot because as I said, Metalsmith is nothing without plugins.

$ yarn init
[...]
$ yarn add metalsmith \
> metalsmith-browser-sync \
> metalsmith-code-highlight \
> metalsmith-collection-metadata \
> metalsmith-collections \
> metalsmith-html-minifier \
> metalsmith-ignore \
> metalsmith-in-place \
> metalsmith-layouts \
> metalsmith-markdown \
> metalsmith-permalinks \
> metalsmith-publish \
> metalsmith-sass \
> metalsmith-title \
> nunjucks-date \
> jstransformer-nunjucks \
> cheerio
[...]

Now I can create my index.js file, import all of this and start building something amazing. I will also require some dependencies only for production, like the html minifier.

const metalsmith = require('metalsmith');
// all dependencies imports...
const cheerio = require('cheerio');
const isDev = ((process.env.NODE_ENV || '').trim().toLowerCase() !== 'production');
const htmlmin = isDev ? null : require('metalsmith-html-minifier');
const browsersync = isDev ? require('metalsmith-browser-sync') : null;

Then I can setup everything. I will start with some configuration variables about the directories and the templating engine. Here I use Nunjucks because I think it was simple yet powerful. It features partials, template inheritance and more all of this with a quite easy syntax.

To help you understand, here is the final structure of the project:

// Paths used by Metalsmith
const dir = {
  base: __dirname,
  source: './src',
  destination: './build'
};

// Configuration for Nunjucks
const templateConfig = {
  directory: dir.source + '/layouts',
  default: 'default.njk', // The default template
  engineOptions: {
    root: dir.source + '/layouts',
    filters: {
      // Filter are function that you can call in the template to change the form of you output.
      // eg. {{ fooVar | barFilter }}
      inspect: json => util.inspect(json), // Just to debug my variables
      summarize: text => {
        // This is to generate the summary for a post, I only take the first paragraph
        const $ = cheerio.load(text.toString());
        return $('p').first().html();
      },
      date: nunjucksDate,
    },
    globals: {
      // A little bit like filter but can take sevral arguments
      // eg. {{ fooGlobal(arg1, arg2, ...) }}
      getContext: function() {
        return this.ctx;
      },
    }
  },
};

nunjucksDate.setDefaultFormat("DD MMM YYYY"); // Configuration for the date filter

Now we can start to use metalsmith for real.

const ms = metalsmith(dir.base)
  .metadata({ // The matadata is available from everywhere in your templates
    sitename: 'Paul Laffitte',
  })
  .source(dir.source)
  .destination(dir.destination)
  .clean(true) // Empty the destination before building
  .use(ignore([ // Metalsmith create a page for each matched files...
    'layouts/**/*', // ...but I don't want it to create pages for my layouts
  ]))
  .use(publish()) // Remove files if the published date is in the future
  .use(collections({
    posts: { // Group my posts in a "collection"
      pattern: 'posts/*',
      sortBy: 'publish',
      reverse: true,
    },
  }))
  .use(collectionMetadata({
    posts: { // Change the default layout for the collection posts
      layout: 'blogpost.njk',
    },
  }))
  .use(markdown()) // Convert markdown files to HTML
  .use(title({ remove: true })) // Remove <h1> elements and put its text in the metadata of the page to be used in my templates
  .use(permalinks({ // Rename the output files with the following pattern
    pattern: ':publish/:title',
  }))
  .use(sass({ // Build css from sass files
    outputDir: 'css/',
  }))
  .use(codeHighlight()) // Highlight <code> elements with highlight.js
  .use(inplace(templateConfig)) // Allows me to use Nunjucks in my posts
  .use(layouts({ // Allows me to use Nunjucks to layout my pages
    ...templateConfig,
    pattern: '**/*.html',
  }));

// Minify the HTML output (remember, I required this module only of we are building for production)
if (htmlmin)
  ms.use(htmlmin());

// Add BrowserSync to live reload on change
if (browsersync)
  ms.use(browsersync({
    server: dir.dest,
    files: [ dir.source + '/**/*' ]
  }));

// And finally let Metalsmith build an awesome blog for me!
ms.build(function(err) {
  if (err) throw err;
});

And that's it, I can start to write some blogposts!

---
publish: 2020-01-23
---

# First post with Metalsmith

As I noticed that a lot of developers have their own blog, I started to think that all good developer should have a blog. The fact that I sometimes feel the need to share my thoughts, findings and my moments of serendipity about development pushed me all the more into that idea. Well, here we go. This is the first post of, hopefully, a long series of other one.

Last update: 30 Aug 2022