Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ enum AssetUtil {

let (structuredThemeStore, assetKeys) = initializeCatalog(from: file)

var images: [String: CGImage] = [:]
var images: [String: (cgImage: CGImage, format: String)] = [:]

for key in assetKeys {
let keyList = unsafeBitCast(
Expand Down Expand Up @@ -129,7 +129,13 @@ enum AssetUtil {
let (width, height, unslicedImage) = resolveImageDimensions(rendition, isVector)
let assetType = determineAssetType(key)
let imageId = UUID().uuidString
images[imageId] = unslicedImage

let fileExtension = (renditionTypeName as NSString).pathExtension.lowercased()

// Skip files without an extension or SVGs
if !fileExtension.isEmpty && fileExtension != "svg", let unslicedImage = unslicedImage {
images[imageId] = (cgImage: unslicedImage, format: fileExtension)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: JSON References Missing Image Files

The code assigns an imageId to every AssetCatalogEntry, but only saves image files to disk for non-SVG assets with a valid CGImage. This creates a mismatch where the JSON output can reference imageIds that don't have corresponding files, potentially breaking downstream consumers.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't do any processing with the SVGs so there's no point in saving them into the ParsedAssets folder. We do however want to know about them so we intentionally still include them in the assets.json file


let asset = AssetCatalogEntry(
imageId: imageId,
Expand Down Expand Up @@ -161,17 +167,17 @@ enum AssetUtil {
.appendingPathComponent("Assets")
.appendingPathExtension("json")
try! data.write(to: url, options: [])
for (id, cgImage) in images {
let fileURL = folder.appendingPathComponent(id)
.appendingPathExtension("png")

guard let dest = CGImageDestinationCreateWithURL(
fileURL as CFURL,
UTType.png.identifier as CFString,
1,
nil
)
else {
for (id, imageInfo) in images {
let format = imageInfo.format
let cgImage = imageInfo.cgImage
let fileURL = folder.appendingPathComponent(id).appendingPathExtension(format)

guard let utType = utTypeForExtension(format) else {
print("⚠️ Unsupported format '\(format)' for \(id), skipping")
continue
}

guard let dest = CGImageDestinationCreateWithURL(fileURL as CFURL, utType as CFString, 1, nil) else {
print("⚠️ Could not create destination for \(fileURL.path)")
continue
}
Expand Down Expand Up @@ -293,6 +299,33 @@ enum AssetUtil {

return (width, height, unslicedImage)
}

/// Maps a file extension to its corresponding UTType identifier
/// Returns nil for unknown or unsupported formats
private static func utTypeForExtension(_ ext: String) -> String? {
switch ext {
case "jpg", "jpeg":
return UTType.jpeg.identifier
case "png":
return UTType.png.identifier
case "heic", "heif":
return UTType.heic.identifier
case "gif":
return UTType.gif.identifier
case "webp":
return UTType.webP.identifier
case "pdf":
return UTType.pdf.identifier
case "svg":
return UTType.svg.identifier
case "tiff", "tif":
return UTType.tiff.identifier
case "bmp":
return UTType.bmp.identifier
default:
return nil
}
}
}

private extension NSObject {
Expand Down