fluentui-apple/scripts/removeUnusedResourcesFromAs...

115 строки
4.9 KiB
Swift

import Foundation
if CommandLine.arguments.count != 3 {
print("usage: swift removeUnusedResourcesFromAssets.swift <xcassets_file_path> <project_root_path>")
} else {
let xcassetsPath = CommandLine.arguments[1]
let rootPath = CommandLine.arguments[2]
let usedResources = findUsedResources(in: rootPath)
removeResources(from: xcassetsPath,
notContainedIn: usedResources)
}
/// Builds a set of resource entries relative to the root of an .xcassets folder based on contents of the .xcfilelist files in the project.
/// - Parameter rootPath: Root path of the project. A search for .resources.xcfilelist files will be performed to build the resource entry list.
/// - Returns: A set containing the combination of all resource entries in the .resources.xcfilelist found in the project.
func findUsedResources(in rootPath: String) -> Set<String> {
let rootURL = URL(fileURLWithPath: rootPath)
let resourceFileSuffix = ".resources.xcfilelist"
var usedResources: Set<String> = []
#if VERBOSE_OUTPUT
print("Parsing *\(resourceFileSuffix) files in path \(rootURL)")
#endif
if let filesEnumerator = FileManager.default.enumerator(at: rootURL,
includingPropertiesForKeys: [.isRegularFileKey],
options: [.skipsHiddenFiles, .skipsPackageDescendants]) {
for case let fileURL as URL in filesEnumerator {
do {
let fileAttributes = try fileURL.resourceValues(forKeys: [.isRegularFileKey])
let filePath = fileURL.relativePath
if fileAttributes.isRegularFile! &&
filePath.hasSuffix(resourceFileSuffix) {
#if VERBOSE_OUTPUT
print("\nUsed resources in file: \(filePath)")
#endif
do {
let resourceFileListContents = try String(contentsOf: fileURL)
for entry in resourceFileListContents.split(separator: "\n") {
let resourceFileEntry = entry.trimmingCharacters(in: .whitespacesAndNewlines)
usedResources.insert(resourceFileEntry)
#if VERBOSE_OUTPUT
print("- \(resourceFileEntry)")
#endif
}
} catch {
preconditionFailure("Failed to read contents resource file: \(filePath) \nError: \(error)")
}
}
} catch {
preconditionFailure("Failed to retrieve resource file: \(fileURL) \nError: \(error)")
}
}
}
return usedResources
}
/// Iterates through all folders in a given .xcassets file and removes the .colorset or .imageset folders that are not contained in the used resources set.
/// - Parameters:
/// - xcassetsPath: Root path of the .xcassets file.
/// - usedResourcesSet: Set containing the list of paths (relative to the .xcassets root) of resources that should not be removed.
func removeResources(from xcassetsPath: String, notContainedIn usedResourcesSet: Set<String>) {
let resourceExtensions = ["colorset", "imageset"]
let xcassetsURL = URL(fileURLWithPath: xcassetsPath)
let xcassetsRelativePath = xcassetsURL.relativePath
#if VERBOSE_OUTPUT
print("\n\nProcessing resources of xcassets in path: \(xcassetsPath)")
#endif
if let directoriesEnumerator = FileManager.default.enumerator(at: xcassetsURL,
includingPropertiesForKeys: [.isDirectoryKey],
options: [.skipsHiddenFiles, .skipsPackageDescendants]) {
for case let directoryURL as URL in directoriesEnumerator {
do {
let directoryAttributes = try directoryURL.resourceValues(forKeys: [.isDirectoryKey])
let dirExtension = directoryURL.pathExtension
if directoryAttributes.isDirectory! && resourceExtensions.contains(dirExtension) {
let directoryEntry = directoryURL.relativePath.withoutPrefix(xcassetsRelativePath).withoutPrefix("/")
let shouldBeKept = usedResourcesSet.contains(directoryEntry)
if !shouldBeKept {
try FileManager.default.removeItem(at: directoryURL)
}
#if VERBOSE_OUTPUT
print(" - \(directoryEntry) (\(shouldBeKept ? "kept" : "removed"))")
#endif
}
} catch {
print(error, directoryURL)
}
}
}
}
extension String {
/// Removes a given prefix from a string.
/// - Parameter prefix: The prefix to be removed.
/// - Returns: The resulting string without the prefix.
func withoutPrefix(_ prefix: String) -> String {
guard self.hasPrefix(prefix) else {
return self
}
return String(self.dropFirst(prefix.count))
}
}