Install it

The package exposes both a JavaScript API, and CLI in case you’re used to that type of workflow from other static site generators. To see how they’re used check out the examples.

Introduction

Metalsmith is an extremely simple, pluggable static site generator. So let us explain why:

Why is Metalsmith a pluggable static site generator?

The task of a static site generator is to produce static build files that can be deployed to a web server. These files are built from source files. Basically for a static site generator this means:

from a source directory read the source files and extract their information

manipulate the information

write the manipulated information to files into a destination directory

Metalsmith is built on this reasoning. It takes the information from the source files from a source directory and it writes the manipulated information to files into a destination directory. All manipulations, however, it exclusively leaves to plugins.

Manipulations can be anything: translating templates, transpiling code, replacing variables, wrapping layouts around content, grouping files, moving files and so on. This is why we say »Everything is a Plugin«. And of course, several manipulations can be applied one after another. Obviously, in this case the sequence matters.

Why is Metalsmith extremely simple?

When all manipulations are performed by plugins, the only thing Metalsmith has to do in its core is to provide for an underlying logic of actually how manipulations are dealt with and for a defined interface for the plugins. To achieve this, we only needed around 400 lines of code — have a look at the source yourself. We believe this is rather simple.

For manipulations Metalsmith uses a very clever, but extremely simple idea. All source files are initially converted into JavaScript objects with the usual {property: property value} pairs. These {property: property value} pairs contain information on the original file itself (such as its birthtime or path) and on its content. The JavaScript object for each file is then supplemented with all variables either specified in the front-matter of the file or elsewhere. The manipulations performed by the plugins are now nothing else then modifications applied to the JavaScript objects either by changing the properties or the property values.

Breaking down Metalsmith into a core and many plugins has several advantages. It reduces complexity. It gives the user the freedom to use exactly only those plugins he or she needs. Furthermore, it distributes the honor and the burden of maintaining the Metalsmith core and its plugins onto the Metalsmith community. With this approach we hope to keep the Metalsmith environment pretty up-to-date.

Writing plugins itself is also rather simple. The plugin-interface is easy to understand and most plugins are also rather short.

Every site needs JavaScript anyway. Just like the popular task runners gulp or grunt Metalsmith is programmed in JavaScript. So, you do not have to rely on a further language such as Ruby, Python or Go. This also helps to keep your workflow simple.

Everything is a Plugin — A first example

All of the logic in Metalsmith is handled by plugins. You simply chain them together. Here’s what the simplest blog looks like. It uses only two plugins, markdown() and layouts()…

… and by the way, if you do not want your destination directory to be cleaned before a new build, just add .clean(false). But what if you want to get fancier by hiding your unfinished drafts and using permalinks? Just add plugins…

A small comment. The layouts() plugin needs the jstransformer-nunjucks package to render layouts. Make sure to install it with npm install jstransformer-nunjucks. Other templating languages can be used as well (see the metalsmith-layouts readme for more information).

How does it work in more detail?

Metalsmith works in three simple steps:

Read all the files in a source directory and transform them into a JavaScript object of JavaScript objects.

Invoke a series of plugins that manipulate these objects.

According to the information contained in the resulting objects write them as files into a destination directory

Every file in the source directory is transformed into a JavaScript Object. For instance,

where the content of the file is always put into the property value of contents. For illustration purposes only we display the value of contents as a string. Technically, however, the property value of contents is realised as a new Buffer('...') object, in order to also handle straight binary data well. mode contains the permission the file has and stats has more technical information on the file such as size or birthtime. Furthermore, the file is also parsed for YAML-front-matter information, which will then also be put into the JS Object. Thus, we finally have an JavaScript object of JavaScript objects. This encompassing JavaScript object is usally called files since it contains all the JavaScript objects that represent the files.

The plugins can manipulate the JavaScript objects representing the original files however they want, and writing one is super simple. Here’s the code for the drafts() plugin from above. You can also find the code in the github repository for metalsmith-drafts. The code just runs through the JS object files and deletes all contained JavaScript objects that have a property value of true for the property draft:

Of course plugins can get a lot more complicated too. That’s what makes Metalsmith powerful; the plugins can do anything you want and the community has written a large amount of plugins already.

Note: The order the plugins are invoked is the order they are in the build script or the metalsmith.json file for cli implementations. This is important for using a plugin that requires a plugins output to work.

If you are still struggling with the concept we like to recommend you the writemetadata() plugin. It is a metalsmith plugin that writes the {property: property value} pairs excerpted from the JavaScript objects representing the files to the filesystem as .json files. You can then view the .json files to find out how files are represented internally in Metalsmith.

We believe, that understanding the internal representation of files as JavaScript objects is really key to fully grasp the concept of Metalsmith. To see this, we look at what happens in the second example chain above:

So, within the Markdown chain above after applying .use(markdown()) the initial representation of the my-file.md becomes my-file.html…

Finally when the .build(function(err)) is performed our JavaScript object is written to relative_to_destpath/myfile/index.html. So you see, how the chain works. It’s rather straight forward, isn’t it?

Metadata & debugging

For Metalsmith we have stated that everything is a plugin. That is true, but in addition the Metalsmith core also provides for a metadata() function. You can specify arbitrary {property: property value} pairs and these information will be globally accessible from each plugin.

As you have seen in the code above, we have also introduced a plugin named metalsmith-debug. For this plugin to actually show debug information you need to define an environment variable DEBUG and set it to:

$ DEBUG=metalsmith:*

The source and destination path, the metadata and all files are then logged to the console.

Further information

Yes, we know. The documentation can be improved. If you want to help, give us a shout. But in the meantime have a look at the Awesome Metalsmith list. There you will find references to a number of excellent tutorials, examples and use cases.

A Little Secret

We keep referring to Metalsmith as a “static site generator”, but it’s a lot more than that. Since everything is a plugin, the core library is actually just an abstraction for manipulating a directory of files.

Which means you could just as easily use it to make…

A Project Scaffolder

Read template files from a directory.

Parse files for template placeholders.

Prompt user to fill in each placeholder.

Render files with a templating engine.

Write filled-in files to a new directory.

A Build Tool

Read files from a source directory.

Convert Sass files to CSS.

Concatenate CSS files.

Minify the CSS file.

Compress images files.

Sprite images in a certain folder.

Write files to a public directory.

An eBook Generator

Read chapter files from a directory.

Build a table of contents from the tree.

Convert Markdown to HTML.

Convert Markdown to PDF.

Convert Markdown to ePUB.

Convert Markdown to MOBI.

Write compiled files to a directory.

Technical Docs

Read files from a source directory.

Convert Markdown files to HTML.

Build a navigation from the tree.

Render each file with a template.

Write HTML to the static directory.

The plugins are all reusable. That PDF generator plugin for eBooks? Use it to generate PDFs for each of your blog posts too!

Writing A Plugin

Writing a plugin is not difficult as you have seen above with the metalsmith-drafts plugin. In order to achieve more complicated tasks you will most likely find and can use npm-packages. Look at how others have done it. Here is an example using debug (which we appreciate if you use it) and multimatch:

The question now is, how does for instance a markdown-engine know, which files to transpile? The answer is easy. Per default, metalsmith-markdown is checking if file has a .md or .markdown extension. Remember, file is a JavaScript object that has its full filename (including its path) as a value.
If the check is not true it jumps over it, otherwise it is passing the file to the engine. After processing it, metalsmith-markdown replaces the .md extension with an .html and the next plugin can now check against the new filename and so on.

A process such as this is called check for pattern matching. Many metalsmith-plugins employ such matching. Either they check against internally set requirements or patterns or they offer an explicit option to check against user defined matches, like we have already seen in the writemetadata-plugin:

The Community Plugins

The core Metalsmith library doesn’t bundle any plugins by default.

Here’s a list of plugins that are provided by the awesome Metalsmith community. But mind you, this list is by no means complete, because not every author PRs his or her plugin. So you might want to search for further plugins:

API

new Metalsmith(dir)

Create a new Metalsmith instance for a working dir.

.use(plugin)

Add the given plugin function to the middleware stack. Metalsmith uses
ware to support middleware, so plugins
should follow the same pattern of taking arguments of (files, metalsmith, callback),
modifying the files or metalsmith.metadata() argument by reference, and then
calling callback to trigger the next step.

.build(fn)

Build with the given settings and a callback having signature fn(err, files).

.source(path)

Set the relative path to the source directory, or get the full one if no path is provided. The source directory defaults to ./src.

.destination(path)

Set the relative path to the destination directory, or get the full one if no path is provided. The destination directory defaults to ./build.

.concurrency(max)

Set the maximum number of files to open at once when reading or writing. Defaults to Infinity. To avoid having too many files open at once (EMFILE errors), set the concurrency to something lower than ulimit -n.

.clean(boolean)

Set whether to remove the destination directory before writing to it, or get the current setting. Defaults to true.

.frontmatter(boolean)

Set whether to parse YAML frontmatter. Defaults to true.

.ignore(path)

Ignore files/paths from being loaded into Metalsmith.

path can be a string, a function, or an array of strings and/or functions.
Strings use the glob syntax from
minimatch to match files and directories
to ignore. Functions are called with the full path to the file as their first
argument, and the lstat object returned by Node’s fs.lstat function as their
second argument, and must return either true to ignore the file, or false to
keep it.

.metadata(json)

Get the global metadata. This is useful for plugins that want to set global-level metadata that can be applied to all files.

.path(paths…)

Resolve any amount of paths... relative to the working directory. This is useful for plugins who want to read extra assets from another directory, for example ./layouts.

.run(files, fn)

Run all of the middleware functions on a dictionary of files and callback with fn(err, files), where files is the altered dictionary.

Metadata API

Add metadata to your files to access these build features. By default, Metalsmith uses a few different metadata fields:

License

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.