Skip to content

Commit 163a3ab

Browse files
committed
Update image parser
1 parent 1f8d8f7 commit 163a3ab

3 files changed

Lines changed: 66 additions & 11 deletions

File tree

Sources/FigmaAPI/Endpoint/ComponentsEndpoint.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ public struct Component: Codable {
4242
public let name: String
4343
public let description: String?
4444
public let containingFrame: ContainingFrame
45+
46+
public init(key: String, nodeId: String, name: String, description: String?, containingFrame: ContainingFrame) {
47+
self.key = key
48+
self.nodeId = nodeId
49+
self.name = name
50+
self.description = description
51+
self.containingFrame = containingFrame
52+
}
4553
}
4654

4755
// MARK: - ContainingFrame

Sources/FigmaAPI/Model/FigmaClientError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct FigmaClientError: Decodable, LocalizedError {
77
var errorDescription: String? {
88
switch err {
99
case "Not found":
10-
return "Figma file not found. Check lightFileId and darkFileId (if you project supports dark mode) in the yaml config file."
10+
return "Figma file not found. Check lightFileId and darkFileId (if your project supports dark mode) in the yaml config file. Also verify that your personal access token is valid and hasn't expired."
1111
default:
1212
return "Figma API: \(err)"
1313
}

Sources/FigmaExport/Loaders/ImagesLoader.swift

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -202,19 +202,62 @@ final class ImagesLoader {
202202
// MARK: - Helpers
203203

204204
private func fetchImageComponents(fileId: String, frameName: String, filter: String? = nil) throws -> [NodeId: Component] {
205-
var components = try loadComponents(fileId: fileId)
206-
.filter {
207-
$0.containingFrame.name == frameName && $0.useForPlatform(platform)
208-
}
205+
let allComponents = try loadComponents(fileId: fileId)
206+
207+
// 1. Initial filter based on your Frame Regex and Page name
208+
let filtered = allComponents.filter { component in
209+
guard let name = component.containingFrame.name else { return false }
210+
let isFrameMatch = name.range(of: frameName, options: [.regularExpression, .caseInsensitive]) != nil
211+
let isPageMatch = component.containingFrame.pageName.contains("Icons")
212+
return isFrameMatch && isPageMatch
213+
}
209214

210-
if let filter {
211-
let assetsFilter = AssetsFilter(filter: filter)
212-
components = components.filter { component -> Bool in
213-
assetsFilter.match(name: component.name)
215+
// 2. Prepare for deduplication using the final name as the key
216+
var finalComponentsByName: [String: Component] = [:]
217+
218+
for component in filtered {
219+
guard let frameName = component.containingFrame.name, frameName.contains(" / ") else { continue }
220+
221+
let frameBaseName = frameName.components(separatedBy: " / ").last?
222+
.trimmingCharacters(in: .whitespaces) ?? ""
223+
224+
let variantValue = component.name.components(separatedBy: "=").last?
225+
.trimmingCharacters(in: .whitespaces) ?? ""
226+
227+
let finalName = (frameBaseName + variantValue.capitalized)
228+
.replacingOccurrences(of: " ", with: "")
229+
230+
let newComponent = Component(
231+
key: component.key,
232+
nodeId: component.nodeId,
233+
name: finalName,
234+
description: component.description,
235+
containingFrame: component.containingFrame
236+
)
237+
238+
// 3. Deduplication: Only store one version for each finalName
239+
if let existing = finalComponentsByName[finalName] {
240+
// Priority: Keep the iOS version if we find multiple
241+
if component.name.lowercased().contains("platform=ios") {
242+
finalComponentsByName[finalName] = newComponent
243+
}
244+
} else {
245+
finalComponentsByName[finalName] = newComponent
214246
}
215247
}
216248

217-
return Dictionary(uniqueKeysWithValues: components.map { ($0.nodeId, $0) })
249+
guard !finalComponentsByName.isEmpty else {
250+
throw FigmaExportError.componentsNotFound
251+
}
252+
253+
// 4. IMPORTANT: Map back using the ORIGINAL nodeId as the key
254+
// This ensures the API call 'GET /v1/images/' uses valid IDs
255+
var result: [NodeId: Component] = [:]
256+
for (_, component) in finalComponentsByName {
257+
result[component.nodeId] = component
258+
}
259+
260+
return result
218261
}
219262

220263
private func _loadImages(
@@ -266,7 +309,11 @@ final class ImagesLoader {
266309
let isRTL = component.useRTL()
267310
return Image(name: name, scale: .all, idiom: idiom, url: url, format: params.format, isRTL: isRTL)
268311
}
269-
return ImagePack(name: packName, images: packImages, platform: platform)
312+
var imagePack = ImagePack(name: packName, images: packImages, platform: platform)
313+
if let containingFrameName = components.first?.value.containingFrame.name, containingFrameName.hasPrefix("ic / img_") {
314+
imagePack.renderMode = .original
315+
}
316+
return imagePack
270317
}
271318
return imagePacks
272319
}

0 commit comments

Comments
 (0)