@@ -309,6 +309,7 @@ public struct BridgeJSCoreError: Swift.Error, CustomStringConvertible {
309309
310310import struct Foundation. URL
311311import struct Foundation. Data
312+ import struct Foundation. ObjCBool
312313import class Foundation. FileManager
313314import class Foundation. JSONDecoder
314315
@@ -328,20 +329,33 @@ public struct BridgeJSConfig: Codable {
328329 /// Default: `false`
329330 public var exposeToGlobal : Bool
330331
331- public init ( tools: [ String : String ] ? = nil , exposeToGlobal: Bool = false ) {
332+ /// Additional directories containing Swift source files to scan for
333+ /// `@JS` annotations.
334+ ///
335+ /// Paths are resolved relative to the target directory. This is useful
336+ /// when Swift files with `@JS` annotations are generated by another
337+ /// build plugin whose outputs aren't included in `target.sourceFiles`.
338+ ///
339+ /// Default: `nil` (no additional directories)
340+ public var additionalSourceDirs : [ String ] ?
341+
342+ public init ( tools: [ String : String ] ? = nil , exposeToGlobal: Bool = false , additionalSourceDirs: [ String ] ? = nil ) {
332343 self . tools = tools
333344 self . exposeToGlobal = exposeToGlobal
345+ self . additionalSourceDirs = additionalSourceDirs
334346 }
335347
336348 enum CodingKeys : String , CodingKey {
337349 case tools
338350 case exposeToGlobal
351+ case additionalSourceDirs
339352 }
340353
341354 public init ( from decoder: Decoder ) throws {
342355 let container = try decoder. container ( keyedBy: CodingKeys . self)
343356 tools = try container. decodeIfPresent ( [ String : String ] . self, forKey: . tools)
344357 exposeToGlobal = try container. decodeIfPresent ( Bool . self, forKey: . exposeToGlobal) ?? false
358+ additionalSourceDirs = try container. decodeIfPresent ( [ String ] . self, forKey: . additionalSourceDirs)
345359 }
346360
347361 /// Load the configuration file from the SwiftPM package target directory.
@@ -380,11 +394,35 @@ public struct BridgeJSConfig: Codable {
380394 return try JSONDecoder ( ) . decode ( BridgeJSConfig . self, from: data)
381395 }
382396
397+ /// Resolve additional source directories relative to a base URL.
398+ ///
399+ /// Returns absolute URLs for each configured additional source directory.
400+ /// Directories that don't exist are silently skipped.
401+ public func resolveAdditionalSourceDirs( relativeTo baseURL: URL ) -> [ URL ] {
402+ guard let dirs = additionalSourceDirs else { return [ ] }
403+ return dirs. compactMap { dir in
404+ let resolved = baseURL. appending ( path: dir) . standardized
405+ var isDirectory : ObjCBool = false
406+ guard FileManager . default. fileExists ( atPath: resolved. path, isDirectory: & isDirectory) ,
407+ isDirectory. boolValue else {
408+ return nil
409+ }
410+ return resolved
411+ }
412+ }
413+
383414 /// Merge the current configuration with the overrides.
384415 func merging( overrides: BridgeJSConfig ) -> BridgeJSConfig {
416+ let mergedDirs : [ String ] ? = {
417+ let base = self . additionalSourceDirs ?? [ ]
418+ let extra = overrides. additionalSourceDirs ?? [ ]
419+ let combined = base + extra
420+ return combined. isEmpty ? nil : combined
421+ } ( )
385422 return BridgeJSConfig (
386423 tools: ( tools ?? [ : ] ) . merging ( overrides. tools ?? [ : ] , uniquingKeysWith: { $1 } ) ,
387- exposeToGlobal: overrides. exposeToGlobal
424+ exposeToGlobal: overrides. exposeToGlobal,
425+ additionalSourceDirs: mergedDirs
388426 )
389427 }
390428}
0 commit comments