diff --git a/Package.swift b/Package.swift index 435ae1a1..4bd0ca45 100644 --- a/Package.swift +++ b/Package.swift @@ -151,7 +151,6 @@ let package = Package( .product(name: "SwiftBasicFormat", package: "swift-syntax"), .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), ], - path: "Plugins/BridgeJS/Sources/BridgeJSTool", exclude: ["TS2Skeleton/JavaScript"] ), .testTarget( diff --git a/Plugins/BridgeJS/Package.swift b/Plugins/BridgeJS/Package.swift index f7241d86..b9cd907c 100644 --- a/Plugins/BridgeJS/Package.swift +++ b/Plugins/BridgeJS/Package.swift @@ -2,13 +2,6 @@ import PackageDescription -let coreDependencies: [Target.Dependency] = [ - .product(name: "SwiftParser", package: "swift-syntax"), - .product(name: "SwiftSyntax", package: "swift-syntax"), - .product(name: "SwiftBasicFormat", package: "swift-syntax"), - .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), -] - let package = Package( name: "BridgeJS", platforms: [.macOS(.v13)], @@ -19,11 +12,42 @@ let package = Package( .target(name: "BridgeJSBuildPlugin"), .executableTarget( name: "BridgeJSTool", - dependencies: coreDependencies + dependencies: [ + "BridgeJSCore", + "TS2Skeleton", + ] + ), + .target( + name: "TS2Skeleton", + dependencies: [ + "BridgeJSCore", + "BridgeJSSkeleton", + ] + ), + .target( + name: "BridgeJSCore", + dependencies: [ + "BridgeJSSkeleton", + .product(name: "SwiftParser", package: "swift-syntax"), + .product(name: "SwiftSyntax", package: "swift-syntax"), + .product(name: "SwiftBasicFormat", package: "swift-syntax"), + .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"), + ] ), + .target(name: "BridgeJSSkeleton"), + + .target( + name: "BridgeJSLink", + dependencies: ["BridgeJSSkeleton"] + ), + .testTarget( name: "BridgeJSToolTests", - dependencies: coreDependencies, + dependencies: [ + "BridgeJSCore", + "BridgeJSLink", + "TS2Skeleton", + ], exclude: ["__Snapshots__", "Inputs"] ), ] diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/BridgeJSCoreError.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/BridgeJSCoreError.swift index 6e313754..9cbec438 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/BridgeJSCoreError.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/BridgeJSCoreError.swift @@ -1,7 +1,7 @@ -struct BridgeJSCoreError: Swift.Error, CustomStringConvertible { - let description: String +public struct BridgeJSCoreError: Swift.Error, CustomStringConvertible { + public let description: String - init(_ message: String) { + public init(_ message: String) { self.description = message } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index dfe161e9..b8b7d603 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -1,6 +1,9 @@ import SwiftBasicFormat import SwiftSyntax import SwiftSyntaxBuilder +#if canImport(BridgeJSSkeleton) +import BridgeJSSkeleton +#endif /// Exports Swift functions and classes to JavaScript /// @@ -11,14 +14,14 @@ import SwiftSyntaxBuilder /// /// The generated skeletons will be used by ``BridgeJSLink`` to generate /// JavaScript glue code and TypeScript definitions. -class ExportSwift { +public class ExportSwift { let progress: ProgressReporting private var exportedFunctions: [ExportedFunction] = [] private var exportedClasses: [ExportedClass] = [] private var typeDeclResolver: TypeDeclResolver = TypeDeclResolver() - init(progress: ProgressReporting) { + public init(progress: ProgressReporting) { self.progress = progress } @@ -27,7 +30,7 @@ class ExportSwift { /// - Parameters: /// - sourceFile: The parsed Swift source file to process /// - inputFilePath: The file path for error reporting - func addSourceFile(_ sourceFile: SourceFileSyntax, _ inputFilePath: String) throws { + public func addSourceFile(_ sourceFile: SourceFileSyntax, _ inputFilePath: String) throws { progress.print("Processing \(inputFilePath)") typeDeclResolver.addSourceFile(sourceFile) @@ -44,7 +47,7 @@ class ExportSwift { /// /// - Returns: A tuple containing the generated Swift code and a skeleton /// describing the exported APIs - func finalize() throws -> (outputSwift: String, outputSkeleton: ExportedSkeleton)? { + public func finalize() throws -> (outputSwift: String, outputSkeleton: ExportedSkeleton)? { guard let outputSwift = renderSwiftGlue() else { return nil } diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift index 37181114..c7966a84 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ImportTS.swift @@ -1,6 +1,9 @@ import SwiftBasicFormat import SwiftSyntax import SwiftSyntaxBuilder +#if canImport(BridgeJSSkeleton) +import BridgeJSSkeleton +#endif /// Imports TypeScript declarations and generates Swift bridge code /// @@ -10,25 +13,25 @@ import SwiftSyntaxBuilder /// /// The generated skeletons will be used by ``BridgeJSLink`` to generate /// JavaScript glue code and TypeScript definitions. -struct ImportTS { - let progress: ProgressReporting - private(set) var skeleton: ImportedModuleSkeleton +public struct ImportTS { + public let progress: ProgressReporting + public private(set) var skeleton: ImportedModuleSkeleton private var moduleName: String { skeleton.moduleName } - init(progress: ProgressReporting, moduleName: String) { + public init(progress: ProgressReporting, moduleName: String) { self.progress = progress self.skeleton = ImportedModuleSkeleton(moduleName: moduleName, children: []) } /// Adds a skeleton to the importer's state - mutating func addSkeleton(_ skeleton: ImportedFileSkeleton) { + public mutating func addSkeleton(_ skeleton: ImportedFileSkeleton) { self.skeleton.children.append(skeleton) } /// Finalizes the import process and generates Swift code - func finalize() throws -> String? { + public func finalize() throws -> String? { var decls: [DeclSyntax] = [] for skeleton in self.skeleton.children { for function in skeleton.functions { diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ProgressReporting.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ProgressReporting.swift index 4e92a198..d1a2aa6d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ProgressReporting.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ProgressReporting.swift @@ -1,7 +1,7 @@ -struct ProgressReporting { +public struct ProgressReporting { let print: (String) -> Void - init(verbose: Bool) { + public init(verbose: Bool) { self.init(print: verbose ? { Swift.print($0) } : { _ in }) } @@ -9,11 +9,11 @@ struct ProgressReporting { self.print = print } - static var silent: ProgressReporting { + public static var silent: ProgressReporting { return ProgressReporting(print: { _ in }) } - func print(_ message: String) { + public func print(_ message: String) { self.print(message) } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 022c5cbb..6693c815 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -1,5 +1,8 @@ import class Foundation.JSONDecoder import struct Foundation.Data +#if canImport(BridgeJSSkeleton) +import BridgeJSSkeleton +#endif struct BridgeJSLink { /// The exported skeletons diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 56e88f92..a0a86003 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -2,111 +2,167 @@ // MARK: - Types -enum BridgeType: Codable, Equatable { +public enum BridgeType: Codable, Equatable { case int, float, double, string, bool, jsObject(String?), swiftHeapObject(String), void } -enum WasmCoreType: String, Codable { +public enum WasmCoreType: String, Codable { case i32, i64, f32, f64, pointer } -struct Parameter: Codable { - let label: String? - let name: String - let type: BridgeType +public struct Parameter: Codable { + public let label: String? + public let name: String + public let type: BridgeType + + public init(label: String?, name: String, type: BridgeType) { + self.label = label + self.name = name + self.type = type + } } -struct Effects: Codable { - var isAsync: Bool - var isThrows: Bool +public struct Effects: Codable { + public var isAsync: Bool + public var isThrows: Bool + + public init(isAsync: Bool, isThrows: Bool) { + self.isAsync = isAsync + self.isThrows = isThrows + } } // MARK: - Exported Skeleton -struct ExportedFunction: Codable { - var name: String - var abiName: String - var parameters: [Parameter] - var returnType: BridgeType - var effects: Effects - var namespace: [String]? +public struct ExportedFunction: Codable { + public var name: String + public var abiName: String + public var parameters: [Parameter] + public var returnType: BridgeType + public var effects: Effects + public var namespace: [String]? + + public init( + name: String, + abiName: String, + parameters: [Parameter], + returnType: BridgeType, + effects: Effects, + namespace: [String]? = nil + ) { + self.name = name + self.abiName = abiName + self.parameters = parameters + self.returnType = returnType + self.effects = effects + self.namespace = namespace + } } -struct ExportedClass: Codable { - var name: String - var constructor: ExportedConstructor? - var methods: [ExportedFunction] - var namespace: [String]? +public struct ExportedClass: Codable { + public var name: String + public var constructor: ExportedConstructor? + public var methods: [ExportedFunction] + public var namespace: [String]? + + public init( + name: String, + constructor: ExportedConstructor? = nil, + methods: [ExportedFunction], + namespace: [String]? = nil + ) { + self.name = name + self.constructor = constructor + self.methods = methods + self.namespace = namespace + } } -struct ExportedConstructor: Codable { - var abiName: String - var parameters: [Parameter] - var effects: Effects - var namespace: [String]? +public struct ExportedConstructor: Codable { + public var abiName: String + public var parameters: [Parameter] + public var effects: Effects + public var namespace: [String]? + + public init(abiName: String, parameters: [Parameter], effects: Effects, namespace: [String]? = nil) { + self.abiName = abiName + self.parameters = parameters + self.effects = effects + self.namespace = namespace + } } -struct ExportedSkeleton: Codable { - let functions: [ExportedFunction] - let classes: [ExportedClass] +public struct ExportedSkeleton: Codable { + public let functions: [ExportedFunction] + public let classes: [ExportedClass] + + public init(functions: [ExportedFunction], classes: [ExportedClass]) { + self.functions = functions + self.classes = classes + } } // MARK: - Imported Skeleton -struct ImportedFunctionSkeleton: Codable { - let name: String - let parameters: [Parameter] - let returnType: BridgeType - let documentation: String? +public struct ImportedFunctionSkeleton: Codable { + public let name: String + public let parameters: [Parameter] + public let returnType: BridgeType + public let documentation: String? - func abiName(context: ImportedTypeSkeleton?) -> String { + public func abiName(context: ImportedTypeSkeleton?) -> String { return context.map { "bjs_\($0.name)_\(name)" } ?? "bjs_\(name)" } } -struct ImportedConstructorSkeleton: Codable { - let parameters: [Parameter] +public struct ImportedConstructorSkeleton: Codable { + public let parameters: [Parameter] - func abiName(context: ImportedTypeSkeleton) -> String { + public func abiName(context: ImportedTypeSkeleton) -> String { return "bjs_\(context.name)_init" } } -struct ImportedPropertySkeleton: Codable { - let name: String - let isReadonly: Bool - let type: BridgeType - let documentation: String? +public struct ImportedPropertySkeleton: Codable { + public let name: String + public let isReadonly: Bool + public let type: BridgeType + public let documentation: String? - func getterAbiName(context: ImportedTypeSkeleton) -> String { + public func getterAbiName(context: ImportedTypeSkeleton) -> String { return "bjs_\(context.name)_\(name)_get" } - func setterAbiName(context: ImportedTypeSkeleton) -> String { + public func setterAbiName(context: ImportedTypeSkeleton) -> String { return "bjs_\(context.name)_\(name)_set" } } -struct ImportedTypeSkeleton: Codable { - let name: String - let constructor: ImportedConstructorSkeleton? - let methods: [ImportedFunctionSkeleton] - let properties: [ImportedPropertySkeleton] - let documentation: String? +public struct ImportedTypeSkeleton: Codable { + public let name: String + public let constructor: ImportedConstructorSkeleton? + public let methods: [ImportedFunctionSkeleton] + public let properties: [ImportedPropertySkeleton] + public let documentation: String? } -struct ImportedFileSkeleton: Codable { - let functions: [ImportedFunctionSkeleton] - let types: [ImportedTypeSkeleton] +public struct ImportedFileSkeleton: Codable { + public let functions: [ImportedFunctionSkeleton] + public let types: [ImportedTypeSkeleton] } -struct ImportedModuleSkeleton: Codable { - let moduleName: String - var children: [ImportedFileSkeleton] +public struct ImportedModuleSkeleton: Codable { + public let moduleName: String + public var children: [ImportedFileSkeleton] + + public init(moduleName: String, children: [ImportedFileSkeleton]) { + self.moduleName = moduleName + self.children = children + } } extension BridgeType { - var abiReturnType: WasmCoreType? { + public var abiReturnType: WasmCoreType? { switch self { case .void: return nil case .bool: return .i32 diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSCore b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSCore deleted file mode 120000 index c869f69c..00000000 --- a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSCore +++ /dev/null @@ -1 +0,0 @@ -../BridgeJSCore \ No newline at end of file diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSSkeleton b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSSkeleton deleted file mode 120000 index a2c26678..00000000 --- a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSSkeleton +++ /dev/null @@ -1 +0,0 @@ -../BridgeJSSkeleton \ No newline at end of file diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift index 6096e2b3..bdeae3c3 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift @@ -8,6 +8,16 @@ @preconcurrency import class Foundation.JSONDecoder import SwiftParser +#if canImport(BridgeJSCore) +import BridgeJSCore +#endif +#if canImport(BridgeJSSkeleton) +import BridgeJSSkeleton +#endif +#if canImport(TS2Skeleton) +import TS2Skeleton +#endif + /// BridgeJS Tool /// /// A command-line tool to generate Swift-JavaScript bridge code for WebAssembly applications. diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/TS2Skeleton b/Plugins/BridgeJS/Sources/BridgeJSTool/TS2Skeleton deleted file mode 120000 index f9ba2f57..00000000 --- a/Plugins/BridgeJS/Sources/BridgeJSTool/TS2Skeleton +++ /dev/null @@ -1 +0,0 @@ -../TS2Skeleton \ No newline at end of file diff --git a/Plugins/BridgeJS/Sources/TS2Skeleton/TS2Skeleton.swift b/Plugins/BridgeJS/Sources/TS2Skeleton/TS2Skeleton.swift index 262393c4..051f2f4a 100644 --- a/Plugins/BridgeJS/Sources/TS2Skeleton/TS2Skeleton.swift +++ b/Plugins/BridgeJS/Sources/TS2Skeleton/TS2Skeleton.swift @@ -12,6 +12,13 @@ import protocol Dispatch.DispatchSourceSignal import class Dispatch.DispatchSource +#if canImport(BridgeJSCore) +import BridgeJSCore +#endif +#if canImport(BridgeJSSkeleton) +import BridgeJSSkeleton +#endif + internal func which(_ executable: String) throws -> URL { func checkCandidate(_ candidate: URL) -> Bool { var isDirectory: ObjCBool = false @@ -46,7 +53,7 @@ internal func which(_ executable: String) throws -> URL { extension ImportTS { /// Processes a TypeScript definition file and extracts its API information - mutating func addSourceFile(_ sourceFile: String, tsconfigPath: String) throws { + public mutating func addSourceFile(_ sourceFile: String, tsconfigPath: String) throws { let nodePath = try which("node") let ts2skeletonPath = URL(fileURLWithPath: #filePath) .deletingLastPathComponent() diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSCore b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSCore deleted file mode 120000 index 852d5b95..00000000 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSCore +++ /dev/null @@ -1 +0,0 @@ -../../Sources/BridgeJSCore \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLink b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLink deleted file mode 120000 index 94a1e954..00000000 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLink +++ /dev/null @@ -1 +0,0 @@ -../../Sources/BridgeJSLink \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift index 3432551b..925a9757 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift @@ -2,6 +2,8 @@ import Foundation import SwiftSyntax import SwiftParser import Testing +@testable import BridgeJSLink +@testable import BridgeJSCore @Suite struct BridgeJSLinkTests { private func snapshot( diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSSkeleton b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSSkeleton deleted file mode 120000 index c2cf2864..00000000 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSSkeleton +++ /dev/null @@ -1 +0,0 @@ -../../Sources/BridgeJSSkeleton \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/ExportSwiftTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/ExportSwiftTests.swift index 626248a7..8351d105 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/ExportSwiftTests.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/ExportSwiftTests.swift @@ -3,6 +3,8 @@ import SwiftSyntax import SwiftParser import Testing +@testable import BridgeJSCore + @Suite struct ExportSwiftTests { private func snapshot( swiftAPI: ExportSwift, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/ImportTSTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/ImportTSTests.swift index 071c3d1d..9db37669 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/ImportTSTests.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/ImportTSTests.swift @@ -1,5 +1,7 @@ import Testing import Foundation +@testable import BridgeJSCore +@testable import TS2Skeleton @Suite struct ImportTSTests { static let inputsDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent( diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/TS2Skeleton b/Plugins/BridgeJS/Tests/BridgeJSToolTests/TS2Skeleton deleted file mode 120000 index feba8470..00000000 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/TS2Skeleton +++ /dev/null @@ -1 +0,0 @@ -../../Sources/TS2Skeleton \ No newline at end of file diff --git a/Sources/BridgeJSTool/BridgeJSCore b/Sources/BridgeJSTool/BridgeJSCore new file mode 120000 index 00000000..9934baee --- /dev/null +++ b/Sources/BridgeJSTool/BridgeJSCore @@ -0,0 +1 @@ +../../Plugins/BridgeJS/Sources/BridgeJSCore \ No newline at end of file diff --git a/Sources/BridgeJSTool/BridgeJSSkeleton b/Sources/BridgeJSTool/BridgeJSSkeleton new file mode 120000 index 00000000..794c5b08 --- /dev/null +++ b/Sources/BridgeJSTool/BridgeJSSkeleton @@ -0,0 +1 @@ +../../Plugins/BridgeJS/Sources/BridgeJSSkeleton \ No newline at end of file diff --git a/Sources/BridgeJSTool/BridgeJSTool b/Sources/BridgeJSTool/BridgeJSTool new file mode 120000 index 00000000..e92c6fbb --- /dev/null +++ b/Sources/BridgeJSTool/BridgeJSTool @@ -0,0 +1 @@ +../../Plugins/BridgeJS/Sources/BridgeJSTool \ No newline at end of file diff --git a/Sources/BridgeJSTool/README.md b/Sources/BridgeJSTool/README.md new file mode 100644 index 00000000..c3ec3cf3 --- /dev/null +++ b/Sources/BridgeJSTool/README.md @@ -0,0 +1,9 @@ +# BridgeJSTool (Merged Sources) + +This directory contains symlinked sources from `Plugins/BridgeJS` to provide a merged version of the BridgeJSTool for the root Package.swift. + +## Source Merging via Symlinks + +This module uses symlinks to merge multiple modules into a single compilation unit. Compiling multiple modules separately is much slower than compiling them as one merged module. + +Since BridgeJSTool runs during Swift package builds via the BridgeJS plugin, fast compilation is critical for developer experience. The source modules use `#if canImport` directives to work both standalone and when merged here. \ No newline at end of file diff --git a/Sources/BridgeJSTool/TS2Skeleton b/Sources/BridgeJSTool/TS2Skeleton new file mode 120000 index 00000000..c41c1280 --- /dev/null +++ b/Sources/BridgeJSTool/TS2Skeleton @@ -0,0 +1 @@ +../../Plugins/BridgeJS/Sources/TS2Skeleton \ No newline at end of file