33@preconcurrency import var Foundation. stderr
44@preconcurrency import struct Foundation. URL
55@preconcurrency import struct Foundation. Data
6+ @preconcurrency import struct Foundation. ObjCBool
67@preconcurrency import class Foundation. JSONEncoder
78@preconcurrency import class Foundation. FileManager
89@preconcurrency import class Foundation. JSONDecoder
10+ @preconcurrency import class Foundation. ProcessInfo
911import SwiftParser
1012
1113#if canImport(BridgeJSCore)
@@ -50,7 +52,7 @@ import TS2Skeleton
5052 do {
5153 try run ( )
5254 } catch {
53- printStderr ( " Error : \( error) " )
55+ printStderr ( " error : \( error) " )
5456 exit ( 1 )
5557 }
5658 }
@@ -83,6 +85,10 @@ import TS2Skeleton
8385 help: " Print verbose output " ,
8486 required: false
8587 ) ,
88+ " target-dir " : OptionRule (
89+ help: " The SwiftPM package target directory " ,
90+ required: true
91+ ) ,
8692 " output-swift " : OptionRule ( help: " The output file path for the Swift source code " , required: true ) ,
8793 " output-skeleton " : OptionRule (
8894 help: " The output file path for the skeleton of the imported TypeScript APIs " ,
@@ -99,6 +105,9 @@ import TS2Skeleton
99105 )
100106 let progress = ProgressReporting ( verbose: doubleDashOptions [ " verbose " ] == " true " )
101107 var importer = ImportTS ( progress: progress, moduleName: doubleDashOptions [ " module-name " ] !)
108+ let targetDirectory = URL ( fileURLWithPath: doubleDashOptions [ " target-dir " ] !)
109+ let config = try BridgeJSConfig . load ( targetDirectory: targetDirectory)
110+ let nodePath : URL = try config. findTool ( " node " , targetDirectory: targetDirectory)
102111 for inputFile in positionalArguments {
103112 if inputFile. hasSuffix ( " .json " ) {
104113 let sourceURL = URL ( fileURLWithPath: inputFile)
@@ -109,7 +118,7 @@ import TS2Skeleton
109118 importer. addSkeleton ( skeleton)
110119 } else if inputFile. hasSuffix ( " .d.ts " ) {
111120 let tsconfigPath = URL ( fileURLWithPath: doubleDashOptions [ " project " ] !)
112- try importer. addSourceFile ( inputFile, tsconfigPath: tsconfigPath. path)
121+ try importer. addSourceFile ( inputFile, tsconfigPath: tsconfigPath. path, nodePath : nodePath )
113122 }
114123 }
115124
@@ -317,3 +326,49 @@ struct ArgumentParser {
317326 return ( positionalArguments, singleDashOptions, doubleDashOptions)
318327 }
319328}
329+
330+ /// Finds an executable in the system PATH or using environment variable override.
331+ ///
332+ /// This function searches for an executable using the following priority:
333+ /// 1. First, checks for an environment variable override in the format `JAVASCRIPTKIT_<EXECUTABLE>_EXEC`
334+ /// - For executables with hyphens, they are converted to underscores (e.g., `my-exec` becomes `JAVASCRIPTKIT_MY_EXEC_EXEC`)
335+ /// 2. If no override is found, searches through directories in the PATH environment variable
336+ ///
337+ /// - Parameters:
338+ /// - executable: The name of the executable to find (e.g., "node", "npm", "my-exec")
339+ /// - environment: The environment variables to use. Defaults to the current process environment.
340+ /// - Returns: The URL of the found executable
341+ internal func which(
342+ _ executable: String ,
343+ environment: [ String : String ] = ProcessInfo . processInfo. environment
344+ ) throws -> URL {
345+ func checkCandidate( _ candidate: URL ) -> Bool {
346+ var isDirectory : ObjCBool = false
347+ let fileExists = FileManager . default. fileExists ( atPath: candidate. path, isDirectory: & isDirectory)
348+ return fileExists && !isDirectory. boolValue && FileManager . default. isExecutableFile ( atPath: candidate. path)
349+ }
350+ do {
351+ // Check overriding environment variable
352+ let envVariable = " JAVASCRIPTKIT_ " + executable. uppercased ( ) . replacingOccurrences ( of: " - " , with: " _ " ) + " _EXEC "
353+ if let executablePath = environment [ envVariable] {
354+ let url = URL ( fileURLWithPath: executablePath)
355+ if checkCandidate ( url) {
356+ return url
357+ }
358+ }
359+ }
360+ let pathSeparator : Character
361+ #if os(Windows)
362+ pathSeparator = " ; "
363+ #else
364+ pathSeparator = " : "
365+ #endif
366+ let paths = environment [ " PATH " ] ? . split ( separator: pathSeparator) ?? [ ]
367+ for path in paths {
368+ let url = URL ( fileURLWithPath: String ( path) ) . appendingPathComponent ( executable)
369+ if checkCandidate ( url) {
370+ return url
371+ }
372+ }
373+ throw BridgeJSCoreError ( " Executable \" \( executable) \" not found in PATH " )
374+ }
0 commit comments