@@ -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,36 @@ 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
408+ else {
409+ return nil
410+ }
411+ return resolved
412+ }
413+ }
414+
383415 /// Merge the current configuration with the overrides.
384416 func merging( overrides: BridgeJSConfig ) -> BridgeJSConfig {
417+ let mergedDirs : [ String ] ? = {
418+ let base = self . additionalSourceDirs ?? [ ]
419+ let extra = overrides. additionalSourceDirs ?? [ ]
420+ let combined = base + extra
421+ return combined. isEmpty ? nil : combined
422+ } ( )
385423 return BridgeJSConfig (
386424 tools: ( tools ?? [ : ] ) . merging ( overrides. tools ?? [ : ] , uniquingKeysWith: { $1 } ) ,
387- exposeToGlobal: overrides. exposeToGlobal
425+ exposeToGlobal: overrides. exposeToGlobal,
426+ additionalSourceDirs: mergedDirs
388427 )
389428 }
390429}
0 commit comments