mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-03-25 09:05:47 +00:00
Render a JSON feed
This commit is contained in:
parent
1d0ffd52a2
commit
dd96d95fc4
6 changed files with 154 additions and 3 deletions
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// JSONFeedPlugin.swift
|
||||
// SiteGenerator
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
final class JSONFeedPlugin: Plugin {
|
||||
let postRepo: PostRepo
|
||||
let jsonFeedWriter: JSONFeedWriter
|
||||
|
||||
init(
|
||||
postRepo: PostRepo = PostRepo(),
|
||||
jsonFeedWriter: JSONFeedWriter = JSONFeedWriter()
|
||||
) {
|
||||
self.postRepo = postRepo
|
||||
self.jsonFeedWriter = jsonFeedWriter
|
||||
}
|
||||
|
||||
// MARK: - Plugin methods
|
||||
|
||||
func setUp(site: Site, sourceURL: URL) throws {
|
||||
guard postRepo.postDataExists(at: sourceURL) else {
|
||||
return
|
||||
}
|
||||
|
||||
try postRepo.readPosts(sourceURL: sourceURL, makePath: jsonFeedWriter.urlPathForPost)
|
||||
}
|
||||
|
||||
func render(site: Site, targetURL: URL, templateRenderer: TemplateRenderer) throws {
|
||||
guard !postRepo.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
try jsonFeedWriter.writeFeed(postRepo.postsForFeed, site: site, to: targetURL, with: templateRenderer)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// JSONFeedWriter.swift
|
||||
// SiteGenerator
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-10.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
private struct Feed: Codable {
|
||||
let version = "https://jsonfeed.org/version/1"
|
||||
let title: String
|
||||
let home_page_url: String
|
||||
let feed_url: String
|
||||
let author: FeedAuthor
|
||||
let icon: String?
|
||||
let favicon: String?
|
||||
let items: [FeedItem]
|
||||
}
|
||||
|
||||
private struct FeedAuthor: Codable {
|
||||
let name: String
|
||||
let avatar: String?
|
||||
let url: String?
|
||||
}
|
||||
|
||||
private struct FeedItem: Codable {
|
||||
let title: String
|
||||
let date_published: Date
|
||||
let id: String
|
||||
let url: String
|
||||
let external_url: String?
|
||||
let author: FeedAuthor
|
||||
let content_html: String
|
||||
let tags: [String]
|
||||
}
|
||||
|
||||
final class JSONFeedWriter {
|
||||
let fileManager: FileManager
|
||||
let feedPath: String
|
||||
let postsPath: String
|
||||
|
||||
var baseURL: URL!
|
||||
|
||||
init(fileManager: FileManager = .default, feedPath: String = "feed.json", postsPath: String = "posts") {
|
||||
self.fileManager = fileManager
|
||||
self.feedPath = feedPath
|
||||
self.postsPath = postsPath
|
||||
}
|
||||
|
||||
#warning("These urlPath methods were copied from PostsPlugin and should possibly be moved somewhere else")
|
||||
|
||||
func urlPath(year: Int) -> String {
|
||||
"/\(postsPath)/\(year)"
|
||||
}
|
||||
|
||||
func urlPath(year: Int, month: Month) -> String {
|
||||
urlPath(year: year).appending("/\(month.padded)")
|
||||
}
|
||||
|
||||
func urlPathForPost(date: Date, slug: String) -> String {
|
||||
urlPath(year: date.year, month: Month(date.month)).appending("/\(slug)")
|
||||
}
|
||||
|
||||
func writeFeed(_ posts: [Post], site: Site, to targetURL: URL, with templateRenderer: TemplateRenderer) throws {
|
||||
let items: [FeedItem] = try posts.map { post in
|
||||
let url = site.url.appendingPathComponent(post.path)
|
||||
return FeedItem(
|
||||
title: post.isLink ? "→ \(post.title)" : post.title,
|
||||
date_published: post.date,
|
||||
id: url.absoluteString,
|
||||
url: url.absoluteString,
|
||||
external_url: post.link?.absoluteString,
|
||||
author: FeedAuthor(name: post.author, avatar: nil, url: nil),
|
||||
content_html: try templateRenderer.renderTemplate(name: "feed-post.html", context: [
|
||||
"post": post,
|
||||
]),
|
||||
tags: post.tags
|
||||
)
|
||||
}
|
||||
let avatar = site.avatarPath.map(site.url.appendingPathComponent)
|
||||
let feed: Feed = Feed(
|
||||
title: site.title,
|
||||
home_page_url: site.url.absoluteString,
|
||||
feed_url: site.url.appendingPathComponent(feedPath).absoluteString,
|
||||
author: FeedAuthor(name: site.author, avatar: avatar?.absoluteString, url: site.url.absoluteString),
|
||||
icon: site.iconPath.map(site.url.appendingPathComponent)?.absoluteString,
|
||||
favicon: site.faviconPath.map(site.url.appendingPathComponent)?.absoluteString,
|
||||
items: items
|
||||
)
|
||||
let encoder = JSONEncoder()
|
||||
encoder.dateEncodingStrategy = .iso8601
|
||||
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]
|
||||
let feedJSON = try encoder.encode(feed)
|
||||
let feedURL = targetURL.appendingPathComponent(feedPath)
|
||||
try feedJSON.write(to: feedURL, options: [.atomic])
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,9 @@ struct HumanSite: Codable {
|
|||
let template: String?
|
||||
let styles: [String]?
|
||||
let scripts: [String]?
|
||||
let avatar: String?
|
||||
let icon: String?
|
||||
let favicon: String?
|
||||
}
|
||||
|
||||
extension Site {
|
||||
|
|
@ -29,7 +32,10 @@ extension Site {
|
|||
url: humanSite.url,
|
||||
template: humanSite.template ?? "page",
|
||||
styles: humanSite.styles ?? [],
|
||||
scripts: humanSite.scripts ?? []
|
||||
scripts: humanSite.scripts ?? [],
|
||||
avatarPath: humanSite.avatar,
|
||||
iconPath: humanSite.icon,
|
||||
faviconPath: humanSite.favicon
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,11 @@ public struct Site {
|
|||
public let template: String
|
||||
public let styles: [String]
|
||||
public let scripts: [String]
|
||||
|
||||
// Used for JSON feed
|
||||
public let avatarPath: String?
|
||||
public let iconPath: String?
|
||||
public let faviconPath: String?
|
||||
}
|
||||
|
||||
extension Site {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ func main(sourcePath: String, targetPath: String) throws {
|
|||
let targetURL = URL(fileURLWithPath: targetPath)
|
||||
let generator = try Generator(
|
||||
sourceURL: sourceURL,
|
||||
plugins: [ProjectsPlugin(), PostsPlugin(), RSSFeedPlugin()],
|
||||
plugins: [ProjectsPlugin(), PostsPlugin(), RSSFeedPlugin(), JSONFeedPlugin()],
|
||||
renderers: [LessRenderer(), MarkdownRenderer()]
|
||||
)
|
||||
try generator.generate(targetURL: targetURL)
|
||||
|
|
|
|||
|
|
@ -8,5 +8,8 @@
|
|||
"/css/normalize.css",
|
||||
"/css/style.css"
|
||||
],
|
||||
"scripts": []
|
||||
"scripts": [],
|
||||
"icon": "images/apple-touch-icon-300.png",
|
||||
"favicon": "images/apple-touch-icon-80.png",
|
||||
"avatar": "images/me.jpg",
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue