Coder Social home page Coder Social logo

metalsmith.io's Introduction

Metalsmith

npm: version ci: build code coverage license: MIT Gitter chat

An extremely simple, pluggable static site generator for NodeJS.

In Metalsmith, all of the logic is handled by plugins. You simply chain them together.

Here's what the simplest blog looks like:

import { fileURLToPath } from 'node:url'
import { dirname } from 'path'
import Metalsmith from 'metalsmith'
import layouts from '@metalsmith/layouts'
import markdown from '@metalsmith/markdown'

const __dirname = dirname(fileURLToPath(import.meta.url))

Metalsmith(__dirname)
  .use(markdown())
  .use(
    layouts({
      pattern: '**/*.html'
    })
  )
  .build(function (err) {
    if (err) throw err
    console.log('Build finished!')
  })

Installation

NPM:

npm install metalsmith

Yarn:

yarn add metalsmith

Quickstart

What if you want to get fancier by hiding unfinished drafts, grouping posts in collections, and using custom permalinks? Just add plugins...

import { fileURLToPath } from 'node:url'
import { dirname } from 'node:path'
import Metalsmith from 'metalsmith'
import collections from '@metalsmith/collections'
import layouts from '@metalsmith/layouts'
import markdown from '@metalsmith/markdown'
import permalinks from '@metalsmith/permalinks'
import drafts from '@metalsmith/drafts'

const __dirname = dirname(fileURLToPath(import.meta.url))
const t1 = performance.now()
const devMode = process.env.NODE_ENV === 'development'

Metalsmith(__dirname) // parent directory of this file
  .source('./src') // source directory
  .destination('./build') // destination directory
  .clean(true) // clean destination before
  .env({
    // pass NODE_ENV & other environment variables
    DEBUG: process.env.DEBUG,
    NODE_ENV: process.env.NODE_ENV
  })
  .metadata({
    // add any variable you want & use them in layout-files
    sitename: 'My Static Site & Blog',
    siteurl: 'https://example.com/',
    description: "It's about saying »Hello« to the world.",
    generatorname: 'Metalsmith',
    generatorurl: 'https://metalsmith.io/'
  })
  .use(drafts(devMode)) // only include drafts when NODE_ENV === 'development'
  .use(
    collections({
      // group all blog posts by adding key
      posts: 'posts/*.md' // collections:'posts' to metalsmith.metadata()
    })
  ) // use `collections.posts` in layouts
  .use(
    markdown({
      // transpile all md file contents into html
      keys: ['description'], // and also file.description
      globalRefs: {
        // define links available to all markdown files
        home: 'https://example.com'
      }
    })
  )
  .use(permalinks()) // change URLs to permalink URLs
  .use(
    layouts({
      // wrap layouts around html
      pattern: '**/*.html'
    })
  )
  .build((err) => {
    // build process
    if (err) throw err // error handling is required
    console.log(`Build success in ${((performance.now() - t1) / 1000).toFixed(1)}s`)
  })

How does it work?

Metalsmith works in three simple steps:

  1. Read all the files in a source directory.
  2. Invoke a series of plugins that manipulate the files.
  3. Write the results to a destination directory!

Each plugin is invoked with the contents of the source directory, and each file can contain YAML front-matter that will be attached as metadata, so a simple file like...

---
title: A Catchy Title
date: 2024-01-01
---
An informative article.

...would be parsed into...

{
  'path/to/my-file.md': {
    title: 'A Catchy Title',
    date: new Date(2024, 1, 1),
    contents: Buffer.from('An informative article'),
    stats: fs.Stats
  }
}

...which any of the plugins can then manipulate however they want. Writing plugins is incredibly simple, just take a look at the example drafts plugin.

Of course they can get a lot more complicated too. That's what makes Metalsmith powerful; the plugins can do anything you want!

Plugins

A Metalsmith plugin is a function that is passed the file list, the metalsmith instance, and a done callback. It is often wrapped in a plugin initializer that accepts configuration options.

Check out the official plugin registry at: https://metalsmith.io/plugins.
Find all the core plugins at: https://github.com/search?q=org%3Ametalsmith+metalsmith-plugin
See the draft plugin for a simple plugin example.

API

Check out the full API reference at: https://metalsmith.io/api.

CLI

In addition to a simple Javascript API, the Metalsmith CLI can read configuration from a metalsmith.json file, so that you can build static-site generators similar to Jekyll or Hexo easily. The example blog above would be configured like this:

metalsmith.json

{
  "source": "src",
  "destination": "build",
  "clean": true,
  "metadata": {
    "sitename": "My Static Site & Blog",
    "siteurl": "https://example.com/",
    "description": "It's about saying »Hello« to the world.",
    "generatorname": "Metalsmith",
    "generatorurl": "https://metalsmith.io/"
  },
  "plugins": [
    { "@metalsmith/drafts": true },
    { "@metalsmith/collections": { "posts": "posts/*.md" } },
    { "@metalsmith/markdown": true },
    { "@metalsmith/permalinks": "posts/:title" },
    { "@metalsmith/layouts": true }
  ]
}

Then run:

metalsmith

# Metalsmith · reading configuration from: /path/to/metalsmith.json
# Metalsmith · successfully built to: /path/to/build

Options recognised by metalsmith.json are source, destination, concurrency, metadata, clean and frontmatter. Checkout the static site, Jekyll examples to see the CLI in action.

Local plugins

If you want to use a custom plugin, but feel like it's too domain-specific to be published to the world, you can include plugins as local npm modules: (simply use a relative path from your root directory)

{
  "plugins": [{ "./lib/metalsmith/plugin.js": true }]
}

The secret...

We often refer to Metalsmith as a "static site generator", but it's a lot more than that. Since everything is a plugin, the core library is just an abstraction for manipulating a directory of files.

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

Resources

Troubleshooting

Set metalsmith.env('DEBUG', '*metalsmith*') to debug your build. This will log debug logs for all plugins using the built-in metalsmith.debug debugger. For older plugins using debug directly, run your build with export DEBUG=metalsmith-*,@metalsmith/* (Linux) or set DEBUG=metalsmith-*,@metalsmith/* for Windows.

Node Version Requirements

Future Metalsmith releases will at least support the oldest supported Node LTS versions.

Metalsmith 2.6.x supports NodeJS versions 14.18.0 and higher.
Metalsmith 2.5.x supports NodeJS versions 12 and higher.
Metalsmith 2.4.x supports NodeJS versions 8 and higher.
Metalsmith 2.3.0 and below support NodeJS versions all the way back to 0.12.

Compatibility & support policy

Metalsmith is supported on all common operating systems (Windows, Linux, Mac). Metalsmith releases adhere to semver (semantic versioning) with 2 minor gray-area exceptions for what could be considered breaking changes:

  • Major Node version support for EOL (End of Life) versions can be dropped in minor releases
  • If a change represents a major improvement that is backwards-compatible with 99% of use cases (not considering outdated plugins), they will be considered eligible for inclusion in minor version updates.

Credits

Special thanks to Ian Storm Taylor, Andrew Meyer, Dominic Barnes, Andrew Goodricke, Ismay Wolff, Kevin Van Lierde and others for their contributions!

metalsmith.io's People

Contributors

ajedi32 avatar almirfilho avatar atlas48 avatar axe312ger avatar aymericbeaumet avatar benwilhelm avatar boeckmt avatar colophonemes avatar dependabot[bot] avatar der-on avatar doup avatar dpobel avatar fidian avatar hurrymaplelad avatar ianstormtaylor avatar imchell avatar juanpicado avatar lambtron avatar lukehler avatar mayo avatar mikestopcontinues avatar robbiethegeek avatar robloach avatar safareli avatar scurker avatar stafyniaksacha avatar thiagodemellobueno avatar webketje avatar wernerglinka avatar woodyrew avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

metalsmith.io's Issues

Add verification tests

Add a verification step post build to ensure key files and elements have been built correctly.

Split single page into multiple pages

We have enough content to warrant splitting it up into multiple pages, and adding some navigation. Currently it is not easy to navigate.

To guide us, it would be good if we think about the different purposes a visitor might have, so that hopefully most visitors will be able quickly find what they're looking for. The ones I can think of:

  • What is metalsmith (just exploring).
  • Comparing static site generators (what sets metalsmith apart from other ssg).
  • Looking for a getting started guide (new user, wants to use metalsmith, doesn't know how to start).
  • Looking for documentation (more advanced user, wants to know specific api info).
  • Looking for plugins / a specific plugin.

Remove outdated information

The troubleshooting section explains usage with node 0.10 and 0.12, I think it's safe to remove that info as currently the oldest supported LTS version is node 6.

Update dependencies

Currently this is running on an old version of Metalsmith and probably other packages.

Adding a detailed walkthrough as a subpage

As mentioned by @Ajedi32 in metalsmith/metalsmith#219 (comment), I've got a pull request ready to go with a new, detailed, step-by-step walkthrough (based loosely upon a wiki page I added to the main repo) as a second page.

Before doing so, I just wanted to provide some advance notice about the updates I'd made. Namely there were a few adjustments to the template and frontmatter to accommodate a second page:

  • Updated index.html to use the title and description in header.Header instead of hard-coding the content.
  • Updated src/index.md to add the em element around "pluggable" to the description value in the frontmatter.
  • Added swig striptags filters to the meta values using the description in index.html to strip out the <em>.

The end result is that the head and header.Header both end up with the exact same markup on build/index.html as seen on the current site. I only bring it up because it's not immediately clear if putting markup directly into the frontmatter is verboten (or even just a bad example).

And then there's the actual content changes themselves:

  • Added links to the new walkthrough page to the "Welcome" and "Installation" sections on src/index.md.
  • Note that this does not update the "Further information" section yet, but that seems like the next section to address.
  • Added an entirely new page as src/step-by-step/index.md.

So I'm happy to submit the pull request, but I wanted to introduce the intent first. I'm not really looking to have this deployed as-is, but I'm happy to share it as a starting point to discuss how to proceed. In addition to the layout considerations, I would really like to hear if there's a particular tone, target audience, or style that the team would prefer.

Create 404 page

  • add 404.njk.md to the source dir
  • title: We're still busy forging this page (or if you know something more clever, feel free)
  • draft: true
  • slug: Page not found (<- this is used as tab title)
  • Add "Back to home" link
  • see https://www.metalsmith.io/docs for inspiration text

Migrate hosting

Following the discussion in Automated Build/Deploy #149, it would be good to migrate over to a more appropriate solution.
The following features would be beneficial:

  • Static hosting *
  • HTTPS (#187)
  • Continuous Deployment
  • Ability to build for a branch *

* Nice to have

I believe Netlify has all these features available and is free for open source projects. It would be nice to hear of other options and features that we might want to take advantage of.

Categorizing the plugins would be nice

The plugins section of the site is starting to be huge, and it's getting difficult to find / track plugins. It would be nice to categorize the plugins, and to have a 'new' section.

Cache Header

Since the site is static and doesn't update too often, it might be nice to set some cache headers to cache the site for a certain duration (on the order of minutes seems reasonable to start).

Current:

→ curl -I https://metalsmith.io
...
cache-control: public, max-age=0, must-revalidate
...
etag: "6221391f11769283ed9fa0f1b4559baa-ssl"
...

Deploy failing

Not sure if this is just a temporary issue with Heroku or Travis, but the automated deploy seems to be failing at the moment:

Installing deploy dependencies
Fetching: multi_json-1.12.1.gem (100%)
Successfully installed multi_json-1.12.1
Fetching: excon-0.54.0.gem (100%)
Successfully installed excon-0.54.0
Fetching: heroku-api-0.4.2.gem (100%)
Successfully installed heroku-api-0.4.2
3 gems installed
Fetching: rendezvous-0.1.2.gem (100%)
Successfully installed rendezvous-0.1.2
1 gem installed
dpl.2
Preparing deploy
authenticated as [email protected]
checking for app 'metalsmith'
found app 'metalsmith'
Cleaning up git repository with `git stash --all`. If you need build artifacts for deployment, set `deploy.skip_cleanup: true`. See https://docs.travis-ci.com/user/deployment/#Uploading-Files.
Saved working directory and index state WIP on (no branch): e0a08ff Merge pull request #216 from arccoza/master
HEAD is now at e0a08ff Merge pull request #216 from arccoza/master
dpl.3
Deploying application
creating application archive
uploading application archive
triggering new deployment
curl: (18) transfer closed with outstanding read data remaining
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
heroku build still pending
Already up-to-date!
# HEAD detached at e0a08ff
nothing to commit, working directory clean
Dropped refs/stash@{0} (46643e03db88ec3061301fe6ee96f36cd85e4920)
/home/travis/.rvm/gems/ruby-1.9.3-p551/gems/excon-0.54.0/lib/excon/middlewares/expects.rb:11:in `response_call': Expected([200]) <=> Actual(503 Service Unavailable) (Heroku::API::Errors::ErrorWithResponse)
body: "{\"id\":\"unavailable\",\"message\":\"Service temporarily unavailable. Please try again later.\"}"
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/excon-0.54.0/lib/excon/middlewares/response_parser.rb:9:in `response_call'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/excon-0.54.0/lib/excon/connection.rb:388:in `response'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/excon-0.54.0/lib/excon/connection.rb:252:in `request'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/heroku-api-0.4.2/lib/heroku/api.rb:84:in `request'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/provider/heroku/api.rb:79:in `get'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/provider/heroku/api.rb:41:in `block in verify_build'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/provider/heroku/api.rb:40:in `loop'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/provider/heroku/api.rb:40:in `verify_build'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/provider/heroku/api.rb:14:in `push_app'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/provider.rb:145:in `block in deploy'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/cli.rb:41:in `fold'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/provider.rb:145:in `deploy'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/provider/heroku/generic.rb:57:in `deploy'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/cli.rb:32:in `run'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/lib/dpl/cli.rb:7:in `run'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/gems/dpl-1.8.24/bin/dpl:5:in `<top (required)>'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/bin/dpl:23:in `load'
	from /home/travis/.rvm/gems/ruby-1.9.3-p551/bin/dpl:23:in `<main>'
failed to deploy

Structure for new guide (docs)

Draft of the table of contents of the new docs & links to interesting pieces of code/ docs that can be included

  1. Introduction (what is metalsmith, how does it work)
  2. Prerequisites (Node, OS)
  3. Installation (NPM/ Yarn, TS)
  4. Quickstart (quick example API/CLI, npx starter)
  5. Concepts (how to interact with the metalsmith build, content & plugins)
    • Files
    • Glob patterns
    • YAML front-matter
    • Metadata
      • Global metadata (metalsmith.metadata)
      • File metadata (front matter)
    • Plugins (analogy to middleware/ plain JS functions)
  6. Plugins
    1. Using plugins
      • Passing options
      • Plugin order is important
      • Conditionally running a plugin
    2. Writing a plugin
      • Plugin conventions (path, pattern, named export)
      • Plugin interoperability
      • Adding options to the plugin
      • Adding debug logs to the plugin
      • Handling errors in a plugin
    3. Publishing a plugin
  7. Topics
    • Rendering content (layouts/in-place)
    • Loading extra global metadata
    • Scaling metalsmith builds (splitting the build process)
  8. Deploying a metalsmith project
    • Shared hosting ((S)FTP)
    • Github Pages
    • Netlify
  9. Companion libraries
    • dotenv
    • cross-env
    • serve

site is out of date

seems like a numver of pull requests were merged, months ago even ... yet website is still pointing to old content.

Rebuild site

Several plugins have been added to plugins.json, but they're not yet visible on metalsmith.io since the site hasn't been rebuilt.

GulpJS

This looks interesting. I'm wondering does this play nice with GulpJS. I've been looking for an static site generator which does, mostly because other SSG's are a little too opinionated and I'm really into gulp lately. MetalSmith's pluggability and use() are similar to how Gulp works.

An example gulpfile.js:

var gulp = require('gulp');
var less = require('gulp-less');

gulp.task('default', ['styles']);

gulp.task('styles', function(){
    gulp.src('./styles/less/style.less')
        .pipe(less())
        .pipe(gulp.dest('./styles'));
});

It uses pipes, passing vinyl files down the pipeline. What does MetalSmith use? An object custom to MetalSmith?

So you'd have a "build" task which would call other tasks. Am I right in saying that with MetalSmith you'd have just the one pipeline/task which handles everything? Or can you say I want this plugin for these files, and this other plugin for these?


I was actually just about to hack together my own SSG for personal use, using gulp. Gulp itself isn't a SSG. So I would be using gulp plugins for compiling stuff, etc. but also I was going to just go through all files in a directory, pulling out the front matter with gulp-front-matter and aggregrate it all into an array (so later I could somehow sort / filter that array / link to items in it from other files). But MetalSmith might just be what I what I want (minus Gulp?).

Deploy Failing - API Key Changed

The automatic deploy via Travis is currently failing with the following error message:

API request failed.
Message: Invalid credentials provided.
Reference: 
failed to deploy

Sounds like the API key specified in .travis.yml is no longer valid. As I don't have direct access to the site's settings on Heroku and I don't work at Segment, I have no way to fix this myself. Could someone who does work at Segment please fix this?

CC: @fouad, @lambtron, @dominicbarnes

Unify sizing

In our css we have some sizing variables that we're not using everywhere for sizing. I want to see if that can be changed (without going overboard). It'd make maintaining a steady vertical rhythm easier.

(It'll also be a good opportunity to go through all the styles and see if there's anything else to address)

Update examples using metalsmith-templates to metalsmith-layouts

The code examples use the now deprecated metalsmith-templates.

Also, maybe it would save newcomers time if the deprecation for metalsmith-templates would be immediately visible in its entry in the the plugins section, preferably with a link to metalsmith-layouts. Saves them from having to go to the repository and see that it's been deprecated.

Blocking external styles in <head>

We're loading some external css (symbolset) for our icons. To speed things up we could concatenate it with our own styles, or use a different solution (like an svg library, which I personally would prefer).

Text wrapping in code examples on site

The elevator pitch code example and some other examples on the site have their right hand side comments wrapping over to the next line which makes it difficult to read the code. It seems to wrap fine on the smaller breakpoint

Screenshot:
metalsmith

PostCSS plugin

Will be good to create a plugin to use all PostCSS opportunities, not only Autoprefixer.

How to build the site ?

While trying to submit my plugin, I tried to build the site but nothing happened. It seems your build.js use more complex tools than the ones I'm used to. Could you please explain how to build ?

Fonts don't look good on all platforms

There is a bit of difference between platforms currently in how good the fonts we're using look. See if this can be improved. I think using something like GWF would probably be a decent way of addressing this (found some fonts that worked quite well).

Massive .psd file in repo

There is a massive 19mb photoshop file in this repo. Now it's not my repo, but I would certainly take that out.

An Icon Reference HTML Page that maps .ss-* class names to corresponding icons

It'd be nice to have a HTML page that shows the mapping between .ss-* class names and the actual icon graphics, so that plugin authors know which icon to use.

Something along the lines of this page: http://fortawesome.github.io/Font-Awesome/icons/

I went to http://symbolset.com/icons/gizmo but they don't have this info.

I can try to write a script that inspects symbolset.css file and generates the HTML page described above. Let me know if that sounds useful.

is metalsmith dead ?

Obviously the project isn't dead, but the question has come up on the slack channel 3 times in the last 5 days. I guess that's a fair indication that many more people peruse metalsmith.io and wonder the same thing. Who knows how many would choose another project which happens to be a little bit more new and shiny.

As to how to address this on metalsmith.io, my suggestion would be to include a chart of package downloads.. it's in keeping with the terse nature of metalsmith.io, the download trend really does speak for itself, and while not relevant directly to metalsmith, I like that it uses dynamic content on a static site.

I'd be happy to have a go at implementing this but wanted to see if maintainers think it's a reasonable approach before I get started.

I've made a rad demo of what I'm talking about. Basically just use npmjs no-auth JSON downloads api, and render the data with chartistjs.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.