diff --git a/Sources/Publish/API/Mutations.swift b/Sources/Publish/API/Mutations.swift index 738133bd..cf88f40f 100644 --- a/Sources/Publish/API/Mutations.swift +++ b/Sources/Publish/API/Mutations.swift @@ -6,3 +6,8 @@ /// Closure type used to implement content mutations. public typealias Mutations = (inout T) throws -> Void +public typealias HTMLIndexMutation = (PublishingContext, String) throws -> String +public typealias HTMLSectionMutation = (PublishingContext, Section, String) throws -> String +public typealias HTMLItemMutation = (PublishingContext, Item, String) throws -> String +public typealias HTMLPageMutation = (PublishingContext, Page, String) throws -> String +public typealias HTMLAllMutation = (PublishingContext, Location, String) throws -> String diff --git a/Sources/Publish/API/PublishingContext.swift b/Sources/Publish/API/PublishingContext.swift index 4dd14caa..ee3937a1 100644 --- a/Sources/Publish/API/PublishingContext.swift +++ b/Sources/Publish/API/PublishingContext.swift @@ -34,6 +34,8 @@ public struct PublishingContext { /// Any date when the website was last generated. public private(set) var lastGenerationDate: Date? + internal var htmlMutations = HTMLMutations() + private let folders: Folder.Group private var tagCache = TagCache() private var stepName: String @@ -273,6 +275,27 @@ public extension PublishingContext { ) } } + + mutating func mutateIndexHTML(using mutation: @escaping HTMLIndexMutation) { + self.htmlMutations.indexMutations.append(mutation) + } + + mutating func mutateSectionHTML(using mutation: @escaping HTMLSectionMutation) { + self.htmlMutations.sectionMutations.append(mutation) + } + + mutating func mutateItemHTML(using mutation: @escaping HTMLItemMutation) { + self.htmlMutations.itemMutations.append(mutation) + } + + mutating func mutatePageHTML(using mutation: @escaping HTMLPageMutation) { + self.htmlMutations.pageMutations.append(mutation) + } + + mutating func mutateAllHTML(using mutation: @escaping HTMLAllMutation) { + self.htmlMutations.allMutations.append(mutation) + } + } internal extension PublishingContext { diff --git a/Sources/Publish/API/PublishingStep.swift b/Sources/Publish/API/PublishingStep.swift index ecd19dbe..40f798c1 100644 --- a/Sources/Publish/API/PublishingStep.swift +++ b/Sources/Publish/API/PublishingStep.swift @@ -227,6 +227,39 @@ public extension PublishingStep { } } + static func mutateIndexHTML(using mutation: @escaping HTMLIndexMutation) -> Self { + step(named: "Mutate Index HTML") { context in + context.mutateIndexHTML(using: mutation) + } + } + + static func mutateSectionHTML(using mutation: @escaping HTMLSectionMutation) -> Self { + step(named: "Mutate Section HTML") { context in + context.mutateSectionHTML(using: mutation) + } + } + + static func mutateItemHTML(using mutation: @escaping HTMLItemMutation) -> Self { + step(named: "Mutate Item HTML") { context in + context.mutateItemHTML(using: mutation) + } + } + + static func mutatePageHTML(using mutation: @escaping HTMLPageMutation) -> Self { + step(named: "Mutate Page HTML") { context in + context.mutatePageHTML(using: mutation) + } + } + + static func mutateAllHTML(using mutation: @escaping HTMLAllMutation) -> Self { + step(named: "Mutate All HTML") { context in + context.mutateAllHTML(using: mutation) + } + } + + + + /// Sort all items, optionally within a specific section, using a key path. /// - parameter section: Any specific section to sort all items within. /// - parameter keyPath: The key path to use when sorting. diff --git a/Sources/Publish/Internal/HTMLGenerator.swift b/Sources/Publish/Internal/HTMLGenerator.swift index b9c69baa..d8b91a0c 100644 --- a/Sources/Publish/Internal/HTMLGenerator.swift +++ b/Sources/Publish/Internal/HTMLGenerator.swift @@ -48,7 +48,9 @@ private extension HTMLGenerator { func generateIndexHTML() throws { let html = try theme.makeIndexHTML(context.index, context) let indexFile = try context.createOutputFile(at: "index.html") - try indexFile.write(html.render(indentedBy: indentation)) + var renderedHtml = html.render(indentedBy: indentation) + renderedHtml = try context.htmlMutations.mutateHtml(context: context, renderedHtml: renderedHtml) + try indexFile.write(renderedHtml) } func generateSectionHTML() throws { @@ -122,16 +124,34 @@ private extension HTMLGenerator { } } + func renderHTML( + for location: T, + indentedBy indentation: Indentation.Kind?, + using generator: (T, PublishingContext) throws -> HTML + ) throws -> String { + let html = try generator(location, context) + return html.render(indentedBy: indentation) + } + func outputHTML( for location: T, indentedBy indentation: Indentation.Kind?, using generator: (T, PublishingContext) throws -> HTML, fileMode: HTMLFileMode ) throws { - let html = try generator(location, context) + var renderedHtml = try renderHTML(for: location, indentedBy: indentation, using: generator) + if let section = location as? Section { + renderedHtml = try context.htmlMutations.mutateHtml(context: context, section: section, renderedHtml: renderedHtml) + } else if let item = location as? Item { + renderedHtml = try context.htmlMutations.mutateHtml(context: context, item: item, renderedHtml: renderedHtml) + } else if let page = location as? Page { + renderedHtml = try context.htmlMutations.mutateHtml(context: context, page: page, renderedHtml: renderedHtml) + } else { + renderedHtml = try context.htmlMutations.mutateHtml(context: context, location: location, renderedHtml: renderedHtml) + } let path = filePath(for: location, fileMode: fileMode) let file = try context.createOutputFile(at: path) - try file.write(html.render(indentedBy: indentation)) + try file.write(renderedHtml) } func filePath(for location: Location, fileMode: HTMLFileMode) -> Path { @@ -143,3 +163,4 @@ private extension HTMLGenerator { } } } + diff --git a/Sources/Publish/Internal/HTMLMutations.swift b/Sources/Publish/Internal/HTMLMutations.swift new file mode 100644 index 00000000..2e7229f0 --- /dev/null +++ b/Sources/Publish/Internal/HTMLMutations.swift @@ -0,0 +1,58 @@ +import Plot + +internal struct HTMLMutations { + var indexMutations: [HTMLIndexMutation] = [] + var sectionMutations: [HTMLSectionMutation] = [] + var itemMutations: [HTMLItemMutation] = [] + var pageMutations: [HTMLPageMutation] = [] + var allMutations: [HTMLAllMutation] = [] + + public init() { + } + + func mutateHtml(context: PublishingContext, renderedHtml: String) throws -> String { + var mutatedHtml = renderedHtml + for mutation in indexMutations { + mutatedHtml = try mutation(context, mutatedHtml) + } + mutatedHtml = try mutateHtml(context: context, location: context.index, renderedHtml: mutatedHtml) + return mutatedHtml + } + + func mutateHtml(context: PublishingContext, section: Section, renderedHtml: String) throws -> String { + var mutatedHtml = renderedHtml + for mutation in sectionMutations { + mutatedHtml = try mutation(context, section, mutatedHtml) + } + mutatedHtml = try mutateHtml(context: context, location: section, renderedHtml: mutatedHtml) + return mutatedHtml + } + + func mutateHtml(context: PublishingContext, item: Item, renderedHtml: String) throws -> String { + var mutatedHtml = renderedHtml + for mutation in itemMutations { + mutatedHtml = try mutation(context, item, mutatedHtml) + } + mutatedHtml = try mutateHtml(context: context, location: item, renderedHtml: mutatedHtml) + return mutatedHtml + } + + func mutateHtml(context: PublishingContext, page: Page, renderedHtml: String) throws -> String { + var mutatedHtml = renderedHtml + for mutation in pageMutations { + mutatedHtml = try mutation(context, page, mutatedHtml) + } + mutatedHtml = try mutateHtml(context: context, location: page, renderedHtml: mutatedHtml) + return mutatedHtml + } + + func mutateHtml(context: PublishingContext, location: Location, renderedHtml: String) throws -> String { + var mutatedHtml = renderedHtml + for mutation in allMutations { + mutatedHtml = try mutation(context, location, mutatedHtml) + } + return mutatedHtml + } + +} +