Write You a Static Site Generator

With NodeJS ⚡️

Adam Kelly

TY Student at Skerries Community College

@adamisntdead

tinyurl.com/node-static

Have A Question? 🤔

What is a Static Site Generator?

Why Would You Use a Static Site Generator?

  • Simplicity
  • Security
  • Performance
  • Flexibility

Writing a Static Site Generator ✏️

Why Would You Write Your Own Static Site Generator?

  • You choose your stack
  • Speed
  • An in-depth knowledge of the internals
  • Easier to debug and solve problems
  • Reduce reliance on external/upstream projects 

Why Would You Use NodeJS

  • Ecosystem
  • Fast
  • Async
  • Isomorphism

Two Big Questions

What Does Your Content Look Like

How Are You Going To Edit Your Content

Smaller Choices

Picking Your Tools

Configuration

JSON

YAML

Content / Layouts

Markdown

Handlebars

Pug

Generic Tools

fs-extra

fast-glob

gray-matter

4 Main Parts Of A Static Site Generator

  • Cleaning
  • Reading
  • Rendering
  • Writing

The Code

 My Choices

  • JSON for configuration
  • Markdown for content
  • YAML for frontmatter
  • Handlebars for layouts

Configuration

{
  "title": "Your Awesome title",
  "description": "Write an awesome description here!",
  "author": "Author Name",

  "static": "assets",
  "public": "public"
}
const fs = require('fs-extra')

async function getConfig() {
  // Reading the 'config.json' file,
  // and parsing the JSON
  return await fs.readJSON('config.json')
}

The Main Generator

class Site {
  constructor(config) {
    this.config = config
    this.pages = []
    this.output = []
  }

  // Main Method - Builds the site
  build() {
    ...
  }

  ...
}
getConfig()
  .then(config => new Site(config))
  .then(site => site.build())
  .catch(err => console.log(err))

The 4 Main Stages

class Site {
  ...

  async build() {
    await this.reset()
    await this.read()
    await this.render()
    await this.write()
  }

  async reset() {
    ...
  }

  async read() {
    ...
  }

  async render() {
    ...
  }

  async write() {
    ...
  }
}
async reset() {
  // Delete the public / output directory
  await fs.remove(this.config.public)

  // Create a new, empty public directory
  await fs.mkdirp(this.config.public)
}
const matter = require('gray-matter')
const slug = require('slug')
const glob = require('fast-glob')

...

async read() {
  // Read the files in the content directory
  const files = await glob('**/*', { cwd: 'content' })

  for (const filename of files) {
    const fileContent = await fs.readFile(`content/${filename}`, 'utf8')

    // Getting the frontmatter
    const { content, data } = matter(fileContent)
    const title = data.title
    const permalink = slug(title).toLowerCase()

    // Add the page to the 'this.pages' object
    this.pages.push({
      title,
      permalink,
      content,
      ...frontmatter.data
    })
  }
}
async render() {
  const site = { pages: this.pages, ...this.config }

  for (const page in this.pages) {
    let content = Handlebars.compile(page.content)({ site, page })

    if (page.markdown) {
      content = marked(renderedContent)
    }

    if (page.layout) {
      const layout = await fs.readFile(`layouts/${page.layout}.hbs`, 'utf8')
      const template = Handlebars.compile(layout)

      content = template({ site, page, content })
    }

    this.output.push({
      path: path.join(this.config.public, page.permalink, 'index.html'),
      content
    })
  }
}
async write() {
  const outputs = []

  for (const output in this.output) {
   outputs.push(fs.outputFile(output.path, output.content)) 
  }

  return Promise.all(outputs)
}

Thats It!

Extending Your Generator

  • Asset Pipelines
  • CLI
  • Watching For Changes (Incremental Builds)
  • Plugins

J

Javascript

APIs

A

Markup

M

Best Practices

What Makes The JAM Stack Special

Best Practices

  • Everything On A CDN
  • Atomic Deploys
  • Instant Cache Invalidation
  • Version Control Everything
  • Automated Builds

Node And The JAM Stack

JAM Tools

E-Commerce

Gumroad

Shopify Buy Button

Stripe

GoCommerce

Forms

Formspree

Typeform

Netlify Forms

Search

Algolia

Google Custom Search

Front End Javascript Search

Login

Auth0

Netlify Identity

Comments

Disqus

Facebook Comments

Other Options

CMS

Contentful

Prismic

Storyblok

Dropbox

Netlify CMS

When Would I Not JAM?

Thank You!

@adamisntdead

tinyurl.com/node-static

Write You A Static Site Generator

By Adam Kelly

Write You A Static Site Generator

  • 819