diff --git a/.tm_properties b/.tm_properties
index c8da1d6..eb43dca 100644
--- a/.tm_properties
+++ b/.tm_properties
@@ -1,2 +1,2 @@
-exclude = "{$exclude,SiteGenerator,samhuri.net,gensite,www,tweets,wayback,actual}"
+exclude = "{$exclude,samhuri.net,gensite,www,tweets,wayback,actual}"
include = "{$include,.gitignore}"
diff --git a/Readme.md b/Readme.md
index d1fc494..edf6a50 100644
--- a/Readme.md
+++ b/Readme.md
@@ -111,7 +111,7 @@ Execution, trying TDD for the first time:
- [x] Minify JS? Now that we're keeping node, why not ... Nope! Ditched node too
-- [ ] Convert to a system of packages: SiteGenerator, samhuri_net, and gensite (executable)
+- [x] Convert to a system of packages: SiteGenerator, samhuri_net, and gensite (executable)
- [x] Create new packages and distribute the code accordingly
@@ -125,27 +125,27 @@ Execution, trying TDD for the first time:
- [x] Replace project templates with Swift code
- - [ ] Replace post templates with Swift code
+ - [x] Replace post templates with Swift code
- [x] Archive
- - [ ] Year posts
+ - [x] Year posts
- - [ ] Month posts
+ - [x] Month posts
- - [ ] Post
+ - [x] Post
- - [ ] Feed post
+ - [x] Recent posts
- - [ ] Munge relative URLs in the RSS and JSON feeds to be absolute instead
+ - [x] Replace RSS feed with Swift code
- - [ ] Recent posts
+ - [x] Feed post template
- - [ ] RSS feed
+ - [x] RSS feed template
- - [ ] Replace RSS feed template with Swift code
+ - [x] Munge relative URLs in the RSS and JSON feeds to be absolute instead
- - [ ] Remove stencil
+ - [x] Remove stencil
- [x] Add a server for local use and simple production setups (or use a file watcher + `python -m SimpleHTTPServer`?)
diff --git a/SiteGenerator/.gitignore b/SiteGenerator/.gitignore
deleted file mode 100644
index 51ef6c2..0000000
--- a/SiteGenerator/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-.DS_Store
-/.build
-/Packages
-/*.xcodeproj
-/.swiftpm
-xcuserdata/
diff --git a/SiteGenerator/Package.resolved b/SiteGenerator/Package.resolved
deleted file mode 100644
index d3725af..0000000
--- a/SiteGenerator/Package.resolved
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "object": {
- "pins": [
- {
- "package": "Ink",
- "repositoryURL": "https://github.com/johnsundell/ink.git",
- "state": {
- "branch": null,
- "revision": "c88bbce588a1ebfde2cf4d61eb9865a3edaa27d4",
- "version": "0.2.0"
- }
- }
- ]
- },
- "version": 1
-}
diff --git a/SiteGenerator/Package.swift b/SiteGenerator/Package.swift
deleted file mode 100644
index 614671e..0000000
--- a/SiteGenerator/Package.swift
+++ /dev/null
@@ -1,33 +0,0 @@
-// swift-tools-version:5.1
-// The swift-tools-version declares the minimum version of Swift required to build this package.
-
-import PackageDescription
-
-let package = Package(
- name: "SiteGenerator",
- platforms: [
- .macOS(.v10_15),
- .iOS(.v13),
- ],
- products: [
- // Products define the executables and libraries produced by a package, and make them visible to other packages.
- .library(
- name: "SiteGenerator",
- targets: ["SiteGenerator"]),
- ],
- dependencies: [
- .package(url: "https://github.com/johnsundell/ink.git", from: "0.2.0"),
- ],
- targets: [
- // Targets are the basic building blocks of a package. A target can define a module or a test suite.
- // Targets can depend on other targets in this package, and on products in packages which this package depends on.
- .target(
- name: "SiteGenerator",
- dependencies: [
- "Ink",
- ]),
- .testTarget(
- name: "SiteGeneratorTests",
- dependencies: ["SiteGenerator"]),
- ]
-)
diff --git a/SiteGenerator/Readme.md b/SiteGenerator/Readme.md
deleted file mode 100644
index dc6d75e..0000000
--- a/SiteGenerator/Readme.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# SiteGenerator
-
-A static site generator.
-
-See https://github.com/samsonjs/samhuri.net for details.
diff --git a/SiteGenerator/Sources/SiteGenerator/Date+Sugar.swift b/SiteGenerator/Sources/SiteGenerator/Date+Sugar.swift
deleted file mode 100644
index 62dd663..0000000
--- a/SiteGenerator/Sources/SiteGenerator/Date+Sugar.swift
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// Date+Sugar.swift
-// SiteGenerator
-//
-// Created by Sami Samhuri on 2019-12-02.
-//
-
-import Foundation
-
-extension Date {
- var year: Int {
- Calendar.current.dateComponents([.year], from: self).year!
- }
-
- var month: Int {
- Calendar.current.dateComponents([.month], from: self).month!
- }
-
- var day: Int {
- Calendar.current.dateComponents([.day], from: self).day!
- }
-}
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/RSSFeedWriter.swift b/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/RSSFeedWriter.swift
deleted file mode 100644
index f20df3c..0000000
--- a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/RSSFeedWriter.swift
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-// RSSFeedWriter.swift
-// SiteGenerator
-//
-// Created by Sami Samhuri on 2019-12-10.
-//
-
-import Foundation
-
-private struct FeedSite {
- let title: String
- let description: String?
- let url: String
-}
-
-private struct FeedPost {
- let title: String
- let date: String
- let author: String
- let link: String
- let guid: String
- let body: String
-}
-
-private let rfc822Formatter: DateFormatter = {
- let f = DateFormatter()
- f.locale = Locale(identifier: "en_US_POSIX")
- f.dateFormat = "EEE, d MMM yyyy HH:mm:ss ZZZ"
- return f
-}()
-
-private extension Date {
- var rfc822: String {
- rfc822Formatter.string(from: self)
- }
-}
-
-final class RSSFeedWriter {
- let fileManager: FileManager
- let feed: RSSFeed
-
- init(fileManager: FileManager = .default, feed: RSSFeed) {
- self.fileManager = fileManager
- self.feed = feed
- }
-
- func writeFeed(_ posts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
- let feedSite = FeedSite(
- title: site.title.escapedForXML(),
- description: site.description?.escapedForXML(),
- url: site.url.absoluteString.escapedForXML()
- )
- let renderedPosts: [FeedPost] = try posts.map { post in
- let title = post.isLink ? "→ \(post.title)" : post.title
- let author = "\(site.email) (\(post.author))"
- let url = site.url.appendingPathComponent(post.path)
- return FeedPost(
- title: title.escapedForXML(),
- date: post.date.rfc822.escapedForXML(),
- author: author.escapedForXML(),
- link: (post.link ?? url).absoluteString.escapedForXML(),
- guid: url.absoluteString.escapedForXML(),
- body: try templateRenderer.renderTemplate(.feedPost, site: site, context: [
- "post": post,
- ]).escapedForXML()
- )
- }
- let feedXML = try templateRenderer.renderTemplate(.rssFeed, site: site, context: [
- "site": feedSite,
- "feedURL": site.url.appendingPathComponent(feed.path).absoluteString.escapedForXML(),
- "posts": renderedPosts,
- ])
- let feedURL = targetURL.appendingPathComponent(feed.path)
- try feedXML.write(to: feedURL, atomically: true, encoding: .utf8)
- }
-}
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostsTemplateRenderer.swift b/SiteGenerator/Sources/SiteGenerator/Posts/PostsTemplateRenderer.swift
deleted file mode 100644
index a2ddb61..0000000
--- a/SiteGenerator/Sources/SiteGenerator/Posts/PostsTemplateRenderer.swift
+++ /dev/null
@@ -1,22 +0,0 @@
-//
-// PostsTemplateRenderer.swift
-// SiteGenerator
-//
-// Created by Sami Samhuri on 2019-12-17.
-//
-
-import Foundation
-
-public enum PostTemplate {
- case archive
- case feedPost
- case monthPosts
- case post
- case recentPosts
- case rssFeed
- case yearPosts
-}
-
-public protocol PostsTemplateRenderer {
- func renderTemplate(_ template: PostTemplate, site: Site, context: [String: Any]) throws -> String
-}
diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/Project.swift b/SiteGenerator/Sources/SiteGenerator/Projects/Project.swift
deleted file mode 100644
index c0d7d1d..0000000
--- a/SiteGenerator/Sources/SiteGenerator/Projects/Project.swift
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// Project.swift
-// SiteGenerator
-//
-// Created by Sami Samhuri on 2019-12-02.
-//
-
-import Foundation
-
-public struct Project {
- public let title: String
- public let description: String
- public let url: URL
-
- public init(title: String, description: String, url: URL) {
- self.title = title
- self.description = description
- self.url = url
- }
-}
diff --git a/SiteGenerator/Tests/LinuxMain.swift b/SiteGenerator/Tests/LinuxMain.swift
deleted file mode 100644
index 8b7ffc0..0000000
--- a/SiteGenerator/Tests/LinuxMain.swift
+++ /dev/null
@@ -1,7 +0,0 @@
-import XCTest
-
-import SiteGeneratorTests
-
-var tests = [XCTestCaseEntry]()
-tests += SiteGeneratorTests.allTests()
-XCTMain(tests)
diff --git a/SiteGenerator/Tests/SiteGeneratorTests/SiteGeneratorTests.swift b/SiteGenerator/Tests/SiteGeneratorTests/SiteGeneratorTests.swift
deleted file mode 100644
index c2062e6..0000000
--- a/SiteGenerator/Tests/SiteGeneratorTests/SiteGeneratorTests.swift
+++ /dev/null
@@ -1,47 +0,0 @@
-import XCTest
-@testable import SiteGenerator
-
-final class SiteGeneratorTests: XCTestCase {
- func testExample() throws {
- // This is an example of a functional test case.
- // Use XCTAssert and related functions to verify your tests produce the correct
- // results.
-
- // Some of the APIs that we use below are available in macOS 10.13 and above.
- guard #available(macOS 10.13, *) else {
- return
- }
-
- let fooBinary = productsDirectory.appendingPathComponent("SiteGenerator")
-
- let process = Process()
- process.executableURL = fooBinary
-
- let pipe = Pipe()
- process.standardOutput = pipe
-
- try process.run()
- process.waitUntilExit()
-
- let data = pipe.fileHandleForReading.readDataToEndOfFile()
- let output = String(data: data, encoding: .utf8)
-
- XCTAssertEqual(output, "")
- }
-
- /// Returns path to the built products directory.
- var productsDirectory: URL {
- #if os(macOS)
- for bundle in Bundle.allBundles where bundle.bundlePath.hasSuffix(".xctest") {
- return bundle.bundleURL.deletingLastPathComponent()
- }
- fatalError("couldn't find the products directory")
- #else
- return Bundle.main.bundleURL
- #endif
- }
-
- static var allTests = [
- ("testExample", testExample),
- ]
-}
diff --git a/SiteGenerator/Tests/SiteGeneratorTests/XCTestManifests.swift b/SiteGenerator/Tests/SiteGeneratorTests/XCTestManifests.swift
deleted file mode 100644
index 2f0d547..0000000
--- a/SiteGenerator/Tests/SiteGeneratorTests/XCTestManifests.swift
+++ /dev/null
@@ -1,9 +0,0 @@
-import XCTest
-
-#if !canImport(ObjectiveC)
-public func allTests() -> [XCTestCaseEntry] {
- return [
- testCase(SiteGeneratorTests.allTests),
- ]
-}
-#endif
diff --git a/gensite/Package.resolved b/gensite/Package.resolved
index 0784686..b3cfe99 100644
--- a/gensite/Package.resolved
+++ b/gensite/Package.resolved
@@ -10,15 +10,6 @@
"version": "0.2.0"
}
},
- {
- "package": "PathKit",
- "repositoryURL": "https://github.com/kylef/PathKit.git",
- "state": {
- "branch": null,
- "revision": "e2f5be30e4c8f531c9c1e8765aa7b71c0a45d7a0",
- "version": "0.9.2"
- }
- },
{
"package": "Plot",
"repositoryURL": "https://github.com/johnsundell/plot.git",
@@ -27,24 +18,6 @@
"revision": "dd7fce79ce4802afdc7d45ce34bddc5cea566202",
"version": "0.2.0"
}
- },
- {
- "package": "Spectre",
- "repositoryURL": "https://github.com/kylef/Spectre.git",
- "state": {
- "branch": null,
- "revision": "f14ff47f45642aa5703900980b014c2e9394b6e5",
- "version": "0.9.0"
- }
- },
- {
- "package": "Stencil",
- "repositoryURL": "https://github.com/stencilproject/Stencil.git",
- "state": {
- "branch": null,
- "revision": "0e9a78d6584e3812cd9c09494d5c7b483e8f533c",
- "version": "0.13.1"
- }
}
]
},
diff --git a/posts/2016/08/ios-git-pre-commit-hook.md b/posts/2016/08/ios-git-pre-commit-hook.md
index 15c53ec..3ac4729 100644
--- a/posts/2016/08/ios-git-pre-commit-hook.md
+++ b/posts/2016/08/ios-git-pre-commit-hook.md
@@ -18,13 +18,13 @@ If you don't care what I did or why then you can just [see the updated script][g
The diff command is repeated. This is any easy win:
-
+```bash
diff-index() {
git diff-index -p -M --cached HEAD -- "$@"
}
if diff-index '*Tests.swift' | ...
-
+```
You get the idea.
@@ -34,7 +34,9 @@ One problem is that the bootstrap script uses an absolute path when creating a s
That's easily fixed by using a relative path to your pre-commit hook, like so:
-
+```bash
+ln -s ../../scripts/pre-commit.sh .git/hooks/pre-commit
+```
Ah, this is more flexible! Of course if you ever move the script itself then it's on you to update the symlink and bootstrap.sh, but that was already the case anyway.
@@ -46,42 +48,42 @@ Ok great so this script tells me there are errors. Well, script, what exactly _a
First ignore the fact I'm talking to a shell script. I don't get out much. Anyway... now we need to pull out the regular expressions and globs so we can reuse them to show what the actual errors are if we find any.
-
+```
You may notice that I snuck in `*Specs.swift` as well. Let's not be choosy about file naming.
Then we need to show where the errors are by using `diff-indef`, with an `|| true` at the end because the whole script fails if any single command fails, and `git diff-index` regularly exits with non-zero status (I didn't look into why that is).
-
+```bash
echo "COMMIT REJECTED for fdescribe/fit/fcontext/xdescribe/xit/xcontext." >&2
echo "Remove focused and disabled tests before committing." >&2
diff-index $test_glob | egrep -2 "$test_pattern" || true >&2
echo '----' >&2
-
+```
And for misplaced views:
-
+```bash
echo "COMMIT REJECTED for misplaced views. Correct them before committing." >&2
git grep -E "$misplaced_pattern" $misplaced_glob || true >&2
echo '----' >&2
-
+```
## Fix all the things, at once
@@ -91,15 +93,21 @@ The first step is to exit at the end using a code in a variable that is set to 1
Up top:
-
failed=0
+```bash
+failed=0
+```
In the middle, where we detect errors:
-
failed=1
+```bash
+failed=1
+```
And at the bottom:
-
exit $failed
+```bash
+exit $failed
+```
That's all there is to it. If we don't exit early then all the code runs.
@@ -113,7 +121,7 @@ Those were all the obvious improvements in my mind and now I'm using this modifi
Here's the whole thing put together:
-
+```bash
#!/usr/bin/env bash
#
# Based on http://merowing.info/2016/08/setting-up-pre-commit-hook-for-ios/
@@ -148,4 +156,4 @@ then
fi
exit $failed
-
\ No newline at end of file
+```
\ No newline at end of file
diff --git a/samhuri.net/Package.resolved b/samhuri.net/Package.resolved
index 0784686..b3cfe99 100644
--- a/samhuri.net/Package.resolved
+++ b/samhuri.net/Package.resolved
@@ -10,15 +10,6 @@
"version": "0.2.0"
}
},
- {
- "package": "PathKit",
- "repositoryURL": "https://github.com/kylef/PathKit.git",
- "state": {
- "branch": null,
- "revision": "e2f5be30e4c8f531c9c1e8765aa7b71c0a45d7a0",
- "version": "0.9.2"
- }
- },
{
"package": "Plot",
"repositoryURL": "https://github.com/johnsundell/plot.git",
@@ -27,24 +18,6 @@
"revision": "dd7fce79ce4802afdc7d45ce34bddc5cea566202",
"version": "0.2.0"
}
- },
- {
- "package": "Spectre",
- "repositoryURL": "https://github.com/kylef/Spectre.git",
- "state": {
- "branch": null,
- "revision": "f14ff47f45642aa5703900980b014c2e9394b6e5",
- "version": "0.9.0"
- }
- },
- {
- "package": "Stencil",
- "repositoryURL": "https://github.com/stencilproject/Stencil.git",
- "state": {
- "branch": null,
- "revision": "0e9a78d6584e3812cd9c09494d5c7b483e8f533c",
- "version": "0.13.1"
- }
}
]
},
diff --git a/samhuri.net/Package.swift b/samhuri.net/Package.swift
index 2da060c..da3f0b5 100644
--- a/samhuri.net/Package.swift
+++ b/samhuri.net/Package.swift
@@ -16,8 +16,7 @@ let package = Package(
targets: ["samhuri.net"]),
],
dependencies: [
- .package(path: "../SiteGenerator"),
- .package(url: "https://github.com/stencilproject/Stencil.git", from: "0.13.0"),
+ .package(url: "https://github.com/johnsundell/ink.git", from: "0.2.0"),
.package(url: "https://github.com/johnsundell/plot.git", from: "0.2.0"),
],
targets: [
@@ -26,9 +25,8 @@ let package = Package(
.target(
name: "samhuri.net",
dependencies: [
+ "Ink",
"Plot",
- "SiteGenerator",
- "Stencil",
]),
.testTarget(
name: "samhuri.netTests",
diff --git a/samhuri.net/Sources/samhuri.net/Date+Sugar.swift b/samhuri.net/Sources/samhuri.net/Date+Sugar.swift
index 82c32b4..1483e3c 100644
--- a/samhuri.net/Sources/samhuri.net/Date+Sugar.swift
+++ b/samhuri.net/Sources/samhuri.net/Date+Sugar.swift
@@ -11,4 +11,12 @@ extension Date {
var year: Int {
Calendar.current.dateComponents([.year], from: self).year!
}
+
+ var month: Int {
+ Calendar.current.dateComponents([.month], from: self).month!
+ }
+
+ var day: Int {
+ Calendar.current.dateComponents([.day], from: self).day!
+ }
}
diff --git a/samhuri.net/Sources/samhuri.net/Page.swift b/samhuri.net/Sources/samhuri.net/Page.swift
index 488da84..2246f9a 100644
--- a/samhuri.net/Sources/samhuri.net/Page.swift
+++ b/samhuri.net/Sources/samhuri.net/Page.swift
@@ -6,7 +6,6 @@
//
import Foundation
-import SiteGenerator
struct Page {
let title: String
diff --git a/samhuri.net/Sources/samhuri.net/PageRenderer.swift b/samhuri.net/Sources/samhuri.net/PageRenderer.swift
index 16e6768..9bcb22a 100644
--- a/samhuri.net/Sources/samhuri.net/PageRenderer.swift
+++ b/samhuri.net/Sources/samhuri.net/PageRenderer.swift
@@ -7,22 +7,8 @@
import Foundation
import Plot
-import SiteGenerator
-
-#warning("Deprecated imports")
-import PathKit
-import Stencil
final class PageRenderer {
- @available(*, deprecated)
- let stencil: Environment
-
- init(templatesURL: URL) {
- let templatesPath = Path(templatesURL.path)
- let loader = FileSystemLoader(paths: [templatesPath])
- self.stencil = Environment(loader: loader)
- }
-
func render(_ body: Node, context: TemplateContext) -> String {
Template.site(body: body, context: context).render(indentedBy: .spaces(2))
}
@@ -30,51 +16,8 @@ final class PageRenderer {
extension PageRenderer: MarkdownPageRenderer {
func renderPage(site: Site, bodyHTML: String, metadata: [String: String]) throws -> String {
- let pageTitle = metadata["Title", default: ""]
+ let pageTitle = metadata["Title"]
let context = SiteContext(site: site, subtitle: pageTitle, templateAssets: .none())
- return render(.page(title: pageTitle, bodyHTML: bodyHTML), context: context)
- }
-}
-
-extension PostTemplate {
- @available(*, deprecated)
- var htmlFilename: String {
- switch self {
- case .archive:
- return "posts-archive.html"
- case .feedPost:
- return "feed-post.html"
- case .monthPosts:
- return "posts-month.html"
- case .post:
- return "post.html"
- case .recentPosts:
- return "recent-posts.html"
- case .rssFeed:
- return "feed.xml"
- case .yearPosts:
- return "posts-year.html"
- }
- }
-}
-
-extension PageRenderer: PostsTemplateRenderer {
- func renderTemplate(_ template: PostTemplate, site: Site, context: [String : Any]) throws -> String {
- let siteContext = SiteContext(site: site, subtitle: nil, templateAssets: .none())
- let contextDict = siteContext.dictionary.merging(context, uniquingKeysWith: { _, new in new })
- return try stencil.renderTemplate(name: template.htmlFilename, context: contextDict)
- }
-}
-
-extension PageRenderer: ProjectsTemplateRenderer {
- func renderProjects(_ projects: [Project], site: Site, assets: TemplateAssets) throws -> String {
- let context = SiteContext(site: site, subtitle: "Projects", templateAssets: assets)
- return render(.projects(projects), context: context)
- }
-
- func renderProject(_ project: Project, site: Site, assets: TemplateAssets) throws -> String {
- let projectContext = ProjectContext(project: project, site: site, templateAssets: assets)
- let context = SiteContext(site: site, subtitle: project.title, templateAssets: assets)
- return render(.project(projectContext), context: context)
+ return render(.page(title: pageTitle ?? "", bodyHTML: bodyHTML), context: context)
}
}
diff --git a/samhuri.net/Sources/samhuri.net/Posts/FeedPostTemplate.swift b/samhuri.net/Sources/samhuri.net/Posts/FeedPostTemplate.swift
new file mode 100644
index 0000000..7ec54ae
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/Posts/FeedPostTemplate.swift
@@ -0,0 +1,30 @@
+//
+// FeedPostTemplate.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-22.
+//
+
+import Foundation
+import Plot
+
+private extension Node where Context == HTML.BodyContext {
+ static func link(_ attributes: Attribute...) -> Self {
+ .element(named: "link", attributes: attributes)
+ }
+}
+
+extension Node where Context == HTML.BodyContext {
+ static func feedPost(_ post: Post, url: URL, styles: [URL]) -> Self {
+ .group([
+ .group(styles.map { style in
+ .link(.rel(.stylesheet), .href(style), .type("text/css"))
+ }),
+ .div(
+ .p(.class("time"), .text(post.formattedDate)),
+ .raw(post.body),
+ .p(.a(.class("permalink"), .href(url), "∞"))
+ ),
+ ])
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Posts/MonthPostsTemplate.swift b/samhuri.net/Sources/samhuri.net/Posts/MonthPostsTemplate.swift
new file mode 100644
index 0000000..d04f7c0
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/Posts/MonthPostsTemplate.swift
@@ -0,0 +1,22 @@
+//
+// MonthPostsTemplate.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-22.
+//
+
+import Foundation
+import Plot
+
+extension Node where Context == HTML.BodyContext {
+ static func monthPosts(_ posts: MonthPosts) -> Self {
+ .group([
+ .div(.class("container"),
+ .h1("\(posts.month.name) \(posts.year)")
+ ),
+ .group(posts.posts.sorted(by: >).map { post in
+ .div(.class("container"), self.post(post))
+ })
+ ])
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Posts/PageRenderer+Posts.swift b/samhuri.net/Sources/samhuri.net/Posts/PageRenderer+Posts.swift
index 4559749..9899bbc 100644
--- a/samhuri.net/Sources/samhuri.net/Posts/PageRenderer+Posts.swift
+++ b/samhuri.net/Sources/samhuri.net/Posts/PageRenderer+Posts.swift
@@ -1,8 +1,69 @@
//
-// File.swift
-//
+// PageRenderer+Posts.swift
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-22.
//
import Foundation
+import Plot
+
+extension PageRenderer: PostsTemplateRenderer {
+ func renderArchive(postsByYear: PostsByYear, site: Site, assets: TemplateAssets) throws -> String {
+ let context = SiteContext(site: site, subtitle: "Archive", templateAssets: assets)
+ return render(.archive(postsByYear), context: context)
+ }
+
+ func renderYearPosts(_ yearPosts: YearPosts, site: Site, assets: TemplateAssets) throws -> String {
+ let context = SiteContext(site: site, subtitle: yearPosts.title, templateAssets: assets)
+ return render(.yearPosts(yearPosts), context: context)
+ }
+
+ func renderMonthPosts(_ posts: MonthPosts, site: Site, assets: TemplateAssets) throws -> String {
+ let context = SiteContext(site: site, subtitle: "\(posts.month.name) \(posts.year)", templateAssets: assets)
+ return render(.monthPosts(posts), context: context)
+ }
+
+ func renderPost(_ post: Post, site: Site, assets: TemplateAssets) throws -> String {
+ let context = SiteContext(site: site, subtitle: post.title, templateAssets: assets)
+ return render(.post(post), context: context)
+ }
+
+ func renderRecentPosts(_ posts: [Post], site: Site, assets: TemplateAssets) throws -> String {
+ let context = SiteContext(site: site, subtitle: nil, templateAssets: assets)
+ return render(.recentPosts(posts), context: context)
+ }
+
+ // MARK: - Feeds
+
+ func renderFeedPost(_ post: Post, site: Site, assets: TemplateAssets) throws -> String {
+ let context = SiteContext(site: site, subtitle: post.title, templateAssets: assets)
+ let url = site.url.appendingPathComponent(post.path)
+ // Turn relative URLs into absolute ones.
+ return Node.feedPost(post, url: url, styles: context.styles)
+ .render(indentedBy: .spaces(2))
+ .replacingOccurrences(of: "href=\"/", with: "href=\"\(site.url)/")
+ .replacingOccurrences(of: "src=\"/", with: "src=\"\(site.url)/")
+ }
+
+ func renderRSSFeed(posts: [Post], feedURL: URL, site: Site, assets: TemplateAssets) throws -> String {
+ try RSS(
+ .title(site.title),
+ .if(site.description != nil, .description(site.description!)),
+ .link(site.url),
+ .pubDate(posts[0].date),
+ .atomLink(feedURL),
+ .group(posts.map { post in
+ let url = site.url.appendingPathComponent(post.path)
+ return .item(
+ .title(post.isLink ? "→ \(post.title)" : post.title),
+ .pubDate(post.date),
+ .element(named: "author", text: post.author),
+ .link(url),
+ .guid(.text(url.absoluteString), .isPermaLink(true)),
+ .content(try renderFeedPost(post, site: site, assets: assets))
+ )
+ })
+ ).render(indentedBy: .spaces(2))
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Posts/PostTemplate.swift b/samhuri.net/Sources/samhuri.net/Posts/PostTemplate.swift
new file mode 100644
index 0000000..f743cd6
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/Posts/PostTemplate.swift
@@ -0,0 +1,34 @@
+//
+// PostTemplate.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-22.
+//
+
+import Foundation
+import Plot
+
+extension Node where Context == HTML.BodyContext {
+ static func post(_ post: Post) -> Self {
+ .group([
+ .article(
+ .header(
+ .h2(postTitleLink(post)),
+ .time(.text(post.formattedDate)),
+ .a(.class("permalink"), .href(post.path), "∞")
+ ),
+ .raw(post.body)
+ ),
+ .div(.class("row clearfix"),
+ .p(.class("fin"), .i(.class("fa fa-code")))
+ )
+ ])
+ }
+
+ static func postTitleLink(_ post: Post) -> Self {
+ .if(post.isLink,
+ .a(.href(post.link?.absoluteString ?? post.path), "→ \(post.title)"),
+ else: .a(.href(post.path), .text(post.title))
+ )
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Posts/PostsArchiveTemplate.swift b/samhuri.net/Sources/samhuri.net/Posts/PostsArchiveTemplate.swift
new file mode 100644
index 0000000..bbbe2c8
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/Posts/PostsArchiveTemplate.swift
@@ -0,0 +1,22 @@
+//
+// ArchiveTemplate.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-21.
+//
+
+import Foundation
+import Plot
+
+extension Node where Context == HTML.BodyContext {
+ static func archive(_ postsByYear: PostsByYear) -> Self {
+ .group([
+ .div(.class("container"),
+ .h1("Archive")
+ ),
+ .group(postsByYear.years.sorted(by: >).map { year in
+ .yearPosts(postsByYear[year])
+ }),
+ ])
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Posts/RecentPostsTemplate.swift b/samhuri.net/Sources/samhuri.net/Posts/RecentPostsTemplate.swift
new file mode 100644
index 0000000..98a4945
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/Posts/RecentPostsTemplate.swift
@@ -0,0 +1,17 @@
+//
+// RecentPostsTemplate.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-22.
+//
+
+import Foundation
+import Plot
+
+extension Node where Context == HTML.BodyContext {
+ static func recentPosts(_ posts: [Post]) -> Self {
+ .div(.class("container"),
+ .group(posts.map(post))
+ )
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Posts/YearPostsTemplate.swift b/samhuri.net/Sources/samhuri.net/Posts/YearPostsTemplate.swift
new file mode 100644
index 0000000..677233b
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/Posts/YearPostsTemplate.swift
@@ -0,0 +1,51 @@
+//
+// YearPostsTemplate.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-21.
+//
+
+import Foundation
+import Plot
+
+extension Node where Context == HTML.BodyContext {
+ static func yearPosts(_ posts: YearPosts) -> Self {
+ .div(.class("container"),
+ .h2(.class("year"),
+ .a(.href(posts.path), .text(posts.title))
+ ),
+
+ .group(posts.months.sorted(by: >).map { month in
+ .monthTitles(posts[month])
+ })
+ )
+ }
+
+ static func monthTitles(_ posts: MonthPosts) -> Self {
+ .group([
+ .h3(.class("month"),
+ .a(.href(posts.path), "\(posts.month.name)")
+ ),
+ .ul(.class("archive"),
+ .group(posts.posts.sorted(by: >).map { post in
+ .postItem(post, date: "\(post.date.day) \(posts.month.abbreviation)")
+ })
+ ),
+ ])
+ }
+}
+
+extension Node where Context == HTML.ListContext {
+ static func postItem(_ post: Post, date: Node) -> Self {
+ .if(post.isLink, .li(
+ .a(.href(post.link?.absoluteString ?? post.path), "→ \(post.title)"),
+ .time(date),
+ .a(.class("permalink"), .href(post.path), "∞")
+ ),
+ else: .li(
+ .a(.href(post.path), .text(post.title)),
+ .time(date)
+ )
+ )
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Projects/PageRenderer+Projects.swift b/samhuri.net/Sources/samhuri.net/Projects/PageRenderer+Projects.swift
index 4559749..e0169d8 100644
--- a/samhuri.net/Sources/samhuri.net/Projects/PageRenderer+Projects.swift
+++ b/samhuri.net/Sources/samhuri.net/Projects/PageRenderer+Projects.swift
@@ -1,8 +1,22 @@
//
-// File.swift
-//
+// PageRenderer+Projects.swift
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-22.
//
import Foundation
+import Plot
+
+extension PageRenderer: ProjectsTemplateRenderer {
+ func renderProjects(_ projects: [Project], site: Site, assets: TemplateAssets) throws -> String {
+ let context = SiteContext(site: site, subtitle: "Projects", templateAssets: assets)
+ return render(.projects(projects), context: context)
+ }
+
+ func renderProject(_ project: Project, site: Site, assets: TemplateAssets) throws -> String {
+ let projectContext = ProjectContext(project: project, site: site, templateAssets: assets)
+ let context = SiteContext(site: site, subtitle: project.title, templateAssets: assets)
+ return render(.project(projectContext), context: context)
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Projects/ProjectContext.swift b/samhuri.net/Sources/samhuri.net/Projects/ProjectContext.swift
index 4e06b40..1ac3cce 100644
--- a/samhuri.net/Sources/samhuri.net/Projects/ProjectContext.swift
+++ b/samhuri.net/Sources/samhuri.net/Projects/ProjectContext.swift
@@ -6,7 +6,6 @@
//
import Foundation
-import SiteGenerator
struct ProjectContext: TemplateContext {
let site: Site
diff --git a/samhuri.net/Sources/samhuri.net/Projects/ProjectTemplates.swift b/samhuri.net/Sources/samhuri.net/Projects/ProjectTemplate.swift
similarity index 68%
rename from samhuri.net/Sources/samhuri.net/Projects/ProjectTemplates.swift
rename to samhuri.net/Sources/samhuri.net/Projects/ProjectTemplate.swift
index 86ccbb6..b487cf0 100644
--- a/samhuri.net/Sources/samhuri.net/Projects/ProjectTemplates.swift
+++ b/samhuri.net/Sources/samhuri.net/Projects/ProjectTemplate.swift
@@ -1,5 +1,5 @@
//
-// ProjectTemplates.swift
+// ProjectTemplate.swift
// samhuri.net
//
// Created by Sami Samhuri on 2019-12-19.
@@ -7,29 +7,9 @@
import Foundation
import Plot
-import SiteGenerator
extension Node where Context == HTML.BodyContext {
- static func projects(_ projects: [Project]) -> Node {
- .group([
- .article(.class("container"),
- .h1("Projects"),
-
- .group(projects.map { project in
- .div(.class("project-listing"),
- .h4(.a(.href(project.url), .text(project.title))),
- .p(.class("description"), .text(project.description))
- )
- })
- ),
-
- .div(.class("row clearfix"),
- .p(.class("fin"), .i(.class("fa fa-code")))
- )
- ])
- }
-
- static func project(_ context: ProjectContext) -> Node {
+ static func project(_ context: ProjectContext) -> Self {
.group([
.article(.class("container project"),
// projects.js picks up this data-title attribute and uses it to render all the Github stuff
diff --git a/samhuri.net/Sources/samhuri.net/Projects/ProjectsTemplate.swift b/samhuri.net/Sources/samhuri.net/Projects/ProjectsTemplate.swift
new file mode 100644
index 0000000..0c50152
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/Projects/ProjectsTemplate.swift
@@ -0,0 +1,30 @@
+//
+// ProjectsTemplate.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-19.
+//
+
+import Foundation
+import Plot
+
+extension Node where Context == HTML.BodyContext {
+ static func projects(_ projects: [Project]) -> Self {
+ .group([
+ .article(.class("container"),
+ .h1("Projects"),
+
+ .group(projects.map { project in
+ .div(.class("project-listing"),
+ .h4(.a(.href(project.url), .text(project.title))),
+ .p(.class("description"), .text(project.description))
+ )
+ })
+ ),
+
+ .div(.class("row clearfix"),
+ .p(.class("fin"), .i(.class("fa fa-code")))
+ )
+ ])
+ }
+}
diff --git a/SiteGenerator/Sources/SiteGenerator/BuiltSite.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/BuiltSite.swift
similarity index 64%
rename from SiteGenerator/Sources/SiteGenerator/BuiltSite.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/BuiltSite.swift
index ef9733d..fb983b7 100644
--- a/SiteGenerator/Sources/SiteGenerator/BuiltSite.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/BuiltSite.swift
@@ -1,16 +1,16 @@
//
// BuiltSite.swift
-// SiteGenerator
+// samhuri.net
//
// 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]
+struct BuiltSite {
+ let site: Site
+ let plugins: [Plugin]
+ let renderers: [Renderer]
init(site: Site, plugins: [Plugin], renderers: [Renderer]) {
self.site = site
diff --git a/SiteGenerator/Sources/SiteGenerator/MarkdownPageRenderer.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/MarkdownPageRenderer.swift
similarity index 77%
rename from SiteGenerator/Sources/SiteGenerator/MarkdownPageRenderer.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/MarkdownPageRenderer.swift
index 61c7813..0de70b2 100644
--- a/SiteGenerator/Sources/SiteGenerator/MarkdownPageRenderer.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/MarkdownPageRenderer.swift
@@ -1,12 +1,12 @@
//
// MarkdownPageRenderer.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-03.
//
import Foundation
-public protocol MarkdownPageRenderer {
+protocol MarkdownPageRenderer {
func renderPage(site: Site, bodyHTML: String, metadata: [String: String]) throws -> String
}
diff --git a/SiteGenerator/Sources/SiteGenerator/MarkdownRenderer.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/MarkdownRenderer.swift
similarity index 84%
rename from SiteGenerator/Sources/SiteGenerator/MarkdownRenderer.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/MarkdownRenderer.swift
index 251b005..31ad658 100644
--- a/SiteGenerator/Sources/SiteGenerator/MarkdownRenderer.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/MarkdownRenderer.swift
@@ -1,6 +1,6 @@
//
// MarkdownRenderer.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-02.
//
@@ -8,21 +8,21 @@
import Foundation
import Ink
-public final class MarkdownRenderer: Renderer {
+final class MarkdownRenderer: Renderer {
let fileManager: FileManager = .default
let markdownParser = MarkdownParser()
let pageRenderer: MarkdownPageRenderer
- public init(pageRenderer: MarkdownPageRenderer) {
+ init(pageRenderer: MarkdownPageRenderer) {
self.pageRenderer = pageRenderer
}
- public func canRenderFile(named filename: String, withExtension ext: String) -> Bool {
+ func canRenderFile(named filename: String, withExtension ext: String) -> Bool {
ext == "md"
}
/// Parse Markdown and render it as HTML, running it through a Stencil template.
- public func render(site: Site, fileURL: URL, targetDir: URL) throws {
+ func render(site: Site, fileURL: URL, targetDir: URL) throws {
let bodyMarkdown = try String(contentsOf: fileURL, encoding: .utf8)
let bodyHTML = markdownParser.html(from: bodyMarkdown).trimmingCharacters(in: .whitespacesAndNewlines)
let metadata = try markdownMetadata(from: fileURL)
diff --git a/SiteGenerator/Sources/SiteGenerator/Plugin.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Plugin.swift
similarity index 81%
rename from SiteGenerator/Sources/SiteGenerator/Plugin.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Plugin.swift
index 1dd1e99..5f02ef2 100644
--- a/SiteGenerator/Sources/SiteGenerator/Plugin.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Plugin.swift
@@ -1,13 +1,13 @@
//
// Plugin.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-02.
//
import Foundation
-public protocol Plugin {
+protocol Plugin {
func setUp(site: Site, sourceURL: URL) throws
func render(site: Site, targetURL: URL) throws
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/JSONFeed.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/JSONFeed.swift
similarity index 92%
rename from SiteGenerator/Sources/SiteGenerator/Posts/Feeds/JSONFeed.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/JSONFeed.swift
index adeb06f..4e0569d 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/JSONFeed.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/JSONFeed.swift
@@ -1,6 +1,6 @@
//
// JSONFeed.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-15.
//
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/JSONFeedWriter.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/JSONFeedWriter.swift
similarity index 93%
rename from SiteGenerator/Sources/SiteGenerator/Posts/Feeds/JSONFeedWriter.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/JSONFeedWriter.swift
index f6268b8..357c11d 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/JSONFeedWriter.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/JSONFeedWriter.swift
@@ -1,6 +1,6 @@
//
// JSONFeedWriter.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-10.
//
@@ -54,9 +54,7 @@ final class JSONFeedWriter {
url: url.absoluteString,
external_url: post.link?.absoluteString,
author: FeedAuthor(name: post.author, avatar: nil, url: nil),
- content_html: try templateRenderer.renderTemplate(.feedPost, site: site, context: [
- "post": post,
- ]),
+ content_html: try templateRenderer.renderFeedPost(post, site: site, assets: .none()),
tags: post.tags
)
}
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/RSSFeed.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/RSSFeed.swift
similarity index 87%
rename from SiteGenerator/Sources/SiteGenerator/Posts/Feeds/RSSFeed.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/RSSFeed.swift
index 38013fd..b41b928 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/RSSFeed.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/RSSFeed.swift
@@ -1,6 +1,6 @@
//
// RSSFeed.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-15.
//
diff --git a/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/RSSFeedWriter.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/RSSFeedWriter.swift
new file mode 100644
index 0000000..58eabf4
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/RSSFeedWriter.swift
@@ -0,0 +1,25 @@
+//
+// RSSFeedWriter.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-10.
+//
+
+import Foundation
+
+final class RSSFeedWriter {
+ let fileManager: FileManager
+ let feed: RSSFeed
+
+ init(fileManager: FileManager = .default, feed: RSSFeed) {
+ self.fileManager = fileManager
+ self.feed = feed
+ }
+
+ func writeFeed(_ posts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
+ let feedURL = site.url.appendingPathComponent(feed.path)
+ let feedXML = try templateRenderer.renderRSSFeed(posts: posts, feedURL: feedURL, site: site, assets: .none())
+ let feedFileURL = targetURL.appendingPathComponent(feed.path)
+ try feedXML.write(to: feedFileURL, atomically: true, encoding: .utf8)
+ }
+}
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/String+EscapeXML.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/String+EscapeXML.swift
similarity index 88%
rename from SiteGenerator/Sources/SiteGenerator/Posts/Feeds/String+EscapeXML.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/String+EscapeXML.swift
index 48c411b..13155c8 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/Feeds/String+EscapeXML.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Feeds/String+EscapeXML.swift
@@ -1,6 +1,6 @@
//
// XMLEscape.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-12.
//
@@ -8,6 +8,7 @@
import Foundation
extension String {
+ @available(*, deprecated)
func escapedForXML() -> String {
replacingOccurrences(of: "&", with: "&")
.replacingOccurrences(of: "<", with: "<")
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/Month.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Month.swift
similarity index 98%
rename from SiteGenerator/Sources/SiteGenerator/Posts/Month.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Month.swift
index 6d6e92a..39111e5 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/Month.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Month.swift
@@ -1,6 +1,6 @@
//
// Month.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-03.
//
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/Post.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Post.swift
similarity index 77%
rename from SiteGenerator/Sources/SiteGenerator/Posts/Post.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Post.swift
index f492048..ec2b005 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/Post.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/Post.swift
@@ -1,6 +1,6 @@
//
// Post.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-01.
//
@@ -18,11 +18,6 @@ struct Post {
let body: String
let path: String
- // These are computed properties but are computed eagerly because
- // Stencil is unable to use real computed properties at this time.
- let isLink: Bool
- let day: Int
-
init(slug: String, title: String, author: String, date: Date, formattedDate: String, link: URL?, tags: [String], body: String, path: String) {
self.slug = slug
self.title = title
@@ -33,10 +28,10 @@ struct Post {
self.tags = tags
self.body = body
self.path = path
+ }
- // Eagerly computed properties
- self.isLink = link != nil
- self.day = date.day
+ var isLink: Bool {
+ link != nil
}
}
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostRepo.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostRepo.swift
similarity index 99%
rename from SiteGenerator/Sources/SiteGenerator/Posts/PostRepo.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostRepo.swift
index 2e7b292..535aad9 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/PostRepo.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostRepo.swift
@@ -1,6 +1,6 @@
//
// PostRepo.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-09.
//
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostTransformer.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostTransformer.swift
similarity index 99%
rename from SiteGenerator/Sources/SiteGenerator/Posts/PostTransformer.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostTransformer.swift
index 257f2fd..a506ded 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/PostTransformer.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostTransformer.swift
@@ -1,6 +1,6 @@
//
// PostTransformer.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-09.
//
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostWriter.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostWriter.swift
similarity index 60%
rename from SiteGenerator/Sources/SiteGenerator/Posts/PostWriter.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostWriter.swift
index b9d83a0..08b9946 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/PostWriter.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostWriter.swift
@@ -1,6 +1,6 @@
//
// PostWriter.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-09.
//
@@ -22,10 +22,7 @@ final class PostWriter {
extension PostWriter {
func writePosts(_ posts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
for post in posts {
- let postHTML = try templateRenderer.renderTemplate(.post, site: site, context: [
- "title": post.title,
- "post": post,
- ])
+ let postHTML = try templateRenderer.renderPost(post, site: site, assets: .none())
let postURL = targetURL
.appendingPathComponent(outputPath)
.appendingPathComponent(filePath(date: post.date, slug: post.slug))
@@ -43,9 +40,7 @@ extension PostWriter {
extension PostWriter {
func writeRecentPosts(_ recentPosts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
- let recentPostsHTML = try templateRenderer.renderTemplate(.recentPosts, site: site, context: [
- "recentPosts": recentPosts,
- ])
+ let recentPostsHTML = try templateRenderer.renderRecentPosts(recentPosts, site: site, assets: .none())
let fileURL = targetURL.appendingPathComponent("index.html")
try fileManager.createDirectory(at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
try recentPostsHTML.write(to: fileURL, atomically: true, encoding: .utf8)
@@ -56,50 +51,20 @@ extension PostWriter {
extension PostWriter {
func writeArchive(posts: PostsByYear, for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
- let allYears = posts.byYear.keys.sorted(by: >)
- let archiveHTML = try templateRenderer.renderTemplate(.archive, site: site, context: [
- "title": "Archive",
- "years": allYears.map { contextDictionaryForYearPosts(posts[$0]) },
- ])
+ let archiveHTML = try templateRenderer.renderArchive(postsByYear: posts, site: site, assets: .none())
let archiveURL = targetURL.appendingPathComponent(outputPath).appendingPathComponent("index.html")
try fileManager.createDirectory(at: archiveURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
try archiveHTML.write(to: archiveURL, atomically: true, encoding: .utf8)
}
-
- private func contextDictionaryForYearPosts(_ posts: YearPosts) -> [String: Any] {
- [
- "path": posts.path,
- "title": posts.title,
- "months": posts.months.sorted(by: >).map { month in
- contextDictionaryForMonthPosts(posts[month], year: posts.year)
- },
- ]
- }
-
- private func contextDictionaryForMonthPosts(_ posts: MonthPosts, year: Int) -> [String: Any] {
- [
- "path": posts.path,
- "name": posts.month.name,
- "abbreviation": posts.month.abbreviation,
- "posts": posts.posts.sorted(by: >),
- ]
- }
}
// MARK: - Yearly post index pages
extension PostWriter {
func writeYearIndexes(posts: PostsByYear, for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
- for (year, yearPosts) in posts.byYear {
- let months = yearPosts.months.sorted(by: >)
+ for yearPosts in posts.byYear.values {
let yearDir = targetURL.appendingPathComponent(yearPosts.path)
- let context: [String: Any] = [
- "title": yearPosts.title,
- "path": yearPosts.path,
- "year": year,
- "months": months.map { contextDictionaryForMonthPosts(posts[year][$0], year: year) },
- ]
- let yearHTML = try templateRenderer.renderTemplate(.yearPosts, site: site, context: context)
+ let yearHTML = try templateRenderer.renderYearPosts(yearPosts, site: site, assets: .none())
let yearURL = yearDir.appendingPathComponent("index.html")
try fileManager.createDirectory(at: yearDir, withIntermediateDirectories: true, attributes: nil)
try yearHTML.write(to: yearURL, atomically: true, encoding: .utf8)
@@ -111,14 +76,10 @@ extension PostWriter {
extension PostWriter {
func writeMonthRollups(posts: PostsByYear, for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
- for (year, yearPosts) in posts.byYear {
- for month in yearPosts.months {
- let monthPosts = yearPosts[month]
+ for yearPosts in posts.byYear.values {
+ for monthPosts in yearPosts.byMonth.values {
let monthDir = targetURL.appendingPathComponent(monthPosts.path)
- let monthHTML = try templateRenderer.renderTemplate(.monthPosts, site: site, context: [
- "title": "\(month.name) \(year)",
- "posts": monthPosts.posts.sorted(by: >),
- ])
+ let monthHTML = try templateRenderer.renderMonthPosts(monthPosts, site: site, assets: .none())
let monthURL = monthDir.appendingPathComponent("index.html")
try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil)
try monthHTML.write(to: monthURL, atomically: true, encoding: .utf8)
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostsByYear.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsByYear.swift
similarity index 93%
rename from SiteGenerator/Sources/SiteGenerator/Posts/PostsByYear.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsByYear.swift
index 9ec1ba1..6c40788 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/PostsByYear.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsByYear.swift
@@ -1,6 +1,6 @@
//
// Posts.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-03.
//
@@ -19,6 +19,10 @@ struct MonthPosts {
var isEmpty: Bool {
posts.isEmpty
}
+
+ var year: Int {
+ posts[0].date.year
+ }
}
// MARK: -
@@ -62,6 +66,14 @@ struct PostsByYear {
posts.forEach { add(post: $0) }
}
+ var isEmpty: Bool {
+ byYear.isEmpty || byYear.values.allSatisfy { $0.isEmpty }
+ }
+
+ var years: [Int] {
+ Array(byYear.keys)
+ }
+
subscript(year: Int) -> YearPosts {
get {
byYear[year, default: YearPosts(year: year, byMonth: [:], path: "\(path)/\(year)")]
@@ -71,10 +83,6 @@ struct PostsByYear {
}
}
- var isEmpty: Bool {
- byYear.isEmpty || byYear.values.allSatisfy { $0.isEmpty }
- }
-
mutating func add(post: Post) {
let (year, month) = (post.date.year, Month(post.date.month))
self[year][month].posts.append(post)
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsPlugin.swift
similarity index 90%
rename from SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsPlugin.swift
index 77b9a6d..ca933de 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsPlugin.swift
@@ -1,13 +1,13 @@
//
// PostsPlugin.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-03.
//
import Foundation
-public final class PostsPlugin: Plugin {
+final class PostsPlugin: Plugin {
let templateRenderer: PostsTemplateRenderer
let postRepo: PostRepo
let postWriter: PostWriter
@@ -30,7 +30,7 @@ public final class PostsPlugin: Plugin {
// MARK: - Plugin methods
- public func setUp(site: Site, sourceURL: URL) throws {
+ func setUp(site: Site, sourceURL: URL) throws {
guard postRepo.postDataExists(at: sourceURL) else {
return
}
@@ -38,7 +38,7 @@ public final class PostsPlugin: Plugin {
try postRepo.readPosts(sourceURL: sourceURL)
}
- public func render(site: Site, targetURL: URL) throws {
+ func render(site: Site, targetURL: URL) throws {
guard !postRepo.isEmpty else {
return
}
diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostsPluginBuilder.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsPluginBuilder.swift
similarity index 86%
rename from SiteGenerator/Sources/SiteGenerator/Posts/PostsPluginBuilder.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsPluginBuilder.swift
index 4126351..d0680b0 100644
--- a/SiteGenerator/Sources/SiteGenerator/Posts/PostsPluginBuilder.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsPluginBuilder.swift
@@ -1,29 +1,29 @@
//
// PostsPluginBuilder.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-15.
//
import Foundation
-public final class PostsPluginBuilder {
+final class PostsPluginBuilder {
private let templateRenderer: PostsTemplateRenderer
private var path: String?
private var jsonFeed: JSONFeed?
private var rssFeed: RSSFeed?
- public init(templateRenderer: PostsTemplateRenderer) {
+ init(templateRenderer: PostsTemplateRenderer) {
self.templateRenderer = templateRenderer
}
- public func path(_ path: String) -> PostsPluginBuilder {
+ func path(_ path: String) -> PostsPluginBuilder {
precondition(self.path == nil, "path is already defined")
self.path = path
return self
}
- public func jsonFeed(
+ func jsonFeed(
path: String? = nil,
avatarPath: String? = nil,
iconPath: String? = nil,
@@ -39,13 +39,13 @@ public final class PostsPluginBuilder {
return self
}
- public func rssFeed(path: String? = nil) -> PostsPluginBuilder {
+ 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 {
+ func build() -> PostsPlugin {
let postRepo: PostRepo
let postWriter: PostWriter
if let outputPath = path {
diff --git a/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsTemplateRenderer.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsTemplateRenderer.swift
new file mode 100644
index 0000000..104ddcf
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Posts/PostsTemplateRenderer.swift
@@ -0,0 +1,26 @@
+//
+// PostsTemplateRenderer.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-17.
+//
+
+import Foundation
+
+protocol PostsTemplateRenderer {
+ func renderArchive(postsByYear: PostsByYear, site: Site, assets: TemplateAssets) throws -> String
+
+ func renderYearPosts(_ yearPosts: YearPosts, site: Site, assets: TemplateAssets) throws -> String
+
+ func renderMonthPosts(_ posts: MonthPosts, site: Site, assets: TemplateAssets) throws -> String
+
+ func renderPost(_ post: Post, site: Site, assets: TemplateAssets) throws -> String
+
+ func renderRecentPosts(_ posts: [Post], site: Site, assets: TemplateAssets) throws -> String
+
+ // MARK: - Feeds
+
+ func renderFeedPost(_ post: Post, site: Site, assets: TemplateAssets) throws -> String
+
+ func renderRSSFeed(posts: [Post], feedURL: URL, site: Site, assets: TemplateAssets) throws -> String
+}
diff --git a/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/Project.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/Project.swift
new file mode 100644
index 0000000..3f83e64
--- /dev/null
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/Project.swift
@@ -0,0 +1,20 @@
+//
+// Project.swift
+// samhuri.net
+//
+// Created by Sami Samhuri on 2019-12-02.
+//
+
+import Foundation
+
+struct Project {
+ let title: String
+ let description: String
+ let url: URL
+
+ init(title: String, description: String, url: URL) {
+ self.title = title
+ self.description = description
+ self.url = url
+ }
+}
diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsPlugin.swift
similarity index 92%
rename from SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsPlugin.swift
index 1598ed5..5361bdf 100644
--- a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsPlugin.swift
@@ -1,6 +1,6 @@
//
// ProjectsPlugin.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-02.
//
@@ -12,7 +12,7 @@ struct PartialProject {
let description: String
}
-public final class ProjectsPlugin: Plugin {
+final class ProjectsPlugin: Plugin {
let fileManager: FileManager = .default
let outputPath: String
let partialProjects: [PartialProject]
@@ -36,7 +36,7 @@ public final class ProjectsPlugin: Plugin {
// MARK: - Plugin methods
- public func setUp(site: Site, sourceURL: URL) throws {
+ func setUp(site: Site, sourceURL: URL) throws {
self.sourceURL = sourceURL
projects = partialProjects.map { partial in
Project(
@@ -47,7 +47,7 @@ public final class ProjectsPlugin: Plugin {
}
}
- public func render(site: Site, targetURL: URL) throws {
+ func render(site: Site, targetURL: URL) throws {
guard !projects.isEmpty else {
return
}
diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPluginBuilder.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsPluginBuilder.swift
similarity index 72%
rename from SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPluginBuilder.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsPluginBuilder.swift
index a3706fd..ed44b03 100644
--- a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPluginBuilder.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsPluginBuilder.swift
@@ -1,41 +1,41 @@
//
// ProjectsPluginBuilder.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-19.
//
import Foundation
-public final class ProjectsPluginBuilder {
+final class ProjectsPluginBuilder {
let templateRenderer: ProjectsTemplateRenderer
private var path: String?
private var projects: [PartialProject] = []
private var projectAssets: TemplateAssets?
- public init(templateRenderer: ProjectsTemplateRenderer) {
+ init(templateRenderer: ProjectsTemplateRenderer) {
self.templateRenderer = templateRenderer
}
- public func path(_ path: String) -> ProjectsPluginBuilder {
+ func path(_ path: String) -> ProjectsPluginBuilder {
precondition(self.path == nil, "path is already defined")
self.path = path
return self
}
- public func projectAssets(_ projectAssets: TemplateAssets) -> ProjectsPluginBuilder {
+ func projectAssets(_ projectAssets: TemplateAssets) -> ProjectsPluginBuilder {
precondition(self.projectAssets == nil, "projectAssets are already defined")
self.projectAssets = projectAssets
return self
}
- public func add(_ title: String, description: String) -> ProjectsPluginBuilder {
+ func add(_ title: String, description: String) -> ProjectsPluginBuilder {
let project = PartialProject(title: title, description: description)
projects.append(project)
return self
}
- public func build() -> ProjectsPlugin {
+ func build() -> ProjectsPlugin {
if projects.isEmpty {
print("WARNING: No projects have been added")
}
diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsTemplateRenderer.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsTemplateRenderer.swift
similarity index 83%
rename from SiteGenerator/Sources/SiteGenerator/Projects/ProjectsTemplateRenderer.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsTemplateRenderer.swift
index 6d888d2..78da515 100644
--- a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsTemplateRenderer.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Projects/ProjectsTemplateRenderer.swift
@@ -1,13 +1,13 @@
//
// ProjectsTemplateRenderer.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-17.
//
import Foundation
-public protocol ProjectsTemplateRenderer {
+protocol ProjectsTemplateRenderer {
func renderProjects(_ projects: [Project], site: Site, assets: TemplateAssets) throws -> String
func renderProject(_ project: Project, site: Site, assets: TemplateAssets) throws -> String
}
diff --git a/SiteGenerator/Sources/SiteGenerator/Renderer.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Renderer.swift
similarity index 84%
rename from SiteGenerator/Sources/SiteGenerator/Renderer.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Renderer.swift
index 674d4b2..541ca09 100644
--- a/SiteGenerator/Sources/SiteGenerator/Renderer.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Renderer.swift
@@ -1,13 +1,13 @@
//
// Renderer.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-02.
//
import Foundation
-public protocol Renderer {
+protocol Renderer {
func canRenderFile(named filename: String, withExtension ext: String) -> Bool
func render(site: Site, fileURL: URL, targetDir: URL) throws
diff --git a/SiteGenerator/Sources/SiteGenerator/Site.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/Site.swift
similarity index 64%
rename from SiteGenerator/Sources/SiteGenerator/Site.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/Site.swift
index 29d198a..55326d2 100644
--- a/SiteGenerator/Sources/SiteGenerator/Site.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/Site.swift
@@ -1,24 +1,24 @@
//
// Site.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-01.
//
import Foundation
-public struct Site {
- public let author: String
- public let email: String
- public let title: String
- public let description: String?
- public let url: URL
- public let styles: [String]
- public let scripts: [String]
- public let renderers: [Renderer]
- public let plugins: [Plugin]
+struct Site {
+ let author: String
+ let email: String
+ let title: String
+ let description: String?
+ let url: URL
+ let styles: [String]
+ let scripts: [String]
+ let renderers: [Renderer]
+ let plugins: [Plugin]
- public init(
+ init(
author: String,
email: String,
title: String,
diff --git a/SiteGenerator/Sources/SiteGenerator/SiteBuilder.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/SiteBuilder.swift
similarity index 78%
rename from SiteGenerator/Sources/SiteGenerator/SiteBuilder.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/SiteBuilder.swift
index ee82d53..b8de1bd 100644
--- a/SiteGenerator/Sources/SiteGenerator/SiteBuilder.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/SiteBuilder.swift
@@ -1,13 +1,13 @@
//
// SiteBuilder.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-15.
//
import Foundation
-public final class SiteBuilder {
+final class SiteBuilder {
private let title: String
private let description: String?
private let author: String
@@ -20,7 +20,7 @@ public final class SiteBuilder {
private var plugins: [Plugin] = []
private var renderers: [Renderer] = []
- public init(
+ init(
title: String,
description: String? = nil,
author: String,
@@ -34,27 +34,27 @@ public final class SiteBuilder {
self.url = url
}
- public func styles(_ styles: String...) -> SiteBuilder {
+ func styles(_ styles: String...) -> SiteBuilder {
self.styles.append(contentsOf: styles)
return self
}
- public func scripts(_ scripts: String...) -> SiteBuilder {
+ func scripts(_ scripts: String...) -> SiteBuilder {
self.scripts.append(contentsOf: scripts)
return self
}
- public func plugin(_ plugin: Plugin) -> SiteBuilder {
+ func plugin(_ plugin: Plugin) -> SiteBuilder {
plugins.append(plugin)
return self
}
- public func renderer(_ renderer: Renderer) -> SiteBuilder {
+ func renderer(_ renderer: Renderer) -> SiteBuilder {
renderers.append(renderer)
return self
}
- public func build() -> Site {
+ func build() -> Site {
Site(
author: author,
email: email,
@@ -71,7 +71,7 @@ public final class SiteBuilder {
// MARK: - Markdown
-public extension SiteBuilder {
+extension SiteBuilder {
func renderMarkdown(pageRenderer: MarkdownPageRenderer) -> SiteBuilder {
renderer(MarkdownRenderer(pageRenderer: pageRenderer))
}
diff --git a/SiteGenerator/Sources/SiteGenerator/SiteGenerator.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/SiteGenerator.swift
similarity index 74%
rename from SiteGenerator/Sources/SiteGenerator/SiteGenerator.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/SiteGenerator.swift
index 6ef37a7..2c441cb 100644
--- a/SiteGenerator/Sources/SiteGenerator/SiteGenerator.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/SiteGenerator.swift
@@ -1,23 +1,23 @@
//
-// SiteGenerator.swift
-// SiteGenerator
+// samhuri.net.swift
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-01.
//
import Foundation
-public final class SiteGenerator {
+final class SiteGenerator {
// Dependencies
let fileManager: FileManager = .default
// Site properties
- public let site: Site
- public let sourceURL: URL
+ let site: Site
+ let sourceURL: URL
let ignoredFilenames = [".DS_Store", ".gitkeep"]
- public init(sourceURL: URL, site: Site) throws {
+ init(sourceURL: URL, site: Site) throws {
self.site = site
self.sourceURL = sourceURL
@@ -30,7 +30,7 @@ public final class SiteGenerator {
}
}
- public func generate(targetURL: URL) throws {
+ func generate(targetURL: URL) throws {
for plugin in site.plugins {
try plugin.render(site: site, targetURL: targetURL)
}
@@ -63,18 +63,24 @@ public final class SiteGenerator {
}
}
- func renderOrCopyFile(url fileURL: URL, targetDir: URL) throws {
- let filename = fileURL.lastPathComponent
+ func renderOrCopyFile(url sourceURL: URL, targetDir: URL) throws {
+ let filename = sourceURL.lastPathComponent
+ let targetURL = targetDir.appendingPathComponent(filename)
+
+ // Clear the way so write operations don't fail later on.
+ if fileManager.fileExists(atPath: targetURL.path) {
+ try fileManager.removeItem(at: targetURL)
+ }
+
let ext = String(filename.split(separator: ".").last!)
for renderer in site.renderers {
if renderer.canRenderFile(named: filename, withExtension: ext) {
- try renderer.render(site: site, fileURL: fileURL, targetDir: targetDir)
+ try renderer.render(site: site, fileURL: sourceURL, targetDir: targetDir)
return
}
}
// Not handled by any renderer. Copy the file unchanged.
- let dest = targetDir.appendingPathComponent(filename)
- try fileManager.copyItem(at: fileURL, to: dest)
+ try fileManager.copyItem(at: sourceURL, to: targetURL)
}
}
diff --git a/SiteGenerator/Sources/SiteGenerator/TemplateAssets.swift b/samhuri.net/Sources/samhuri.net/SiteGenerator/TemplateAssets.swift
similarity index 50%
rename from SiteGenerator/Sources/SiteGenerator/TemplateAssets.swift
rename to samhuri.net/Sources/samhuri.net/SiteGenerator/TemplateAssets.swift
index 64204ba..97768d0 100644
--- a/SiteGenerator/Sources/SiteGenerator/TemplateAssets.swift
+++ b/samhuri.net/Sources/samhuri.net/SiteGenerator/TemplateAssets.swift
@@ -1,22 +1,22 @@
//
// TemplateAssets.swift
-// SiteGenerator
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-20.
//
import Foundation
-public struct TemplateAssets {
- public let scripts: [String]
- public let styles: [String]
+struct TemplateAssets {
+ let scripts: [String]
+ let styles: [String]
- public init(scripts: [String], styles: [String]) {
+ init(scripts: [String], styles: [String]) {
self.scripts = scripts
self.styles = styles
}
- public static func none() -> TemplateAssets {
+ static func none() -> TemplateAssets {
TemplateAssets(scripts: [], styles: [])
}
}
diff --git a/samhuri.net/Sources/samhuri.net/Templates/HTMLElements.swift b/samhuri.net/Sources/samhuri.net/Templates/HTMLElements.swift
index 0f9591e..6df1879 100644
--- a/samhuri.net/Sources/samhuri.net/Templates/HTMLElements.swift
+++ b/samhuri.net/Sources/samhuri.net/Templates/HTMLElements.swift
@@ -9,23 +9,23 @@ import Foundation
import Plot
extension Node where Context == HTML.HeadContext {
- static func jsonFeedLink(_ url: URLRepresentable, title: String) -> Node {
+ static func jsonFeedLink(_ url: URLRepresentable, title: String) -> Self {
.link(.rel(.alternate), .href(url), .type("application/json"), .attribute(named: "title", value: title))
}
}
extension Node where Context == HTML.HeadContext {
- static func appleTouchIcon(_ url: URLRepresentable) -> Node {
+ static func appleTouchIcon(_ url: URLRepresentable) -> Self {
.link(.attribute(named: "rel", value: "apple-touch-icon"), .href(url))
}
- static func safariPinnedTabIcon(_ url: URLRepresentable, color: String) -> Node {
+ static func safariPinnedTabIcon(_ url: URLRepresentable, color: String) -> Self {
.link(.attribute(named: "rel", value: "mask-icon"), .attribute(named: "color", value: color), .href(url))
}
}
extension Node where Context == HTML.BodyContext {
- static func asyncStylesheetLinks(_ urls: [URLRepresentable]) -> Node {
+ static func asyncStylesheetLinks(_ urls: [URLRepresentable]) -> Self {
.script("""
(function() {
var urls = [\(urls.map { "'\($0)'" }.joined(separator: ", "))];
@@ -40,3 +40,9 @@ extension Node where Context == HTML.BodyContext {
""")
}
}
+
+extension Node where Context == HTML.BodyContext {
+ static func time(_ nodes: Node...) -> Self {
+ .element(named: "time", nodes: nodes)
+ }
+}
diff --git a/samhuri.net/Sources/samhuri.net/Templates/PageContext.swift b/samhuri.net/Sources/samhuri.net/Templates/PageContext.swift
deleted file mode 100644
index 11d568e..0000000
--- a/samhuri.net/Sources/samhuri.net/Templates/PageContext.swift
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// PageContext.swift
-// samhuri.net
-//
-// Created by Sami Samhuri on 2019-12-02.
-//
-
-import Foundation
-import SiteGenerator
-
-struct PageContext: TemplateContext {
- let site: Site
- @available(*, deprecated) let body: String
- let page: Page
- @available(*, deprecated) let metadata: [String: String]
-
- var title: String {
- "\(site.title): \(page.title)"
- }
-
- var templateAssets: TemplateAssets {
- page.templateAssets
- }
-}
-
-extension PageContext {
- @available(*, deprecated)
- var dictionary: [String: Any] {
- [
- "site": site,
- "body": body,
- "page": page,
- "metadata": metadata,
- "styles": site.styles + templateAssets.styles,
- "scripts": site.scripts + templateAssets.scripts,
- "currentYear": Date().year,
- ]
- }
-}
diff --git a/samhuri.net/Sources/samhuri.net/Templates/PageTemplate.swift b/samhuri.net/Sources/samhuri.net/Templates/PageTemplate.swift
index 67709b3..f62a86a 100644
--- a/samhuri.net/Sources/samhuri.net/Templates/PageTemplate.swift
+++ b/samhuri.net/Sources/samhuri.net/Templates/PageTemplate.swift
@@ -1,6 +1,6 @@
//
-// PartialTemplates.swift
-//
+// PageTemplate.swift
+// samhuri.net
//
// Created by Sami Samhuri on 2019-12-19.
//
@@ -9,7 +9,7 @@ import Foundation
import Plot
extension Node where Context == HTML.BodyContext {
- static func page(title: String, bodyHTML: String) -> Node {
+ static func page(title: String, bodyHTML: String) -> Self {
.group([
.article(.class("container"),
.h1(.text(title)),
diff --git a/samhuri.net/Sources/samhuri.net/Templates/SiteContext.swift b/samhuri.net/Sources/samhuri.net/Templates/SiteContext.swift
index 770b612..b8e79b9 100644
--- a/samhuri.net/Sources/samhuri.net/Templates/SiteContext.swift
+++ b/samhuri.net/Sources/samhuri.net/Templates/SiteContext.swift
@@ -6,7 +6,6 @@
//
import Foundation
-import SiteGenerator
struct SiteContext: TemplateContext {
let site: Site
diff --git a/samhuri.net/Sources/samhuri.net/Templates/TemplateContext.swift b/samhuri.net/Sources/samhuri.net/Templates/TemplateContext.swift
index 4933e2f..00c1eff 100644
--- a/samhuri.net/Sources/samhuri.net/Templates/TemplateContext.swift
+++ b/samhuri.net/Sources/samhuri.net/Templates/TemplateContext.swift
@@ -6,7 +6,6 @@
//
import Foundation
-import SiteGenerator
protocol TemplateContext {
// Concrete requirements, must be implemented
diff --git a/samhuri.net/Sources/samhuri.net/samhuri.net.swift b/samhuri.net/Sources/samhuri.net/samhuri.net.swift
index 52a2f0d..0d91be6 100644
--- a/samhuri.net/Sources/samhuri.net/samhuri.net.swift
+++ b/samhuri.net/Sources/samhuri.net/samhuri.net.swift
@@ -1,5 +1,4 @@
import Foundation
-import SiteGenerator
public enum samhuri {}
@@ -53,8 +52,7 @@ public extension samhuri {
email: "sami@samhuri.net",
url: siteURLOverride ?? URL(string: "https://samhuri.net")!
)
- .styles("normalize.css", "style.css")
- .styles("https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css")
+ .styles("normalize.css", "style.css", "font-awesome.min.css")
.renderMarkdown(pageRenderer: renderer)
.plugin(projectsPlugin)
.plugin(postsPlugin)
@@ -62,8 +60,7 @@ public extension samhuri {
}
public func generate(sourceURL: URL, targetURL: URL) throws {
- let templatesURL = sourceURL.appendingPathComponent("templates")
- let renderer = PageRenderer(templatesURL: templatesURL)
+ let renderer = PageRenderer()
let site = buildSite(renderer: renderer)
let generator = try SiteGenerator(sourceURL: sourceURL, site: site)
try generator.generate(targetURL: targetURL)
diff --git a/templates/feed-post.html b/templates/feed-post.html
deleted file mode 100644
index 1c4a394..0000000
--- a/templates/feed-post.html
+++ /dev/null
@@ -1,7 +0,0 @@
-