From 0c17d5c5438faa699f8594be8e392f3b8b92fe72 Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Fri, 20 Dec 2019 22:27:24 -0800 Subject: [PATCH] Migrate projects plugin from Stencil and JSON to Swift --- Readme.md | 4 +- .../Sources/SiteGenerator/File.swift | 8 +++ .../SiteGenerator/Projects/Project.swift | 14 ++-- .../Projects/ProjectsPlugin.swift | 49 +++++++------ .../Projects/ProjectsPluginBuilder.swift | 49 +++++++++++++ .../Projects/ProjectsTemplateRenderer.swift | 8 +-- .../Sources/SiteGenerator/SiteBuilder.swift | 14 ---- .../SiteGenerator/TemplateAssets.swift | 22 ++++++ projects.json | 60 ---------------- .../Sources/samhuri.net/Date+Sugar.swift | 14 ++++ samhuri.net/Sources/samhuri.net/Page.swift | 6 +- .../Sources/samhuri.net/PageRenderer.swift | 41 ++++------- .../samhuri.net/Projects/ProjectContext.swift | 33 +++++++++ .../Projects/ProjectTemplates.swift | 71 +++++++++++++++++++ .../samhuri.net/Templates/PageContext.swift | 16 ++--- .../samhuri.net/Templates/SiteContext.swift | 18 +---- .../Templates/TemplateContext.swift | 34 ++++++++- .../Sources/samhuri.net/samhuri.net.swift | 28 +++++++- templates/project.html | 42 ----------- templates/projects.html | 19 ----- templates/samhuri.net.html | 12 +--- 21 files changed, 317 insertions(+), 245 deletions(-) create mode 100644 SiteGenerator/Sources/SiteGenerator/File.swift create mode 100644 SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPluginBuilder.swift create mode 100644 SiteGenerator/Sources/SiteGenerator/TemplateAssets.swift delete mode 100644 projects.json create mode 100644 samhuri.net/Sources/samhuri.net/Date+Sugar.swift create mode 100644 samhuri.net/Sources/samhuri.net/Projects/ProjectContext.swift create mode 100644 samhuri.net/Sources/samhuri.net/Projects/ProjectTemplates.swift delete mode 100644 templates/project.html delete mode 100644 templates/projects.html diff --git a/Readme.md b/Readme.md index fe2c9c1..def738e 100644 --- a/Readme.md +++ b/Readme.md @@ -121,9 +121,9 @@ Execution, trying TDD for the first time: - [x] Replace page template with Swift code - - [ ] Replace projects.json with Swift code + - [x] Replace projects.json with Swift code - - [ ] Replace project templates with Swift code + - [x] Replace project templates with Swift code - [ ] Replace post templates with Swift code diff --git a/SiteGenerator/Sources/SiteGenerator/File.swift b/SiteGenerator/Sources/SiteGenerator/File.swift new file mode 100644 index 0000000..52031e9 --- /dev/null +++ b/SiteGenerator/Sources/SiteGenerator/File.swift @@ -0,0 +1,8 @@ +// +// File.swift +// +// +// Created by Sami Samhuri on 2019-12-20. +// + +import Foundation diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/Project.swift b/SiteGenerator/Sources/SiteGenerator/Projects/Project.swift index de366ba..c0d7d1d 100644 --- a/SiteGenerator/Sources/SiteGenerator/Projects/Project.swift +++ b/SiteGenerator/Sources/SiteGenerator/Projects/Project.swift @@ -7,8 +7,14 @@ import Foundation -struct Project: Codable { - let title: String - let description: String - var path: String! +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/Sources/SiteGenerator/Projects/ProjectsPlugin.swift b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift index bf4f24b..1598ed5 100644 --- a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift +++ b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPlugin.swift @@ -7,42 +7,47 @@ import Foundation -private struct Projects: Codable { - let projects: [Project] - - static func decode(from url: URL) throws -> Projects { - let json = try Data(contentsOf: url) - let projects = try JSONDecoder().decode(Projects.self, from: json) - return projects - } +struct PartialProject { + let title: String + let description: String } -final class ProjectsPlugin: Plugin { +public final class ProjectsPlugin: Plugin { let fileManager: FileManager = .default let outputPath: String + let partialProjects: [PartialProject] let templateRenderer: ProjectsTemplateRenderer + let projectAssets: TemplateAssets var projects: [Project] = [] var sourceURL: URL! - init(templateRenderer: ProjectsTemplateRenderer, outputPath: String? = nil) { + init( + projects: [PartialProject], + templateRenderer: ProjectsTemplateRenderer, + projectAssets: TemplateAssets, + outputPath: String? = nil + ) { + self.partialProjects = projects self.templateRenderer = templateRenderer + self.projectAssets = projectAssets self.outputPath = outputPath ?? "projects" } // MARK: - Plugin methods - func setUp(site: Site, sourceURL: URL) throws { + public func setUp(site: Site, sourceURL: URL) throws { self.sourceURL = sourceURL - let projectsURL = sourceURL.appendingPathComponent("projects.json") - if fileManager.fileExists(atPath: projectsURL.path) { - self.projects = try Projects.decode(from: projectsURL).projects.map { project in - Project(title: project.title, description: project.description, path: "/\(outputPath)/\(project.title)") - } + projects = partialProjects.map { partial in + Project( + title: partial.title, + description: partial.description, + url: site.url.appendingPathComponent("\(outputPath)/\(partial.title)") + ) } } - func render(site: Site, targetURL: URL) throws { + public func render(site: Site, targetURL: URL) throws { guard !projects.isEmpty else { return } @@ -50,18 +55,12 @@ final class ProjectsPlugin: Plugin { let projectsDir = targetURL.appendingPathComponent(outputPath) try fileManager.createDirectory(at: projectsDir, withIntermediateDirectories: true, attributes: nil) let projectsURL = projectsDir.appendingPathComponent("index.html") - let projectsHTML = try templateRenderer.renderTemplate(.projects, site: site, context: [ - "title": "Projects", - "projects": projects, - ]) + let projectsHTML = try templateRenderer.renderProjects(projects, site: site, assets: .none()) try projectsHTML.write(to: projectsURL, atomically: true, encoding: .utf8) for project in projects { let projectURL = projectsDir.appendingPathComponent("\(project.title)/index.html") - let projectHTML = try templateRenderer.renderTemplate(.project, site: site, context: [ - "title": "\(project.title)", - "project": project, - ]) + let projectHTML = try templateRenderer.renderProject(project, site: site, assets: projectAssets) try fileManager.createDirectory(at: projectURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil) try projectHTML.write(to: projectURL, atomically: true, encoding: .utf8) } diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPluginBuilder.swift b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPluginBuilder.swift new file mode 100644 index 0000000..a3706fd --- /dev/null +++ b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsPluginBuilder.swift @@ -0,0 +1,49 @@ +// +// ProjectsPluginBuilder.swift +// SiteGenerator +// +// Created by Sami Samhuri on 2019-12-19. +// + +import Foundation + +public final class ProjectsPluginBuilder { + let templateRenderer: ProjectsTemplateRenderer + private var path: String? + private var projects: [PartialProject] = [] + private var projectAssets: TemplateAssets? + + public init(templateRenderer: ProjectsTemplateRenderer) { + self.templateRenderer = templateRenderer + } + + public func path(_ path: String) -> ProjectsPluginBuilder { + precondition(self.path == nil, "path is already defined") + self.path = path + return self + } + + public 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 { + let project = PartialProject(title: title, description: description) + projects.append(project) + return self + } + + public func build() -> ProjectsPlugin { + if projects.isEmpty { + print("WARNING: No projects have been added") + } + return ProjectsPlugin( + projects: projects, + templateRenderer: templateRenderer, + projectAssets: projectAssets ?? .none(), + outputPath: path + ) + } +} diff --git a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsTemplateRenderer.swift b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsTemplateRenderer.swift index 5d8e962..6d888d2 100644 --- a/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsTemplateRenderer.swift +++ b/SiteGenerator/Sources/SiteGenerator/Projects/ProjectsTemplateRenderer.swift @@ -7,11 +7,7 @@ import Foundation -public enum ProjectTemplate { - case project - case projects -} - public protocol ProjectsTemplateRenderer { - func renderTemplate(_ template: ProjectTemplate, site: Site, context: [String: Any]) throws -> String + 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/SiteBuilder.swift b/SiteGenerator/Sources/SiteGenerator/SiteBuilder.swift index d402b23..ee82d53 100644 --- a/SiteGenerator/Sources/SiteGenerator/SiteBuilder.swift +++ b/SiteGenerator/Sources/SiteGenerator/SiteBuilder.swift @@ -76,17 +76,3 @@ public extension SiteBuilder { renderer(MarkdownRenderer(pageRenderer: pageRenderer)) } } - -// MARK: - Projects - -public extension SiteBuilder { - func projects(templateRenderer: ProjectsTemplateRenderer, path: String? = nil) -> SiteBuilder { - plugin(ProjectsPlugin(templateRenderer: templateRenderer, outputPath: path)) - } -} - -// MARK: - Posts - -public extension SiteBuilder { - // anything nice we can do there? -} diff --git a/SiteGenerator/Sources/SiteGenerator/TemplateAssets.swift b/SiteGenerator/Sources/SiteGenerator/TemplateAssets.swift new file mode 100644 index 0000000..64204ba --- /dev/null +++ b/SiteGenerator/Sources/SiteGenerator/TemplateAssets.swift @@ -0,0 +1,22 @@ +// +// TemplateAssets.swift +// SiteGenerator +// +// Created by Sami Samhuri on 2019-12-20. +// + +import Foundation + +public struct TemplateAssets { + public let scripts: [String] + public let styles: [String] + + public init(scripts: [String], styles: [String]) { + self.scripts = scripts + self.styles = styles + } + + public static func none() -> TemplateAssets { + TemplateAssets(scripts: [], styles: []) + } +} diff --git a/projects.json b/projects.json deleted file mode 100644 index 78360f9..0000000 --- a/projects.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "projects": [ - { - "title": "bin", - "description": "my collection of scripts in ~/bin" - }, - { - "title": "config", - "description": "important dot files (zsh, emacs, vim, screen)" - }, - { - "title": "compiler", - "description": "a compiler targeting x86 in Ruby" - }, - { - "title": "lake", - "description": "a simple implementation of Scheme in C" - }, - { - "title": "strftime", - "description": "strftime for JavaScript" - }, - { - "title": "format", - "description": "printf for JavaScript" - }, - { - "title": "gitter", - "description": "a GitHub client for Node (v3 API)" - }, - { - "title": "mojo.el", - "description": "turn emacs into a sweet mojo editor" - }, - { - "title": "ThePusher", - "description": "Github post-receive hook router" - }, - { - "title": "NorthWatcher", - "description": "cron for filesystem changes" - }, - { - "title": "repl-edit", - "description": "edit Node repl commands with your text editor" - }, - { - "title": "cheat.el", - "description": "cheat from emacs" - }, - { - "title": "batteries", - "description": "a general purpose node library" - }, - { - "title": "samhuri.net", - "description": "this site" - } - ] -} diff --git a/samhuri.net/Sources/samhuri.net/Date+Sugar.swift b/samhuri.net/Sources/samhuri.net/Date+Sugar.swift new file mode 100644 index 0000000..82c32b4 --- /dev/null +++ b/samhuri.net/Sources/samhuri.net/Date+Sugar.swift @@ -0,0 +1,14 @@ +// +// Date+Sugar.swift +// samhuri.net +// +// Created by Sami Samhuri on 2019-12-19. +// + +import Foundation + +extension Date { + var year: Int { + Calendar.current.dateComponents([.year], from: self).year! + } +} diff --git a/samhuri.net/Sources/samhuri.net/Page.swift b/samhuri.net/Sources/samhuri.net/Page.swift index aa3a7c1..488da84 100644 --- a/samhuri.net/Sources/samhuri.net/Page.swift +++ b/samhuri.net/Sources/samhuri.net/Page.swift @@ -6,11 +6,11 @@ // import Foundation +import SiteGenerator struct Page { let title: String - let styles: [String] - let scripts: [String] + let templateAssets: TemplateAssets } extension Page { @@ -22,6 +22,6 @@ extension Page { .split(separator: ",") .map { $0.trimmingCharacters(in: .whitespaces) } let title = metadata["Title", default: ""] - self.init(title: title, styles: styles, scripts: scripts) + self.init(title: title, templateAssets: TemplateAssets(scripts: scripts, styles: styles)) } } diff --git a/samhuri.net/Sources/samhuri.net/PageRenderer.swift b/samhuri.net/Sources/samhuri.net/PageRenderer.swift index 7b4b05b..16e6768 100644 --- a/samhuri.net/Sources/samhuri.net/PageRenderer.swift +++ b/samhuri.net/Sources/samhuri.net/PageRenderer.swift @@ -22,15 +22,17 @@ final class PageRenderer { 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)) + } } extension PageRenderer: MarkdownPageRenderer { func renderPage(site: Site, bodyHTML: String, metadata: [String: String]) throws -> String { - let page = Page(metadata: metadata) - let context = PageContext(site: site, body: bodyHTML, page: page, metadata: metadata) - let body: Node = .page(title: page.title, bodyHTML: bodyHTML) - return Template.site(body: body, context: context) - .render(indentedBy: .spaces(2)) + let pageTitle = metadata["Title", default: ""] + let context = SiteContext(site: site, subtitle: pageTitle, templateAssets: .none()) + return render(.page(title: pageTitle, bodyHTML: bodyHTML), context: context) } } @@ -58,34 +60,21 @@ extension PostTemplate { extension PageRenderer: PostsTemplateRenderer { func renderTemplate(_ template: PostTemplate, site: Site, context: [String : Any]) throws -> String { - let siteContext = SiteContext(site: site) + 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 ProjectTemplate { - @available(*, deprecated) - var htmlFilename: String { - switch self { - case .project: - return "project.html" - case .projects: - return "projects.html" - } - } -} - extension PageRenderer: ProjectsTemplateRenderer { - func renderTemplate(_ template: ProjectTemplate, site: Site, 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: template.htmlFilename, context: contextDict) + 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) } -} -extension Date { - var year: Int { - Calendar.current.dateComponents([.year], from: self).year! + 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 new file mode 100644 index 0000000..4e06b40 --- /dev/null +++ b/samhuri.net/Sources/samhuri.net/Projects/ProjectContext.swift @@ -0,0 +1,33 @@ +// +// ProjectContext.swift +// samhuri.net +// +// Created by Sami Samhuri on 2019-12-19. +// + +import Foundation +import SiteGenerator + +struct ProjectContext: TemplateContext { + let site: Site + let title: String + let description: String + let githubURL: URL + let templateAssets: TemplateAssets + + init(project: Project, site: Site, templateAssets: TemplateAssets) { + self.site = site + self.title = project.title + self.description = project.description + self.githubURL = URL(string: "https://github.com/samsonjs/\(title)")! + self.templateAssets = templateAssets + } + + var stargazersURL: URL { + githubURL.appendingPathComponent("stargazers") + } + + var networkURL: URL { + githubURL.appendingPathComponent("network/members") + } +} diff --git a/samhuri.net/Sources/samhuri.net/Projects/ProjectTemplates.swift b/samhuri.net/Sources/samhuri.net/Projects/ProjectTemplates.swift new file mode 100644 index 0000000..86ccbb6 --- /dev/null +++ b/samhuri.net/Sources/samhuri.net/Projects/ProjectTemplates.swift @@ -0,0 +1,71 @@ +// +// ProjectTemplates.swift +// samhuri.net +// +// Created by Sami Samhuri on 2019-12-19. +// + +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 { + .group([ + .article(.class("container project"), + // projects.js picks up this data-title attribute and uses it to render all the Github stuff + .h1(.id("project"), .data(named: "title", value: context.title), .text(context.title)), + .h4(.text(context.description)), + + .div(.class("project-stats"), + .p( + .a(.href(context.githubURL), "GitHub"), + "•", + .a(.id("nstar"), .href(context.stargazersURL)), + "•", + .a(.id("nfork"), .href(context.networkURL)) + ), + .p("Last updated on ", .span(.id("updated"))) + ), + + .div(.class("project-info row clearfix"), + .div(.class("column half"), + .h3("Contributors"), + .div(.id("contributors")) + ), + .div(.class("column half"), + .h3("Languages"), + .div(.id("langs")) + ) + ) + ), + + .div(.class("row clearfix"), + .p(.class("fin"), .i(.class("fa fa-code"))) + ), + + .group(context.scripts.map { url in + .script(.attribute(named: "defer"), .src(url)) + }) + ]) + } +} diff --git a/samhuri.net/Sources/samhuri.net/Templates/PageContext.swift b/samhuri.net/Sources/samhuri.net/Templates/PageContext.swift index 731ab0e..11d568e 100644 --- a/samhuri.net/Sources/samhuri.net/Templates/PageContext.swift +++ b/samhuri.net/Sources/samhuri.net/Templates/PageContext.swift @@ -18,16 +18,8 @@ struct PageContext: TemplateContext { "\(site.title): \(page.title)" } - var styles: [URL] { - (site.styles + page.styles).map { style in - style.hasPrefix("http") ? URL(string: style)! : url(for: style) - } - } - - var scripts: [URL] { - (site.scripts + page.scripts).map { script in - script.hasPrefix("http") ? URL(string: script)! : url(for: script) - } + var templateAssets: TemplateAssets { + page.templateAssets } } @@ -39,8 +31,8 @@ extension PageContext { "body": body, "page": page, "metadata": metadata, - "styles": site.styles + page.styles, - "scripts": site.scripts + page.scripts, + "styles": site.styles + templateAssets.styles, + "scripts": site.scripts + templateAssets.scripts, "currentYear": Date().year, ] } diff --git a/samhuri.net/Sources/samhuri.net/Templates/SiteContext.swift b/samhuri.net/Sources/samhuri.net/Templates/SiteContext.swift index 63fe678..770b612 100644 --- a/samhuri.net/Sources/samhuri.net/Templates/SiteContext.swift +++ b/samhuri.net/Sources/samhuri.net/Templates/SiteContext.swift @@ -11,11 +11,7 @@ import SiteGenerator struct SiteContext: TemplateContext { let site: Site let subtitle: String? - - init(site: Site, subtitle: String? = nil) { - self.site = site - self.subtitle = subtitle - } + let templateAssets: TemplateAssets var title: String { guard let subtitle = subtitle else { @@ -24,18 +20,6 @@ struct SiteContext: TemplateContext { return "\(site.title): \(subtitle)" } - - var styles: [URL] { - site.styles.map { style in - style.hasPrefix("http") ? URL(string: style)! : url(for: style) - } - } - - var scripts: [URL] { - site.scripts.map { script in - script.hasPrefix("http") ? URL(string: script)! : url(for: script) - } - } } extension SiteContext { diff --git a/samhuri.net/Sources/samhuri.net/Templates/TemplateContext.swift b/samhuri.net/Sources/samhuri.net/Templates/TemplateContext.swift index f42b4da..4933e2f 100644 --- a/samhuri.net/Sources/samhuri.net/Templates/TemplateContext.swift +++ b/samhuri.net/Sources/samhuri.net/Templates/TemplateContext.swift @@ -13,18 +13,36 @@ protocol TemplateContext { var site: Site { get } var title: String { get } - var styles: [URL] { get } - var scripts: [URL] { get } + var templateAssets: TemplateAssets { get } // These all have default implementations + var styles: [URL] { get } + var scripts: [URL] { get } + var currentYear: Int { get } func url(for path: String) -> URL func imageURL(_ filename: String) -> URL + func scriptURL(_ filename: String) -> URL + func styleURL(_ filename: String) -> URL } extension TemplateContext { + var styles: [URL] { + let allStyles = site.styles + templateAssets.styles + return allStyles.map { style in + style.hasPrefix("http") ? URL(string: style)! : styleURL(style) + } + } + + var scripts: [URL] { + let allScripts = site.scripts + templateAssets.scripts + return allScripts.map { script in + script.hasPrefix("http") ? URL(string: script)! : scriptURL(script) + } + } + var currentYear: Int { Date().year } @@ -38,4 +56,16 @@ extension TemplateContext { .appendingPathComponent("images") .appendingPathComponent(filename) } + + func scriptURL(_ filename: String) -> URL { + site.url + .appendingPathComponent("js") + .appendingPathComponent(filename) + } + + func styleURL(_ filename: String) -> URL { + site.url + .appendingPathComponent("css") + .appendingPathComponent(filename) + } } diff --git a/samhuri.net/Sources/samhuri.net/samhuri.net.swift b/samhuri.net/Sources/samhuri.net/samhuri.net.swift index 5cade17..52a2f0d 100644 --- a/samhuri.net/Sources/samhuri.net/samhuri.net.swift +++ b/samhuri.net/Sources/samhuri.net/samhuri.net.swift @@ -12,6 +12,30 @@ public extension samhuri { } func buildSite(renderer: PageRenderer) -> Site { + let projectsPlugin = ProjectsPluginBuilder(templateRenderer: renderer) + .path("projects") + .projectAssets(TemplateAssets(scripts: [ + "https://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js", + "gitter.js", + "store.js", + "projects.js", + ], styles: [])) + .add("bin", description: "my collection of scripts in ~/bin") + .add("config", description: "important dot files (zsh, emacs, vim, screen)") + .add("compiler", description: "a compiler targeting x86 in Ruby") + .add("lake", description: "a simple implementation of Scheme in C") + .add("strftime", description: "strftime for JavaScript") + .add("format", description: "printf for JavaScript") + .add("gitter", description: "a GitHub client for Node (v3 API)") + .add("mojo.el", description: "turn emacs into a sweet mojo editor") + .add("ThePusher", description: "Github post-receive hook router") + .add("NorthWatcher", description: "cron for filesystem changes") + .add("repl-edit", description: "edit Node repl commands with your text editor") + .add("cheat.el", description: "cheat from emacs") + .add("batteries", description: "a general purpose node library") + .add("samhuri.net", description: "this site") + .build() + let postsPlugin = PostsPluginBuilder(templateRenderer: renderer) .path("posts") .jsonFeed( @@ -29,10 +53,10 @@ public extension samhuri { email: "sami@samhuri.net", url: siteURLOverride ?? URL(string: "https://samhuri.net")! ) - .styles("/css/normalize.css", "/css/style.css") + .styles("normalize.css", "style.css") .styles("https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.min.css") .renderMarkdown(pageRenderer: renderer) - .projects(templateRenderer: renderer) + .plugin(projectsPlugin) .plugin(postsPlugin) .build() } diff --git a/templates/project.html b/templates/project.html deleted file mode 100644 index a4d8dcb..0000000 --- a/templates/project.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "samhuri.net.html" %} -{% block body %} -
- -

{{ project.title }}

-

{{ project.description }}

- -
-

- GitHub - • - - • - -

- -

- Last updated on -

-
- -
-
-

Contributors

-
-
- -
-

Languages

-
-
-
-
-
-

-
- - - - - -{% endblock %} diff --git a/templates/projects.html b/templates/projects.html deleted file mode 100644 index 88c1650..0000000 --- a/templates/projects.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "samhuri.net.html" %} - -{% block body %} - -
-

Projects

- -{% for project in projects %} -
-

{{ project.title }}

-

{{ project.description }}

-
-{% endfor %} -
-
-

-
- -{% endblock %} diff --git a/templates/samhuri.net.html b/templates/samhuri.net.html index cdd3edc..0d8e6f9 100644 --- a/templates/samhuri.net.html +++ b/templates/samhuri.net.html @@ -64,7 +64,7 @@ {% endfor %} - -