diff --git a/WebsiteGenerator.xcodeproj/project.pbxproj b/WebsiteGenerator.xcodeproj/project.pbxproj index 0ce3aee..e98e903 100644 --- a/WebsiteGenerator.xcodeproj/project.pbxproj +++ b/WebsiteGenerator.xcodeproj/project.pbxproj @@ -23,7 +23,7 @@ E22E878C289E4A8900E51191 /* Ink in Frameworks */ = {isa = PBXBuildFile; productRef = E22E878B289E4A8900E51191 /* Ink */; }; E22E8793289E7EC700E51191 /* Page+LocalizedMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22E8792289E7EC700E51191 /* Page+LocalizedMetadata.swift */; }; E22E8795289E81D700E51191 /* FileSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22E8794289E81D700E51191 /* FileSystem.swift */; }; - E22E8798289EA42C00E51191 /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22E8797289EA42C00E51191 /* ImageProcessor.swift */; }; + E22E8798289EA42C00E51191 /* FileProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22E8797289EA42C00E51191 /* FileProcessor.swift */; }; E22E879B289EE02F00E51191 /* Optional+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22E879A289EE02F00E51191 /* Optional+Extensions.swift */; }; E22E879E289EFDFC00E51191 /* OverviewPageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22E879D289EFDFC00E51191 /* OverviewPageGenerator.swift */; }; E22E87A0289F008200E51191 /* ThumbnailListGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E22E879F289F008200E51191 /* ThumbnailListGenerator.swift */; }; @@ -82,7 +82,7 @@ E22E8788289DDF5700E51191 /* Page+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Page+Metadata.swift"; sourceTree = ""; }; E22E8792289E7EC700E51191 /* Page+LocalizedMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Page+LocalizedMetadata.swift"; sourceTree = ""; }; E22E8794289E81D700E51191 /* FileSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystem.swift; sourceTree = ""; }; - E22E8797289EA42C00E51191 /* ImageProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProcessor.swift; sourceTree = ""; }; + E22E8797289EA42C00E51191 /* FileProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProcessor.swift; sourceTree = ""; }; E22E879A289EE02F00E51191 /* Optional+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extensions.swift"; sourceTree = ""; }; E22E879D289EFDFC00E51191 /* OverviewPageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewPageGenerator.swift; sourceTree = ""; }; E22E879F289F008200E51191 /* ThumbnailListGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbnailListGenerator.swift; sourceTree = ""; }; @@ -149,7 +149,7 @@ E2C5A5D328A0222B00102A25 /* Templates */, E22E8799289EE02300E51191 /* Extensions */, E22E876B289D855D00E51191 /* ThumbnailStyle.swift */, - E22E8797289EA42C00E51191 /* ImageProcessor.swift */, + E22E8797289EA42C00E51191 /* FileProcessor.swift */, E22E8777289DA0E100E51191 /* GenerationError.swift */, E22E8794289E81D700E51191 /* FileSystem.swift */, ); @@ -315,7 +315,7 @@ E22E876E289D868100E51191 /* Site+LocalizedMetadata.swift in Sources */, E2C5A5D528A0223C00102A25 /* OverviewPageTemplate.swift in Sources */, E22E876C289D855D00E51191 /* ThumbnailStyle.swift in Sources */, - E22E8798289EA42C00E51191 /* ImageProcessor.swift in Sources */, + E22E8798289EA42C00E51191 /* FileProcessor.swift in Sources */, E26555E428A2C4FA00BAF496 /* LinkPreviewMetadataProvider.swift in Sources */, E22E87AA289F1AEE00E51191 /* PageHeadGenerator.swift in Sources */, E2D55EDB28A2511D00B9453E /* OverviewSectionCleanTemplate.swift in Sources */, diff --git a/WebsiteGenerator/ImageProcessor.swift b/WebsiteGenerator/FileProcessor.swift similarity index 90% rename from WebsiteGenerator/ImageProcessor.swift rename to WebsiteGenerator/FileProcessor.swift index d8b16b5..11e0c4b 100644 --- a/WebsiteGenerator/ImageProcessor.swift +++ b/WebsiteGenerator/FileProcessor.swift @@ -3,7 +3,7 @@ import Foundation import AppKit #endif -final class ImageProcessor { +final class FileProcessor { struct ImageOutput: Hashable { @@ -32,12 +32,44 @@ final class ImageProcessor { let outputFolder: URL + /** + The files required by the site. + + The content are the links to the files relative to the source root folder. + The files will be placed at the same path relative to the output folder + */ + private var requiredFiles: Set = [] + + private var tasks: [String : ImageOutput] = [:] + init(inputFolder: URL, outputFolder: URL) { self.inputFolder = inputFolder self.outputFolder = outputFolder } - private var tasks: [String : ImageOutput] = [:] + // MARK: Files + + /** + Add a file as required, so that it will be copied to the output directory. + */ + func require(file: String) { + requiredFiles.insert(file) + } + + func copyRequiredFiles() throws { + var missingFiles = [String]() + for file in requiredFiles { + let sourceUrl = inputFolder.appendingPathComponent(file) + guard sourceUrl.exists else { + missingFiles.append(file) + continue + } + let destinationUrl = outputFolder.appendingPathComponent(file) + try FileSystem.copy(sourceUrl, to: destinationUrl) + } + } + + // MARK: Images @discardableResult func requireImage(source: String, destination: String, width: Int, desiredHeight: Int? = nil, createDoubleVersion: Bool = false) throws -> NSSize { diff --git a/WebsiteGenerator/FileSystem.swift b/WebsiteGenerator/FileSystem.swift index 85a5dc8..80091ef 100644 --- a/WebsiteGenerator/FileSystem.swift +++ b/WebsiteGenerator/FileSystem.swift @@ -9,6 +9,9 @@ enum FileSystem { .filter { $0.isDirectory } } + /** + Copy a file to the destination, creating the containing folder if needed + */ static func copy(_ source: URL, to destination: URL) throws { try destination.ensureParentFolderExistence() try source.copy(to: destination) diff --git a/WebsiteGenerator/Generators/IndexPageGenerator.swift b/WebsiteGenerator/Generators/IndexPageGenerator.swift index e7f9523..6ffb6eb 100644 --- a/WebsiteGenerator/Generators/IndexPageGenerator.swift +++ b/WebsiteGenerator/Generators/IndexPageGenerator.swift @@ -20,7 +20,7 @@ struct IndexPageGenerator { private let factory: LocalizedSiteTemplate - init(factory: LocalizedSiteTemplate, imageProcessor: ImageProcessor) { + init(factory: LocalizedSiteTemplate) { self.factory = factory } diff --git a/WebsiteGenerator/Generators/MarkdownProcessor.swift b/WebsiteGenerator/Generators/MarkdownProcessor.swift index dbda979..dccbeb6 100644 --- a/WebsiteGenerator/Generators/MarkdownProcessor.swift +++ b/WebsiteGenerator/Generators/MarkdownProcessor.swift @@ -3,10 +3,10 @@ import Ink struct PageContentGenerator { - private let imageProcessor: ImageProcessor + private let files: FileProcessor - init(imageProcessor: ImageProcessor) { - self.imageProcessor = imageProcessor + init(files: FileProcessor) { + self.files = files } func generate(page: Page, language: String, at url: URL) throws -> String { @@ -71,7 +71,7 @@ struct PageContentGenerator { let size: NSSize let imagePath = page.pathRelativeToRootForContainedInputFile(file) do { - size = try imageProcessor.requireImage( + size = try files.requireImage( source: imagePath, destination: imagePath, width: pageImageWidth, diff --git a/WebsiteGenerator/Generators/OverviewPageGenerator.swift b/WebsiteGenerator/Generators/OverviewPageGenerator.swift index 06be9df..d2dfd5f 100644 --- a/WebsiteGenerator/Generators/OverviewPageGenerator.swift +++ b/WebsiteGenerator/Generators/OverviewPageGenerator.swift @@ -6,9 +6,9 @@ struct OverviewPageGenerator { let outputFolder: URL - init(factory: LocalizedSiteTemplate, imageProcessor: ImageProcessor) { + init(factory: LocalizedSiteTemplate, files: FileProcessor) { self.factory = factory - self.outputFolder = imageProcessor.outputFolder + self.outputFolder = files.outputFolder } func generate( diff --git a/WebsiteGenerator/Generators/OverviewSectionGenerator.swift b/WebsiteGenerator/Generators/OverviewSectionGenerator.swift index 758d5f6..b3c27f2 100644 --- a/WebsiteGenerator/Generators/OverviewSectionGenerator.swift +++ b/WebsiteGenerator/Generators/OverviewSectionGenerator.swift @@ -6,15 +6,15 @@ struct OverviewSectionGenerator { private let singleSectionsTemplate: OverviewSectionCleanTemplate - let imageProcessor: ImageProcessor + let files: FileProcessor private let generator: ThumbnailListGenerator - init(factory: TemplateFactory, imageProcessor: ImageProcessor) { + init(factory: TemplateFactory, files: FileProcessor) { self.multipleSectionsTemplate = factory.overviewSection self.singleSectionsTemplate = factory.overviewSectionClean - self.imageProcessor = imageProcessor - self.generator = ThumbnailListGenerator(factory: factory, imageProcessor: imageProcessor) + self.files = files + self.generator = ThumbnailListGenerator(factory: factory, files: files) } func generate(sections: [Section], in parent: SiteElement, language: String, sectionItemCount: Int) throws -> String { diff --git a/WebsiteGenerator/Generators/PageGenerator.swift b/WebsiteGenerator/Generators/PageGenerator.swift index 5358596..ab7cfce 100644 --- a/WebsiteGenerator/Generators/PageGenerator.swift +++ b/WebsiteGenerator/Generators/PageGenerator.swift @@ -12,11 +12,11 @@ struct PageGenerator { private let factory: LocalizedSiteTemplate - private let imageProcessor: ImageProcessor + private let files: FileProcessor - init(factory: LocalizedSiteTemplate, imageProcessor: ImageProcessor) { + init(factory: LocalizedSiteTemplate, files: FileProcessor) { self.factory = factory - self.imageProcessor = imageProcessor + self.files = files } func generate(page: Page, language: String, backText: String, nextPage: NavigationLink?, previousPage: NavigationLink?) throws { @@ -45,8 +45,8 @@ struct PageGenerator { content[.nextPageUrl] = nextPage?.link content[.footer] = try page.customFooterContent() - let url = imageProcessor.outputFolder.appendingPathComponent(path) try factory.contentPage.generate(content, to: url) + let url = files.outputFolder.appendingPathComponent(path) } private func makeContent(page: Page, language: String, url: URL) throws -> String { @@ -55,7 +55,7 @@ struct PageGenerator { return factory.placeholder } print("Generated page \(page.path)") - return try PageContentGenerator(imageProcessor: imageProcessor).generate(page: page, language: language, at: url) + return try PageContentGenerator(files: files).generate(page: page, language: language, at: url) } private func makeHead(page: Page, language: String) throws -> String { diff --git a/WebsiteGenerator/Generators/PageHeadGenerator.swift b/WebsiteGenerator/Generators/PageHeadGenerator.swift index 613d315..0d95f40 100644 --- a/WebsiteGenerator/Generators/PageHeadGenerator.swift +++ b/WebsiteGenerator/Generators/PageHeadGenerator.swift @@ -30,11 +30,11 @@ struct PageHeadGenerator { let template: PageHeadTemplate - let imageProcessor: ImageProcessor + let files: FileProcessor - init(factory: TemplateFactory, imageProcessor: ImageProcessor) { + init(factory: TemplateFactory, files: FileProcessor) { self.template = factory.pageHead - self.imageProcessor = imageProcessor + self.files = files } func generate(page: PageHeadInfoProvider) throws -> String { @@ -47,7 +47,7 @@ struct PageHeadGenerator { // since we don't want a single large image for thumbnails. // Warning: Link preview source path must be relative to root let linkPreviewImagePath = image.insert("-link", beforeLast: ".") - try imageProcessor.requireImage( + try files.requireImage( source: image, destination: linkPreviewImagePath, width: Site.linkPreviewDesiredImageWidth) diff --git a/WebsiteGenerator/Generators/SiteGenerator.swift b/WebsiteGenerator/Generators/SiteGenerator.swift index f11e728..9e6bd73 100644 --- a/WebsiteGenerator/Generators/SiteGenerator.swift +++ b/WebsiteGenerator/Generators/SiteGenerator.swift @@ -6,17 +6,17 @@ struct SiteGenerator { let templates: TemplateFactory - private let imageProcessor: ImageProcessor + private let files: FileProcessor private var outputFolder: URL { - imageProcessor.outputFolder + files.outputFolder } - init(site: Site, imageProcessor: ImageProcessor) throws { + init(site: Site, files: FileProcessor) throws { self.site = site let templatesFolder = site.inputFolder.appendingPathComponent("templates") self.templates = try TemplateFactory(templateFolder: templatesFolder) - self.imageProcessor = imageProcessor + self.files = files } func generate() throws { @@ -26,12 +26,12 @@ struct SiteGenerator { factory: templates, language: language, site: site, - imageProcessor: imageProcessor) + files: files) // Generate sections - let overviewGenerator = OverviewPageGenerator(factory: template, imageProcessor: imageProcessor) - let pageGenerator = PageGenerator(factory: template, imageProcessor: imageProcessor) + let overviewGenerator = OverviewPageGenerator(factory: template, files: files) + let pageGenerator = PageGenerator(factory: template, files: files) let backLinkText = try site.backLinkText(for: language) var elementsToProcess: [(element: SiteElement, backText: String?)] = site.elements.map { ($0, backLinkText) } while let (element, backText) = elementsToProcess.popLast() { @@ -52,12 +52,14 @@ struct SiteGenerator { backText: backText ?? metadata.defaultBackLinkText, nextPage: nil, previousPage: nil) + for file in page.metadata.requiredFiles { + let relativePath = page.path + "/" + file + files.require(file: relativePath) + } } } - let generator = IndexPageGenerator( - factory: template, - imageProcessor: imageProcessor) + let generator = IndexPageGenerator(factory: template) // Generate front page let relativeUrl = site.localizedPath(for: language) diff --git a/WebsiteGenerator/Generators/ThumbnailListGenerator.swift b/WebsiteGenerator/Generators/ThumbnailListGenerator.swift index bb52615..5649933 100644 --- a/WebsiteGenerator/Generators/ThumbnailListGenerator.swift +++ b/WebsiteGenerator/Generators/ThumbnailListGenerator.swift @@ -4,11 +4,11 @@ struct ThumbnailListGenerator { private let factory: TemplateFactory - let imageProcessor: ImageProcessor + let files: FileProcessor - init(factory: TemplateFactory, imageProcessor: ImageProcessor) { + init(factory: TemplateFactory, files: FileProcessor) { self.factory = factory - self.imageProcessor = imageProcessor + self.files = files } func generateContent(items: [ThumbnailInfo], style: ThumbnailStyle) throws -> String { @@ -26,7 +26,7 @@ struct ThumbnailListGenerator { factory.largeThumbnail.makeCorner(text: $0) } - try imageProcessor.requireImage( + try files.requireImage( source: thumbnail.imageFilePath, destination: thumbnail.imageFilePath, width: style.width, diff --git a/WebsiteGenerator/Templates/Filled/LocalizedSiteTemplate.swift b/WebsiteGenerator/Templates/Filled/LocalizedSiteTemplate.swift index e06eac1..4f948c2 100644 --- a/WebsiteGenerator/Templates/Filled/LocalizedSiteTemplate.swift +++ b/WebsiteGenerator/Templates/Filled/LocalizedSiteTemplate.swift @@ -54,7 +54,7 @@ struct LocalizedSiteTemplate { factory.contentPage } - init(factory: TemplateFactory, language: String, site: Site, imageProcessor: ImageProcessor) throws { + init(factory: TemplateFactory, language: String, site: Site, files: FileProcessor) throws { self.author = site.metadata.author self.factory = factory @@ -92,10 +92,10 @@ struct LocalizedSiteTemplate { topBarWebsiteTitle: title) self.pageHead = PageHeadGenerator( factory: factory, - imageProcessor: imageProcessor) + files: files) self.overviewSection = OverviewSectionGenerator( factory: factory, - imageProcessor: imageProcessor) + files: files) self.placeholder = factory.placeholder.generate([ .title: metadata.placeholderTitle, diff --git a/WebsiteGenerator/main.swift b/WebsiteGenerator/main.swift index 0011993..13a35d4 100644 --- a/WebsiteGenerator/main.swift +++ b/WebsiteGenerator/main.swift @@ -2,7 +2,7 @@ import Foundation let contentDirectory = URL(fileURLWithPath: "/Users/ch/Projects/MakerSpace") let outputDirectory = URL(fileURLWithPath: "/Users/ch/Projects/MakerSpace/Site") -let imageProcessor = ImageProcessor( +let files = FileProcessor( inputFolder: contentDirectory, outputFolder: outputDirectory) // 1: Load all site content @@ -10,11 +10,13 @@ guard let site = try Site(folder: contentDirectory) else { exit(0) } // site.printContents() -let siteGenerator = try SiteGenerator(site: site, imageProcessor: imageProcessor) +let siteGenerator = try SiteGenerator(site: site, files: files) try siteGenerator.generate() print("Pages generated") -try imageProcessor.createImages() +try files.createImages() print("Images generated") +try files.copyRequiredFiles() +print("Required files copied") #warning("Check that all metadata for each language is present")