Metalsmith static site generator

  • Tutorial


Every year there is a development of technologies used by front-end developers. And here we are talking not only about specific frameworks and architectural patterns for implementing client logic in browsers, but also about various alternative tools, such as, for example, static site generators. Their main goal is to simplify the process of creating static sites. Of course, they are not a universal tool, but in some cases they fit perfectly:

  • Web interface prototype
  • Blog with rarely updated content
  • Separate static part of another web application
  • Business website or landing page
  • Online Documentation

A more detailed comparative analysis of various generators can be found in this article , as well as see the rating based on the activity of the respective projects on github. We will consider, though not the most popular, but, nevertheless, elegant and simple representative of this class of tools - metalsmith .

Metalsmith


This generator is entirely written in javascript, the source code can be found in the official repository . The concept of work can be divided into several stages: reading the template files from the folder with the source of the site, sequential processing of their plugins and writing the results to the target directory.



At the moment, there are about 670 different plugins. If you still can’t find something, writing your own plug-in is not at all difficult thanks to the simple and intuitive plug-in API. For example, a plugin that displays file names at a specific step may look like this:

function plugin() {
  return (files, metalsmith, done) => {
    Object.keys(files).forEach(name => console.log(name));
    done();
  };
}

Although, if you need it (for example, for debugging), you can use my plugin - metalsmith-inspect-files , which renders a tree of files and directories at the time of its use in the plugin chain.

Making a simple blog


In order to demonstrate the capabilities of the tool, we will use it to create a simple static blog, all the code is available in the repository . The configuration for the assembly is located either in the metalsmith.json file or directly in the script, the latter, in my opinion, is most preferable because it allows you to more flexibly configure the assembly process:

const Metalsmith = require('metalsmith');
const timer = require('./plugins/timer');
const jade = require('metalsmith-jade');
const layouts = require('metalsmith-layouts');
const permalinks = require('metalsmith-permalinks');
const collections = require('metalsmith-collections');
const less = require('metalsmith-less');
const ignore = require('metalsmith-ignore');
const cleanCss = require('metalsmith-clean-css');
const metalsmithInspectFiles = require('metalsmith-inspect-files');
const partial = require('metalsmith-partial');
Metalsmith(__dirname)
    .source('./source')
    .metadata({ // глобальные переменные доступные в каждом шаблоне
        title: 'Example blog',
        layout: 'index.jade',
        menuLinks: [
            {title:'Home', url: '/'},
            {title:'Articles', url: '/articles/'},
            {title:'About', url: '/about/'}
        ]
    })
    .destination('./build')
    .clean(true)
    .use(collections({ // коллекции страниц определяемые шаблоном
        articles: {
            pattern: [
                'articles/**',
                '!articles/index.jade'
            ],
            sortBy: 'title'
        },
    }))
    .use(partial({ // шаблоны вставки внутри других шаблонов
        directory: './partials',
        engine: 'jade'
    }))
    .use(jade({ // шаблонизатор
        useMetadata: true
    }))
    .use(permalinks({ // красивые url /about.html => /about/index.html
        relative: false
    }))
    .use(layouts({ // шаблон-каркас одинаковый для всех страниц
        engine: 'jade',
        default: 'index.jade',
        pattern: '**/*.html'
    }))
    .use(less()) // компиляция less в css
    .use(cleanCss()) // сжатие css
    .use(ignore([ // фильтрация файлов
        '**/*.less'
    ]))
    .use(metalsmithInspectFiles())
    .build((err, files) => {
        if (err) { throw err; }
        timer('Build time: ')();
    });

Our blog consists of several separate separate pages and one collection of pages of the same type. The structure of the source directories (without external directories layouts and partials):

      |-articles
      |   |-article-one.jade
      |   |-article-three.jade
      |   |-article-two.jade
      |    -index.jade
      |-assets
      |   |-images
      |   |    -favicon.png
      |   |-js
      |   |    -turbolinks.min.js
      |    -stylesheets
      |        -main.less
      |-about.jade
       -index.jade

And the result of the work (the contents of the build dad) looks like this:
      |-about
      |    -index.html
      |-articles
      |   |-article-one
      |   |    -index.html
      |   |-article-three
      |   |    -index.html
      |   |-article-two
      |   |    -index.html
      |    -index.html
      |-assets
      |   |-images
      |   |    -favicon.png
      |   |-js
      |   |    -turbolinks.min.js
      |    -stylesheets
      |        -main.css
       -index.html

The metalsmith-permalinks plugin , which converts * .html files whose name is different from index.html, into the corresponding directories with index.html, is immediately noticeable so that the url of these pages looks more pleasant. The main features of any generator of static sites are templates (layouts and partials), as a means to comply with the principle of DRY (Don’t repeat yourself). The template language used is jade (or pug), which is popular among representatives of the javascript community. So in our case, the main layout looks like representing the frame of the page:

doctype html
html
    head
        meta(charset='utf-8')
        meta(name='description' content='Simple metalsmith blog')
        meta(name='viewport' content='width=device-width, initial-scale=1.0')
        title=title
        link(href='/assets/stylesheets/main.css', rel='stylesheet')
        link(rel="icon" href="/assets/images/favicon.png")
    body
        .container
            nav!=partial('menu.jade', {menuLinks, currentPath: path})
            section.page-content!=contents
            footer!=partial('footer.jade')
    script(src='/assets/js/turbolinks.min.js')

It, in turn, uses partials, the functionality of which is implemented by the metalsmith-partial plugin (note the explicit transfer of variables for rendering the template). The included menu template is as follows:

ul.menu
    each page in menuLinks
        - isActive = ('/'+currentPath+'/').startsWith(page.url) && (page.url != '/' || page.url === currentPath + '/')
        li(class=(isActive ? 'active' : ''))
            a(href=page.url)=page.title

To iterate through the pages of the collection (articles), the metalsmith-collections plugin is used , the pages themselves for generating the list are available in an array variable with the name of the collection.

Turbolinks


The turbolinks library allows you to 'revive' the content provided by the server with statics, adding SPA behavior when clicking on links within our site, loading the contents of each new page with AJAX and replacing it with the current one. If for some reason the request turned out to be long (more than 500ms), a progress-bar will be shown, which will inform the user that the transition to another page is taking place (standard means by which the browser shows this when the link is clicked normally , will not be involved due to the lack of page reloading), although slowly. Turbolinks in this example is connected as a minified distribution file, so to speak, “for the sake of simplicity”. In case the javascript part of the site is not so simple,metalsmith-webpack-2 or gulp-metalsmith .

Useful links and conclusions


Despite the fact that metalsmith is not the most popular of the generators, in my opinion, it deserves attention, because it is simple and flexible (almost any third-party tool can be implemented in the plugin pipeline with a few lines of code), which means it can be used to create various sites, although, of course, it is not a “silver bullet” and has its drawbacks. For example, most plug-ins lack detailed documentation, therefore, in order to understand the details of their work, one often has to study the source code, since they are almost always a simple facade designed to adapt other popular tools with good documentation for the metalsmith plugin-API.


Also popular now: