@fidian/metalsmith-site

@fidian/metalsmith-site =======================

Usage no npm install needed!

<script type="module">
  import fidianMetalsmithSite from 'https://cdn.skypack.dev/@fidian/metalsmith-site';
</script>

README

@fidian/metalsmith-site

When trying to get started with a simple Metalsmith site, I want a certain setup to exist.

  • Default metadata is inserted into each file object.
  • File metadata has links to other files so I can build file trees, navigate into any direction, quickly link all children.
  • A link function is added allowing automatic HTML link creation between two file objects.
  • Atomizer (Atomic CSS) processes all files to build the necessary CSS.
  • Redirections from old paths that people bookmarked are made.
  • File contents are processed with Handlebars, then processed with Markdown, and then finally wrapped in a layout that is also using Handlebars.
  • Built-in web server and live reloading of all files, data files, and Handlebars files.
  • Ability to hook into the build process to add my own site-specific plugins and configurations.

This package does that for you.

Installation

Want the quick way to get going? Look at metalsmith-site-example.

Prefer to do this step by step? Cool with me. First, install the package.

npm install --save @fidian/metalsmith-site

Add some helpful scripts to your package.json.

{
    ...
    "scripts": {
        "build": "node metalsmith.js",
        "start": "SERVE=true node metalsmith.js"
    }
    ...
}

Finally, create this boilerplate metalsmith.js.

const metalsmithSite = require('@fidian/metalsmith-site');
metalsmithSite.run({
    baseDirectory: __dirname
    // Additional config goes here
}, err => {
    if (err) {
        console.error(err);
    }
});

There are several configuration options listed below so you can configure how the plugins work and add your own functionality at key moments during the build.

Project Structure

For this to work, here is how the repository should be laid out.

  • atomizer.[js,json] - Atomizer configuration object. Without this file, atomizer won't run. The contents of this file are passed directly to metalsmith-atomizer, allowing you to change the defaults easily.
  • build/ - Where files will be written. This file is created if it does not exist.
  • default-metadata.[js,json] - Results in an object used for each file's default metadata.
  • handlebars/ - Stores the partials and helpers used for both the page generation and the layout wrapping.
    • pages/ - Partials and helpers used while content is changed to HTML.
      • data/ - Data files to load.
      • decorators/ - Handlebars decorators.
      • helpers/ - Functions to add.
      • partials/ - HTML templates.
    • layouts/ - Partials and helpers used exclusively for layout wrapping.
      • data/ - Data files to load.
      • decorators/ - Handlebars decorators.
      • helpers/ - Functions to add.
      • partials/ - HTML templates.
  • metalsmith.js - The file from above.
  • node_modules/
  • package.json
  • redirects.[js,json] - Write redirect files to your project. This file generates an object whose keys are the filenames to create and the values are where to redirect. Redirections can be relative to the file or root-relative.
  • site/ - Your source files for the site.

Configuration

These are all properties that control normal settings.

  • baseDirectory (string, since 1.0.0) The folder to use as the base. Typically uses metalsmith-sugar and it uses ../../.., which should resolve to your project's root. If that's not working, you could simply set this to __dirname, as is done in the example. Default: ../../.. from the folder of metalsmith-sugar.
  • destination (since 1.2.0) Where generated files are placed. Default: ./build
  • serve (boolean, since 1.0.0) When enabled, the site is served on port 8080 (default). This setting can also be enabled by setting the SERVE environment variable to any non-empty string. Default: false
  • source (since 1.2.0) The files that are loaded into the build system. Default: ./site
  • watch (array of strings that specify file globs, since 1.0.0) Watches files and folders for changes when the server is enabled. When any changes are detected, a full site rebuild is performed. Default: ['*.js', '*.json', 'handlebars/**', 'site/**']

There's also the following properties that are hook functions. They are executed before and after each step, in the following order. Note that the serve step (and thus serveBefore and serveAfter) may not be executed if not serving. All of these hook functions are expected to take a single argument, the instance of metalsmith-sugar in order to add additional plugins. The function can optionally return a Promise in case the plugin can't be added synchronously.

  • buildBefore (hook function, since 1.0.0)
  • metadataBefore (hook function, since 1.0.0)
  • metadataAfter (hook function, since 1.0.0)
  • contentsBefore (hook function, since 1.0.0)
  • contentsAfter (hook function, since 1.0.0)
  • layoutsBefore (hook function, since 1.0.0)
  • layoutsAfter (hook function, since 1.0.0)
  • cssBefore (hook function, since 1.0.0)
  • cssAfter (hook function, since 1.0.0)
  • redirectsBefore (hook function, since 1.0.0)
  • redirectsAfter (hook function, since 1.0.0)
  • serveBefore (hook function, since 1.0.0)
  • serveAfter (hook function, since 1.0.0)
  • buildAfter (hook function, since 1.0.0)

Also, there's a special hook that takes a single done callback. Your code can read files, perform additional processing, or fulfill any needs in order to complete the build. When you're ready and the file modifications are complete, call done(). At this point, there is no Metalsmith instance and the files will have been written to build/.

  • postProcess (callback function, since 1.0.0) How to add a bit of processing after each build. Details below.

API

  • .run(config, [buildComplete]) Starts a build of Metalsmith or starts the server. The buildComplete function is called whenever any build is finished, including the live reload builds.

Recipes

Modifying metadata

If you want to have dynamic metadata, such as adding a build date, you can accomplish in two ways. One would be to use default-metadata.js.

module.exports = {
    buildDate: new Date()
};

Another way would be to leverage the metadataAfter hook.

const metalsmithSite = require('@fidian/metalsmith-site');
metalsmithSite.run({
    baseDirectory: __dirname
    metadataAfter: (sugar) => {
        sugar.use((files, metalsmith, done) => {
            // Access the metadata object with metalsmith._metadata
            metalsmith._metadata.buildDate = new Date();
            done();
        });
    }
}, err => {
    if (err) {
        console.error(err);
    }
});

Setting the default layout for all pages

Pages can each specify a layout, which will override the global layout property.

Edit default-metadata.json:

{
    "layout": "default-layout"
}

This will wrap all pages in the Mustache template stored as handlebars/layouts/default-layout.html. If you want to change one page's layout, you can do that by editing the file's metadata.

---
title: Sample page that changes the layout
layout: changed-layout
---

Your Markdown goes here for a page. It will be inserted into the layout file `handlebars/layouts/changed-layout.html`.

Here's a sample layout file. Note that we use triple braces for {{{contents}}} so the HTML isn't escaped.

<!DOCTYPE html>
<html><head><title>{{title}}</title></head>
<body>{{{contents}}}</body>
</html>

Debugging

This uses debug, so use the DEBUG environment variable when running a build to see lots of output. Most of the other plugins do the same. You can see all output by using DEBUG='*' npm run build or simply this library's output with DEBUG=metalsmith-site npm run build.