mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-03-25 09:05:47 +00:00
Port site.json to Swift code in samhuri_net module
This commit is contained in:
parent
487875098a
commit
44fef3fb78
20 changed files with 319 additions and 152 deletions
|
|
@ -115,7 +115,7 @@ Execution, trying TDD for the first time:
|
|||
|
||||
- [x] Create new packages and distribute the code accordingly
|
||||
|
||||
- [ ] Replace site.json with Swift code
|
||||
- [x] Replace site.json with Swift code
|
||||
|
||||
- [ ] Replace page template with Swift code
|
||||
|
||||
|
|
|
|||
20
SiteGenerator/Sources/SiteGenerator/BuiltSite.swift
Normal file
20
SiteGenerator/Sources/SiteGenerator/BuiltSite.swift
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// BuiltSite.swift
|
||||
// SiteGenerator
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-15.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct BuiltSite {
|
||||
public let site: Site
|
||||
public let plugins: [Plugin]
|
||||
public let renderers: [Renderer]
|
||||
|
||||
init(site: Site, plugins: [Plugin], renderers: [Renderer]) {
|
||||
self.site = site
|
||||
self.plugins = plugins
|
||||
self.renderers = renderers
|
||||
}
|
||||
}
|
||||
|
|
@ -15,10 +15,6 @@ struct PageContext {
|
|||
}
|
||||
|
||||
extension PageContext {
|
||||
var template: String {
|
||||
page.template ?? site.template
|
||||
}
|
||||
|
||||
var dictionary: [String: Any] {
|
||||
[
|
||||
"site": site,
|
||||
|
|
|
|||
|
|
@ -9,11 +9,9 @@ import Foundation
|
|||
|
||||
struct SiteContext {
|
||||
let site: Site
|
||||
let template: String
|
||||
|
||||
init(site: Site, template: String? = nil) {
|
||||
init(site: Site) {
|
||||
self.site = site
|
||||
self.template = template ?? site.template
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
//
|
||||
// HumanSite.swift
|
||||
// SiteGenerator
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-01.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// This is used to make the JSON simpler to write with optionals.
|
||||
struct HumanSite: Codable {
|
||||
let author: String
|
||||
let email: String?
|
||||
let title: String
|
||||
let description: String?
|
||||
let url: URL
|
||||
let template: String?
|
||||
let styles: [String]?
|
||||
let scripts: [String]?
|
||||
let avatar: String?
|
||||
let icon: String?
|
||||
let favicon: String?
|
||||
let plugins: [String: [String: String]]?
|
||||
}
|
||||
|
||||
extension Site {
|
||||
init(humanSite: HumanSite) {
|
||||
self.init(
|
||||
author: humanSite.author,
|
||||
email: humanSite.email,
|
||||
title: humanSite.title,
|
||||
description: humanSite.description,
|
||||
url: humanSite.url,
|
||||
template: humanSite.template ?? "page",
|
||||
styles: humanSite.styles ?? [],
|
||||
scripts: humanSite.scripts ?? [],
|
||||
avatarPath: humanSite.avatar,
|
||||
iconPath: humanSite.icon,
|
||||
faviconPath: humanSite.favicon,
|
||||
plugins: (humanSite.plugins ?? [:]).reduce(into: [:], { dict, pair in
|
||||
let (name, options) = pair
|
||||
guard let sitePlugin = SitePlugin(rawValue: name) else {
|
||||
print("warning: unknown site plugin \"\(name)\"")
|
||||
return
|
||||
}
|
||||
dict[sitePlugin] = options
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,23 +12,31 @@ public struct Site {
|
|||
public let email: String?
|
||||
public let title: String
|
||||
public let description: String?
|
||||
public var url: URL
|
||||
public let template: String
|
||||
public let url: URL
|
||||
public let styles: [String]
|
||||
public let scripts: [String]
|
||||
public let renderers: [Renderer]
|
||||
public let plugins: [Plugin]
|
||||
|
||||
// Used for JSON feed
|
||||
public let avatarPath: String?
|
||||
public let iconPath: String?
|
||||
public let faviconPath: String?
|
||||
|
||||
public let plugins: [SitePlugin: [String: Any]]
|
||||
}
|
||||
|
||||
extension Site {
|
||||
static func decode(from url: URL) throws -> Site {
|
||||
let json = try Data(contentsOf: url)
|
||||
let humanSite = try JSONDecoder().decode(HumanSite.self, from: json)
|
||||
return Site(humanSite: humanSite)
|
||||
public init(
|
||||
author: String,
|
||||
email: String?,
|
||||
title: String,
|
||||
description: String?,
|
||||
url: URL,
|
||||
styles: [String],
|
||||
scripts: [String],
|
||||
renderers: [Renderer],
|
||||
plugins: [Plugin]
|
||||
) {
|
||||
self.author = author
|
||||
self.email = email
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.url = url
|
||||
self.styles = styles
|
||||
self.scripts = scripts
|
||||
self.renderers = renderers
|
||||
self.plugins = plugins
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,16 +20,16 @@ final class SiteTemplateRenderer: TemplateRenderer {
|
|||
self.stencil = Environment(loader: loader)
|
||||
}
|
||||
|
||||
func renderPage(bodyHTML: String, metadata: [String: String]) throws -> String {
|
||||
func renderPage(template: String, bodyHTML: String, metadata: [String: String]) throws -> String {
|
||||
let page = Page(metadata: metadata)
|
||||
let context = PageContext(site: site, body: bodyHTML, page: page, metadata: metadata)
|
||||
let pageHTML = try stencil.renderTemplate(name: "\(context.template).html", context: context.dictionary)
|
||||
let pageHTML = try stencil.renderTemplate(name: template, context: context.dictionary)
|
||||
return pageHTML
|
||||
}
|
||||
|
||||
func renderTemplate(name: String?, context: [String: Any]) throws -> String {
|
||||
let siteContext = SiteContext(site: site, template: name)
|
||||
func renderTemplate(name: String, context: [String: Any]) throws -> String {
|
||||
let siteContext = SiteContext(site: site)
|
||||
let contextDict = siteContext.dictionary.merging(context, uniquingKeysWith: { _, new in new })
|
||||
return try stencil.renderTemplate(name: siteContext.template, context: contextDict)
|
||||
return try stencil.renderTemplate(name: name, context: contextDict)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@
|
|||
import Foundation
|
||||
|
||||
public protocol TemplateRenderer: AnyObject {
|
||||
func renderPage(bodyHTML: String, metadata: [String: String]) throws -> String
|
||||
func renderTemplate(name: String?, context: [String: Any]) throws -> String
|
||||
func renderPage(template: String, bodyHTML: String, metadata: [String: String]) throws -> String
|
||||
func renderTemplate(name: String, context: [String: Any]) throws -> String
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ private struct FeedItem: Codable {
|
|||
|
||||
final class JSONFeedWriter {
|
||||
let fileManager: FileManager
|
||||
let feedPath: String
|
||||
let jsonFeed: JSONFeed
|
||||
|
||||
init(fileManager: FileManager = .default, feedPath: String = "feed.json") {
|
||||
init(fileManager: FileManager = .default, feed: JSONFeed) {
|
||||
self.fileManager = fileManager
|
||||
self.feedPath = feedPath
|
||||
self.jsonFeed = feed
|
||||
}
|
||||
|
||||
func writeFeed(_ posts: [Post], site: Site, to targetURL: URL, with templateRenderer: TemplateRenderer) throws {
|
||||
|
|
@ -60,21 +60,21 @@ final class JSONFeedWriter {
|
|||
tags: post.tags
|
||||
)
|
||||
}
|
||||
let avatar = site.avatarPath.map(site.url.appendingPathComponent)
|
||||
let avatar = jsonFeed.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,
|
||||
feed_url: site.url.appendingPathComponent(jsonFeed.path).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,
|
||||
icon: jsonFeed.iconPath.map(site.url.appendingPathComponent)?.absoluteString,
|
||||
favicon: jsonFeed.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)
|
||||
let feedURL = targetURL.appendingPathComponent(jsonFeed.path)
|
||||
try feedJSON.write(to: feedURL, options: [.atomic])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ private extension Date {
|
|||
|
||||
final class RSSFeedWriter {
|
||||
let fileManager: FileManager
|
||||
let feedPath: String
|
||||
let feed: RSSFeed
|
||||
|
||||
init(fileManager: FileManager = .default, feedPath: String = "feed.xml") {
|
||||
init(fileManager: FileManager = .default, feed: RSSFeed) {
|
||||
self.fileManager = fileManager
|
||||
self.feedPath = feedPath
|
||||
self.feed = feed
|
||||
}
|
||||
|
||||
func writeFeed(_ posts: [Post], site: Site, to targetURL: URL, with templateRenderer: TemplateRenderer) throws {
|
||||
|
|
@ -74,10 +74,10 @@ final class RSSFeedWriter {
|
|||
}
|
||||
let feedXML = try templateRenderer.renderTemplate(name: "feed.xml", context: [
|
||||
"site": feedSite,
|
||||
"feedURL": site.url.appendingPathComponent(feedPath).absoluteString.escapedForXML(),
|
||||
"feedURL": site.url.appendingPathComponent(feed.path).absoluteString.escapedForXML(),
|
||||
"posts": renderedPosts,
|
||||
])
|
||||
let feedURL = targetURL.appendingPathComponent(feedPath)
|
||||
let feedURL = targetURL.appendingPathComponent(feed.path)
|
||||
try feedXML.write(to: feedURL, atomically: true, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
SiteGenerator/Sources/SiteGenerator/Posts/JSONFeed.swift
Normal file
15
SiteGenerator/Sources/SiteGenerator/Posts/JSONFeed.swift
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// JSONFeed.swift
|
||||
// SiteGenerator
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-15.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct JSONFeed {
|
||||
let path: String
|
||||
let avatarPath: String?
|
||||
let iconPath: String?
|
||||
let faviconPath: String?
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
final class PostsPlugin: Plugin {
|
||||
public final class PostsPlugin: Plugin {
|
||||
let postRepo: PostRepo
|
||||
let postWriter: PostWriter
|
||||
let jsonFeedWriter: JSONFeedWriter?
|
||||
|
|
@ -41,7 +41,8 @@ final class PostsPlugin: Plugin {
|
|||
|
||||
let jsonFeedWriter: JSONFeedWriter?
|
||||
if let jsonFeedPath = options["json_feed"] as? String {
|
||||
jsonFeedWriter = JSONFeedWriter(feedPath: jsonFeedPath)
|
||||
let jsonFeed = JSONFeed(path: jsonFeedPath, avatarPath: nil, iconPath: nil, faviconPath: nil)
|
||||
jsonFeedWriter = JSONFeedWriter(feed: jsonFeed)
|
||||
}
|
||||
else {
|
||||
jsonFeedWriter = nil
|
||||
|
|
@ -49,7 +50,8 @@ final class PostsPlugin: Plugin {
|
|||
|
||||
let rssFeedWriter: RSSFeedWriter?
|
||||
if let rssFeedPath = options["rss_feed"] as? String {
|
||||
rssFeedWriter = RSSFeedWriter(feedPath: rssFeedPath)
|
||||
let rssFeed = RSSFeed(path: rssFeedPath)
|
||||
rssFeedWriter = RSSFeedWriter(feed: rssFeed)
|
||||
}
|
||||
else {
|
||||
rssFeedWriter = nil
|
||||
|
|
@ -58,7 +60,7 @@ final class PostsPlugin: Plugin {
|
|||
self.init(postRepo: postRepo, postWriter: postWriter, jsonFeedWriter: jsonFeedWriter, rssFeedWriter: rssFeedWriter)
|
||||
}
|
||||
|
||||
func setUp(site: Site, sourceURL: URL) throws {
|
||||
public func setUp(site: Site, sourceURL: URL) throws {
|
||||
guard postRepo.postDataExists(at: sourceURL) else {
|
||||
return
|
||||
}
|
||||
|
|
@ -66,7 +68,7 @@ final class PostsPlugin: Plugin {
|
|||
try postRepo.readPosts(sourceURL: sourceURL)
|
||||
}
|
||||
|
||||
func render(site: Site, targetURL: URL, templateRenderer: TemplateRenderer) throws {
|
||||
public func render(site: Site, targetURL: URL, templateRenderer: TemplateRenderer) throws {
|
||||
guard !postRepo.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// PostsPluginBuilder.swift
|
||||
// SiteGenerator
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-15.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class PostsPluginBuilder {
|
||||
private var path: String?
|
||||
private var jsonFeed: JSONFeed?
|
||||
private var rssFeed: RSSFeed?
|
||||
|
||||
public init() {}
|
||||
|
||||
public func path(_ path: String) -> PostsPluginBuilder {
|
||||
precondition(self.path == nil, "path is already defined")
|
||||
self.path = path
|
||||
return self
|
||||
}
|
||||
|
||||
public func jsonFeed(
|
||||
path: String? = nil,
|
||||
avatarPath: String? = nil,
|
||||
iconPath: String? = nil,
|
||||
faviconPath: String? = nil
|
||||
) -> PostsPluginBuilder {
|
||||
precondition(jsonFeed == nil, "JSON feed is already defined")
|
||||
jsonFeed = JSONFeed(
|
||||
path: path ?? "feed.json",
|
||||
avatarPath: avatarPath,
|
||||
iconPath: iconPath,
|
||||
faviconPath: faviconPath
|
||||
)
|
||||
return self
|
||||
}
|
||||
|
||||
public func rssFeed(path: String? = nil) -> PostsPluginBuilder {
|
||||
precondition(rssFeed == nil, "RSS feed is already defined")
|
||||
rssFeed = RSSFeed(path: path ?? "feed.xml")
|
||||
return self
|
||||
}
|
||||
|
||||
public func build() -> PostsPlugin {
|
||||
let postRepo: PostRepo
|
||||
let postWriter: PostWriter
|
||||
if let outputPath = path {
|
||||
postRepo = PostRepo(outputPath: outputPath)
|
||||
postWriter = PostWriter(outputPath: outputPath)
|
||||
}
|
||||
else {
|
||||
postRepo = PostRepo()
|
||||
postWriter = PostWriter()
|
||||
}
|
||||
|
||||
let jsonFeedWriter: JSONFeedWriter?
|
||||
if let jsonFeed = jsonFeed {
|
||||
jsonFeedWriter = JSONFeedWriter(feed: jsonFeed)
|
||||
}
|
||||
else {
|
||||
jsonFeedWriter = nil
|
||||
}
|
||||
|
||||
let rssFeedWriter: RSSFeedWriter?
|
||||
if let rssFeed = rssFeed {
|
||||
rssFeedWriter = RSSFeedWriter(feed: rssFeed)
|
||||
}
|
||||
else {
|
||||
rssFeedWriter = nil
|
||||
}
|
||||
|
||||
return PostsPlugin(
|
||||
postRepo: postRepo,
|
||||
postWriter: postWriter,
|
||||
jsonFeedWriter: jsonFeedWriter,
|
||||
rssFeedWriter: rssFeedWriter
|
||||
)
|
||||
}
|
||||
}
|
||||
12
SiteGenerator/Sources/SiteGenerator/Posts/RSSFeed.swift
Normal file
12
SiteGenerator/Sources/SiteGenerator/Posts/RSSFeed.swift
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
//
|
||||
// RSSFeed.swift
|
||||
// SiteGenerator
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-15.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct RSSFeed {
|
||||
let path: String
|
||||
}
|
||||
|
|
@ -24,8 +24,8 @@ final class ProjectsPlugin: Plugin {
|
|||
var projects: [Project] = []
|
||||
var sourceURL: URL!
|
||||
|
||||
init(outputPath: String = "projects") {
|
||||
self.outputPath = outputPath
|
||||
init(outputPath: String? = nil) {
|
||||
self.outputPath = outputPath ?? "projects"
|
||||
}
|
||||
|
||||
convenience init(options: [String: Any]) {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,11 @@ import Ink
|
|||
public final class MarkdownRenderer: Renderer {
|
||||
let fileManager: FileManager = .default
|
||||
let markdownParser = MarkdownParser()
|
||||
let defaultTemplate: String
|
||||
|
||||
public init() {}
|
||||
public init(defaultTemplate: String) {
|
||||
self.defaultTemplate = defaultTemplate
|
||||
}
|
||||
|
||||
public func canRenderFile(named filename: String, withExtension ext: String) -> Bool {
|
||||
ext == "md"
|
||||
|
|
@ -23,7 +26,7 @@ public final class MarkdownRenderer: Renderer {
|
|||
let bodyMarkdown = try String(contentsOf: fileURL, encoding: .utf8)
|
||||
let bodyHTML = markdownParser.html(from: bodyMarkdown).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let metadata = try markdownMetadata(from: fileURL)
|
||||
let pageHTML = try templateRenderer.renderPage(bodyHTML: bodyHTML, metadata: metadata)
|
||||
let pageHTML = try templateRenderer.renderPage(template: defaultTemplate, bodyHTML: bodyHTML, metadata: metadata)
|
||||
|
||||
let mdFilename = fileURL.lastPathComponent
|
||||
let htmlPath: String
|
||||
|
|
|
|||
105
SiteGenerator/Sources/SiteGenerator/SiteBuilder.swift
Normal file
105
SiteGenerator/Sources/SiteGenerator/SiteBuilder.swift
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
//
|
||||
// SiteBuilder.swift
|
||||
// SiteGenerator
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-15.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public final class SiteBuilder {
|
||||
private let author: String
|
||||
private let title: String
|
||||
private let url: URL
|
||||
|
||||
private var email: String?
|
||||
private var description: String?
|
||||
|
||||
private var styles: [String] = []
|
||||
private var scripts: [String] = []
|
||||
|
||||
private var plugins: [Plugin] = []
|
||||
private var renderers: [Renderer] = []
|
||||
|
||||
public init(
|
||||
author: String,
|
||||
email: String? = nil,
|
||||
title: String,
|
||||
description: String? = nil,
|
||||
url: URL
|
||||
) {
|
||||
self.author = author
|
||||
self.email = email
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.url = url
|
||||
}
|
||||
|
||||
public func email(_ email: String) -> SiteBuilder {
|
||||
precondition(self.email == nil, "email is already defined")
|
||||
self.email = email
|
||||
return self
|
||||
}
|
||||
|
||||
public func description(_ description: String) -> SiteBuilder {
|
||||
precondition(self.description == nil, "description is already defined")
|
||||
self.description = description
|
||||
return self
|
||||
}
|
||||
|
||||
public func styles(_ styles: String...) -> SiteBuilder {
|
||||
self.styles.append(contentsOf: styles)
|
||||
return self
|
||||
}
|
||||
|
||||
public func scripts(_ scripts: String...) -> SiteBuilder {
|
||||
self.scripts.append(contentsOf: scripts)
|
||||
return self
|
||||
}
|
||||
|
||||
public func plugin(_ plugin: Plugin) -> SiteBuilder {
|
||||
plugins.append(plugin)
|
||||
return self
|
||||
}
|
||||
|
||||
public func renderer(_ renderer: Renderer) -> SiteBuilder {
|
||||
renderers.append(renderer)
|
||||
return self
|
||||
}
|
||||
|
||||
public func build() -> Site {
|
||||
Site(
|
||||
author: author,
|
||||
email: email,
|
||||
title: title,
|
||||
description: description,
|
||||
url: url,
|
||||
styles: styles,
|
||||
scripts: scripts,
|
||||
renderers: renderers,
|
||||
plugins: plugins
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Markdown
|
||||
|
||||
public extension SiteBuilder {
|
||||
func renderMarkdown(defaultTemplate: String) -> SiteBuilder {
|
||||
renderer(MarkdownRenderer(defaultTemplate: defaultTemplate))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Projects
|
||||
|
||||
public extension SiteBuilder {
|
||||
func projects(path: String? = nil) -> SiteBuilder {
|
||||
plugin(ProjectsPlugin(outputPath: path))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Posts
|
||||
|
||||
public extension SiteBuilder {
|
||||
// anything nice we can do there?
|
||||
}
|
||||
|
|
@ -15,44 +15,27 @@ public final class SiteGenerator {
|
|||
// Site properties
|
||||
public let site: Site
|
||||
public let sourceURL: URL
|
||||
public private(set) var plugins: [Plugin] = []
|
||||
public let renderers: [Renderer]
|
||||
|
||||
let ignoredFilenames = [".DS_Store", ".gitkeep"]
|
||||
|
||||
public init(sourceURL: URL, siteURLOverride: URL? = nil, renderers: [Renderer]) throws {
|
||||
let siteURL = sourceURL.appendingPathComponent("site.json")
|
||||
var site = try Site.decode(from: siteURL)
|
||||
if let url = siteURLOverride {
|
||||
print("Overriding site URL \(site.url) with \(url)")
|
||||
site.url = url
|
||||
}
|
||||
public init(sourceURL: URL, site: Site) throws {
|
||||
self.site = site
|
||||
self.sourceURL = sourceURL
|
||||
|
||||
let templatesURL = sourceURL.appendingPathComponent("templates")
|
||||
self.templateRenderer = SiteTemplateRenderer(site: site, templatesURL: templatesURL)
|
||||
|
||||
self.site = site
|
||||
self.sourceURL = sourceURL
|
||||
self.renderers = renderers
|
||||
|
||||
try initializePlugins()
|
||||
}
|
||||
|
||||
private func initializePlugins() throws {
|
||||
plugins = site.plugins.map { pair in
|
||||
let (sitePlugin, options) = pair
|
||||
return sitePlugin.construct(options: options)
|
||||
for plugin in site.plugins {
|
||||
try plugin.setUp(site: site, sourceURL: sourceURL)
|
||||
}
|
||||
try plugins.forEach(addPlugin)
|
||||
}
|
||||
|
||||
public func addPlugin(_ plugin: Plugin) throws {
|
||||
try plugin.setUp(site: site, sourceURL: sourceURL)
|
||||
plugins.append(plugin)
|
||||
}
|
||||
|
||||
public func generate(targetURL: URL) throws {
|
||||
for plugin in plugins {
|
||||
for plugin in site.plugins {
|
||||
try plugin.render(site: site, targetURL: targetURL, templateRenderer: templateRenderer)
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +70,7 @@ public final class SiteGenerator {
|
|||
func renderOrCopyFile(url fileURL: URL, targetDir: URL) throws {
|
||||
let filename = fileURL.lastPathComponent
|
||||
let ext = String(filename.split(separator: ".").last!)
|
||||
for renderer in renderers {
|
||||
for renderer in site.renderers {
|
||||
if renderer.canRenderFile(named: filename, withExtension: ext) {
|
||||
try renderer.render(fileURL: fileURL, targetDir: targetDir, templateRenderer: templateRenderer)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -5,11 +5,28 @@ public struct samhuri_net {
|
|||
public init() {}
|
||||
|
||||
public func generate(sourceURL: URL, targetURL: URL, siteURLOverride: URL? = nil) throws {
|
||||
let generator = try SiteGenerator(
|
||||
sourceURL: sourceURL,
|
||||
siteURLOverride: siteURLOverride,
|
||||
renderers: [MarkdownRenderer()]
|
||||
let postsPlugin = PostsPluginBuilder()
|
||||
.path("posts")
|
||||
.jsonFeed(
|
||||
avatarPath: "images/me.jpg",
|
||||
iconPath: "images/apple-touch-icon-300.png",
|
||||
faviconPath: "images/apple-touch-icon-80.png"
|
||||
)
|
||||
.rssFeed()
|
||||
.build()
|
||||
let site = SiteBuilder(
|
||||
author: "Sami Samhuri",
|
||||
email: "sami@samhuri.net",
|
||||
title: "samhuri.net",
|
||||
description: "just some blog",
|
||||
url: siteURLOverride ?? URL(string: "https://samhuri.net")!
|
||||
)
|
||||
.styles("css/normalize.css", "css/style.css")
|
||||
.renderMarkdown(defaultTemplate: "page.html")
|
||||
.projects()
|
||||
.plugin(postsPlugin)
|
||||
.build()
|
||||
let generator = try SiteGenerator(sourceURL: sourceURL, site: site)
|
||||
try generator.generate(targetURL: targetURL)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
22
site.json
22
site.json
|
|
@ -1,22 +0,0 @@
|
|||
{
|
||||
"title": "samhuri.net",
|
||||
"description": "just some blog",
|
||||
"author": "Sami Samhuri",
|
||||
"email": "sami@samhuri.net",
|
||||
"url": "https://samhuri.net",
|
||||
"styles": [
|
||||
"/css/normalize.css",
|
||||
"/css/style.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"icon": "images/apple-touch-icon-300.png",
|
||||
"favicon": "images/apple-touch-icon-80.png",
|
||||
"avatar": "images/me.jpg",
|
||||
"plugins": {
|
||||
"projects": {},
|
||||
"posts": {
|
||||
"json_feed": "feed.json",
|
||||
"rss_feed": "feed.xml"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue