SwiftySites - No frills static site generator

Hello fellow Swift devs!

I'd like to introduce to y'all a small project that I've been working on called SwiftySites:

It's really just another static site generator but with a slightly different approach:

  • A Site instance is defined as a collection of content types, templates and actual content.
  • A Content type is any Swift struct in which one or more properties may be annotated as @Markdown.
  • For each content type, a number of instances can be declared with an associated path within the site.
  • Each Template applies to a subset of content instances and produces outputs which may be of any kind: HTML, JSON, etc.

Templates rely heavily on string interpolation and all the matching is done using regular expressions.

Here's a brief example from the Getting Started documentation article:

let config = SiteConfig(title: "My Site", productionUrl: URL(string: "https://swiftysites.dev")!)

// We define two articles.
let articles = [
    Article(path: "/articles/fish", title: "Fish" , body: "Many _fish_ in the sea."),
    .init(path: "/articles/turtles", title: "Turtles", body: "Turtles **all the way** down.")
]

// We declare a function to list all available articles.
func getArticleLinks() -> String {
    articles.map(\.title).joined()
}

// We also define two additional site pages.
let pages = [
    // The home page contains a list of articles.
    Page(path: "/", contents: "Home Page \(getArticleLinks())"),
    .init(path: "/about", contents: "About"),
]

// Both pages will be rendered using the following template.
let pageTemplates = [
    Template { (p: Page) in
        "<title>\(config.title)</title><body>\(p.contents)</body>"
    }
]

// For the articles we define an HTML and a JSON template.
let articleTemplates = [
    // The HTML template excludes articles which begin with "t" like "turtles".
    Template<Article>(exclude: #/\/articles\/t.*/#) { article in
        "<h1>\(article.title)</h1><main>\(article.body)</main>"
    },
    // The JSON template only applies to articles which begin with "t".
    Template(#/\/articles\/t.*/#, suffix: "json") { (a: Article) in
        """
        { title: "\(a.title)", content: "\(a.body)" }
        """
    }
]

// The site includes all pages and articles, along with the corresponding template definitions.
let site = Site(config, content: (pages, articles), template: (pageTemplates, articleTemplates))

// We will render programmatically now but the following command tipically goes on main.swift.
let result = site.render(clean: true, skipSitemap: true, skipStatic: true)

result.urls
// [https://swiftysites.dev/, https://swiftysites.dev/about/, https://swiftysites.dev/articles/fish/, https://swiftysites.dev/articles/turtles/index.json]

The solution is not as fancy as some of the other products out there but it has served me well for the last few years so I thought I'd share.

As an example my own personal blog is maintained using SwiftySites. I found the integration with GitHub Pages and Netlify to be quite neat. It's also great for integrating DocC documentation.

That's it! Any feedback, contributions, etc. will be greatly appreciated.

3 Likes