Skip to content

BridgeJS: Fix missing TypeScript interface definitions for imported types #410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ struct BridgeJSLink {
var dtsLines: [String] = []
dtsLines.append(contentsOf: namespaceDeclarations())
dtsLines.append(contentsOf: dtsClassLines)
dtsLines.append(contentsOf: generateImportedTypeDefinitions())
dtsLines.append("export type Exports = {")
dtsLines.append(contentsOf: dtsExportLines.map { $0.indent(count: 4) })
dtsLines.append("}")
Expand All @@ -246,6 +247,38 @@ struct BridgeJSLink {
return (outputJs, outputDts)
}

private func generateImportedTypeDefinitions() -> [String] {
var typeDefinitions: [String] = []

for skeletonSet in importedSkeletons {
for fileSkeleton in skeletonSet.children {
for type in fileSkeleton.types {
typeDefinitions.append("export interface \(type.name) {")

// Add methods
for method in type.methods {
let methodSignature =
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType));"
typeDefinitions.append(methodSignature.indent(count: 4))
}

// Add properties
for property in type.properties {
let propertySignature =
property.isReadonly
? "readonly \(property.name): \(property.type.tsType);"
: "\(property.name): \(property.type.tsType);"
typeDefinitions.append(propertySignature.indent(count: 4))
}

typeDefinitions.append("}")
}
}
}

return typeDefinitions
}

private func namespaceDeclarations() -> [String] {
var dtsLines: [String] = []
var namespaceFunctions: [String: [ExportedFunction]] = [:]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Test case for multiple imported types with methods and properties
export interface DatabaseConnection {
connect(url: string): void;
execute(query: string): any;
readonly isConnected: boolean;
connectionTimeout: number;
}

export interface Logger {
log(message: string): void;
error(message: string, error: any): void;
readonly level: string;
}

export interface ConfigManager {
get(key: string): any;
set(key: string, value: any): void;
readonly configPath: string;
}

export function createDatabaseConnection(config: any): DatabaseConnection;
export function createLogger(level: string): Logger;
export function getConfigManager(): ConfigManager;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Test case similar to the TS2Skeleton use case that caused the original bug
export interface TypeScriptProcessor {
convert(ts: string): string;
validate(ts: string): boolean;
readonly version: string;
}

export interface CodeGenerator {
generate(input: any): string;
readonly outputFormat: string;
}

export function createTS2Skeleton(): TypeScriptProcessor;
export function createCodeGenerator(format: string): CodeGenerator;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
// To update this file, just rebuild your project or run
// `swift package bridge-js`.

export interface Animatable {
animate(keyframes: any, options: any): any;
getAnimations(options: any): any;
}
export type Exports = {
}
export type Imports = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
// DO NOT EDIT.
//
// To update this file, just rebuild your project or run
// `swift package bridge-js`.

export interface DatabaseConnection {
connect(url: string): void;
execute(query: string): any;
readonly isConnected: boolean;
connectionTimeout: number;
}
export interface Logger {
log(message: string): void;
error(message: string, error: any): void;
readonly level: string;
}
export interface ConfigManager {
get(key: string): any;
set(key: string, value: any): void;
readonly configPath: string;
}
export type Exports = {
}
export type Imports = {
createDatabaseConnection(config: any): DatabaseConnection;
createLogger(level: string): Logger;
getConfigManager(): ConfigManager;
}
export function createInstantiator(options: {
imports: Imports;
}, swift: any): Promise<{
addImports: (importObject: WebAssembly.Imports) => void;
setInstance: (instance: WebAssembly.Instance) => void;
createExports: (instance: WebAssembly.Instance) => Exports;
}>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
// DO NOT EDIT.
//
// To update this file, just rebuild your project or run
// `swift package bridge-js`.

export async function createInstantiator(options, swift) {
let instance;
let memory;
let setException;
const textDecoder = new TextDecoder("utf-8");
const textEncoder = new TextEncoder("utf-8");

let tmpRetString;
let tmpRetBytes;
let tmpRetException;
return {
/** @param {WebAssembly.Imports} importObject */
addImports: (importObject) => {
const bjs = {};
importObject["bjs"] = bjs;
bjs["swift_js_return_string"] = function(ptr, len) {
const bytes = new Uint8Array(memory.buffer, ptr, len);
tmpRetString = textDecoder.decode(bytes);
}
bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) {
const source = swift.memory.getObject(sourceId);
const bytes = new Uint8Array(memory.buffer, bytesPtr);
bytes.set(source);
}
bjs["swift_js_make_js_string"] = function(ptr, len) {
const bytes = new Uint8Array(memory.buffer, ptr, len);
return swift.memory.retain(textDecoder.decode(bytes));
}
bjs["swift_js_init_memory_with_result"] = function(ptr, len) {
const target = new Uint8Array(memory.buffer, ptr, len);
target.set(tmpRetBytes);
tmpRetBytes = undefined;
}
bjs["swift_js_throw"] = function(id) {
tmpRetException = swift.memory.retainByRef(id);
}
bjs["swift_js_retain"] = function(id) {
return swift.memory.retainByRef(id);
}
bjs["swift_js_release"] = function(id) {
swift.memory.release(id);
}
const TestModule = importObject["TestModule"] = {};
TestModule["bjs_createDatabaseConnection"] = function bjs_createDatabaseConnection(config) {
try {
let ret = options.imports.createDatabaseConnection(swift.memory.getObject(config));
return swift.memory.retain(ret);
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_createLogger"] = function bjs_createLogger(level) {
try {
const levelObject = swift.memory.getObject(level);
swift.memory.release(level);
let ret = options.imports.createLogger(levelObject);
return swift.memory.retain(ret);
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_getConfigManager"] = function bjs_getConfigManager() {
try {
let ret = options.imports.getConfigManager();
return swift.memory.retain(ret);
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_DatabaseConnection_isConnected_get"] = function bjs_DatabaseConnection_isConnected_get(self) {
try {
let ret = swift.memory.getObject(self).isConnected;
return ret !== 0;
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_DatabaseConnection_connectionTimeout_get"] = function bjs_DatabaseConnection_connectionTimeout_get(self) {
try {
let ret = swift.memory.getObject(self).connectionTimeout;
return ret;
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_DatabaseConnection_connectionTimeout_set"] = function bjs_DatabaseConnection_connectionTimeout_set(self, newValue) {
try {
swift.memory.getObject(self).connectionTimeout = newValue;
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_DatabaseConnection_connect"] = function bjs_DatabaseConnection_connect(self, url) {
try {
const urlObject = swift.memory.getObject(url);
swift.memory.release(url);
swift.memory.getObject(self).connect(urlObject);
} catch (error) {
setException(error);
}
}
TestModule["bjs_DatabaseConnection_execute"] = function bjs_DatabaseConnection_execute(self, query) {
try {
const queryObject = swift.memory.getObject(query);
swift.memory.release(query);
let ret = swift.memory.getObject(self).execute(queryObject);
return swift.memory.retain(ret);
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_Logger_level_get"] = function bjs_Logger_level_get(self) {
try {
let ret = swift.memory.getObject(self).level;
tmpRetBytes = textEncoder.encode(ret);
return tmpRetBytes.length;
} catch (error) {
setException(error);
}
}
TestModule["bjs_Logger_log"] = function bjs_Logger_log(self, message) {
try {
const messageObject = swift.memory.getObject(message);
swift.memory.release(message);
swift.memory.getObject(self).log(messageObject);
} catch (error) {
setException(error);
}
}
TestModule["bjs_Logger_error"] = function bjs_Logger_error(self, message, error) {
try {
const messageObject = swift.memory.getObject(message);
swift.memory.release(message);
swift.memory.getObject(self).error(messageObject, swift.memory.getObject(error));
} catch (error) {
setException(error);
}
}
TestModule["bjs_ConfigManager_configPath_get"] = function bjs_ConfigManager_configPath_get(self) {
try {
let ret = swift.memory.getObject(self).configPath;
tmpRetBytes = textEncoder.encode(ret);
return tmpRetBytes.length;
} catch (error) {
setException(error);
}
}
TestModule["bjs_ConfigManager_get"] = function bjs_ConfigManager_get(self, key) {
try {
const keyObject = swift.memory.getObject(key);
swift.memory.release(key);
let ret = swift.memory.getObject(self).get(keyObject);
return swift.memory.retain(ret);
} catch (error) {
setException(error);
return 0
}
}
TestModule["bjs_ConfigManager_set"] = function bjs_ConfigManager_set(self, key, value) {
try {
const keyObject = swift.memory.getObject(key);
swift.memory.release(key);
swift.memory.getObject(self).set(keyObject, swift.memory.getObject(value));
} catch (error) {
setException(error);
}
}
},
setInstance: (i) => {
instance = i;
memory = instance.exports.memory;
setException = (error) => {
instance.exports._swift_js_exception.value = swift.memory.retain(error)
}
},
/** @param {WebAssembly.Instance} instance */
createExports: (instance) => {
const js = swift.memory.heap;

return {

};
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
// DO NOT EDIT.
//
// To update this file, just rebuild your project or run
// `swift package bridge-js`.

export interface TypeScriptProcessor {
convert(ts: string): string;
validate(ts: string): boolean;
readonly version: string;
}
export interface CodeGenerator {
generate(input: any): string;
readonly outputFormat: string;
}
export type Exports = {
}
export type Imports = {
createTS2Skeleton(): TypeScriptProcessor;
createCodeGenerator(format: string): CodeGenerator;
}
export function createInstantiator(options: {
imports: Imports;
}, swift: any): Promise<{
addImports: (importObject: WebAssembly.Imports) => void;
setInstance: (instance: WebAssembly.Instance) => void;
createExports: (instance: WebAssembly.Instance) => Exports;
}>;
Loading