mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-03-31 09:56:03 +00:00
Shuffle some more code around and clean things up
This commit is contained in:
parent
f71c9aabbb
commit
9dfd5080ef
19 changed files with 179 additions and 167 deletions
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// FileManager+DirectoryExistence.swift
|
||||
// samhuri.net
|
||||
//
|
||||
// Created by Sami Samhuri on 2020-01-01.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension FileManager {
|
||||
func directoryExists(at fileURL: URL) -> Bool {
|
||||
var isDir: ObjCBool = false
|
||||
_ = fileExists(atPath: fileURL.path, isDirectory: &isDir)
|
||||
return isDir.boolValue
|
||||
}
|
||||
}
|
||||
|
|
@ -49,6 +49,9 @@ extension FilePermissions: RawRepresentable {
|
|||
|
||||
extension FilePermissions: ExpressibleByStringLiteral {
|
||||
init(stringLiteral value: String) {
|
||||
guard let _ = FilePermissions(string: value) else {
|
||||
fatalError("Invalid FilePermissions string literal: \(value)")
|
||||
}
|
||||
self.init(string: value)!
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,33 +23,28 @@ struct Permissions: OptionSet {
|
|||
}
|
||||
|
||||
init?(string: String) {
|
||||
guard string.count == 3 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.init(rawValue: 0)
|
||||
|
||||
switch string[string.startIndex] {
|
||||
case "r":
|
||||
insert(.read)
|
||||
case "-":
|
||||
break
|
||||
default:
|
||||
return nil
|
||||
case "r": insert(.read)
|
||||
case "-": break
|
||||
default: return nil
|
||||
}
|
||||
|
||||
switch string[string.index(string.startIndex, offsetBy: 1)] {
|
||||
case "w":
|
||||
insert(.write)
|
||||
case "-":
|
||||
break
|
||||
default:
|
||||
return nil
|
||||
case "w": insert(.write)
|
||||
case "-": break
|
||||
default: return nil
|
||||
}
|
||||
|
||||
switch string[string.index(string.startIndex, offsetBy: 2)] {
|
||||
case "x":
|
||||
insert(.execute)
|
||||
case "-":
|
||||
break
|
||||
default:
|
||||
return nil
|
||||
case "x": insert(.execute)
|
||||
case "-": break
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,6 +61,9 @@ extension Permissions: CustomStringConvertible {
|
|||
|
||||
extension Permissions: ExpressibleByStringLiteral {
|
||||
init(stringLiteral value: String) {
|
||||
guard let _ = Permissions(string: value) else {
|
||||
fatalError("Invalid Permissions string literal: \(value)")
|
||||
}
|
||||
self.init(string: value)!
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
import Foundation
|
||||
|
||||
struct Month: Equatable {
|
||||
static let all = (1 ... 12).map(Month.init(_:))
|
||||
static let all = names.map(Month.init(_:))
|
||||
|
||||
static let names = [
|
||||
"January", "February", "March", "April",
|
||||
|
|
@ -18,14 +18,22 @@ struct Month: Equatable {
|
|||
|
||||
let number: Int
|
||||
|
||||
init(_ number: Int) {
|
||||
precondition((1 ... 12).contains(number), "Month number must be from 1 to 12, got \(number)")
|
||||
init?(_ number: Int) {
|
||||
guard number < Month.all.count else {
|
||||
return nil
|
||||
}
|
||||
self.number = number
|
||||
}
|
||||
|
||||
init(_ name: String) {
|
||||
precondition(Month.names.contains(name), "Month name is unknown: \(name)")
|
||||
self.number = 1 + Month.names.firstIndex(of: name)!
|
||||
init?(_ name: String) {
|
||||
guard let index = Month.names.firstIndex(of: name) else {
|
||||
return nil
|
||||
}
|
||||
self.number = index + 1
|
||||
}
|
||||
|
||||
init(_ date: Date) {
|
||||
self.init(date.month)!
|
||||
}
|
||||
|
||||
var padded: String {
|
||||
|
|
@ -55,12 +63,18 @@ extension Month: Comparable {
|
|||
|
||||
extension Month: ExpressibleByIntegerLiteral {
|
||||
init(integerLiteral value: Int) {
|
||||
self.init(value)
|
||||
guard let _ = Month(value) else {
|
||||
fatalError("Invalid month number in string literal: \(value)")
|
||||
}
|
||||
self.init(value)!
|
||||
}
|
||||
}
|
||||
|
||||
extension Month: ExpressibleByStringLiteral {
|
||||
init(stringLiteral value: String) {
|
||||
self.init(value)
|
||||
guard let _ = Month(value) else {
|
||||
fatalError("Invalid month name in string literal: \(value)")
|
||||
}
|
||||
self.init(value)!
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import Foundation
|
|||
|
||||
struct MonthPosts {
|
||||
let month: Month
|
||||
var posts: [Post]
|
||||
private(set) var posts: [Post]
|
||||
let path: String
|
||||
|
||||
var title: String {
|
||||
|
|
@ -23,4 +23,8 @@ struct MonthPosts {
|
|||
var year: Int {
|
||||
posts[0].date.year
|
||||
}
|
||||
|
||||
mutating func add(post: Post) {
|
||||
posts.append(post)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ struct PostsByYear {
|
|||
}
|
||||
|
||||
mutating func add(post: Post) {
|
||||
let (year, month) = (post.date.year, Month(post.date.month))
|
||||
self[year][month].posts.append(post)
|
||||
let (year, month) = (post.date.year, Month(post.date))
|
||||
self[year].add(post: post, to: month)
|
||||
}
|
||||
|
||||
/// Returns an array of all posts.
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ struct YearPosts {
|
|||
}
|
||||
}
|
||||
|
||||
mutating func add(post: Post, to month: Month) {
|
||||
self[month].add(post: post)
|
||||
}
|
||||
|
||||
/// Returns an array of all posts.
|
||||
func flattened() -> [Post] {
|
||||
byMonth.values.flatMap { $0.posts }
|
||||
|
|
|
|||
48
samhuri.net/Sources/samhuri.net/Posts/PostMetadata.swift
Normal file
48
samhuri.net/Sources/samhuri.net/Posts/PostMetadata.swift
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// PostMetadata.swift
|
||||
// samhuri.net
|
||||
//
|
||||
// Created by Sami Samhuri on 2020-01-01.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct PostMetadata {
|
||||
let title: String
|
||||
let author: String
|
||||
let date: Date
|
||||
let formattedDate: String
|
||||
let link: URL?
|
||||
let tags: [String]
|
||||
let scripts: [String]
|
||||
let styles: [String]
|
||||
}
|
||||
|
||||
extension PostMetadata {
|
||||
enum Error: Swift.Error {
|
||||
case deficientMetadata(slug: String, missingKeys: [String], metadata: [String: String])
|
||||
case invalidTimestamp(String)
|
||||
}
|
||||
|
||||
init(dictionary: [String: String], slug: String) throws {
|
||||
let requiredKeys = ["Title", "Author", "Date", "Timestamp"]
|
||||
let missingKeys = requiredKeys.filter { dictionary[$0] == nil }
|
||||
guard missingKeys.isEmpty else {
|
||||
throw Error.deficientMetadata(slug: slug, missingKeys: missingKeys, metadata: dictionary)
|
||||
}
|
||||
guard let timestamp = dictionary["Timestamp"], let timeInterval = TimeInterval(timestamp) else {
|
||||
throw Error.invalidTimestamp(dictionary["Timestamp"]!)
|
||||
}
|
||||
|
||||
self.init(
|
||||
title: dictionary["Title"]!,
|
||||
author: dictionary["Author"]!,
|
||||
date: Date(timeIntervalSince1970: timeInterval),
|
||||
formattedDate: dictionary["Date"]!,
|
||||
link: dictionary["Link"].flatMap { URL(string: $0) },
|
||||
tags: dictionary.commaSeparatedList(key: "Tags"),
|
||||
scripts: dictionary.commaSeparatedList(key: "Scripts"),
|
||||
styles: dictionary.commaSeparatedList(key: "Styles")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import Ink
|
||||
|
||||
struct RawPost {
|
||||
let slug: String
|
||||
|
|
@ -18,13 +19,13 @@ final class PostRepo {
|
|||
let feedPostsCount = 30
|
||||
|
||||
let fileManager: FileManager
|
||||
let outputPath: String
|
||||
let markdownParser: MarkdownParser
|
||||
|
||||
private(set) var posts: PostsByYear!
|
||||
|
||||
init(fileManager: FileManager = .default, outputPath: String = "posts") {
|
||||
init(fileManager: FileManager = .default, markdownParser: MarkdownParser = MarkdownParser()) {
|
||||
self.fileManager = fileManager
|
||||
self.outputPath = outputPath
|
||||
self.markdownParser = markdownParser
|
||||
}
|
||||
|
||||
var isEmpty: Bool {
|
||||
|
|
@ -32,7 +33,7 @@ final class PostRepo {
|
|||
}
|
||||
|
||||
var sortedPosts: [Post] {
|
||||
posts?.flattened().sorted(by: >) ?? []
|
||||
posts.flattened().sorted(by: >)
|
||||
}
|
||||
|
||||
var recentPosts: [Post] {
|
||||
|
|
@ -48,14 +49,46 @@ final class PostRepo {
|
|||
return fileManager.fileExists(atPath: postsURL.path)
|
||||
}
|
||||
|
||||
func readPosts(sourceURL: URL) throws {
|
||||
let postTransformer = PostTransformer(outputPath: outputPath)
|
||||
func readPosts(sourceURL: URL, outputPath: String) throws {
|
||||
let posts = try readRawPosts(sourceURL: sourceURL)
|
||||
.map(postTransformer.makePost)
|
||||
.map { try makePost(from: $0, outputPath: outputPath) }
|
||||
self.posts = PostsByYear(posts: posts, path: "/\(outputPath)")
|
||||
}
|
||||
}
|
||||
|
||||
private func readRawPosts(sourceURL: URL) throws -> [RawPost] {
|
||||
private extension PostRepo {
|
||||
func makePost(from rawPost: RawPost, outputPath: String) throws -> Post {
|
||||
let result = markdownParser.parse(rawPost.markdown)
|
||||
let metadata = try PostMetadata(dictionary: result.metadata, slug: rawPost.slug)
|
||||
let path = pathForPost(root: outputPath, date: metadata.date, slug: rawPost.slug)
|
||||
return Post(
|
||||
slug: rawPost.slug,
|
||||
title: metadata.title,
|
||||
author: metadata.author,
|
||||
date: metadata.date,
|
||||
formattedDate: metadata.formattedDate,
|
||||
link: metadata.link,
|
||||
tags: metadata.tags,
|
||||
scripts: metadata.scripts,
|
||||
styles: metadata.styles,
|
||||
body: result.html,
|
||||
path: path
|
||||
)
|
||||
}
|
||||
|
||||
func pathForPost(root: String, date: Date, slug: String) -> String {
|
||||
// format: /{root}/{year}/{month}/{slug}
|
||||
// e.g. /posts/2019/12/first-post
|
||||
[
|
||||
"",
|
||||
root,
|
||||
"\(date.year)",
|
||||
Month(date).padded,
|
||||
slug,
|
||||
].joined(separator: "/")
|
||||
}
|
||||
|
||||
func readRawPosts(sourceURL: URL) throws -> [RawPost] {
|
||||
let postsURL = sourceURL.appendingPathComponent(postsPath)
|
||||
return try enumerateMarkdownFiles(directory: postsURL)
|
||||
.compactMap { url in
|
||||
|
|
@ -69,22 +102,20 @@ final class PostRepo {
|
|||
}
|
||||
}
|
||||
|
||||
private func readRawPost(url: URL) throws -> RawPost {
|
||||
func readRawPost(url: URL) throws -> RawPost {
|
||||
let slug = url.deletingPathExtension().lastPathComponent
|
||||
let markdown = try String(contentsOf: url)
|
||||
return RawPost(slug: slug, markdown: markdown)
|
||||
}
|
||||
|
||||
private func enumerateMarkdownFiles(directory: URL) throws -> [URL] {
|
||||
return try fileManager.contentsOfDirectory(atPath: directory.path).flatMap { (filename: String) -> [URL] in
|
||||
let fileURL = directory.appendingPathComponent(filename)
|
||||
var isDir: ObjCBool = false
|
||||
_ = fileManager.fileExists(atPath: fileURL.path, isDirectory: &isDir)
|
||||
if isDir.boolValue {
|
||||
return try enumerateMarkdownFiles(directory: fileURL)
|
||||
func enumerateMarkdownFiles(directory: URL) throws -> [URL] {
|
||||
return try fileManager.contentsOfDirectory(atPath: directory.path).flatMap { (name: String) -> [URL] in
|
||||
let url = directory.appendingPathComponent(name)
|
||||
if fileManager.directoryExists(at: url) {
|
||||
return try enumerateMarkdownFiles(directory: url)
|
||||
}
|
||||
else {
|
||||
return fileURL.pathExtension == "md" ? [fileURL] : []
|
||||
return url.pathExtension == "md" ? [url] : []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,98 +0,0 @@
|
|||
//
|
||||
// PostTransformer.swift
|
||||
// samhuri.net
|
||||
//
|
||||
// Created by Sami Samhuri on 2019-12-09.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Ink
|
||||
|
||||
final class PostTransformer {
|
||||
let markdownParser: MarkdownParser
|
||||
let outputPath: String
|
||||
|
||||
init(markdownParser: MarkdownParser = MarkdownParser(), outputPath: String = "posts") {
|
||||
self.markdownParser = markdownParser
|
||||
self.outputPath = outputPath
|
||||
}
|
||||
|
||||
func makePost(from rawPost: RawPost) throws -> Post {
|
||||
let result = markdownParser.parse(rawPost.markdown)
|
||||
let metadata = try parseMetadata(result.metadata, slug: rawPost.slug)
|
||||
let path = pathForPost(date: metadata.date, slug: rawPost.slug)
|
||||
return Post(
|
||||
slug: rawPost.slug,
|
||||
title: metadata.title,
|
||||
author: metadata.author,
|
||||
date: metadata.date,
|
||||
formattedDate: metadata.formattedDate,
|
||||
link: metadata.link,
|
||||
tags: metadata.tags,
|
||||
scripts: metadata.scripts,
|
||||
styles: metadata.styles,
|
||||
body: result.html,
|
||||
path: path
|
||||
)
|
||||
}
|
||||
|
||||
func pathForPost(date: Date, slug: String) -> String {
|
||||
// format: /posts/2019/12/first-post
|
||||
[
|
||||
"",
|
||||
outputPath,
|
||||
"\(date.year)",
|
||||
Month(date.month).padded,
|
||||
slug,
|
||||
].joined(separator: "/")
|
||||
}
|
||||
}
|
||||
|
||||
private struct ParsedMetadata {
|
||||
let title: String
|
||||
let author: String
|
||||
let date: Date
|
||||
let formattedDate: String
|
||||
let link: URL?
|
||||
let tags: [String]
|
||||
let scripts: [String]
|
||||
let styles: [String]
|
||||
}
|
||||
|
||||
private extension PostTransformer {
|
||||
enum Error: Swift.Error {
|
||||
case deficientMetadata(slug: String, missingKeys: [String], metadata: [String: String])
|
||||
case invalidTimestamp(String)
|
||||
}
|
||||
|
||||
func parseMetadata(_ metadata: [String: String], slug: String) throws -> ParsedMetadata {
|
||||
let requiredKeys = ["Title", "Author", "Date", "Timestamp"]
|
||||
let missingKeys = requiredKeys.filter { metadata[$0] == nil }
|
||||
guard missingKeys.isEmpty else {
|
||||
throw Error.deficientMetadata(slug: slug, missingKeys: missingKeys, metadata: metadata)
|
||||
}
|
||||
guard let timeInterval = TimeInterval(metadata["Timestamp"]!) else {
|
||||
throw Error.invalidTimestamp(metadata["Timestamp"]!)
|
||||
}
|
||||
|
||||
let title = metadata["Title"]!
|
||||
let author = metadata["Author"]!
|
||||
let date = Date(timeIntervalSince1970: timeInterval)
|
||||
let formattedDate = metadata["Date"]!
|
||||
let link = metadata["Link"].flatMap { URL(string: $0) }
|
||||
let tags = metadata.commaSeparatedList(key: "Tags")
|
||||
let scripts = metadata.commaSeparatedList(key: "Scripts")
|
||||
let styles = metadata.commaSeparatedList(key: "Styles")
|
||||
|
||||
return ParsedMetadata(
|
||||
title: title,
|
||||
author: author,
|
||||
date: date,
|
||||
formattedDate: formattedDate,
|
||||
link: link,
|
||||
tags: tags,
|
||||
scripts: scripts,
|
||||
styles: styles
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ extension PostWriter {
|
|||
}
|
||||
|
||||
private func filePath(date: Date, slug: String) -> String {
|
||||
"/\(date.year)/\(Month(date.month).padded)/\(slug)/index.html"
|
||||
"/\(date.year)/\(Month(date).padded)/\(slug)/index.html"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,14 +47,11 @@ extension PostsPlugin {
|
|||
}
|
||||
|
||||
func build() -> PostsPlugin {
|
||||
let postRepo: PostRepo
|
||||
let postWriter: PostWriter
|
||||
if let outputPath = path {
|
||||
postRepo = PostRepo(outputPath: outputPath)
|
||||
postWriter = PostWriter(outputPath: outputPath)
|
||||
}
|
||||
else {
|
||||
postRepo = PostRepo()
|
||||
postWriter = PostWriter()
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +73,7 @@ extension PostsPlugin {
|
|||
|
||||
return PostsPlugin(
|
||||
renderer: renderer,
|
||||
postRepo: postRepo,
|
||||
postRepo: PostRepo(),
|
||||
postWriter: postWriter,
|
||||
jsonFeedWriter: jsonFeedWriter,
|
||||
rssFeedWriter: rssFeedWriter
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ final class PostsPlugin: Plugin {
|
|||
return
|
||||
}
|
||||
|
||||
try postRepo.readPosts(sourceURL: sourceURL)
|
||||
try postRepo.readPosts(sourceURL: sourceURL, outputPath: postWriter.outputPath)
|
||||
}
|
||||
|
||||
func render(site: Site, targetURL: URL) throws {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ extension PageRenderer: RSSFeedRendering {
|
|||
func renderRSSFeed(posts: [Post], feedURL: URL, site: Site) throws -> String {
|
||||
try RSS(
|
||||
.title(site.title),
|
||||
.if(site.description != nil, .description(site.description!)),
|
||||
.description(site.description),
|
||||
.link(site.url),
|
||||
.pubDate(posts[0].date),
|
||||
.atomLink(feedURL),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ final class ProjectsPlugin: Plugin {
|
|||
let projectAssets: TemplateAssets
|
||||
|
||||
var projects: [Project] = []
|
||||
var sourceURL: URL!
|
||||
|
||||
init(
|
||||
projects: [PartialProject],
|
||||
|
|
@ -39,7 +38,6 @@ final class ProjectsPlugin: Plugin {
|
|||
// MARK: - Plugin methods
|
||||
|
||||
func setUp(site: Site, sourceURL: URL) throws {
|
||||
self.sourceURL = sourceURL
|
||||
projects = partialProjects.map { partial in
|
||||
Project(
|
||||
title: partial.title,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import Foundation
|
|||
import Ink
|
||||
|
||||
final class MarkdownRenderer: Renderer {
|
||||
let fileManager: FileManager = .default
|
||||
let fileWriter: FileWriting
|
||||
let markdownParser = MarkdownParser()
|
||||
let pageRenderer: PageRendering
|
||||
|
|
@ -25,7 +24,7 @@ final class MarkdownRenderer: Renderer {
|
|||
|
||||
/// Parse Markdown and render it as HTML, running it through a Stencil template.
|
||||
func render(site: Site, fileURL: URL, targetDir: URL) throws {
|
||||
let bodyMarkdown = try String(contentsOf: fileURL, encoding: .utf8)
|
||||
let bodyMarkdown = try String(contentsOf: fileURL)
|
||||
let bodyHTML = markdownParser.html(from: bodyMarkdown).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let metadata = try markdownMetadata(from: fileURL)
|
||||
let pageHTML = try pageRenderer.renderPage(site: site, bodyHTML: bodyHTML, metadata: metadata)
|
||||
|
|
@ -43,7 +42,7 @@ final class MarkdownRenderer: Renderer {
|
|||
}
|
||||
|
||||
func markdownMetadata(from url: URL) throws -> [String: String] {
|
||||
let md = try String(contentsOf: url, encoding: .utf8)
|
||||
let md = try String(contentsOf: url)
|
||||
return markdownParser.parse(md).metadata
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import Foundation
|
|||
extension Site {
|
||||
final class Builder {
|
||||
private let title: String
|
||||
private let description: String?
|
||||
private let description: String
|
||||
private let author: String
|
||||
private let email: String
|
||||
private let url: URL
|
||||
|
|
@ -23,7 +23,7 @@ extension Site {
|
|||
|
||||
init(
|
||||
title: String,
|
||||
description: String? = nil,
|
||||
description: String,
|
||||
author: String,
|
||||
email: String,
|
||||
url: URL
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ struct Site {
|
|||
let author: String
|
||||
let email: String
|
||||
let title: String
|
||||
let description: String?
|
||||
let description: String
|
||||
let url: URL
|
||||
let styles: [String]
|
||||
let scripts: [String]
|
||||
|
|
|
|||
|
|
@ -41,17 +41,15 @@ final class SiteGenerator {
|
|||
|
||||
// Recursively copy or render every file in the given path.
|
||||
func renderPath(_ path: String, to targetURL: URL) throws {
|
||||
for filename in try fileManager.contentsOfDirectory(atPath: path) {
|
||||
guard !ignoredFilenames.contains(filename) else {
|
||||
for name in try fileManager.contentsOfDirectory(atPath: path) {
|
||||
guard !ignoredFilenames.contains(name) else {
|
||||
continue
|
||||
}
|
||||
|
||||
// Recurse into subdirectories, updating the target directory as well.
|
||||
let fileURL = URL(fileURLWithPath: path).appendingPathComponent(filename)
|
||||
var isDir: ObjCBool = false
|
||||
_ = fileManager.fileExists(atPath: fileURL.path, isDirectory: &isDir)
|
||||
guard !isDir.boolValue else {
|
||||
try renderPath(fileURL.path, to: targetURL.appendingPathComponent(filename))
|
||||
let url = URL(fileURLWithPath: path).appendingPathComponent(name)
|
||||
guard !fileManager.directoryExists(at: url) else {
|
||||
try renderPath(url.path, to: targetURL.appendingPathComponent(name))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +57,7 @@ final class SiteGenerator {
|
|||
try fileManager.createDirectory(at: targetURL, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
// Process the file, transforming it if necessary.
|
||||
try renderOrCopyFile(url: fileURL, targetDir: targetURL)
|
||||
try renderOrCopyFile(url: url, targetDir: targetURL)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue