metalpress
Version:
Create a blog easily with Metalsmith.
429 lines (341 loc) • 12.3 kB
Markdown
---
layout: post.liquid
title: Dreaming
tags: dreaming
---
There's a total of 395 static site generators [out there](https://staticsitegenerators.net/) on the web, and probably even more found on Github. It's pretty clear to see that developers want good tooling to rapidly build static sites. Github advocates for [Jekyll](https://jekyllrb.com/) and allows you to freely [host](https://pages.github.com/) a Jekyll site as a Organization or Project. A lot of people might be okay with this solution for a simple blog or open source project, however if you want a faster development environment and more flexibility to customize the functionality of your site, you might find this solution limiting. As I was rebuilding my site, I definitely wanted to shift off of Jekyll and go with something that allowed for extending functionality with Javasrcipt in ES6.
## Metalsmith
[Metalsmith](https://github.com/segmentio/metalsmith) is an *extremely pluggable* static site generator built with Javascript and Node.js by the team over at [Segment](https://segment.com/home). It has a simple design in and of itself, however the power comes in from the number of [plugins](https://github.com/lambtron/awesome-metalsmith/blob/master/PLUGINS.md) available in the Metalsmith ecosystem. In this post we'll take a look at some of these plugins and how they might fit into a simple workflow of building a static blog.
To get started let's create a new directory: `mkdir metalsmith-blog` and initialize it with `npm init`. You can build your site with either the Metalsmith CLI or the Javascript API. Let's take a look at both and you'll get to see which one might work better for your particular use case.
## CLI
To use the Metalsmith CLI, let's install it globally with `npm i -g metalsmith`. In order for metalsmith to do it's work, we'll need to create a `metalsmith.json` file with a configuration for our site:
```json
{
"source": "src",
"destination": "build",
"plugins": {}
}
```
With this configuration, Metalsmith will read all the files in the source directory (`src`) and copy them into the destination (`build`) directory. As you can see we'll also have the opportunity to include plugins that will process our files along the way. Let's add a few plugins that we can use to build a basic blog page with posts.
We'll need a way to render markdown files to html, so let's install `metalsmith-markdown`.
```sh
$ npm i metalsmith-markdown --save
```
Great, we'll also need a way to render our markdown in some sort of template. Metalsmith used to have a `metalsmith-templates` plugin, however this has been divided into two smaller plugins named `metalsmith-layouts` and `metalsmith-in-place`. At first, I wasn't sure the difference here, but there's a difference between the two. `metalsmith-in-place` renders files within your `src` directory, whereas `metalsmith-layouts` renders files from a root `layouts` directory. This allows you to separate layout specific code from source-specific template logic that may exist in a source file. One of the benefits of Metalsmith is how flexible it is in regards to templating. With consolidate.js behind the scenes, you can use any [templating engine]() of your choice. In this case, we'll use liquid because of it's familiar for people migrating from Jekyll.
Let's install them both:
```sh
$ npm i metalsmith-layouts metalsmith-in-place --save
```
Let's also install tinyliquid for our template engine.
```sh
$ npm i tinyliquid --save
```
Great, lastly we'll need a way to output a collection of posts and create pretty permalinks such as `/blog/metalsmith-post/` versus `/blog/metalsmith-post/index.html`. Let's do that by installing `metalsmith-collections` and `metalsmith-permalinks`.
```sh
$ npm i metalsmith-permalinks metalsmith-collections --save
```
Cool! Now let's set up our plugins in our `metalsmith.json`:
```json
"plugins": {
"metalsmith-collections": {
"posts": {
"pattern": "blog/!(index).md"
}
},
"metalsmith-markdown": {
"gfm": true
},
"metalsmith-layouts": {
"engine": "liquid",
"directory": "layouts",
"includeDir": "layouts/includes"
},
"metalsmith-in-place": {
"engine": "liquid",
"pattern": "**/*.html",
"includeDir": "layouts/includes"
},
"metalsmith-permalinks": {
"pattern": "blog/:title"
}
}
```
You'll notice we're configuring each plugin with it's respective options. You can learn more about the options at each plugin's Github repo. Here we're collecting posts from our blog directory, rending layouts and source files while allowing us to utilize liquid's includes `{% include partial-name %}`, and creating permalinks for our posts with the `title` from our post front-matter.
Now let's set up some layouts we can use for our landing page, post, and blog page:
```sh
$ mkdir -p layouts/includes && touch layouts/base.liquid && touch layouts/includes/blog.liquid && touch layouts/post.liquid
```
#### layouts/base.liquid
```html
<!DOCTYPE html>
<html>
<head>
<title>World Traveler</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<header class="header">
<div class="navbar">
<a class="navbar-brand" href="/">World Traveler</a>
<ul class="nav navbar-nav">
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/blog">Blog</a>
</li>
</ul>
</div>
</header>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="content">
{{ contents }}
</div>
</div>
</div>
</div>
</body>
</html>
```
#### layouts/includes/blog.liquid
```html
<div class="posts">
<ul class="posts__list">
{% for post in collections.posts %}
<li class="post">
<a href="/blog/{{ post.title | downcase | split: ' ' | join: '-'}}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
</div>
```
Here we're looping over our post collection with `collections.posts` and including our post partial passing in the post data.
#### layouts/post.liquid
```html
<!DOCTYPE html>
<html>
<head>
<title>World Traveler</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
</head>
<body>
<header class="header">
<div class="navbar">
<a class="navbar-brand" href="/">World Traveler</a>
<ul class="nav navbar-nav">
<li>
<a href="/">Home</a>
</li>
<li>
<a href="/blog">Blog</a>
</li>
</ul>
</div>
</header>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="post">
<h3 class="post-title">{{ title }}</h3>
<p class="post-date">{{ date | date: "%b %d, %Y" }}</p>
<div class="post-content">
{{ contents }}
</div>
</div>
</div>
</div>
</div>
</body>
</html>
```
Finally, let's create a `src` directory and add in a markdown for a landing, blog page, and new blog post:
```
$ mkdir -p src/blog && touch src/index.md && touch src/blog/index.md && touch src/blog/2015-11-05-new-york-visit.md
```
#### src/index.md
```md
---
layout: base.liquid
title: World Traveler
permalink: false
---
A site about my travels...
```
You'll notice here we're setting `permalink` to false. This tells our permalink plugin not to render this page under `blog` which we have set up as our permalink path.
#### src/blog/index.md
```md
---
layout: base.liquid
title: Blog
permalink: false
---
{% include blog %}
```
#### src/posts/2015-11-05-new-york-visit.md
```
---
layout: post.liquid
title: Traveling to New York
date: 2015-11-06
---
This month we'll learn about the delights of New York City...
```
Now let's add a npm script to run the CLI with `npm run build`:
```json
{
"scripts": {
"build": "metalsmith"
}
}
```
Finally we can run:
```sh
$ npm run build
```
Here we have a simple landing page and blog page with a blog post. Let's add some styling with sass.
```sh
$ npm i metalsmith-sass --save
```
```json
"metalsmith-sass": {
"outputDir": "css"
}
```
```sh
$ mkdir src/sass && touch src/sass/style.scss
```
#### src/styles/style.scss
```sass
body {
margin: 0;
font-family: 'Helvetica';
font-weight: 100;
font-size: 18px;
line-height: 1.5;
}
a {
color: #053580;
font-weight: 400;
text-shadow: 0px 0px 0px #fff;
}
.content {
padding: 20px;
}
.navbar {
padding: 20px;
}
```
And we'll add our link tag: `<link rel="stylesheet" href="/css/style.css">`
Let's also add a nice header image courtesy of [Unsplash](https://unsplash.com/photos/SR68yPh06As)
Add the jpeg in `src/assets`.
```sass
.header {
background-image: url("/assets/header.jpeg");
background-size: cover;
min-height: 300px;
background-position: center -95%;
}
```
Let's run the build again and start a local server.
```sh
$ npm run build && serve build
```
So now that we have a pretty good sense of what Metalsmith can do, let's take a look at how you might run the same build using the API with Babel and finish off our blog by minifying our html. This will make our site nice and fast!
If you aren't already up to speed with Babel 6, go ahead and install the CLI with: `npm i -g babel-cli`. Then we can set up our `.babelrc`.
```sh
$ touch .babelrc && npm install babel-preset-es2015 --save-dev
```
#### .babelrc
```json
{
"presets": ["es2015"]
}
```
## Metalsmith's API
Let's firstly install `metalsmith`.
```sh
$ npm i metalsmith --save
```
Next, let's create a `build.js` and write our build script.
```sh
$ touch build.js
```
```js
import Metalsmith from 'metalsmith';
import collections from 'metalsmith-collections';
import layouts from 'metalsmith-layouts';
import inPlace from 'metalsmith-in-place';
import markdown from 'metalsmith-markdown';
import permalinks from 'metalsmith-permalinks';
import sass from 'metalsmith-sass';
Metalsmith(__dirname)
.use(collections({
posts: {
pattern: 'blog/!(index).md',
sortBy: 'date',
reverse: true
}
}))
.use(sass({
outputDir: 'css'
}))
.use(markdown({
gfm: true
}))
.use(permalinks({
pattern: 'blog/:title'
}))
.use(layouts({
engine: 'liquid',
directory: 'layouts',
includeDir: 'layouts/includes'
}))
.use(inPlace({
engine: 'liquid',
pattern: '**/*.html',
includeDir: 'layouts/includes'
}))
.build(function () {
console.log('Cheers!');
});
```
Great, here we can see Metalsmith uses `.use` to invoke the plugins in order from top to bottom similar to express middleware, each function gets invoked with all the files from the previous function, processed and passed along. Let's run our build with `babel-node`:
```sh
$ babel-node build.js
```
Lastly, let's add html minification.
```sh
$ npm i metalsmith-html-minifier --save
```
```js
...
import htmlMinifier from 'metalsmith-html-minifier';
...
.use(htmlMinifier())
.build(function () {
console.log('Cheers!');
})
```
And now we can adjust our build script.
```sh
{
"scripts": {
"build": "babel-node build.js"
}
}
```
Again, let's run our build.
```sh
$ npm run build
```
## Conclusion
As you can see Metalsmith is *extremely* flexible and has a number of [plugins](http://www.metalsmith.io/#the-plugins) you can choose from to extend your site to fit your needs. There are plugins ranging from tag generators, to s3 upload, to browserify and webpack bundling. You'll definitely have the flexibility and you can also run your builds nicely with ES6 using Babel 6.
From here, you may want to expand the structure of your layouts and src directories to better suite your project. Also, if you're looking for a way to build multiple permalinks, you can use something like [metalsmith-branch](https://github.com/ericgj/metalsmith-branch) to break out the permalinks for different paths.
I hope you were able to learn more about Metalsmith and you'll consider using it on your next project! Check out the [Demo](http://cameronjroe.com/metalsmith-blog/) and [Code](https://github.com/cameronjroe/metalsmith-blog).