3
3
@preconcurrency import var Foundation. stderr
4
4
@preconcurrency import struct Foundation. URL
5
5
@preconcurrency import struct Foundation. Data
6
+ @preconcurrency import struct Foundation. ObjCBool
6
7
@preconcurrency import class Foundation. JSONEncoder
7
8
@preconcurrency import class Foundation. FileManager
8
9
@preconcurrency import class Foundation. JSONDecoder
10
+ @preconcurrency import class Foundation. ProcessInfo
9
11
import SwiftParser
10
12
11
13
#if canImport(BridgeJSCore)
@@ -50,7 +52,7 @@ import TS2Skeleton
50
52
do {
51
53
try run ( )
52
54
} catch {
53
- printStderr ( " Error : \( error) " )
55
+ printStderr ( " error : \( error) " )
54
56
exit ( 1 )
55
57
}
56
58
}
@@ -83,6 +85,10 @@ import TS2Skeleton
83
85
help: " Print verbose output " ,
84
86
required: false
85
87
) ,
88
+ " target-dir " : OptionRule (
89
+ help: " The SwiftPM package target directory " ,
90
+ required: true
91
+ ) ,
86
92
" output-swift " : OptionRule ( help: " The output file path for the Swift source code " , required: true ) ,
87
93
" output-skeleton " : OptionRule (
88
94
help: " The output file path for the skeleton of the imported TypeScript APIs " ,
@@ -99,6 +105,9 @@ import TS2Skeleton
99
105
)
100
106
let progress = ProgressReporting ( verbose: doubleDashOptions [ " verbose " ] == " true " )
101
107
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)
102
111
for inputFile in positionalArguments {
103
112
if inputFile. hasSuffix ( " .json " ) {
104
113
let sourceURL = URL ( fileURLWithPath: inputFile)
@@ -109,7 +118,7 @@ import TS2Skeleton
109
118
importer. addSkeleton ( skeleton)
110
119
} else if inputFile. hasSuffix ( " .d.ts " ) {
111
120
let tsconfigPath = URL ( fileURLWithPath: doubleDashOptions [ " project " ] !)
112
- try importer. addSourceFile ( inputFile, tsconfigPath: tsconfigPath. path)
121
+ try importer. addSourceFile ( inputFile, tsconfigPath: tsconfigPath. path, nodePath : nodePath )
113
122
}
114
123
}
115
124
@@ -317,3 +326,49 @@ struct ArgumentParser {
317
326
return ( positionalArguments, singleDashOptions, doubleDashOptions)
318
327
}
319
328
}
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