Improve relative paths, check missing files
This commit is contained in:
@@ -5,7 +5,6 @@ import AppKit
|
||||
typealias SourceFile = (data: Data, didChange: Bool)
|
||||
typealias SourceTextFile = (content: String, didChange: Bool)
|
||||
|
||||
#warning("Skip external files")
|
||||
final class FileSystem {
|
||||
|
||||
private static let hashesFileName = "hashes.json"
|
||||
@@ -39,6 +38,20 @@ final class FileSystem {
|
||||
*/
|
||||
private var requiredFiles: Set<String> = []
|
||||
|
||||
/**
|
||||
The files marked as external in element metadata.
|
||||
|
||||
Files included here are not generated, since they are assumed to be added separately.
|
||||
*/
|
||||
private var externalFiles: Set<String> = []
|
||||
|
||||
/**
|
||||
The files marked as expected, i.e. they exist after the generation is completed.
|
||||
|
||||
The key of the dictionary is the file path, the value is the file providing the link
|
||||
*/
|
||||
private var expectedFiles: [String : String] = [:]
|
||||
|
||||
/**
|
||||
The image creation tasks.
|
||||
|
||||
@@ -227,7 +240,6 @@ final class FileSystem {
|
||||
return scaledSize
|
||||
}
|
||||
|
||||
#warning("Implement image functions")
|
||||
func createImages() {
|
||||
for (destination, image) in imageTasks.sorted(by: { $0.key < $1.key }) {
|
||||
createImageIfNeeded(image, for: destination)
|
||||
@@ -322,12 +334,34 @@ final class FileSystem {
|
||||
requiredFiles.insert(file)
|
||||
}
|
||||
|
||||
/**
|
||||
Mark a file as explicitly missing.
|
||||
|
||||
This is done for the `externalFiles` entries in metadata,
|
||||
to indicate that these files will be copied to the output folder manually.
|
||||
*/
|
||||
func exclude(file: String) {
|
||||
externalFiles.insert(file)
|
||||
}
|
||||
|
||||
/**
|
||||
Mark a file as expected to be present in the output folder after generation.
|
||||
|
||||
This is done for all links between pages, which only exist after the pages have been generated.
|
||||
*/
|
||||
func expect(file: String, source: String) {
|
||||
expectedFiles[file] = source
|
||||
}
|
||||
|
||||
func copyRequiredFiles() {
|
||||
var missingFiles = [String]()
|
||||
for file in requiredFiles {
|
||||
let sourceUrl = input.appendingPathComponent(file)
|
||||
let cleanPath = cleanRelativeURL(file)
|
||||
let sourceUrl = input.appendingPathComponent(cleanPath)
|
||||
let destinationUrl = output.appendingPathComponent(cleanPath)
|
||||
guard sourceUrl.exists else {
|
||||
missingFiles.append(file)
|
||||
if !externalFiles.contains(file) {
|
||||
log.add(error: "Missing required file", source: cleanPath)
|
||||
}
|
||||
continue
|
||||
}
|
||||
let data: Data
|
||||
@@ -337,15 +371,40 @@ final class FileSystem {
|
||||
log.add(error: "Failed to read data at \(sourceUrl.path)", source: source, error: error)
|
||||
continue
|
||||
}
|
||||
let destinationUrl = output.appendingPathComponent(file)
|
||||
write(data, to: destinationUrl)
|
||||
writeIfChanged(data, to: destinationUrl)
|
||||
}
|
||||
for (file, source) in expectedFiles {
|
||||
guard !externalFiles.contains(file) else {
|
||||
continue
|
||||
}
|
||||
let cleanPath = cleanRelativeURL(file)
|
||||
let destinationUrl = output.appendingPathComponent(cleanPath)
|
||||
if !destinationUrl.exists {
|
||||
log.add(error: "Missing \(cleanPath)", source: source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func cleanRelativeURL(_ raw: String) -> String {
|
||||
let raw = raw.dropAfterLast("#") // Clean links to page content
|
||||
guard raw.contains("..") else {
|
||||
return raw
|
||||
}
|
||||
var result: [String] = []
|
||||
for component in raw.components(separatedBy: "/") {
|
||||
if component == ".." {
|
||||
_ = result.popLast()
|
||||
} else {
|
||||
result.append(component)
|
||||
}
|
||||
}
|
||||
return result.joined(separator: "/")
|
||||
}
|
||||
|
||||
// MARK: Writing files
|
||||
|
||||
@discardableResult
|
||||
func write(_ data: Data, to url: URL) -> Bool {
|
||||
func writeIfChanged(_ data: Data, to url: URL) -> Bool {
|
||||
// Only write changed files
|
||||
if url.exists, let oldContent = try? Data(contentsOf: url), data == oldContent {
|
||||
return false
|
||||
@@ -362,7 +421,7 @@ final class FileSystem {
|
||||
@discardableResult
|
||||
func write(_ string: String, to url: URL) -> Bool {
|
||||
let data = string.data(using: .utf8)!
|
||||
return write(data, to: url)
|
||||
return writeIfChanged(data, to: url)
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user