// // source.swift // gen_html // // Created by Isaac Paul on 9/30/25. // import Foundation import PackagePlugin final class AppError: LocalizedError, Sendable { let message: String init(_ message: String) { self.message = message } init(_ message: String, _ error:Error) { self.message = "\(message) : \(error.localizedDescription)" } static func failure(_ message: String) -> Result { return .failure(AppError(message)) } var errorDescription: String? { get { return message } } var failureReason: String? { get { return message } } } let toolName = "BindingGenerator" extension BindingPlugin: BuildToolPlugin { func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { guard let swiftTarget = target as? SwiftSourceModuleTarget else { print("Unexpected target: \(type(of: target)). Needs SwiftSourceModuleTarget") throw AppError("Unexpected target: \(type(of: target)). Needs SwiftSourceModuleTarget") } let toolUrl = try context.tool(named: toolName).url return try createBuildCommands( outputDir: context.pluginWorkDirectoryURL, inputDir: swiftTarget.directoryURL, inputFiles: swiftTarget.sourceFiles, toolExe: toolUrl ) } } #if canImport(XcodeProjectPlugin) import XcodeProjectPlugin import System // The entry point for Xcode project builds. extension BindingPlugin: XcodeBuildToolPlugin { func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command] { let toolUrl = try context.tool(named: toolName).url return try createBuildCommands( outputDir: context.pluginWorkDirectoryURL, inputDir: context.xcodeProject.directoryURL, inputFiles: target.inputFiles, toolExe: toolUrl ) } } #endif struct Config: Codable { var root_path: String? } extension URL { func isHtml() -> Bool { return self.pathExtension == "html" || self.pathExtension == "htm" } } extension String { func uppercaseFirstLetter() -> String { guard let firstLetter = self.first else { return self } return firstLetter.uppercased() + self.dropFirst() } } @main struct BindingPlugin { private static let configFile = "binding_generator_plugin.json" private func createBuildCommands( outputDir: URL, inputDir: URL, inputFiles: FileList, toolExe: URL ) throws -> [Command] { let configFilePath = inputFiles.first(where: { $0.url.lastPathComponent == BindingPlugin.configFile }) let configFileURL = configFilePath?.url //let listSearchDir: [String] let htmlFiles = inputFiles.filter { $0.url.isHtml() }.map { $0.url } let rootPath:URL if let configFileURL = configFileURL { let configData = try Data(contentsOf: configFileURL) let config = try JSONDecoder().decode(Config.self, from: configData) let baseDirectory:URL = configFileURL.deletingLastPathComponent() if let configRootPath = config.root_path { rootPath = baseDirectory.appending(path: configRootPath) } else { rootPath = inputDir } /* for source in config.sources ?? [] { let sourceFileOrDirectory = baseDirectory.appendingPathComponent(source) if sourceFileOrDirectory.isHtml() { htmlFiles.append(sourceFileOrDirectory) } else { let files = try FileManager.default.contentsOfDirectory( at: sourceFileOrDirectory, includingPropertiesForKeys: nil ) let filteredFiles = files.filter { $0.isHtml() } htmlFiles.append(contentsOf: filteredFiles) } }*/ } else { rootPath = inputDir } /* /Users/isaacpaul/Projects/swift-projects/HRW/Plugins/BindingPlugin/source.swift:146:13: error: 'let' cannot appear nested inside another 'var' or 'let' pattern :0: error: error opening input file '/Users/isaacpaul/Library/Developer/Xcode/DerivedData/HRW-bicwrilrmihgqogjvxyqtcosfaro/Build/Intermediates.noindex/BuildToolPluginIntermediates/hrw.output/HRWTests/BindingPlugin/Example.swift' (No such file or directory) // Showing All Messages Error opening input file '/Users/isaacpaul/Library/Developer/Xcode/DerivedData/HRW-bicwrilrmihgqogjvxyqtcosfaro/Build/Intermediates.noindex/BuildToolPluginIntermediates/hrw.output/HRWTests/BindingPlugin/ExampleBinding.swift' (No such file or directory) */ return htmlFiles.map { eachFile in let fileName = eachFile.lastPathComponent let ext = eachFile.pathExtension let extIndex = fileName.index(fileName.startIndex, offsetBy: fileName.count - (ext.count + 1)) let withoutExt = String(fileName[fileName.startIndex..