Check for unused files in output folder
This commit is contained in:
parent
2cad27b504
commit
6b6db702f1
@ -47,7 +47,9 @@ struct ImageVersion {
|
||||
}
|
||||
|
||||
func wasNowGenerated() {
|
||||
image.generatedImageVersions.insert(versionId)
|
||||
DispatchQueue.main.async {
|
||||
image.generatedImageVersions.insert(versionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import Foundation
|
||||
|
||||
final class PageGenerator {
|
||||
|
||||
private let content: Content
|
||||
@ -50,7 +52,8 @@ final class PageGenerator {
|
||||
url: tag.absoluteUrl(in: language))
|
||||
}
|
||||
|
||||
let headers = makeHeaders(requiredItems: results.requiredHeaders, results: results)
|
||||
let requiredHeaders = DispatchQueue.main.sync { results.requiredHeaders }
|
||||
let headers = makeHeaders(requiredItems: requiredHeaders, results: results)
|
||||
results.require(files: headers.compactMap { $0.requiredFile })
|
||||
|
||||
let iconUrl = content.settings.navigation.localized(in: language).rootUrl
|
||||
@ -60,6 +63,7 @@ final class PageGenerator {
|
||||
url: languageUrl)
|
||||
|
||||
let imageUrl = localized.linkPreview.image?.linkPreviewImage(results: results)
|
||||
let icons = DispatchQueue.main.sync { results.requiredIcons }
|
||||
|
||||
let pageHeader = PageHeader(
|
||||
language: language,
|
||||
@ -71,11 +75,13 @@ final class PageGenerator {
|
||||
languageButton: languageButton,
|
||||
links: content.navigationBar(in: language),
|
||||
headers: headers,
|
||||
icons: results.requiredIcons)
|
||||
icons: icons)
|
||||
|
||||
let footers = DispatchQueue.main.sync { results.requiredFooters }
|
||||
|
||||
let fullPage = GenericPage(
|
||||
header: pageHeader,
|
||||
additionalFooter: results.requiredFooters.sorted().joined()) { content in
|
||||
additionalFooter: footers.sorted().joined()) { content in
|
||||
content += "<article>"
|
||||
if !localized.hideTitle {
|
||||
if !page.hideDate {
|
||||
|
@ -46,6 +46,12 @@ final class GenerationResults: ObservableObject {
|
||||
@Published
|
||||
var emptyPages: Set<LocalizedPageId> = []
|
||||
|
||||
@Published
|
||||
var outputFiles: Set<String> = []
|
||||
|
||||
@Published
|
||||
var unusedFilesInOutput: Set<String> = []
|
||||
|
||||
/**
|
||||
The url redirects to install to prevent broken links.
|
||||
|
||||
@ -114,6 +120,8 @@ final class GenerationResults: ObservableObject {
|
||||
self.unsavedOutputFiles = []
|
||||
self.emptyPages = []
|
||||
self.redirects = [:]
|
||||
self.outputFiles = []
|
||||
self.unusedFilesInOutput = []
|
||||
}
|
||||
for result in cache.values {
|
||||
result.reset()
|
||||
@ -229,6 +237,15 @@ final class GenerationResults: ObservableObject {
|
||||
func redirect(from originalUrl: String, to newUrl: String) {
|
||||
update { self.redirects[originalUrl] = newUrl }
|
||||
}
|
||||
|
||||
func created(outputFile: String) {
|
||||
update { self.outputFiles.insert(outputFile.withLeadingSlashRemoved) }
|
||||
}
|
||||
|
||||
func determineFiles(unusedIn existingFiles: Set<String>) {
|
||||
let unused = existingFiles.subtracting(outputFiles)
|
||||
update { self.unusedFilesInOutput = unused }
|
||||
}
|
||||
}
|
||||
|
||||
private extension Dictionary where Value == Set<LocalizedItemId> {
|
||||
|
@ -18,7 +18,6 @@ import SFSafeSymbols
|
||||
- Graphs, Map, GPX for hikes
|
||||
|
||||
**Generation**
|
||||
- Consistency: Check output folder for unused files
|
||||
- Empty properties: Show warnings for empty link previews, etc.
|
||||
|
||||
**Fixes**
|
||||
|
@ -5,6 +5,9 @@ extension Content {
|
||||
func generateWebsiteInAllLanguages() {
|
||||
performGenerationIfIdle {
|
||||
self.results.reset()
|
||||
self.storage.writeNotification = { [weak self] in
|
||||
self?.results.created(outputFile: $0)
|
||||
}
|
||||
self.generatePagesInternal()
|
||||
self.generatePostFeedPagesInternal()
|
||||
self.generateTagPagesInternal()
|
||||
@ -15,6 +18,7 @@ extension Content {
|
||||
self.results.recalculate()
|
||||
self.generateListOfExternalFiles()
|
||||
self.generateListOfUrlMappings()
|
||||
self.updateUnusedFiles()
|
||||
self.status("Generation completed")
|
||||
}
|
||||
}
|
||||
@ -77,6 +81,7 @@ extension Content {
|
||||
status("Generating required images: \(completed) / \(count)")
|
||||
}
|
||||
if imageGenerator.generate(version: image) {
|
||||
results.created(outputFile: image.outputPath)
|
||||
continue
|
||||
}
|
||||
results.failed(image: image)
|
||||
@ -343,4 +348,11 @@ extension Content {
|
||||
|
||||
storage.write(content, to: redirectsListFileName)
|
||||
}
|
||||
|
||||
private func updateUnusedFiles() {
|
||||
let existing = storage.getAllOutputFiles()
|
||||
DispatchQueue.main.async {
|
||||
self.results.determineFiles(unusedIn: existing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,8 +59,6 @@ final class RemotePush: ObservableObject {
|
||||
|
||||
process.arguments = ["-c", argument]
|
||||
|
||||
print(argument)
|
||||
|
||||
let pipe = Pipe()
|
||||
process.standardOutput = pipe
|
||||
process.standardError = pipe
|
||||
|
@ -388,6 +388,36 @@ struct SecurityBookmark {
|
||||
return operation(url)
|
||||
}
|
||||
|
||||
func getAllFiles() -> Set<String> {
|
||||
guard url.startAccessingSecurityScopedResource() else {
|
||||
reportError("Failed to start security scope")
|
||||
return []
|
||||
}
|
||||
guard let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]) else {
|
||||
reportError("Failed to get folder enumerator")
|
||||
return []
|
||||
}
|
||||
|
||||
var relativePaths = Set<String>()
|
||||
let prefix = url.path().withTrailingSlash
|
||||
|
||||
for case let fileURL as URL in enumerator {
|
||||
guard !fileURL.hasDirectoryPath else {
|
||||
continue
|
||||
}
|
||||
let fullPath = fileURL.path()
|
||||
guard fullPath.hasPrefix(prefix) else {
|
||||
print("Expected prefix \(prefix) for \(fullPath)")
|
||||
return []
|
||||
}
|
||||
let relativePath = fullPath.replacingOccurrences(of: prefix, with: "")
|
||||
relativePaths.insert(relativePath)
|
||||
}
|
||||
|
||||
url.stopAccessingSecurityScopedResource()
|
||||
return relativePaths
|
||||
}
|
||||
|
||||
// MARK: Unscoped helpers
|
||||
|
||||
private func create(folder: URL) -> Bool {
|
||||
|
@ -45,6 +45,8 @@ final class Storage: ObservableObject {
|
||||
|
||||
var errorNotification: StorageErrorCallback?
|
||||
|
||||
var writeNotification: ((String) -> Void)?
|
||||
|
||||
/**
|
||||
Create the storage.
|
||||
*/
|
||||
@ -319,6 +321,7 @@ final class Storage: ObservableObject {
|
||||
*/
|
||||
func copy(file fileId: String, to relativeOutputPath: String) -> Bool {
|
||||
guard let contentScope, let outputScope else { return false }
|
||||
didWrite(outputFile: relativeOutputPath)
|
||||
return contentScope.transfer(
|
||||
file: filePath(file: fileId),
|
||||
to: relativeOutputPath, of: outputScope)
|
||||
@ -460,6 +463,7 @@ final class Storage: ObservableObject {
|
||||
@discardableResult
|
||||
func write(_ content: String, to relativeOutputPath: String) -> Bool {
|
||||
guard let outputScope else { return false }
|
||||
didWrite(outputFile: relativeOutputPath)
|
||||
return outputScope.write(content, to: relativeOutputPath)
|
||||
}
|
||||
|
||||
@ -468,6 +472,7 @@ final class Storage: ObservableObject {
|
||||
*/
|
||||
func write(_ data: Data, to relativeOutputPath: String) -> Bool {
|
||||
guard let outputScope else { return false }
|
||||
didWrite(outputFile: relativeOutputPath)
|
||||
return outputScope.write(data, to: relativeOutputPath)
|
||||
}
|
||||
|
||||
@ -584,4 +589,15 @@ final class Storage: ObservableObject {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: Output notifications
|
||||
|
||||
func didWrite(outputFile: String) {
|
||||
writeNotification?(outputFile)
|
||||
}
|
||||
|
||||
func getAllOutputFiles() -> Set<String> {
|
||||
guard let outputScope else { return [] }
|
||||
return outputScope.getAllFiles()
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,14 @@ struct GenerationContentView: View {
|
||||
GenerationStringIssuesView(
|
||||
text: "invalid blocks",
|
||||
items: $content.results.invalidBlocks)
|
||||
GenerationStringIssuesView(
|
||||
text: "output files",
|
||||
statusWhenNonEmpty: .nominal,
|
||||
items: $content.results.outputFiles)
|
||||
GenerationStringIssuesView(
|
||||
text: "additional output files",
|
||||
statusWhenNonEmpty: .warning,
|
||||
items: $content.results.unusedFilesInOutput)
|
||||
GenerationStringIssuesView(
|
||||
text: "warnings",
|
||||
statusWhenNonEmpty: .warning,
|
||||
|
Loading…
x
Reference in New Issue
Block a user