Skip to content


Repository files navigation

Turn Back Now Total alerts Build Status

This is my own little corner of the tubes. Proceed at your own risk.

require './build'

Please ignore everything below. Or don't. I don't use it anymore in favor of a ridiculous over-the-top masochistic bespoke build, but I'm keeping it around for historical reference. And the lulz.

Long live Grunt

All Systems Go

module.exports = (grunt) ->

Yes, I use Node and Grunt, I'm a horrible person. This is the real gruntfile. Because grunt configuration tends to get hideously overgrown, I've taken the liberty of hiding it in a separate file so as not to dominate the readme.

  grunt.initConfig(require "./grunt_config.yaml")

Below you'll find some of the giants whose shoulders I stand on or...hang off of, or something.

  # Plugins
  grunt.loadNpmTasks plugin for plugin in [

The pipeline is sort of randomly cobbled together but looks something like the following:

src build deploy
jade hbs
md+yfm + = html
styl -> css
css,icons,images assets
raw -> *
  # Tasks
  grunt.registerTask 'default', 'build'
  grunt.registerTask 'build', [
  grunt.registerTask 'serve', [

Heavy Lifting

Just in case.

  d = console.log

Gotta log when everything goes as planned. This seems to be the conventional way to do it.

  ok = (n) ->
    grunt.log.ok(n + ' ' + grunt.util.pluralize(n, 'file/files') + ' created.')


You might ask what makes this website special. I don't know why you would, but you might. Well, besides being mine, this website is static. It has (excepting bits that are obviously interactive) no JavaScript. At all. It also has no database or server-side logic. At all. This makes page loads quite snappy and limited only by the actual size of the page and the speed of your connection. I use all of the crap in this repo to make it that way, but once built, it's just stuff.

Coming from Hakyll by way of Hyde, my pages are written in markdown with some YFM metadata. Rendering them by hand seems kind of silly, but evidently there's no middle ground between the hard way and going full-on framework a la assemble or something. Neither is really desirable, but here's the lesser of two evils.

The render task takes MD+YFM content plus a HBS template, and outputs static HTML. One HTML file per input page. An optional index may be supplied.

  matter = require 'gray-matter'
  marked = require 'marked'
  handle = require 'handlebars'
  pandoc = require 'pdc'

  grunt.registerMultiTask 'render', 'Render 1..* page(s) and/or an index of multiple pages', ->

    gruntTask = this

    return ok 0 unless @files.length > 0 # Do I need this? Probably.

Some Helpers

Get the extension of a Grunt "src/dest" file. I can't believe this doesn't come standard.

    extension = (file) ->

Capitalize a string

    capitalize = (str) ->
      str.charAt(0).toUpperCase() + str.slice(1)

Invoking handlebars.

    renderStatic = (pageData, dest, tmpl) ->
      renderedHtml = (handle.compile tmpl) pageData
      grunt.file.write "#{dest}", renderedHtml

Compiling content. We use Marked for markdown...

    compileMarkdown = (content, highlightLanguage) ->
        highlight: (code) ->
          hljs = require('highlight.js')
          result = if highlightLanguage then hljs.highlightAuto(code, [highlightLanguage]) else hljs.highlightAuto code

      marked content, {breaks: false, smartypants: true}

...and Pandoc for LaTeX. I couldn't find a synchronous Pandoc wrapper, so Grunt async it is! I could also promises, but feh.

    compileLatex = (content, callback) ->
      pandoc.path = require('path').resolve(process.env.HOME, '.cabal/bin/pandoc')
      pandoc content, 'latex', 'html', (err, result) ->
        if err
          console.warn err
          callback(err, result)
          callback(err, result)

Rendering Index

A HBS template for an index page (listing metadata about each content file) is optional. I use it for listing projects, and perhaps blog entries if I ever muster the patience to write any. Not likely.

    if @data.index
      template =
      metadata = @files.
        map((f) -> f.src).
        map (x) ->
      fname = "#{@files[0].orig.dest}/index.html"

Infer the page's title from the task args. E.g. grunt render:Posts:Javascript will set title to 'Posts - Javascript'. We can also configure a title directly.

      title = @data.title || capitalize @target

      renderStatic {index: metadata, title: title},fname, template
      grunt.log.ok "created #{fname} from #{ (f)->f.src}"

Rendering Files

Perhaps instead of configuring a single template, it could be specified per-document with YFM. Or just override. I dunno.

    template = @data.template
    literate = @data.literate (file) ->

If the task has literate: true we will detect the source language from the inner file extension and use it to highlight code blocks.

      codeExt = null
      if literate
        codeExt = "#{file.src}".split('.').reverse()[1]
        console.log "Ext is #{codeExt}"

Regardless, we read each file, pull out its metadata, compile its content, and render it.

      pageContent = file.src
      pageData = matter(pageContent)
      if extension(file) is 'md' = compileMarkdown pageData.content, codeExt
        renderStatic, file.dest, template
        done = gruntTask.async()
        compileLatex pageData.content, (err, result) ->
          if result
            pageData.content = result
            renderStatic pageData, file.dest, template
          done(err || true)

    ok @files.length


  writ = require 'writ'
  grunt.registerMultiTask 'writ', 'Emit source for literate programs using writ', ->
    writ.writ file.src, file.orig.dest for file in @files