diff --git a/Readme.md b/Readme.md index 974999b..4368679 100644 --- a/Readme.md +++ b/Readme.md @@ -73,14 +73,16 @@ Execution, trying TDD for the first time: - [x] Search for other _data.json and .ejs files and eliminate any that are found -- [ ] Link years to year indexes in the posts archive +- [x] Link years to year indexes in the posts archive -- [ ] Fix missing days on post dates in the archive and year indexes +- [x] Fix missing days on post dates in the archive and year indexes -- [ ] Find a way to add the site name to HTML titles rendered by plugins +- [x] Find a way to add the site name to HTML titles rendered by plugins - [ ] Clean up the posts plugin + - [ ] Why don't plain data structures always work with Stencil? Maybe computed properties are a no-go but we can at least use structs instead of dictionaries for the actual rendering + - [ ] Consider using Swift for samhuri.net as well, and then making SiteGenerator a package that it uses ... then we can use Plot or pointfree.co's swift-html - [ ] Replace remaining Ruby with Swift diff --git a/SiteGenerator/Sources/SiteGenerator/Generator/Contexts/PageContext.swift b/SiteGenerator/Sources/SiteGenerator/Generator/Contexts/PageContext.swift index a3c23df..151f12c 100644 --- a/SiteGenerator/Sources/SiteGenerator/Generator/Contexts/PageContext.swift +++ b/SiteGenerator/Sources/SiteGenerator/Generator/Contexts/PageContext.swift @@ -12,14 +12,6 @@ struct PageContext { let body: String let page: Page let metadata: [String: String] - - var title: String { - guard !page.title.isEmpty else { - return site.title - } - - return "\(site.title): \(page.title)" - } } extension PageContext: TemplateContext { @@ -30,7 +22,6 @@ extension PageContext: TemplateContext { var dictionary: [String: Any] { [ "site": site, - "title": title, "body": body, "page": page, "metadata": metadata, diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/Post.swift b/SiteGenerator/Sources/SiteGenerator/Posts/Post.swift index 2f22e83..3c95fb2 100644 --- a/SiteGenerator/Sources/SiteGenerator/Posts/Post.swift +++ b/SiteGenerator/Sources/SiteGenerator/Posts/Post.swift @@ -17,20 +17,28 @@ struct Post { let tags: [String] let bodyMarkdown: String - var isLink: Bool { - link != nil + var dictionary: [String: Any] { + var result: [String: Any] = [ + "slug": slug, + "title": title, + "author": author, + "day": date.day, + "month": date.month, + "year": date.year, + "formattedDate": formattedDate, + "tags": tags + ] + if let link = link { + result["isLink"] = true + result["link"] = link + } + return result } - var path: String { - let dateComponents = Calendar.current.dateComponents([.year, .month], from: date) - let year = dateComponents.year! - let month = dateComponents.month! - return "/" + [ - "posts", - String(format: "%02d", year), - String(format: "%02d", month), - "\(slug)", - ].joined(separator: "/") + func dictionary(withPath path: String) -> [String: Any] { + var dict = dictionary + dict["path"] = path + return dict } } diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift b/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift index 05b3924..f63b9c0 100644 --- a/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift +++ b/SiteGenerator/Sources/SiteGenerator/Posts/PostsPlugin.swift @@ -62,8 +62,8 @@ final class PostsPlugin: Plugin { print("renderPostsByDate(postsDir: \(postsDir), templateRenderer: \(templateRenderer)") for post in posts.flattened() { let monthDir = postsDir - .appendingPathComponent(String(format: "%02d", post.date.year)) - .appendingPathComponent(String(format: "%02d", post.date.month)) + .appendingPathComponent("\(post.date.year)") + .appendingPathComponent(Month(post.date.month).padded) try renderPost(post, monthDir: monthDir, templateRenderer: templateRenderer) } } @@ -73,11 +73,10 @@ final class PostsPlugin: Plugin { let recentPosts = posts.flattened().prefix(10) let renderedRecentPosts: [[String: Any]] = recentPosts.map { post in let html = markdownParser.html(from: post.bodyMarkdown) - return RenderedPost(post: post, body: html).dictionary + let path = self.path(for: post) + return RenderedPost(path: path, post: post, body: html).dictionary } - #warning("FIXME: get the site name out of here somehow") let recentPostsHTML = try templateRenderer.renderTemplate(name: "recent-posts", context: [ - "title": "samhuri.net", "recentPosts": renderedRecentPosts, ]) let fileURL = targetURL.appendingPathComponent(recentPostsPath) @@ -88,21 +87,23 @@ final class PostsPlugin: Plugin { print("renderArchive(postsDir: \(postsDir), templateRenderer: \(templateRenderer)") let allYears = posts.byYear.keys.sorted(by: >) let allMonths = (1 ... 12).map(Month.init(_:)) - let postsByMonthByYearForContext: [[String: Any]] = allYears.map { year in + let yearsWithPostsByMonthForContext: [[String: Any]] = allYears.map { year in [ - "number": "\(year)", - "months": posts[year].byMonth.keys.sorted(by: >).map { month in - [ - "number": month.padded, - "posts": posts[year][month].posts, + "path": self.path(year: year), + "title": "\(year)", + "months": posts[year].byMonth.keys.sorted(by: >).map { (month: Month) -> [String: Any] in + let sortedPosts = posts[year][month].posts.sorted(by: { $0.date > $1.date }) + return [ + "path": self.path(year: year, month: month), + "title": month.padded, + "posts": sortedPosts.map { $0.dictionary(withPath: self.path(for: $0)) }, ] - } as Any, + }, ] } let context: [String: Any] = [ "title": "Archive", - "path": postsPath, - "years": postsByMonthByYearForContext, + "years": yearsWithPostsByMonthForContext, "monthNames": allMonths.reduce(into: [String: String](), { dict, month in dict[month.padded] = month.name }), @@ -130,11 +131,10 @@ final class PostsPlugin: Plugin { try fileManager.createDirectory(at: yearDir, withIntermediateDirectories: true, attributes: nil) let months = Array(sortedPostsByMonth.keys.sorted().reversed()) - let postsByMonthForContext: [String: [Post]] = sortedPostsByMonth.reduce(into: [:]) { dict, pair in + let postsByMonthForContext: [String: [[String: Any]]] = sortedPostsByMonth.reduce(into: [:]) { dict, pair in let (month, posts) = pair - dict[month.padded] = posts + dict[month.padded] = posts.map { $0.dictionary(withPath: self.path(for: $0)) } } - #warning("FIXME: get the site name in the head title but not the body title") let context: [String: Any] = [ "title": "\(year)", "path": postsPath, @@ -166,12 +166,12 @@ final class PostsPlugin: Plugin { } let renderedPosts = sortedPosts.map { post -> RenderedPost in + let path = self.path(for: post) let bodyHTML = markdownParser.html(from: post.bodyMarkdown) - return RenderedPost(post: post, body: bodyHTML) + return RenderedPost(path: path, post: post, body: bodyHTML) } let monthDir = yearDir.appendingPathComponent(month.padded) try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil) - #warning("FIXME: get the site name in the head title but not the body title") let context: [String: Any] = [ "title": "\(month.name) \(year)", "posts": renderedPosts.map { $0.dictionary }, @@ -187,10 +187,10 @@ final class PostsPlugin: Plugin { print("renderPost(\(post.debugDescription), monthDir: \(monthDir), templateRenderer: \(templateRenderer)") try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil) let filename = "\(post.slug).html" + let path = self.path(for: post) let postURL = monthDir.appendingPathComponent(filename) let bodyHTML = markdownParser.html(from: post.bodyMarkdown) - let renderedPost = RenderedPost(post: post, body: bodyHTML) - #warning("FIXME: get the site name in the head title but not the body title") + let renderedPost = RenderedPost(path: path, post: post, body: bodyHTML) let postHTML = try templateRenderer.renderTemplate(name: "post", context: [ "title": "\(renderedPost.post.title)", "post": renderedPost.dictionary, @@ -211,4 +211,20 @@ final class PostsPlugin: Plugin { } } } + + private func path(for post: Post) -> String { + path(year: post.date.year, month: Month(post.date.month), filename: "\(post.slug).html") + } + + private func path(year: Int) -> String { + "/\(postsPath)/\(year)" + } + + private func path(year: Int, month: Month) -> String { + path(year: year).appending("/\(month.padded)") + } + + private func path(year: Int, month: Month, filename: String) -> String { + path(year: year, month: month).appending("/\(filename)") + } } diff --git a/SiteGenerator/Sources/SiteGenerator/Posts/RenderedPost.swift b/SiteGenerator/Sources/SiteGenerator/Posts/RenderedPost.swift index dbdae27..245b603 100644 --- a/SiteGenerator/Sources/SiteGenerator/Posts/RenderedPost.swift +++ b/SiteGenerator/Sources/SiteGenerator/Posts/RenderedPost.swift @@ -8,6 +8,7 @@ import Foundation struct RenderedPost { + let path: String let post: Post let body: String @@ -18,9 +19,8 @@ struct RenderedPost { "date": post.date, "day": post.date.day, "formattedDate": post.formattedDate, - "isLink": post.isLink, "link": post.link as Any, - "path": post.path, + "path": path, "body": body, ] } diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift index 843e89d..bfc5785 100644 --- a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift +++ b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift @@ -46,7 +46,6 @@ final class ProjectsPlugin: Plugin { let projectsDir = targetURL.appendingPathComponent(path) try fileManager.createDirectory(at: projectsDir, withIntermediateDirectories: true, attributes: nil) let projectsURL = projectsDir.appendingPathComponent("index.html") - #warning("FIXME: get the site name in the head title but not the body title") let projectsHTML = try templateRenderer.renderTemplate(name: "projects", context: [ "title": "Projects", "projects": projects, @@ -56,7 +55,6 @@ final class ProjectsPlugin: Plugin { for project in projects { let filename = "\(project.title).html" let projectURL = projectsDir.appendingPathComponent(filename) - #warning("FIXME: get the site name in the head title but not the body title") let projectHTML = try templateRenderer.renderTemplate(name: "project", context: [ "title": "\(project.title)", "project": project, diff --git a/SiteGenerator/Sources/SiteGenerator/main.swift b/SiteGenerator/Sources/SiteGenerator/main.swift index a978011..5057512 100644 --- a/SiteGenerator/Sources/SiteGenerator/main.swift +++ b/SiteGenerator/Sources/SiteGenerator/main.swift @@ -45,6 +45,6 @@ do { exit(0) } catch { - fputs("error: \(error.localizedDescription)", stderr) + fputs("error: \(error)", stderr) exit(-1) } diff --git a/templates/posts-archive.html b/templates/posts-archive.html index 4a04bcd..6e4b9c0 100644 --- a/templates/posts-archive.html +++ b/templates/posts-archive.html @@ -2,24 +2,31 @@ {% block body %} +