@@ -76,13 +76,80 @@ function printUsage() {
7676 console . error ( 'Usage: ts2swift <d.ts file path> -p <tsconfig.json path> [--global <d.ts>]... [-o output.swift]' ) ;
7777}
7878
79+ /**
80+ * Run ts2swift for a single input file (programmatic API, no process I/O).
81+ * @param {string } filePath - Path to the .d.ts file
82+ * @param {{ tsconfigPath: string, logLevel?: string, globalFiles?: string[] } } options
83+ * @returns {string } Generated Swift source
84+ * @throws {Error } on parse/type-check errors (diagnostics are included in the message)
85+ */
86+ export function run ( filePath , options ) {
87+ const { tsconfigPath, logLevel = 'info' , globalFiles : globalFilesOpt = [ ] } = options ;
88+ const globalFiles = Array . isArray ( globalFilesOpt ) ? globalFilesOpt : ( globalFilesOpt ? [ globalFilesOpt ] : [ ] ) ;
89+
90+ const diagnosticEngine = new DiagnosticEngine ( logLevel ) ;
91+
92+ const configFile = ts . readConfigFile ( tsconfigPath , ts . sys . readFile ) ;
93+ const configParseResult = ts . parseJsonConfigFileContent (
94+ configFile . config ,
95+ ts . sys ,
96+ path . dirname ( path . resolve ( tsconfigPath ) )
97+ ) ;
98+
99+ if ( configParseResult . errors . length > 0 ) {
100+ const message = ts . formatDiagnosticsWithColorAndContext ( configParseResult . errors , {
101+ getCanonicalFileName : ( fileName ) => fileName ,
102+ getNewLine : ( ) => ts . sys . newLine ,
103+ getCurrentDirectory : ( ) => ts . sys . getCurrentDirectory ( ) ,
104+ } ) ;
105+ throw new Error ( `TypeScript config/parse errors:\n${ message } ` ) ;
106+ }
107+
108+ const program = TypeProcessor . createProgram ( [ filePath , ...globalFiles ] , configParseResult . options ) ;
109+ const diagnostics = program . getSemanticDiagnostics ( ) ;
110+ if ( diagnostics . length > 0 ) {
111+ const message = ts . formatDiagnosticsWithColorAndContext ( diagnostics , {
112+ getCanonicalFileName : ( fileName ) => fileName ,
113+ getNewLine : ( ) => ts . sys . newLine ,
114+ getCurrentDirectory : ( ) => ts . sys . getCurrentDirectory ( ) ,
115+ } ) ;
116+ throw new Error ( `TypeScript semantic errors:\n${ message } ` ) ;
117+ }
118+
119+ const prelude = [
120+ "// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit," ,
121+ "// DO NOT EDIT." ,
122+ "//" ,
123+ "// To update this file, just rebuild your project or run" ,
124+ "// `swift package bridge-js`." ,
125+ "" ,
126+ "@_spi(Experimental) @_spi(BridgeJS) import JavaScriptKit" ,
127+ "" ,
128+ "" ,
129+ ] . join ( "\n" ) ;
130+
131+ /** @type {string[] } */
132+ const bodies = [ ] ;
133+ const globalFileSet = new Set ( globalFiles ) ;
134+ for ( const inputPath of [ filePath , ...globalFiles ] ) {
135+ const processor = new TypeProcessor ( program . getTypeChecker ( ) , diagnosticEngine , {
136+ defaultImportFromGlobal : globalFileSet . has ( inputPath ) ,
137+ } ) ;
138+ const result = processor . processTypeDeclarations ( program , inputPath ) ;
139+ const body = result . content . trim ( ) ;
140+ if ( body . length > 0 ) bodies . push ( body ) ;
141+ }
142+
143+ const hasAny = bodies . length > 0 ;
144+ return hasAny ? prelude + bodies . join ( "\n\n" ) + "\n" : "" ;
145+ }
146+
79147/**
80148 * Main function to run the CLI
81149 * @param {string[] } args - Command-line arguments
82150 * @returns {void }
83151 */
84152export function main ( args ) {
85- // Parse command line arguments
86153 const options = parseArgs ( {
87154 args,
88155 options : {
@@ -118,64 +185,24 @@ export function main(args) {
118185 }
119186
120187 const filePath = options . positionals [ 0 ] ;
121- const diagnosticEngine = new DiagnosticEngine ( options . values [ "log-level" ] || "info" ) ;
122-
123- diagnosticEngine . print ( "verbose" , `Processing ${ filePath } ...` ) ;
124-
125- // Create TypeScript program and process declarations
126- const configFile = ts . readConfigFile ( tsconfigPath , ts . sys . readFile ) ;
127- const configParseResult = ts . parseJsonConfigFileContent (
128- configFile . config ,
129- ts . sys ,
130- path . dirname ( path . resolve ( tsconfigPath ) )
131- ) ;
132-
133- if ( configParseResult . errors . length > 0 ) {
134- diagnosticEngine . tsDiagnose ( configParseResult . errors ) ;
135- process . exit ( 1 ) ;
136- }
137-
188+ const logLevel = options . values [ "log-level" ] || "info" ;
138189 /** @type {string[] } */
139190 const globalFiles = Array . isArray ( options . values . global )
140191 ? options . values . global
141192 : ( options . values . global ? [ options . values . global ] : [ ] ) ;
142193
143- const program = TypeProcessor . createProgram ( [ filePath , ...globalFiles ] , configParseResult . options ) ;
144- const diagnostics = program . getSemanticDiagnostics ( ) ;
145- if ( diagnostics . length > 0 ) {
146- diagnosticEngine . tsDiagnose ( diagnostics ) ;
147- process . exit ( 1 ) ;
148- }
149-
150- const prelude = [
151- "// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit," ,
152- "// DO NOT EDIT." ,
153- "//" ,
154- "// To update this file, just rebuild your project or run" ,
155- "// `swift package bridge-js`." ,
156- "" ,
157- "@_spi(Experimental) @_spi(BridgeJS) import JavaScriptKit" ,
158- "" ,
159- "" ,
160- ] . join ( "\n" ) ;
194+ const diagnosticEngine = new DiagnosticEngine ( logLevel ) ;
195+ diagnosticEngine . print ( "verbose" , `Processing ${ filePath } ...` ) ;
161196
162- /** @type {string[] } */
163- const bodies = [ ] ;
164- const globalFileSet = new Set ( globalFiles ) ;
165- for ( const inputPath of [ filePath , ...globalFiles ] ) {
166- const processor = new TypeProcessor ( program . getTypeChecker ( ) , diagnosticEngine , {
167- defaultImportFromGlobal : globalFileSet . has ( inputPath ) ,
168- } ) ;
169- const result = processor . processTypeDeclarations ( program , inputPath ) ;
170- const body = result . content . trim ( ) ;
171- if ( body . length > 0 ) bodies . push ( body ) ;
197+ let swiftOutput ;
198+ try {
199+ swiftOutput = run ( filePath , { tsconfigPath, logLevel, globalFiles } ) ;
200+ } catch ( err ) {
201+ console . error ( err . message ) ;
202+ process . exit ( 1 ) ;
172203 }
173-
174- const hasAny = bodies . length > 0 ;
175- const swiftOutput = hasAny ? prelude + bodies . join ( "\n\n" ) + "\n" : "" ;
176-
177204 if ( options . values . output ) {
178- if ( hasAny ) {
205+ if ( swiftOutput . length > 0 ) {
179206 fs . mkdirSync ( path . dirname ( options . values . output ) , { recursive : true } ) ;
180207 fs . writeFileSync ( options . values . output , swiftOutput ) ;
181208 }
0 commit comments