diff --git a/Sources/FigmaGenerator/Extensions.swift b/Sources/FigmaGenerator/Extensions.swift index f9311c9..46dc531 100644 --- a/Sources/FigmaGenerator/Extensions.swift +++ b/Sources/FigmaGenerator/Extensions.swift @@ -50,6 +50,11 @@ extension File { let components = Array(pair.1.dropFirst()) return (pair.0, components) } + + func findAllComponents(componentID: String) -> [(Node, [NameComponent])] { + return document.findAllWith(componentID: componentID, path: []) + .map { ($0.0, Array($0.1.dropFirst())) } + } } extension Style { @@ -89,6 +94,18 @@ extension Node { return nil } } + + func findAllWith(componentID id: String, path: [String]) -> [(Node, [NameComponent])] { + let currentPath = path + [name] + var results: [(Node, [NameComponent])] = [] + if componentId == id { + results.append((self, currentPath)) + } + for ch in children ?? [] { + results.append(contentsOf: ch.findAllWith(componentID: id, path: currentPath)) + } + return results + } } extension Paint { diff --git a/Sources/FigmaGenerator/IconDownloader.swift b/Sources/FigmaGenerator/IconDownloader.swift index 4411be7..d62d1d0 100644 --- a/Sources/FigmaGenerator/IconDownloader.swift +++ b/Sources/FigmaGenerator/IconDownloader.swift @@ -84,6 +84,42 @@ final class IconDownloader { } } + func downloadSpecialPDFs(output: URL) throws { + for (key, _) in file.components { + for pair in file.findAllComponents(componentID: key) { + guard pair.0.name.contains("/") == false else { + print("Skip component with id: \(pair.0.id) ... invalid name: \(pair.0.name)") + continue + } + + let fullName = pair.1.joined(separator: "/") + + guard docPaths?.contains(where: { fullName.hasPrefix($0) }) ?? true else { + continue + } + + let assetRelatedPath = (dropCanvaName ? pair.1.dropFirst().joined(separator: "/") : fullName) + ".imageset" + + let assetURL = output.appendingPathComponent(assetRelatedPath) + guard FileManager.default.fileExists(atPath: assetURL.path, isDirectory: nil) == false else { + continue + } + + missedAssets[assetURL] = pair.0 + } + } + + let ids = missedAssets.map { $0.value.id } + guard ids.isEmpty == false else { return } + + let info = try getDownloadInfo(ids: ids, format: "pdf") + try downloadMissedPDFs(links: info) + + try Set(missedAssets.map { $0.key.deletingLastPathComponent() }).forEach { + try placeContentJSON(folder: $0, topLevel: output) + } + } + private func placeContentJSON(folder: URL, topLevel: URL) throws { guard topLevel.path != folder.path else { return } diff --git a/Sources/FigmaGenerator/main.swift b/Sources/FigmaGenerator/main.swift index ddfe8f4..e237e0d 100644 --- a/Sources/FigmaGenerator/main.swift +++ b/Sources/FigmaGenerator/main.swift @@ -31,6 +31,9 @@ struct FigmaGenerator: ParsableCommand { @Option(name: .customLong("ios-icons-output"), parsing: .next, help: "iOS Icon Output Folder (.xcassets)") var ios_icons_output_folder: String? + + @Option(name: .customLong("ios-icons-special-output"), parsing: .next, help: "iOS Special Icon Output Folder (.xcassets), only ✅ For Developers canvas") + var ios_icons_special_output_folder: String? @Option(name: .customLong("ios-typo-output"), parsing: .next, help: "iOS Fonts Output File") var ios_typo_output: String? @@ -98,6 +101,16 @@ struct FigmaGenerator: ParsableCommand { downloaded.useAbsoluteBounds = use_absolute_bounds try downloaded.downloadPDFs(output: output) } + + if let folder = ios_icons_special_output_folder { + let output = folder.absoluteFileURL(baseURL: homeDir) + let downloaded = IconDownloader(file: file, fileKey: figma_file_key, accessToken: personal_access_tokens) + downloaded.docPaths = doc_paths + downloaded.dropCanvaName = drop_canva_name + downloaded.iconsAsTemplate = icons_as_template + downloaded.useAbsoluteBounds = use_absolute_bounds + try downloaded.downloadSpecialPDFs(output: output) + } } }