Skip to content

Commit

Permalink
feat: Simplify API by getting rid of SiteMetadata
Browse files Browse the repository at this point in the history
Saga doesn't use the site metadata at all, and instead of Saga passing it around and having to deal with the generic types, your site's code can just create whatever struct or enum holding whatever values you want to use in your templates.
  • Loading branch information
kevinrenskers committed Nov 23, 2023
1 parent 49a1df4 commit 71a3bd1
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 91 deletions.
12 changes: 6 additions & 6 deletions Example/Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/loopwerk/Parsley",
"state": {
"branch": null,
"revision": "10da1efa3ea278a4828b8c87fd430bdfa326ffbc",
"version": "0.8.0"
"revision": "3240bdfee97f3bbde5c4ec150a29b0abcb0d3d21",
"version": "0.9.0"
}
},
{
Expand All @@ -33,17 +33,17 @@
"repositoryURL": "https://github.com/loopwerk/SagaParsleyMarkdownReader",
"state": {
"branch": null,
"revision": "dff2d3fa3f4eb83b74ec026cdf9b2f62df0383af",
"version": "0.5.0"
"revision": "d4fe9fca9829c1fb294af0160c23d5fc4bcf5fe4",
"version": "0.6.0"
}
},
{
"package": "SagaSwimRenderer",
"repositoryURL": "https://github.com/loopwerk/SagaSwimRenderer",
"state": {
"branch": null,
"revision": "b4b833d20846c19581f4a0328cfdefa8d4e564ed",
"version": "0.6.1"
"revision": "f53481e5c9972b83ca2d16ca2b962c63d460e23f",
"version": "0.7.0"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Example/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ let package = Package(
dependencies: [
.package(path: "../"),
.package(url: "https://github.com/loopwerk/SagaParsleyMarkdownReader", from: "0.5.0"),
.package(url: "https://github.com/loopwerk/SagaSwimRenderer", from: "0.6.1"),
.package(url: "https://github.com/loopwerk/SagaSwimRenderer", from: "0.7.0"),
],
targets: [
.executableTarget(
Expand Down
20 changes: 7 additions & 13 deletions Example/Sources/Example/run.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import PathKit
import SagaParsleyMarkdownReader
import SagaSwimRenderer

enum SiteMetadata {
static let url = URL(string: "http://www.example.com")!
static let name = "Example website"
static let author = "Kevin Renskers"
}

struct ArticleMetadata: Metadata {
let tags: [String]
var summary: String?
Expand All @@ -15,18 +21,6 @@ struct AppMetadata: Metadata {
let images: [String]?
}

// SiteMetadata is given to every template.
// You can put whatever you want in here, as long as it's Decodable.
struct SiteMetadata: Metadata {
let url: URL
let name: String
}

let siteMetadata = SiteMetadata(
url: URL(string: "http://www.example.com")!,
name: "Example website"
)

// An easy way to only get public articles, since ArticleMetadata.public is optional
extension Item where M == ArticleMetadata {
var `public`: Bool {
Expand Down Expand Up @@ -63,7 +57,7 @@ struct Run {
}()

static func main() async throws {
try await Saga(input: "content", output: "deploy", siteMetadata: siteMetadata)
try await Saga(input: "content", output: "deploy")
// All markdown files within the "articles" subfolder will be parsed to html,
// using ArticleMetadata as the Item's metadata type.
// Furthermore we are only interested in public articles.
Expand Down
56 changes: 24 additions & 32 deletions Example/Sources/Example/templates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import Saga
import SagaSwimRenderer
import Foundation

func baseHtml(siteMetadata: SiteMetadata, title pageTitle: String, @NodeBuilder children: () -> NodeConvertible) -> Node {
func baseHtml(title pageTitle: String, @NodeBuilder children: () -> NodeConvertible) -> Node {
html(lang: "en-US") {
head {
title { siteMetadata.name+": "+pageTitle }
title { SiteMetadata.name+": "+pageTitle }
link(href: "/static/style.css", rel: "stylesheet")
link(href: "/static/prism.css", rel: "stylesheet")
}
Expand Down Expand Up @@ -34,8 +34,8 @@ extension Date {
}
}

func renderArticle(context: ItemRenderingContext<ArticleMetadata, SiteMetadata>) -> Node {
return baseHtml(siteMetadata: context.siteMetadata, title: context.item.title) {
func renderArticle(context: ItemRenderingContext<ArticleMetadata>) -> Node {
return baseHtml(title: context.item.title) {
div(id: "article") {
h1 { context.item.title }
h2 {
Expand Down Expand Up @@ -85,24 +85,24 @@ func renderPagination(_ paginator: Paginator?) -> Node {
}
}

func renderArticles(context: ItemsRenderingContext<ArticleMetadata, SiteMetadata>) -> Node {
baseHtml(siteMetadata: context.siteMetadata, title: "Articles") {
func renderArticles(context: ItemsRenderingContext<ArticleMetadata>) -> Node {
baseHtml(title: "Articles") {
h1 { "Articles" }
context.items.map(articleInList)
renderPagination(context.paginator)
}
}

func renderPartition<T>(context: PartitionedRenderingContext<T, ArticleMetadata, SiteMetadata>) -> Node {
baseHtml(siteMetadata: context.siteMetadata, title: "Articles in \(context.key)") {
func renderPartition<T>(context: PartitionedRenderingContext<T, ArticleMetadata>) -> Node {
baseHtml(title: "Articles in \(context.key)") {
h1 { "Articles in \(context.key)" }
context.items.map(articleInList)
renderPagination(context.paginator)
}
}

func renderPage(context: ItemRenderingContext<EmptyMetadata, SiteMetadata>) -> Node {
baseHtml(siteMetadata: context.siteMetadata, title: context.item.title) {
func renderPage(context: ItemRenderingContext<EmptyMetadata>) -> Node {
baseHtml(title: context.item.title) {
div(id: "page") {
h1 { context.item.title }
Node.raw(context.item.body)
Expand All @@ -117,8 +117,8 @@ func renderPage(context: ItemRenderingContext<EmptyMetadata, SiteMetadata>) -> N
}
}

func renderApps(context: ItemsRenderingContext<AppMetadata, SiteMetadata>) -> Node {
baseHtml(siteMetadata: context.siteMetadata, title: "Apps") {
func renderApps(context: ItemsRenderingContext<AppMetadata>) -> Node {
baseHtml(title: "Apps") {
h1 { "Apps" }
context.items.map { app in
div(class: "app") {
Expand Down Expand Up @@ -151,36 +151,28 @@ extension Item where M == ArticleMetadata {
}
}

func renderFeed(context: ItemsRenderingContext<ArticleMetadata, SiteMetadata>) -> Node {
func renderFeed(context: ItemsRenderingContext<ArticleMetadata>) -> Node {
AtomFeed(
title: context.siteMetadata.name,
author: "Kevin Renskers",
baseURL: context.siteMetadata.url,
pagePath: "articles/",
feedPath: "articles/feed.xml",
title: SiteMetadata.name,
author: SiteMetadata.author,
baseURL: SiteMetadata.url,
feedPath: context.outputPath.string,
items: Array(context.items.prefix(20)),
summary: { item in
if let article = item as? Item<ArticleMetadata> {
return article.summary
}
return nil
return item.summary
}
).node()
}

func renderTagFeed(context: PartitionedRenderingContext<String, ArticleMetadata, SiteMetadata>) -> Node {
func renderTagFeed(context: PartitionedRenderingContext<String, ArticleMetadata>) -> Node {
AtomFeed(
title: context.siteMetadata.name,
author: "Kevin Renskers",
baseURL: context.siteMetadata.url,
pagePath: "articles/tag/\(context.key)/",
feedPath: "articles/tag/\(context.key)/feed.xml",
title: SiteMetadata.name,
author: SiteMetadata.author,
baseURL: SiteMetadata.url,
feedPath: context.outputPath.string,
items: Array(context.items.prefix(20)),
summary: { item in
if let article = item as? Item<ArticleMetadata> {
return article.summary
}
return nil
return item.summary
}
).node()
}
10 changes: 5 additions & 5 deletions Sources/Saga/ProcessingStep.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Foundation
import PathKit

internal class ProcessStep<M: Metadata, SiteMetadata: Metadata> {
internal class ProcessStep<M: Metadata> {
let folder: Path?
let readers: [Reader<M>]
let filter: (Item<M>) -> Bool
let writers: [Writer<M, SiteMetadata>]
let writers: [Writer<M>]
var items: [Item<M>]

init(folder: Path?, readers: [Reader<M>], filter: @escaping (Item<M>) -> Bool, writers: [Writer<M, SiteMetadata>]) {
init(folder: Path?, readers: [Reader<M>], filter: @escaping (Item<M>) -> Bool, writers: [Writer<M>]) {
self.folder = folder
self.readers = readers
self.filter = filter
Expand All @@ -21,7 +21,7 @@ internal class AnyProcessStep {
let runReaders: () async throws -> ()
let runWriters: () throws -> ()

init<M: Metadata, SiteMetadata: Metadata>(step: ProcessStep<M, SiteMetadata>, fileStorage: [FileContainer], inputPath: Path, outputPath: Path, itemWriteMode: ItemWriteMode, siteMetadata: SiteMetadata, fileIO: FileIO) {
init<M: Metadata>(step: ProcessStep<M>, fileStorage: [FileContainer], inputPath: Path, outputPath: Path, itemWriteMode: ItemWriteMode, fileIO: FileIO) {
runReaders = {
var items = [Item<M>]()

Expand Down Expand Up @@ -71,7 +71,7 @@ internal class AnyProcessStep {
.sorted(by: { left, right in left.date > right.date })

for writer in step.writers {
try writer.run(step.items, allItems, siteMetadata, outputPath, step.folder ?? "", fileIO)
try writer.run(step.items, allItems, outputPath, step.folder ?? "", fileIO)
}
}
}
Expand Down
11 changes: 5 additions & 6 deletions Sources/Saga/RenderingContexts.swift
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import PathKit

public struct ItemRenderingContext<M: Metadata, SiteMetadata: Metadata> {
public struct ItemRenderingContext<M: Metadata> {
public let item: Item<M>
public let items: [Item<M>]
public let allItems: [AnyItem]
public let siteMetadata: SiteMetadata
}

public struct ItemsRenderingContext<M: Metadata, SiteMetadata: Metadata> {
public struct ItemsRenderingContext<M: Metadata> {
public let items: [Item<M>]
public let allItems: [AnyItem]
public let siteMetadata: SiteMetadata
public let paginator: Paginator?
public let outputPath: Path
}

public typealias ContextKey = CustomStringConvertible & Comparable
public struct PartitionedRenderingContext<T: ContextKey, M: Metadata, SiteMetadata: Metadata> {
public struct PartitionedRenderingContext<T: ContextKey, M: Metadata> {
public let key: T
public let items: [Item<M>]
public let allItems: [AnyItem]
public let siteMetadata: SiteMetadata
public let paginator: Paginator?
public let outputPath: Path
}

/// A model representing a paginator.
Expand Down
13 changes: 4 additions & 9 deletions Sources/Saga/Saga.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import PathKit
/// @main
/// struct Run {
/// static func main() async throws {
/// try await Saga(input: "content", output: "deploy", siteMetadata: EmptyMetadata())
/// try await Saga(input: "content", output: "deploy")
/// // All files in the input folder will be parsed to html, and written to the output folder.
/// .register(
/// metadata: EmptyMetadata.self,
Expand All @@ -26,7 +26,7 @@ import PathKit
/// }
/// }
/// ```
public class Saga<SiteMetadata: Metadata> {
public class Saga {
/// The root working path. This is automatically set to the same folder that holds `Package.swift`.
public let rootPath: Path

Expand All @@ -36,21 +36,17 @@ public class Saga<SiteMetadata: Metadata> {
/// The path that Saga will write the rendered website to, relative to the `rootPath`. For example "deploy".
public let outputPath: Path

/// The metadata used to hold site-wide information, such as the website name or URL. This will be included in all rendering contexts.
public let siteMetadata: SiteMetadata

/// An array of all file containters.
public let fileStorage: [FileContainer]

internal var processSteps = [AnyProcessStep]()
internal let fileIO: FileIO

public init(input: Path, output: Path = "deploy", siteMetadata: SiteMetadata, fileIO: FileIO = .diskAccess, originFilePath: StaticString = #file) throws {
public init(input: Path, output: Path = "deploy", fileIO: FileIO = .diskAccess, originFilePath: StaticString = #file) throws {
let originFile = Path("\(originFilePath)")
rootPath = try fileIO.resolveSwiftPackageFolder(originFile)
inputPath = rootPath + input
outputPath = rootPath + output
self.siteMetadata = siteMetadata
self.fileIO = fileIO

// 1. Find all files in the source folder
Expand All @@ -75,7 +71,7 @@ public class Saga<SiteMetadata: Metadata> {
/// - writers: The writers that will be used by this step.
/// - Returns: The Saga instance itself, so you can chain further calls onto it.
@discardableResult
public func register<M: Metadata>(folder: Path? = nil, metadata: M.Type, readers: [Reader<M>], itemWriteMode: ItemWriteMode = .moveToSubfolder, filter: @escaping ((Item<M>) -> Bool) = { _ in true }, writers: [Writer<M, SiteMetadata>]) throws -> Self {
public func register<M: Metadata>(folder: Path? = nil, metadata: M.Type, readers: [Reader<M>], itemWriteMode: ItemWriteMode = .moveToSubfolder, filter: @escaping ((Item<M>) -> Bool) = { _ in true }, writers: [Writer<M>]) throws -> Self {
let step = ProcessStep(folder: folder, readers: readers, filter: filter, writers: writers)
self.processSteps.append(
.init(
Expand All @@ -84,7 +80,6 @@ public class Saga<SiteMetadata: Metadata> {
inputPath: inputPath,
outputPath: outputPath,
itemWriteMode: itemWriteMode,
siteMetadata: siteMetadata,
fileIO: fileIO
))
return self
Expand Down
Loading

0 comments on commit 71a3bd1

Please sign in to comment.