From 33da1c8ec22293c94bcc8c4c2265d74e1996def6 Mon Sep 17 00:00:00 2001 From: Peter van den Hamer <13396568+vdhamer@users.noreply.github.com> Date: Mon, 23 Sep 2024 13:40:46 +0200 Subject: [PATCH] Fix: #126 --- .../Extensions/URL-SelectDirectories.swift | 45 +++++++++++++++ .../URL-SelectSiteRootDirectory.swift | 37 ------------ .../PublishingContext-ResourceLoading.swift | 2 +- .../Ignite/Publishing/PublishingContext.swift | 56 ++++++++----------- 4 files changed, 68 insertions(+), 72 deletions(-) create mode 100644 Sources/Ignite/Extensions/URL-SelectDirectories.swift delete mode 100644 Sources/Ignite/Extensions/URL-SelectSiteRootDirectory.swift diff --git a/Sources/Ignite/Extensions/URL-SelectDirectories.swift b/Sources/Ignite/Extensions/URL-SelectDirectories.swift new file mode 100644 index 0000000..32049be --- /dev/null +++ b/Sources/Ignite/Extensions/URL-SelectDirectories.swift @@ -0,0 +1,45 @@ +// +// URL-selectDirectories.swift +// Ignite +// https://www.github.com/twostraws/Ignite +// See LICENSE for license information. +// + +import Foundation + +extension URL { + /// Returns URL where to find Assets/Content/Includes and URL where to generate the static web site. + /// When building a package, the website is built at the source URL (and both URLs are equal). + /// When building a MacOS app, the website is built in a subdirectory of the app's sandbox. + /// - Parameter file: path of a Swift source file to find source root directory by scanning path upwards. + /// - Returns tupple containing source URL and URL where output is built. + public static func selectDirectories(from file: StaticString) throws -> SourceBuildDirectories { + var currentURL = URL(filePath: file.description) + + repeat { + currentURL = currentURL.deletingLastPathComponent() + + let packageURL = currentURL.appending(path: "Package.swift") + if FileManager.default.fileExists(atPath: packageURL.path) { + return SourceBuildDirectories(source: packageURL.deletingLastPathComponent(), + build: packageURL.deletingLastPathComponent()) + } + } while currentURL.path() != "/" + + let buildDirectory: String = NSHomeDirectory() // app's home directory for a sandboxed MacOS app + if buildDirectory.contains("/Library/Containers/") { + let buildDirectoryURL: URL = URL(filePath: buildDirectory) + return SourceBuildDirectories(source: buildDirectoryURL, + build: buildDirectoryURL) + } + + throw PublishingError.missingPackageDirectory + } + +} + +/// Provides URL to where to find Assets/Content/Includes input directories and Build output directory +public struct SourceBuildDirectories { + public let source: URL + public let build: URL +} diff --git a/Sources/Ignite/Extensions/URL-SelectSiteRootDirectory.swift b/Sources/Ignite/Extensions/URL-SelectSiteRootDirectory.swift deleted file mode 100644 index eb31dbe..0000000 --- a/Sources/Ignite/Extensions/URL-SelectSiteRootDirectory.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// URL-SelectSiteRootDirectory.swift -// Ignite -// https://www.github.com/twostraws/Ignite -// See LICENSE for license information. -// - -import Foundation - -extension URL { - /// Locates the source of a Swift package, regardless of how deep - /// in a subfolder we currently are. - public static func selectSiteRootDirectory(from file: StaticString) throws -> URL { - var currentURL = URL(filePath: file.description) - - repeat { - currentURL = currentURL.deletingLastPathComponent() - - let packageURL = currentURL.appending(path: "Package.swift") - if FileManager.default.fileExists(atPath: packageURL.path) { - return packageURL.deletingLastPathComponent() - } - } while currentURL.path() != "/" - - let homeDir: String = NSHomeDirectory() // on a sandboxed MacOS app, this is the app's home directory - if homeDir.contains("/Library/Containers/") { - do { - return try URL("file://" + homeDir, strategy: .url) - } catch { - throw PublishingError.missingSandboxHomeDirectory - } - } - - throw PublishingError.missingPackageDirectory - } - -} diff --git a/Sources/Ignite/Publishing/PublishingContext-ResourceLoading.swift b/Sources/Ignite/Publishing/PublishingContext-ResourceLoading.swift index 0a2052c..59c50e6 100644 --- a/Sources/Ignite/Publishing/PublishingContext-ResourceLoading.swift +++ b/Sources/Ignite/Publishing/PublishingContext-ResourceLoading.swift @@ -12,7 +12,7 @@ extension PublishingContext { /// - Parameter resource: The file to look for, e.g. "quotes.json" /// - Returns: The URL, if the file can be found. public func url(forResource resource: String) -> URL? { - let fullURL = rootDirectory.appending(path: "Resources/\(resource)") + let fullURL = sourceDirectory.appending(path: "Resources/\(resource)") if FileManager.default.fileExists(atPath: fullURL.path()) { return fullURL diff --git a/Sources/Ignite/Publishing/PublishingContext.swift b/Sources/Ignite/Publishing/PublishingContext.swift index a1e2010..a03cb2a 100644 --- a/Sources/Ignite/Publishing/PublishingContext.swift +++ b/Sources/Ignite/Publishing/PublishingContext.swift @@ -15,7 +15,7 @@ public class PublishingContext { public var site: any Site /// The root directory for the user's website package. - var rootDirectory: URL + var sourceDirectory: URL /// The directory containing their custom assets. var assetsDirectory: URL @@ -45,25 +45,6 @@ public class PublishingContext { /// control!) private(set) var siteMap = [Location]() - /// Creates a new publishing context for a specific site, setting a root URL. - /// - Parameters: - /// - site: The site we're currently publishing. - /// - rootURL: The URL of the root directory, where other key - /// folders are located. - /// - buildDirectoryPath: The path where the artifacts are generated. - /// The default is "Build". - init(for site: any Site, rootURL: URL, buildDirectoryPath: String = "Build") throws { - self.site = site - - self.rootDirectory = rootURL - assetsDirectory = rootDirectory.appending(path: "Assets") - contentDirectory = rootDirectory.appending(path: "Content") - includesDirectory = rootDirectory.appending(path: "Includes") - buildDirectory = rootDirectory.appending(path: buildDirectoryPath) - - try parseContent() - } - /// Creates a new publishing context for a specific site, providing the path to /// one of the user's file. This then navigates upwards to find the root directory. /// - Parameters: @@ -74,11 +55,13 @@ public class PublishingContext { init(for site: any Site, from file: StaticString, buildDirectoryPath: String = "Build") throws { self.site = site - rootDirectory = try URL.selectSiteRootDirectory(from: file) - assetsDirectory = rootDirectory.appending(path: "Assets") - contentDirectory = rootDirectory.appending(path: "Content") - includesDirectory = rootDirectory.appending(path: "Includes") - buildDirectory = rootDirectory.appending(path: buildDirectoryPath) + let sourceBuildDirectories = try URL.selectDirectories(from: file) + sourceDirectory = sourceBuildDirectories.source + buildDirectory = sourceBuildDirectories.build.appending(path: buildDirectoryPath) + + assetsDirectory = sourceDirectory.appending(path: "Assets") + contentDirectory = sourceDirectory.appending(path: "Content") + includesDirectory = sourceDirectory.appending(path: "Includes") try parseContent() } @@ -179,16 +162,21 @@ public class PublishingContext { /// and CSS, icons CSS and fonts if enabled, and syntax highlighters /// if enabled. func copyResources() throws { - let assets = try FileManager.default.contentsOfDirectory( - at: assetsDirectory, - includingPropertiesForKeys: nil - ) - - for asset in assets { - try FileManager.default.copyItem( - at: assetsDirectory.appending(path: asset.lastPathComponent), - to: buildDirectory.appending(path: asset.lastPathComponent) + do { + let assets = try FileManager.default.contentsOfDirectory( + at: assetsDirectory, + includingPropertiesForKeys: nil ) + + for asset in assets { + try FileManager.default.copyItem( + at: assetsDirectory.appending(path: asset.lastPathComponent), + to: buildDirectory.appending(path: asset.lastPathComponent) + ) + } + } catch { + print("Could not copy assets from \(assetsDirectory) to \(buildDirectory): \(error).") + throw error } if site.useDefaultBootstrapURLs == .localBootstrap {