From e22f9ba894ee1129e59666fb6cea3354a3ebc655 Mon Sep 17 00:00:00 2001 From: Thibaut Schmitt Date: Thu, 17 Jul 2025 10:23:05 +0200 Subject: [PATCH] Optional generation of Images extension. Make extensionOutputPath optional for Images and Colors cause it's not needed if no extensionName are provided --- README.md | 8 ++- .../Generated/UIColorYolo+GenAllScript.swift | 2 +- .../colors.xcassets/Colors/Contents.json | 6 -- Sources/ResgenSwift/Colors/Colors.swift | 11 +++ .../ResgenSwift/Colors/ColorsToolError.swift | 4 ++ .../Colors/ColorsToolOptions.swift | 10 +-- .../Generate/Model/ConfigurationFile.swift | 12 ++-- .../ColorsConfiguration+Runnable.swift | 32 ++++----- .../ImagesConfiguration+Runnable.swift | 34 ++++----- Sources/ResgenSwift/Images/Images.swift | 72 +++++++++++++------ Sources/ResgenSwift/Images/ImagesError.swift | 4 ++ .../ResgenSwift/Images/ImagesOptions.swift | 34 +++++---- 12 files changed, 132 insertions(+), 97 deletions(-) delete mode 100644 SampleFiles/Colors/colors.xcassets/Colors/Contents.json diff --git a/README.md b/README.md index 311ddc5..8951a82 100644 --- a/README.md +++ b/README.md @@ -54,12 +54,13 @@ swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \ 1. `-f`: force generation 2. Input colors file 3. `--style` can be `all` or `light` -4. `--extension-output-path`: path where to generate generated extension +4. `--extension-output-path` *(optional)* : path where to generate generated extension 5. `--extension-name` *(optional)* : name of the class to add SwiftUI getters 6. `--extension-name-ui-kit` *(optional)* : name of the class to add UIKit getters 7. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppColor+GreatApp.swift`) 8. `--static-members` *(optional)*: generate static properties or not +> ⚠️ Passing a `extensionOutputPath` without any `extensionName` does not generate extension. **But** passing an `extensionName` without `extensionOutputPath` will result in a error. ## Strings @@ -266,7 +267,7 @@ events: ## Images -Images generator will generate images assets along with extensions to access those images easily. +Images generator will generate images assets along with extensions to access those images easily. If the extension name is not specified, no extension will be generated. ```sh swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \ @@ -283,12 +284,13 @@ swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \ 1. `-f`: force generation 2. Input images definitions file 3. `--xcassets-path`: xcasset path where to generate imagesets -4. `--extension-output-path`: path where to generate generated extension +4. `--extension-output-path` *(optional)* : path where to generate generated extension 5. `--extension-name` *(optional)* : name of the class to add SwiftUI getters 6. `--extension-name-ui-kit` *(optional)* : name of the class to add UIKit getters 6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`) 7. `--static-members` *(optional)*: generate static properties or not +> ⚠️ Passing a `extensionOutputPath` without any `extensionName` does not generate extension. **But** passing an `extensionName` without `extensionOutputPath` will result in a error. > ⚠️ Svg images will be copied in the assets and rendered as "Original", however if those images are not rendered correctly you can force the png generation by adding the key word "png" like this: id arrow_back 15 ? png ## All at once diff --git a/SampleFiles/Colors/Generated/UIColorYolo+GenAllScript.swift b/SampleFiles/Colors/Generated/UIColorYolo+GenAllScript.swift index d2c7564..28b639c 100644 --- a/SampleFiles/Colors/Generated/UIColorYolo+GenAllScript.swift +++ b/SampleFiles/Colors/Generated/UIColorYolo+GenAllScript.swift @@ -1,4 +1,4 @@ -// Generated by ResgenSwift.Color 1.2 +// Generated by ResgenSwift.Color 2.1.0 import UIKit diff --git a/SampleFiles/Colors/colors.xcassets/Colors/Contents.json b/SampleFiles/Colors/colors.xcassets/Colors/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/SampleFiles/Colors/colors.xcassets/Colors/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Sources/ResgenSwift/Colors/Colors.swift b/Sources/ResgenSwift/Colors/Colors.swift index 89ee25b..f8ba0cf 100644 --- a/Sources/ResgenSwift/Colors/Colors.swift +++ b/Sources/ResgenSwift/Colors/Colors.swift @@ -110,6 +110,17 @@ struct Colors: ParsableCommand { } } + // If an extension need to be generated, ensure extensionOutputPath is defined + if options.extensionName != nil || + options.extensionNameUIKit != nil { + guard let extensionOutputPath = options.extensionOutputPath, + extensionOutputPath.isEmpty == false else { + let error = ColorsToolError.missingExtensionPath + print(error.description) + Self.exit(withError: error) + } + } + // Check if needed to regenerate let fileToCompareToInput: String = { // If there is no extension file to compare diff --git a/Sources/ResgenSwift/Colors/ColorsToolError.swift b/Sources/ResgenSwift/Colors/ColorsToolError.swift index 2a54abf..207d3f3 100644 --- a/Sources/ResgenSwift/Colors/ColorsToolError.swift +++ b/Sources/ResgenSwift/Colors/ColorsToolError.swift @@ -17,6 +17,7 @@ enum ColorsToolError: Error { case fileNotExists(String) case badColorDefinition(String, String) case deleteExistingColors(String) + case missingExtensionPath var description: String { switch self { @@ -43,6 +44,9 @@ enum ColorsToolError: Error { case .deleteExistingColors(let assetsFolder): return "error: [\(Colors.toolName)] An error occured while deleting colors folder `\(assetsFolder)`" + + case .missingExtensionPath: + return "error: [\(Colors.toolName)] Extension need to be generated but no `extensionOutputPath` is provided" } } } diff --git a/Sources/ResgenSwift/Colors/ColorsToolOptions.swift b/Sources/ResgenSwift/Colors/ColorsToolOptions.swift index 742587c..ceb3b94 100644 --- a/Sources/ResgenSwift/Colors/ColorsToolOptions.swift +++ b/Sources/ResgenSwift/Colors/ColorsToolOptions.swift @@ -24,12 +24,12 @@ struct ColorsToolOptions: ParsableArguments { @Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var xcassetsPath: String - @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) - var extensionOutputPath: String - @Option(help: "Tell if it will generate static properties or not") var staticMembers: Bool = false + @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) + var extensionOutputPath: String? + @Option(help: "Extension name. If not specified, no extension will be generated.") var extensionName: String? @@ -56,7 +56,7 @@ extension ColorsToolOptions { } var extensionFilePath: String? { - guard let extensionFileName else { return nil } + guard let extensionOutputPath, let extensionFileName else { return nil } return "\(extensionOutputPath)/\(extensionFileName)" } @@ -73,7 +73,7 @@ extension ColorsToolOptions { } var extensionFilePathUIKit: String? { - guard let extensionFileNameUIKit else { return nil } + guard let extensionOutputPath, let extensionFileNameUIKit else { return nil } return "\(extensionOutputPath)/\(extensionFileNameUIKit)" } diff --git a/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift b/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift index 5a84654..1fe0a31 100644 --- a/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift +++ b/Sources/ResgenSwift/Generate/Model/ConfigurationFile.swift @@ -131,7 +131,7 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible { let inputFile: String let style: String let xcassetsPath: String - let extensionOutputPath: String + let extensionOutputPath: String? let extensionName: String? let extensionNameUIKit: String? let extensionSuffix: String? @@ -148,7 +148,7 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible { inputFile: String, style: String, xcassetsPath: String, - extensionOutputPath: String, + extensionOutputPath: String?, extensionName: String?, extensionNameUIKit: String?, extensionSuffix: String?, @@ -170,7 +170,7 @@ struct ColorsConfiguration: Codable, CustomDebugStringConvertible { - Input file: \(inputFile) - Style: \(style) - Xcassets path: \(xcassetsPath) - - Extension output path: \(extensionOutputPath) + - Extension output path: \(extensionOutputPath ?? "-") - Extension name: \(extensionName ?? "-") - Extension name UIKit: \(extensionNameUIKit ?? "-") - Extension suffix: \(extensionSuffix ?? "-") @@ -230,7 +230,7 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible { let inputFile: String let xcassetsPath: String - let extensionOutputPath: String + let extensionOutputPath: String? let extensionName: String? let extensionNameUIKit: String? let extensionSuffix: String? @@ -246,7 +246,7 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible { internal init( inputFile: String, xcassetsPath: String, - extensionOutputPath: String, + extensionOutputPath: String?, extensionName: String?, extensionNameUIKit: String?, extensionSuffix: String?, @@ -266,7 +266,7 @@ struct ImagesConfiguration: Codable, CustomDebugStringConvertible { Images configuration: - Input file: \(inputFile) - Xcassets path: \(xcassetsPath) - - Extension output path: \(extensionOutputPath) + - Extension output path: \(extensionOutputPath ?? "-") - Extension name: \(extensionName ?? "-") - Extension name UIKit: \(extensionNameUIKit ?? "-") - Extension suffix: \(extensionSuffix ?? "-") diff --git a/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+Runnable.swift b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+Runnable.swift index 6f8bc11..b360d42 100644 --- a/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+Runnable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ColorsConfiguration+Runnable.swift @@ -27,29 +27,23 @@ extension ColorsConfiguration: Runnable { style, "--xcassets-path", xcassetsPath.prependIfRelativePath(projectDirectory), - "--extension-output-path", - extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] - if let extensionName { - args += [ - "--extension-name", - extensionName - ] - } - if let extensionNameUIKit { - args += [ - "--extension-name-ui-kit", - extensionNameUIKit - ] - } - if let extensionSuffix { - args += [ - "--extension-suffix", - extensionSuffix - ] + // Add optional parameters + [ + ("--extension-output-path", extensionOutputPath?.prependIfRelativePath(projectDirectory)), + ("--extension-name", extensionName), + ("--extension-name-ui-kit", extensionNameUIKit), + ("--extension-suffix", extensionSuffix) + ].forEach { argumentName, argumentValue in + if let argumentValue { + args += [ + argumentName, + argumentValue + ] + } } return args diff --git a/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+Runnable.swift b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+Runnable.swift index 91d9a43..5631859 100644 --- a/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+Runnable.swift +++ b/Sources/ResgenSwift/Generate/Runnable/ImagesConfiguration+Runnable.swift @@ -25,31 +25,23 @@ extension ImagesConfiguration: Runnable { inputFile.prependIfRelativePath(projectDirectory), "--xcassets-path", xcassetsPath.prependIfRelativePath(projectDirectory), - "--extension-output-path", - extensionOutputPath.prependIfRelativePath(projectDirectory), "--static-members", "\(staticMembersOptions)" ] - if let extensionName { - args += [ - "--extension-name", - extensionName - ] - } - - if let extensionNameUIKit { - args += [ - "--extension-name-ui-kit", - extensionNameUIKit - ] - } - - if let extensionSuffix { - args += [ - "--extension-suffix", - extensionSuffix - ] + // Add optional parameters + [ + ("--extension-output-path", extensionOutputPath?.prependIfRelativePath(projectDirectory)), + ("--extension-name", extensionName), + ("--extension-name-ui-kit", extensionNameUIKit), + ("--extension-suffix", extensionSuffix) + ].forEach { argumentName, argumentValue in + if let argumentValue { + args += [ + argumentName, + argumentValue + ] + } } return args diff --git a/Sources/ResgenSwift/Images/Images.swift b/Sources/ResgenSwift/Images/Images.swift index 5458f72..aa2438f 100644 --- a/Sources/ResgenSwift/Images/Images.swift +++ b/Sources/ResgenSwift/Images/Images.swift @@ -21,8 +21,6 @@ struct Images: ParsableCommand { // MARK: - Static static let toolName = "Images" - static let defaultExtensionName = "Image" - static let defaultExtensionNameUIKit = "UIImage" // MARK: - Command Options @@ -55,23 +53,29 @@ struct Images: ParsableCommand { ) // Generate extension - ImageExtensionGenerator.generateExtensionFile( - images: imagesToGenerate, - staticVar: options.staticMembers, - inputFilename: options.inputFilenameWithoutExt, - extensionName: options.extensionName, - extensionFilePath: options.extensionFilePath, - isSwiftUI: true - ) + if let extensionName = options.extensionName, + let extensionFilePath = options.extensionFilePath { + ImageExtensionGenerator.generateExtensionFile( + images: imagesToGenerate, + staticVar: options.staticMembers, + inputFilename: options.inputFilenameWithoutExt, + extensionName: extensionName, + extensionFilePath: extensionFilePath, + isSwiftUI: true + ) + } - ImageExtensionGenerator.generateExtensionFile( - images: imagesToGenerate, - staticVar: options.staticMembers, - inputFilename: options.inputFilenameWithoutExt, - extensionName: options.extensionNameUIKit, - extensionFilePath: options.extensionFilePathUIKit, - isSwiftUI: false - ) + if let extensionNameUIKit = options.extensionNameUIKit, + let extensionFilePathUIKit = options.extensionFilePathUIKit { + ImageExtensionGenerator.generateExtensionFile( + images: imagesToGenerate, + staticVar: options.staticMembers, + inputFilename: options.inputFilenameWithoutExt, + extensionName: extensionNameUIKit, + extensionFilePath: extensionFilePathUIKit, + isSwiftUI: false + ) + } print("[\(Self.toolName)] Images generated") } @@ -96,17 +100,39 @@ struct Images: ParsableCommand { _ = Self.getSvgConverterPath() // Extension for UIKit and SwiftUI should have different name - guard options.extensionName != options.extensionNameUIKit else { - let error = ImagesError.extensionNamesCollision(options.extensionName) - print(error.description) - Self.exit(withError: error) + if let extensionName = options.extensionName, + let extensionNameUIKit = options.extensionNameUIKit { + guard extensionName != extensionNameUIKit else { + let error = ImagesError.extensionNamesCollision(extensionName) + print(error.description) + Self.exit(withError: error) + } + } + + // If an extension need to be generated, ensure extensionOutputPath is defined + if options.extensionName != nil || + options.extensionNameUIKit != nil { + guard let extensionOutputPath = options.extensionOutputPath, + extensionOutputPath.isEmpty == false else { + let error = ImagesError.missingExtensionPath + print(error.description) + Self.exit(withError: error) + } } // Check if needed to regenerate + let fileToCompareToInput: String = { + // If there is no extension file to compare + // Then check the xcassets file instead + if let extensionFilePath = options.extensionFilePath { + return extensionFilePath + } + return options.xcassetsPath + }() guard GeneratorChecker.shouldGenerate( force: options.forceExecution, inputFilePath: options.inputFile, - extensionFilePath: options.extensionFilePath + extensionFilePath: fileToCompareToInput ) else { print("[\(Self.toolName)] Images are already up to date :) ") return false diff --git a/Sources/ResgenSwift/Images/ImagesError.swift b/Sources/ResgenSwift/Images/ImagesError.swift index 85bd5b9..efe42de 100644 --- a/Sources/ResgenSwift/Images/ImagesError.swift +++ b/Sources/ResgenSwift/Images/ImagesError.swift @@ -18,6 +18,7 @@ enum ImagesError: Error { case magickConvertNotFound case writeFile(String, String) case createAssetFolder(String) + case missingExtensionPath case unknown(String) var description: String { @@ -49,6 +50,9 @@ enum ImagesError: Error { case .createAssetFolder(let folder): return "error: [\(Colors.toolName)] An error occured while creating folder `\(folder)`" + case .missingExtensionPath: + return "error: [\(Colors.toolName)] Extension need to be generated but no `extensionOutputPath` is provided" + case .unknown(let errorDescription): return "error: [\(Images.toolName)] Unknown error: \(errorDescription)" } diff --git a/Sources/ResgenSwift/Images/ImagesOptions.swift b/Sources/ResgenSwift/Images/ImagesOptions.swift index 03554ed..8f8cf09 100644 --- a/Sources/ResgenSwift/Images/ImagesOptions.swift +++ b/Sources/ResgenSwift/Images/ImagesOptions.swift @@ -24,17 +24,17 @@ struct ImagesOptions: ParsableArguments { @Option(help: "Xcassets path where to generate images.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) var xcassetsPath: String - @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) - var extensionOutputPath: String - @Option(help: "Tell if it will generate static properties or not") var staticMembers: Bool = false - @Option(help: "Extension name. If not specified, it will generate an Image extension.") - var extensionName: String = Images.defaultExtensionName + @Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() }) + var extensionOutputPath: String? - @Option(help: "Extension name. If not specified, it will generate an UIImage extension.") - var extensionNameUIKit: String = Images.defaultExtensionNameUIKit + @Option(help: "Extension name. If not specified, no extension will be generated.") + var extensionName: String? + + @Option(help: "Extension name. If not specified, no extension will be generated.") + var extensionNameUIKit: String? @Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Image{extensionSuffix}.swift") var extensionSuffix: String? @@ -46,28 +46,36 @@ extension ImagesOptions { // MARK: - SwiftUI - var extensionFileName: String { + var extensionFileName: String? { + guard let extensionName else { return nil } + if let extensionSuffix { return "\(extensionName)+\(extensionSuffix).swift" } return "\(extensionName).swift" } - var extensionFilePath: String { - "\(extensionOutputPath)/\(extensionFileName)" + var extensionFilePath: String? { + guard let extensionOutputPath, let extensionFileName else { return nil } + + return "\(extensionOutputPath)/\(extensionFileName)" } // MARK: - UIKit - var extensionFileNameUIKit: String { + var extensionFileNameUIKit: String? { + guard let extensionNameUIKit else { return nil } + if let extensionSuffix { return "\(extensionNameUIKit)+\(extensionSuffix).swift" } return "\(extensionNameUIKit).swift" } - var extensionFilePathUIKit: String { - "\(extensionOutputPath)/\(extensionFileNameUIKit)" + var extensionFilePathUIKit: String? { + guard let extensionOutputPath, let extensionFileNameUIKit else { return nil } + + return "\(extensionOutputPath)/\(extensionFileNameUIKit)" } // MARK: -