Overview

HTTPS SSH

Static as Fuck - static site generator

Static as Fuck is a platform for building static site generators. It has no features. It was made specifically to generate HTML for Purple Pwny Studios and Lux and Nox, and as such, is not well tested. It is the antithesis of "feature-rich." Unless you're a minimalist, disgusted by the complexity of existing solutions, you probably shouldn't use it.

How it works

Given a directory structure:

site_root
|- assets
|- tpl
|- src
|- build

Unprocessed site pages are stored in src, templates are stored in tpl, HTML output is stored in build, and other resources are stored in assets. To use SaF, you need to create what I've termed a "build file" in the site_root directory. This is a Lua script that will invoke the methods provided by SaF to build the HTML. This is also the place to implement any desired features -- tags, blog awareness, RSS feeds, etc. For example, the following build script is used to generate Purple Pwny as of June 2012:

-- pp_build.lua
require("saf")
local Markdown = require("discount")

local tplPath = saf.TEMPLATE_PATH .. "mnml/mnml.lua"

-- get and build all source files in blog path, must build these first in order to have listing on homepage
local blogs = {}
for _,path in ipairs(saf.GetSrcFilePaths(saf.SOURCE_PATH .. 'blog/')) do
    local unwrappedContent -- a place to store the unprocessed content, for use in the RSS feed below
    local blogPage = saf.BuildPage(path,
                                   function(c,m)  -- apply template
                                        unwrappedContent = Markdown(c)
                                        return saf.BuildPage(tplPath,nil,{meta=m,content=unwrappedContent}).content
                                   end
                                  )
    blogPage.unwrappedContent = unwrappedContent
    table.insert(blogs,blogPage)
end

-- sort posts by date
table.sort(blogs,function(a,b) 
                    if a.meta.date > b.meta.date then 
                        return true
                    else 
                        return false
                    end
                 end)

-- Build homepage, using result of blog build 
local idx    = saf.BuildPage(saf.SOURCE_PATH .. "index.lua", 
                             function(c,m) 
                                return saf.BuildPage(tplPath,saf.NO_FILTER,{meta = m,content = c}).content; 
                             end, 
                             {blogs = blogs}
                            )

saf.CopyDirectory(saf.ASSET_PATH, saf.BUILD_PATH .. 'assets/');
saf.CopyDirectory(saf.TEMPLATE_PATH .. 'mnml/styles/', saf.BUILD_PATH .. 'assets/styles/mnml/');

-- build RSS feed
local rss = [[
<?xml version="1.0"?>
<rss version="2.0">
    <channel>
        <title>Purple Pwny Studios</title>
        <link>http://purplepwny.com</link>
        <description>Recent posts from Purple Pwny studios.</description>
]]
-- these are sorted by date, so just do the first 10
local upperIdx = ( #blogs < 10 ) and #blogs or 10 
for i = 1,upperIdx  do
    rss = rss .. [[
        <item>
            <title>]] .. blogs[i].meta.title .. [[</title>
            <link>]] .. blogs[i].meta.url .. [[</link>]]
    local _,endIdx = string.find(blogs[i].unwrappedContent,"<\/.->",320) --location of the end of the first closing tag, after 320 characters 
    rss = rss .. [[<description>]] .. "<![CDATA["  .. string.sub(blogs[i].unwrappedContent,0,endIdx) .. " [...] ]]>" .. [[</description>
        </item>
    ]]
end
rss = rss .. [[
    </channel>
</rss>]]
saf.WriteToFile(rss, saf.BUILD_PATH .. "feed.xml")

This script first generates the HTML for everything in site_root/src/blog/ and outputs it to site_root/build/blog/, then the blog posts are ordered by date so that a listing of them can be displayed on the site homepage, some assets are copied to the build directory, and finally an RSS feed of the blog posts is generated.

For the case of a blog post with content authored in Markdown, the files in site_root/src/blog/ look like this:

-- example_post.lua
function meta()
    return{
        title  = "Hello World!",
        date   = os.time{month=06,day=19,year=2012},
        author = "Lux"
    }
end

function content(params)
    return [[
    Hello world! I am the content of a blog post. 
    # Markdown h1
    ## Markdown h2
    ]]
end