mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-04-27 14:57:40 +00:00
Set file permissions explicitly so it works properly on Linux
This commit is contained in:
parent
56833f88e7
commit
2fe6bfc73f
14 changed files with 272 additions and 96 deletions
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// DirectoryCreating.swift
|
||||||
|
// samhuri.net
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 2019-12-24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol DirectoryCreating {
|
||||||
|
func createDirectory(at url: URL) throws
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FileManager: DirectoryCreating {
|
||||||
|
func createDirectory(at url: URL) throws {
|
||||||
|
try createDirectory(at: url, withIntermediateDirectories: true, attributes: [.posixPermissions: FilePermissions.directoryDefault.rawValue])
|
||||||
|
}
|
||||||
|
}
|
||||||
47
samhuri.net/Sources/samhuri.net/Files/FilePermissions.swift
Normal file
47
samhuri.net/Sources/samhuri.net/Files/FilePermissions.swift
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
//
|
||||||
|
// FilePermissions.swift
|
||||||
|
// samhuri.net
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 2019-12-24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct FilePermissions: CustomStringConvertible {
|
||||||
|
let user: Permissions
|
||||||
|
let group: Permissions
|
||||||
|
let other: Permissions
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
[user, group, other].map { $0.description }.joined()
|
||||||
|
}
|
||||||
|
|
||||||
|
static let `default`: FilePermissions = "rw-r--r--"
|
||||||
|
static let directoryDefault: FilePermissions = "rwxr-xr-x"
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FilePermissions {
|
||||||
|
init(string: String) {
|
||||||
|
user = Permissions(string: String(string.prefix(3)))
|
||||||
|
group = Permissions(string: String(string.dropFirst(3).prefix(3)))
|
||||||
|
other = Permissions(string: String(string.dropFirst(6).prefix(3)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FilePermissions: RawRepresentable {
|
||||||
|
var rawValue: Int16 {
|
||||||
|
user.rawValue << 6 | group.rawValue << 3 | other.rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
init(rawValue: Int16) {
|
||||||
|
user = Permissions(rawValue: rawValue >> 6 & 7)
|
||||||
|
group = Permissions(rawValue: rawValue >> 3 & 7)
|
||||||
|
other = Permissions(rawValue: rawValue >> 0 & 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FilePermissions: ExpressibleByStringLiteral {
|
||||||
|
init(stringLiteral value: String) {
|
||||||
|
self.init(string: value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
// FilePermissionsSetting.swift
|
||||||
|
// samhuri.net
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 2019-12-24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol FilePermissionsSetting {
|
||||||
|
func setPermissions(_ permissions: FilePermissions, ofItemAt fileURL: URL) throws
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FileManager: FilePermissionsSetting {
|
||||||
|
func setPermissions(_ permissions: FilePermissions, ofItemAt fileURL: URL) throws {
|
||||||
|
let attributes: [FileAttributeKey: Any] = [
|
||||||
|
.posixPermissions: permissions.rawValue,
|
||||||
|
]
|
||||||
|
try setAttributes(attributes, ofItemAtPath: fileURL.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
35
samhuri.net/Sources/samhuri.net/Files/FileWriter.swift
Normal file
35
samhuri.net/Sources/samhuri.net/Files/FileWriter.swift
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
//
|
||||||
|
// FileWriter.swift
|
||||||
|
// samhuri.net
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 2019-12-24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// On Linux umask doesn't seem to be respected and files are written without
|
||||||
|
/// group and other read permissions by default. This class explicitly sets
|
||||||
|
/// permissions and then it works properly on macOS and Linux.
|
||||||
|
final class FileWriter {
|
||||||
|
typealias FileManager = DirectoryCreating & FilePermissionsSetting
|
||||||
|
|
||||||
|
let fileManager: FileManager
|
||||||
|
|
||||||
|
init(fileManager: FileManager = Foundation.FileManager.default) {
|
||||||
|
self.fileManager = fileManager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FileWriter: FileWriting {
|
||||||
|
func write(data: Data, to fileURL: URL, permissions: FilePermissions) throws {
|
||||||
|
try fileManager.createDirectory(at: fileURL.deletingLastPathComponent())
|
||||||
|
try data.write(to: fileURL, options: .atomic)
|
||||||
|
try fileManager.setPermissions(permissions, ofItemAt: fileURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
func write(string: String, to fileURL: URL, permissions: FilePermissions) throws {
|
||||||
|
try fileManager.createDirectory(at: fileURL.deletingLastPathComponent())
|
||||||
|
try string.write(to: fileURL, atomically: true, encoding: .utf8)
|
||||||
|
try fileManager.setPermissions(permissions, ofItemAt: fileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
samhuri.net/Sources/samhuri.net/Files/FileWriting.swift
Normal file
26
samhuri.net/Sources/samhuri.net/Files/FileWriting.swift
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// FileWriting.swift
|
||||||
|
// samhuri.net
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 2019-12-24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol FileWriting {
|
||||||
|
func write(data: Data, to fileURL: URL) throws
|
||||||
|
func write(data: Data, to fileURL: URL, permissions: FilePermissions) throws
|
||||||
|
|
||||||
|
func write(string: String, to fileURL: URL) throws
|
||||||
|
func write(string: String, to fileURL: URL, permissions: FilePermissions) throws
|
||||||
|
}
|
||||||
|
|
||||||
|
extension FileWriting {
|
||||||
|
func write(data: Data, to fileURL: URL) throws {
|
||||||
|
try write(data: data, to: fileURL, permissions: .default)
|
||||||
|
}
|
||||||
|
|
||||||
|
func write(string: String, to fileURL: URL) throws {
|
||||||
|
try write(string: string, to: fileURL, permissions: .default)
|
||||||
|
}
|
||||||
|
}
|
||||||
49
samhuri.net/Sources/samhuri.net/Files/Permissions.swift
Normal file
49
samhuri.net/Sources/samhuri.net/Files/Permissions.swift
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
//
|
||||||
|
// Permissions.swift
|
||||||
|
// samhuri.net
|
||||||
|
//
|
||||||
|
// Created by Sami Samhuri on 2019-12-24.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Permissions: OptionSet {
|
||||||
|
let rawValue: Int16
|
||||||
|
|
||||||
|
static let execute = Permissions(rawValue: 1 << 0)
|
||||||
|
static let write = Permissions(rawValue: 1 << 1)
|
||||||
|
static let read = Permissions(rawValue: 1 << 2)
|
||||||
|
|
||||||
|
init(rawValue: Int16) {
|
||||||
|
self.rawValue = rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
init(string: String) {
|
||||||
|
self.init(rawValue: 0)
|
||||||
|
if string[string.startIndex] == "r" {
|
||||||
|
insert(.read)
|
||||||
|
}
|
||||||
|
if string[string.index(string.startIndex, offsetBy: 1)] == "w" {
|
||||||
|
insert(.write)
|
||||||
|
}
|
||||||
|
if string[string.index(string.startIndex, offsetBy: 2)] == "x" {
|
||||||
|
insert(.execute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Permissions: CustomStringConvertible {
|
||||||
|
var description: String {
|
||||||
|
[
|
||||||
|
contains(.read) ? "r" : "-",
|
||||||
|
contains(.write) ? "w" : "-",
|
||||||
|
contains(.execute) ? "x" : "-",
|
||||||
|
].joined()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Permissions: ExpressibleByStringLiteral {
|
||||||
|
init(stringLiteral value: String) {
|
||||||
|
self.init(string: value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,11 +10,13 @@ import Ink
|
||||||
|
|
||||||
final class MarkdownRenderer: Renderer {
|
final class MarkdownRenderer: Renderer {
|
||||||
let fileManager: FileManager = .default
|
let fileManager: FileManager = .default
|
||||||
|
let fileWriter: FileWriting
|
||||||
let markdownParser = MarkdownParser()
|
let markdownParser = MarkdownParser()
|
||||||
let pageRenderer: MarkdownPageRenderer
|
let pageRenderer: MarkdownPageRenderer
|
||||||
|
|
||||||
init(pageRenderer: MarkdownPageRenderer) {
|
init(pageRenderer: MarkdownPageRenderer, fileWriter: FileWriting = FileWriter()) {
|
||||||
self.pageRenderer = pageRenderer
|
self.pageRenderer = pageRenderer
|
||||||
|
self.fileWriter = fileWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func canRenderFile(named filename: String, withExtension ext: String) -> Bool {
|
func canRenderFile(named filename: String, withExtension ext: String) -> Bool {
|
||||||
|
|
@ -37,8 +39,7 @@ final class MarkdownRenderer: Renderer {
|
||||||
htmlPath = mdFilename.replacingOccurrences(of: ".md", with: "/index.html")
|
htmlPath = mdFilename.replacingOccurrences(of: ".md", with: "/index.html")
|
||||||
}
|
}
|
||||||
let htmlURL = targetDir.appendingPathComponent(htmlPath)
|
let htmlURL = targetDir.appendingPathComponent(htmlPath)
|
||||||
try fileManager.createDirectory(at: htmlURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
|
try fileWriter.write(string: pageHTML, to: htmlURL)
|
||||||
try pageHTML.write(to: htmlURL, atomically: true, encoding: .utf8)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func markdownMetadata(from url: URL) throws -> [String: String] {
|
func markdownMetadata(from url: URL) throws -> [String: String] {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,54 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
final class JSONFeedWriter {
|
||||||
|
let fileWriter: FileWriting
|
||||||
|
let jsonFeed: JSONFeed
|
||||||
|
|
||||||
|
init(jsonFeed: JSONFeed, fileWriter: FileWriting = FileWriter()) {
|
||||||
|
self.jsonFeed = jsonFeed
|
||||||
|
self.fileWriter = fileWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFeed(_ posts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
|
||||||
|
let feed = Feed(
|
||||||
|
title: site.title,
|
||||||
|
home_page_url: site.url.absoluteString,
|
||||||
|
feed_url: site.url.appendingPathComponent(jsonFeed.path).absoluteString,
|
||||||
|
author: FeedAuthor(
|
||||||
|
name: site.author,
|
||||||
|
avatar: jsonFeed.avatarPath.map(site.url.appendingPathComponent)?.absoluteString,
|
||||||
|
url: site.url.absoluteString
|
||||||
|
),
|
||||||
|
icon: jsonFeed.iconPath.map(site.url.appendingPathComponent)?.absoluteString,
|
||||||
|
favicon: jsonFeed.faviconPath.map(site.url.appendingPathComponent)?.absoluteString,
|
||||||
|
items: try posts.map { post in
|
||||||
|
let url = site.url.appendingPathComponent(post.path)
|
||||||
|
return FeedItem(
|
||||||
|
title: post.isLink ? "→ \(post.title)" : post.title,
|
||||||
|
date_published: post.date,
|
||||||
|
id: url.absoluteString,
|
||||||
|
url: url.absoluteString,
|
||||||
|
external_url: post.link?.absoluteString,
|
||||||
|
author: FeedAuthor(name: post.author, avatar: nil, url: nil),
|
||||||
|
content_html: try templateRenderer.renderFeedPost(post, site: site, assets: .none()),
|
||||||
|
tags: post.tags
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
encoder.dateEncodingStrategy = .iso8601
|
||||||
|
#if os(Linux)
|
||||||
|
encoder.outputFormatting = [.prettyPrinted]
|
||||||
|
#else
|
||||||
|
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]
|
||||||
|
#endif
|
||||||
|
let feedJSON = try encoder.encode(feed)
|
||||||
|
let feedURL = targetURL.appendingPathComponent(jsonFeed.path)
|
||||||
|
try fileWriter.write(data: feedJSON, to: feedURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct Feed: Codable {
|
private struct Feed: Codable {
|
||||||
let version = "https://jsonfeed.org/version/1"
|
let version = "https://jsonfeed.org/version/1"
|
||||||
let title: String
|
let title: String
|
||||||
|
|
@ -34,49 +82,3 @@ private struct FeedItem: Codable {
|
||||||
let content_html: String
|
let content_html: String
|
||||||
let tags: [String]
|
let tags: [String]
|
||||||
}
|
}
|
||||||
|
|
||||||
final class JSONFeedWriter {
|
|
||||||
let fileManager: FileManager
|
|
||||||
let jsonFeed: JSONFeed
|
|
||||||
|
|
||||||
init(fileManager: FileManager = .default, feed: JSONFeed) {
|
|
||||||
self.fileManager = fileManager
|
|
||||||
self.jsonFeed = feed
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeFeed(_ posts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
|
|
||||||
let items: [FeedItem] = try posts.map { post in
|
|
||||||
let url = site.url.appendingPathComponent(post.path)
|
|
||||||
return FeedItem(
|
|
||||||
title: post.isLink ? "→ \(post.title)" : post.title,
|
|
||||||
date_published: post.date,
|
|
||||||
id: url.absoluteString,
|
|
||||||
url: url.absoluteString,
|
|
||||||
external_url: post.link?.absoluteString,
|
|
||||||
author: FeedAuthor(name: post.author, avatar: nil, url: nil),
|
|
||||||
content_html: try templateRenderer.renderFeedPost(post, site: site, assets: .none()),
|
|
||||||
tags: post.tags
|
|
||||||
)
|
|
||||||
}
|
|
||||||
let avatar = jsonFeed.avatarPath.map(site.url.appendingPathComponent)
|
|
||||||
let feed: Feed = Feed(
|
|
||||||
title: site.title,
|
|
||||||
home_page_url: site.url.absoluteString,
|
|
||||||
feed_url: site.url.appendingPathComponent(jsonFeed.path).absoluteString,
|
|
||||||
author: FeedAuthor(name: site.author, avatar: avatar?.absoluteString, url: site.url.absoluteString),
|
|
||||||
icon: jsonFeed.iconPath.map(site.url.appendingPathComponent)?.absoluteString,
|
|
||||||
favicon: jsonFeed.faviconPath.map(site.url.appendingPathComponent)?.absoluteString,
|
|
||||||
items: items
|
|
||||||
)
|
|
||||||
let encoder = JSONEncoder()
|
|
||||||
encoder.dateEncodingStrategy = .iso8601
|
|
||||||
#if os(Linux)
|
|
||||||
encoder.outputFormatting = [.prettyPrinted]
|
|
||||||
#else
|
|
||||||
encoder.outputFormatting = [.prettyPrinted, .withoutEscapingSlashes]
|
|
||||||
#endif
|
|
||||||
let feedJSON = try encoder.encode(feed)
|
|
||||||
let feedURL = targetURL.appendingPathComponent(jsonFeed.path)
|
|
||||||
try feedJSON.write(to: feedURL, options: [.atomic])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,18 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class RSSFeedWriter {
|
final class RSSFeedWriter {
|
||||||
let fileManager: FileManager
|
let fileWriter: FileWriting
|
||||||
let feed: RSSFeed
|
let rssFeed: RSSFeed
|
||||||
|
|
||||||
init(fileManager: FileManager = .default, feed: RSSFeed) {
|
init(rssFeed: RSSFeed, fileWriter: FileWriting = FileWriter()) {
|
||||||
self.fileManager = fileManager
|
self.rssFeed = rssFeed
|
||||||
self.feed = feed
|
self.fileWriter = fileWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFeed(_ posts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
|
func writeFeed(_ posts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
|
||||||
let feedURL = site.url.appendingPathComponent(feed.path)
|
let feedURL = site.url.appendingPathComponent(rssFeed.path)
|
||||||
let feedXML = try templateRenderer.renderRSSFeed(posts: posts, feedURL: feedURL, site: site, assets: .none())
|
let feedXML = try templateRenderer.renderRSSFeed(posts: posts, feedURL: feedURL, site: site, assets: .none())
|
||||||
let feedFileURL = targetURL.appendingPathComponent(feed.path)
|
let feedFileURL = targetURL.appendingPathComponent(rssFeed.path)
|
||||||
try feedXML.write(to: feedFileURL, atomically: true, encoding: .utf8)
|
try fileWriter.write(string: feedXML, to: feedFileURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
//
|
|
||||||
// XMLEscape.swift
|
|
||||||
// samhuri.net
|
|
||||||
//
|
|
||||||
// Created by Sami Samhuri on 2019-12-12.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
@available(*, deprecated)
|
|
||||||
func escapedForXML() -> String {
|
|
||||||
replacingOccurrences(of: "&", with: "&")
|
|
||||||
.replacingOccurrences(of: "<", with: "<")
|
|
||||||
.replacingOccurrences(of: ">", with: ">")
|
|
||||||
.replacingOccurrences(of: "\"", with: """)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -8,11 +8,11 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class PostWriter {
|
final class PostWriter {
|
||||||
let fileManager: FileManager
|
let fileWriter: FileWriting
|
||||||
let outputPath: String
|
let outputPath: String
|
||||||
|
|
||||||
init(fileManager: FileManager = .default, outputPath: String = "posts") {
|
init(outputPath: String = "posts", fileWriter: FileWriting = FileWriter()) {
|
||||||
self.fileManager = fileManager
|
self.fileWriter = fileWriter
|
||||||
self.outputPath = outputPath
|
self.outputPath = outputPath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -26,8 +26,7 @@ extension PostWriter {
|
||||||
let postURL = targetURL
|
let postURL = targetURL
|
||||||
.appendingPathComponent(outputPath)
|
.appendingPathComponent(outputPath)
|
||||||
.appendingPathComponent(filePath(date: post.date, slug: post.slug))
|
.appendingPathComponent(filePath(date: post.date, slug: post.slug))
|
||||||
try fileManager.createDirectory(at: postURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
|
try fileWriter.write(string: postHTML, to: postURL)
|
||||||
try postHTML.write(to: postURL, atomically: true, encoding: .utf8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,8 +41,7 @@ extension PostWriter {
|
||||||
func writeRecentPosts(_ recentPosts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
|
func writeRecentPosts(_ recentPosts: [Post], for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
|
||||||
let recentPostsHTML = try templateRenderer.renderRecentPosts(recentPosts, site: site, assets: .none())
|
let recentPostsHTML = try templateRenderer.renderRecentPosts(recentPosts, site: site, assets: .none())
|
||||||
let fileURL = targetURL.appendingPathComponent("index.html")
|
let fileURL = targetURL.appendingPathComponent("index.html")
|
||||||
try fileManager.createDirectory(at: fileURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
|
try fileWriter.write(string: recentPostsHTML, to: fileURL)
|
||||||
try recentPostsHTML.write(to: fileURL, atomically: true, encoding: .utf8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,8 +51,7 @@ extension PostWriter {
|
||||||
func writeArchive(posts: PostsByYear, for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
|
func writeArchive(posts: PostsByYear, for site: Site, to targetURL: URL, with templateRenderer: PostsTemplateRenderer) throws {
|
||||||
let archiveHTML = try templateRenderer.renderArchive(postsByYear: posts, site: site, assets: .none())
|
let archiveHTML = try templateRenderer.renderArchive(postsByYear: posts, site: site, assets: .none())
|
||||||
let archiveURL = targetURL.appendingPathComponent(outputPath).appendingPathComponent("index.html")
|
let archiveURL = targetURL.appendingPathComponent(outputPath).appendingPathComponent("index.html")
|
||||||
try fileManager.createDirectory(at: archiveURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
|
try fileWriter.write(string: archiveHTML, to: archiveURL)
|
||||||
try archiveHTML.write(to: archiveURL, atomically: true, encoding: .utf8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,8 +63,7 @@ extension PostWriter {
|
||||||
let yearDir = targetURL.appendingPathComponent(yearPosts.path)
|
let yearDir = targetURL.appendingPathComponent(yearPosts.path)
|
||||||
let yearHTML = try templateRenderer.renderYearPosts(yearPosts, site: site, assets: .none())
|
let yearHTML = try templateRenderer.renderYearPosts(yearPosts, site: site, assets: .none())
|
||||||
let yearURL = yearDir.appendingPathComponent("index.html")
|
let yearURL = yearDir.appendingPathComponent("index.html")
|
||||||
try fileManager.createDirectory(at: yearDir, withIntermediateDirectories: true, attributes: nil)
|
try fileWriter.write(string: yearHTML, to: yearURL)
|
||||||
try yearHTML.write(to: yearURL, atomically: true, encoding: .utf8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -81,8 +77,7 @@ extension PostWriter {
|
||||||
let monthDir = targetURL.appendingPathComponent(monthPosts.path)
|
let monthDir = targetURL.appendingPathComponent(monthPosts.path)
|
||||||
let monthHTML = try templateRenderer.renderMonthPosts(monthPosts, site: site, assets: .none())
|
let monthHTML = try templateRenderer.renderMonthPosts(monthPosts, site: site, assets: .none())
|
||||||
let monthURL = monthDir.appendingPathComponent("index.html")
|
let monthURL = monthDir.appendingPathComponent("index.html")
|
||||||
try fileManager.createDirectory(at: monthDir, withIntermediateDirectories: true, attributes: nil)
|
try fileWriter.write(string: monthHTML, to: monthURL)
|
||||||
try monthHTML.write(to: monthURL, atomically: true, encoding: .utf8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ final class PostsPluginBuilder {
|
||||||
|
|
||||||
let jsonFeedWriter: JSONFeedWriter?
|
let jsonFeedWriter: JSONFeedWriter?
|
||||||
if let jsonFeed = jsonFeed {
|
if let jsonFeed = jsonFeed {
|
||||||
jsonFeedWriter = JSONFeedWriter(feed: jsonFeed)
|
jsonFeedWriter = JSONFeedWriter(jsonFeed: jsonFeed)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
jsonFeedWriter = nil
|
jsonFeedWriter = nil
|
||||||
|
|
@ -67,7 +67,7 @@ final class PostsPluginBuilder {
|
||||||
|
|
||||||
let rssFeedWriter: RSSFeedWriter?
|
let rssFeedWriter: RSSFeedWriter?
|
||||||
if let rssFeed = rssFeed {
|
if let rssFeed = rssFeed {
|
||||||
rssFeedWriter = RSSFeedWriter(feed: rssFeed)
|
rssFeedWriter = RSSFeedWriter(rssFeed: rssFeed)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rssFeedWriter = nil
|
rssFeedWriter = nil
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ struct PartialProject {
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ProjectsPlugin: Plugin {
|
final class ProjectsPlugin: Plugin {
|
||||||
let fileManager: FileManager = .default
|
let fileWriter: FileWriting
|
||||||
let outputPath: String
|
let outputPath: String
|
||||||
let partialProjects: [PartialProject]
|
let partialProjects: [PartialProject]
|
||||||
let templateRenderer: ProjectsTemplateRenderer
|
let templateRenderer: ProjectsTemplateRenderer
|
||||||
|
|
@ -26,12 +26,14 @@ final class ProjectsPlugin: Plugin {
|
||||||
projects: [PartialProject],
|
projects: [PartialProject],
|
||||||
templateRenderer: ProjectsTemplateRenderer,
|
templateRenderer: ProjectsTemplateRenderer,
|
||||||
projectAssets: TemplateAssets,
|
projectAssets: TemplateAssets,
|
||||||
outputPath: String? = nil
|
outputPath: String? = nil,
|
||||||
|
fileWriter: FileWriting = FileWriter()
|
||||||
) {
|
) {
|
||||||
self.partialProjects = projects
|
self.partialProjects = projects
|
||||||
self.templateRenderer = templateRenderer
|
self.templateRenderer = templateRenderer
|
||||||
self.projectAssets = projectAssets
|
self.projectAssets = projectAssets
|
||||||
self.outputPath = outputPath ?? "projects"
|
self.outputPath = outputPath ?? "projects"
|
||||||
|
self.fileWriter = fileWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Plugin methods
|
// MARK: - Plugin methods
|
||||||
|
|
@ -53,16 +55,14 @@ final class ProjectsPlugin: Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
let projectsDir = targetURL.appendingPathComponent(outputPath)
|
let projectsDir = targetURL.appendingPathComponent(outputPath)
|
||||||
try fileManager.createDirectory(at: projectsDir, withIntermediateDirectories: true, attributes: nil)
|
|
||||||
let projectsURL = projectsDir.appendingPathComponent("index.html")
|
let projectsURL = projectsDir.appendingPathComponent("index.html")
|
||||||
let projectsHTML = try templateRenderer.renderProjects(projects, site: site, assets: .none())
|
let projectsHTML = try templateRenderer.renderProjects(projects, site: site, assets: .none())
|
||||||
try projectsHTML.write(to: projectsURL, atomically: true, encoding: .utf8)
|
try fileWriter.write(string: projectsHTML, to: projectsURL)
|
||||||
|
|
||||||
for project in projects {
|
for project in projects {
|
||||||
let projectURL = projectsDir.appendingPathComponent("\(project.title)/index.html")
|
let projectURL = projectsDir.appendingPathComponent("\(project.title)/index.html")
|
||||||
let projectHTML = try templateRenderer.renderProject(project, site: site, assets: projectAssets)
|
let projectHTML = try templateRenderer.renderProject(project, site: site, assets: projectAssets)
|
||||||
try fileManager.createDirectory(at: projectURL.deletingLastPathComponent(), withIntermediateDirectories: true, attributes: nil)
|
try fileWriter.write(string: projectHTML, to: projectURL)
|
||||||
try projectHTML.write(to: projectURL, atomically: true, encoding: .utf8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue