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.