diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/ivanmikhailovskii.xcuserdatad/UserInterfaceState.xcuserstate b/.swiftpm/xcode/package.xcworkspace/xcuserdata/ivanmikhailovskii.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..fff09f3 Binary files /dev/null and b/.swiftpm/xcode/package.xcworkspace/xcuserdata/ivanmikhailovskii.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/.swiftpm/xcode/xcuserdata/ivanmikhailovskii.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/.swiftpm/xcode/xcuserdata/ivanmikhailovskii.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..cbadbdc --- /dev/null +++ b/.swiftpm/xcode/xcuserdata/ivanmikhailovskii.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/.swiftpm/xcode/xcuserdata/ivanmikhailovskii.xcuserdatad/xcschemes/xcschememanagement.plist b/.swiftpm/xcode/xcuserdata/ivanmikhailovskii.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..505af2a --- /dev/null +++ b/.swiftpm/xcode/xcuserdata/ivanmikhailovskii.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,27 @@ + + + + + SchemeUserState + + SQAssetsValidator.xcscheme_^#shared#^_ + + orderHint + 0 + + Spectre (Playground).xcscheme + + orderHint + 1 + + + SuppressBuildableAutocreation + + SQAssetsValidatort + + primary + + + + + diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..c9bbd1f --- /dev/null +++ b/Package.resolved @@ -0,0 +1,61 @@ +{ + "object": { + "pins": [ + { + "package": "PathKit", + "repositoryURL": "https://github.com/kylef/PathKit.git", + "state": { + "branch": null, + "revision": "3bfd2737b700b9a36565a8c94f4ad2b050a5e574", + "version": "1.0.1" + } + }, + { + "package": "Spectre", + "repositoryURL": "https://github.com/kylef/Spectre.git", + "state": { + "branch": null, + "revision": "26cc5e9ae0947092c7139ef7ba612e34646086c7", + "version": "0.10.1" + } + }, + { + "package": "Stencil", + "repositoryURL": "https://github.com/stencilproject/Stencil.git", + "state": { + "branch": null, + "revision": "4f222ac85d673f35df29962fc4c36ccfdaf9da5b", + "version": "0.15.1" + } + }, + { + "package": "swift-argument-parser", + "repositoryURL": "https://github.com/apple/swift-argument-parser", + "state": { + "branch": null, + "revision": "6b2aa2748a7881eebb9f84fb10c01293e15b52ca", + "version": "0.5.0" + } + }, + { + "package": "swift-log", + "repositoryURL": "https://github.com/apple/swift-log.git", + "state": { + "branch": null, + "revision": "3d8596ed08bd13520157f0355e35caed215ffbfa", + "version": "1.6.3" + } + }, + { + "package": "Yams", + "repositoryURL": "https://github.com/jpsim/Yams.git", + "state": { + "branch": null, + "revision": "9ff1cc9327586db4e0c8f46f064b6a82ec1566fa", + "version": "4.0.6" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..6ca9287 --- /dev/null +++ b/Package.swift @@ -0,0 +1,39 @@ +// swift-tools-version:5.7 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "SQAssetsValidator", + platforms: [ + .macOS(.v13), + ], + products: [ + .executable(name: "SQAssetsValidatort", targets: ["SQAssetsValidator"]) + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-argument-parser", from: "0.3.0"), + .package(url: "https://github.com/jpsim/Yams.git", from: "4.0.0"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.4.0"), + .package(url: "https://github.com/stencilproject/Stencil.git", from: "0.14.0"), + ], + targets: [ + // Main target + .target( + name: "SQAssetsValidator", + dependencies: [ + "Utils", + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Yams", package: "Yams"), + .product(name: "Logging", package: "swift-log") + ], + path: "./Sources/AssetsValidator" + ), + + // Shared target + .target( + name: "Utils", + path: "./Sources/Utils" + ), + ] +) diff --git a/README.md b/README.md index 6977727..4351fe6 100644 --- a/README.md +++ b/README.md @@ -1 +1,24 @@ # SQAssetsValidator + +validator-config.yaml + +``` +icon: + graphicsFilePath: ./SQCoreGraphics.swift + iconPath: ./Resources/Assets/Assets.xcassets/Icons + illustrationsPath: ./Resources/Assets/Assets.xcassets/Illustrations + ignore: + - imgHeaderLogo + - imgSplashLogoSmall + - icNotification + - icNotificationMini +palette: + folderFilePath: ./Palette + colorPath: ./Resources/Assets/Colors.xcassets/Colors +typography: + resourcesJSON: ./Resources/Core/core_typography.json + folderFilesPath: ./Styles +dimension: + resourcesJSON: ./Resources/Core/core_dimensions.json + dimensionsFilePath: ./SQCoreDimensions.swift +``` diff --git a/Sources/AssetsValidator/Input/Params.swift b/Sources/AssetsValidator/Input/Params.swift new file mode 100644 index 0000000..c2dc163 --- /dev/null +++ b/Sources/AssetsValidator/Input/Params.swift @@ -0,0 +1,40 @@ +// +// Params.swift +// SQAssetsValidator +// +// Created by Ivan Mikhailovskii on 22.07.2025. +// + +struct Params: Decodable { + + struct Palette: Decodable { + let folderFilePath: String + let ignore: [String]? + let colorPath: String + } + + struct Icon: Decodable { + let graphicsFilePath: String + let iconPath: String + let illustrationsPath: String + let ignore: [String]? + } + + struct Typography: Decodable { + let folderFilesPath: String + let resourcesJSON: String + let ignore: [String]? + } + + struct Dimension: Decodable { + let dimensionsFilePath: String + let resourcesJSON: String + let ignore: [String]? + } + + let sqCorePath: String? + let icon: Icon? + let palette: Palette? + let typography: Typography? + let dimension: Dimension? +} diff --git a/Sources/AssetsValidator/Input/ParamsReader.swift b/Sources/AssetsValidator/Input/ParamsReader.swift new file mode 100644 index 0000000..8362899 --- /dev/null +++ b/Sources/AssetsValidator/Input/ParamsReader.swift @@ -0,0 +1,36 @@ +// +// ParceParams.swift +// SQAssetsValidator +// +// Created by Ivan Mikhailovskii on 22.07.2025. +// + +import Foundation +import Yams + +final class ParamsReader { + + private let fileManager: FileManager + private let inputPath: String + + init( + inputPath: String = "./validator-config.yaml", + fileManager: FileManager = .default + ) { + self.inputPath = inputPath + self.fileManager = fileManager + } + + func read() throws -> Params { + return try readParams(filePath: inputPath) + } + + private func readParams( + filePath: String + ) throws -> Params { + let data = try Data(contentsOf: URL(fileURLWithPath: filePath)) + + let decoder = YAMLDecoder() + return try decoder.decode(Params.self, from: String(data: data, encoding: .utf8)!) + } +} diff --git a/Sources/AssetsValidator/Subcommands/DimensionsValidator.swift b/Sources/AssetsValidator/Subcommands/DimensionsValidator.swift new file mode 100644 index 0000000..ef110d4 --- /dev/null +++ b/Sources/AssetsValidator/Subcommands/DimensionsValidator.swift @@ -0,0 +1,62 @@ +// +// DimensionsValidator.swift +// SQAssetsValidator +// +// Created by Ivan Mikhailovskii on 22.07.2025. +// + +import Foundation +import ArgumentParser +import Logging +import Utils + +extension SQAssetsValidatorCommand { + + struct DimensionsValidator: ParsableCommand { + + static let configuration = CommandConfiguration( + commandName: "dimensions", + abstract: "Validate dimensions from project", + discussion: "Validate dimensions from project" + ) + + func run() throws { + let logger = Logger(label: "assets-validator") + let params = try ParamsReader().read() + + let url = URL(fileURLWithPath: params.dimension?.resourcesJSON ?? "") + let dictonary = try? UtilsJson.loadJSON(from: url) + + var named = [String]() + + dictonary?.keys.forEach { + named.append($0) + } + + let urlDimensions = URL(fileURLWithPath: params.dimension?.dimensionsFilePath ?? "") + + guard let data = try? Data(contentsOf: urlDimensions) + else { fatalError() } + + let fileContent = String(decoding: data, as: UTF8.self) + + var notFoundResources = "" + named.forEach { name in + if params.dimension?.ignore?.contains(name) ?? false { + return + } + + if !fileContent.contains(name) { + notFoundResources.append("not found: \(name)\n") + } + } + + if notFoundResources.isEmpty { + logger.info("Dimensions resources validate") + } else { + logger.error(Logger.Message(stringLiteral: notFoundResources)) + fatalError() + } + } + } +} diff --git a/Sources/AssetsValidator/Subcommands/IconValidator.swift b/Sources/AssetsValidator/Subcommands/IconValidator.swift new file mode 100644 index 0000000..712548b --- /dev/null +++ b/Sources/AssetsValidator/Subcommands/IconValidator.swift @@ -0,0 +1,79 @@ +// +// IconValidator.swift +// SQAssetsValidator +// +// Created by Ivan Mikhailovskii on 21.07.2025. +// + +import Foundation +import ArgumentParser +import Logging + +extension SQAssetsValidatorCommand { + + struct IconValidator: ParsableCommand { + + static let configuration = CommandConfiguration( + commandName: "icons", + abstract: "Validate icons from project", + discussion: "Validate icons from project" + ) + + func run() throws { + let logger = Logger(label: "assets-validator") + let params = try ParamsReader().read() + + let url = URL(fileURLWithPath: "\(params.icon?.graphicsFilePath ?? "")") + guard let data = try? Data(contentsOf: url) + else { return } + + let fileContent = String(decoding: data, as: UTF8.self) + + let namedRanges = fileContent.ranges(of: #/\s+named:\s+"(\w+)",/#) + + var named = [String]() + namedRanges.forEach { range in + let rawNamedEntry = String(fileContent[fileContent.index(after: range.lowerBound).. [String: Any] { + let data = try? Data(contentsOf: url) + + guard let data, + let json = try? JSONSerialization.jsonObject(with: data, options: []), + let dictionary = json as? [String: Any] else { + + throw JSONError.parsingFailed + } + + return dictionary + } +}