Skip to content

Commit bb11466

Browse files
BridgeJS: Fix missing TypeScript interface definitions for imported types
Previously, BridgeJS generated imports referencing types (e.g., createTS2Skeleton(): TS2Skeleton) without generating the corresponding TypeScript interface definitions, causing reference errors. This fix adds generateImportedTypeDefinitions() function to generate export interface declarations for imported types with their methods and properties, ensuring TypeScript definitions are complete. Added test cases to prevent regression with multiple imported types scenarios.
1 parent cc50be3 commit bb11466

File tree

9 files changed

+478
-0
lines changed

9 files changed

+478
-0
lines changed

Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ struct BridgeJSLink {
221221
var dtsLines: [String] = []
222222
dtsLines.append(contentsOf: namespaceDeclarations())
223223
dtsLines.append(contentsOf: dtsClassLines)
224+
dtsLines.append(contentsOf: generateImportedTypeDefinitions())
224225
dtsLines.append("export type Exports = {")
225226
dtsLines.append(contentsOf: dtsExportLines.map { $0.indent(count: 4) })
226227
dtsLines.append("}")
@@ -246,6 +247,38 @@ struct BridgeJSLink {
246247
return (outputJs, outputDts)
247248
}
248249

250+
private func generateImportedTypeDefinitions() -> [String] {
251+
var typeDefinitions: [String] = []
252+
253+
for skeletonSet in importedSkeletons {
254+
for fileSkeleton in skeletonSet.children {
255+
for type in fileSkeleton.types {
256+
typeDefinitions.append("export interface \(type.name) {")
257+
258+
// Add methods
259+
for method in type.methods {
260+
let methodSignature =
261+
"\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType));"
262+
typeDefinitions.append(methodSignature.indent(count: 4))
263+
}
264+
265+
// Add properties
266+
for property in type.properties {
267+
let propertySignature =
268+
property.isReadonly
269+
? "readonly \(property.name): \(property.type.tsType);"
270+
: "\(property.name): \(property.type.tsType);"
271+
typeDefinitions.append(propertySignature.indent(count: 4))
272+
}
273+
274+
typeDefinitions.append("}")
275+
}
276+
}
277+
}
278+
279+
return typeDefinitions
280+
}
281+
249282
private func namespaceDeclarations() -> [String] {
250283
var dtsLines: [String] = []
251284
var namespaceFunctions: [String: [ExportedFunction]] = [:]
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Test case for multiple imported types with methods and properties
2+
export interface DatabaseConnection {
3+
connect(url: string): void;
4+
execute(query: string): any;
5+
readonly isConnected: boolean;
6+
connectionTimeout: number;
7+
}
8+
9+
export interface Logger {
10+
log(message: string): void;
11+
error(message: string, error: any): void;
12+
readonly level: string;
13+
}
14+
15+
export interface ConfigManager {
16+
get(key: string): any;
17+
set(key: string, value: any): void;
18+
readonly configPath: string;
19+
}
20+
21+
export function createDatabaseConnection(config: any): DatabaseConnection;
22+
export function createLogger(level: string): Logger;
23+
export function getConfigManager(): ConfigManager;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Test case similar to the TS2Skeleton use case that caused the original bug
2+
export interface TypeScriptProcessor {
3+
convert(ts: string): string;
4+
validate(ts: string): boolean;
5+
readonly version: string;
6+
}
7+
8+
export interface CodeGenerator {
9+
generate(input: any): string;
10+
readonly outputFormat: string;
11+
}
12+
13+
export function createTS2Skeleton(): TypeScriptProcessor;
14+
export function createCodeGenerator(format: string): CodeGenerator;

Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
// To update this file, just rebuild your project or run
55
// `swift package bridge-js`.
66

7+
export interface Animatable {
8+
animate(keyframes: any, options: any): any;
9+
getAnimations(options: any): any;
10+
}
711
export type Exports = {
812
}
913
export type Imports = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
2+
// DO NOT EDIT.
3+
//
4+
// To update this file, just rebuild your project or run
5+
// `swift package bridge-js`.
6+
7+
export interface DatabaseConnection {
8+
connect(url: string): void;
9+
execute(query: string): any;
10+
readonly isConnected: boolean;
11+
connectionTimeout: number;
12+
}
13+
export interface Logger {
14+
log(message: string): void;
15+
error(message: string, error: any): void;
16+
readonly level: string;
17+
}
18+
export interface ConfigManager {
19+
get(key: string): any;
20+
set(key: string, value: any): void;
21+
readonly configPath: string;
22+
}
23+
export type Exports = {
24+
}
25+
export type Imports = {
26+
createDatabaseConnection(config: any): DatabaseConnection;
27+
createLogger(level: string): Logger;
28+
getConfigManager(): ConfigManager;
29+
}
30+
export function createInstantiator(options: {
31+
imports: Imports;
32+
}, swift: any): Promise<{
33+
addImports: (importObject: WebAssembly.Imports) => void;
34+
setInstance: (instance: WebAssembly.Instance) => void;
35+
createExports: (instance: WebAssembly.Instance) => Exports;
36+
}>;
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
2+
// DO NOT EDIT.
3+
//
4+
// To update this file, just rebuild your project or run
5+
// `swift package bridge-js`.
6+
7+
export async function createInstantiator(options, swift) {
8+
let instance;
9+
let memory;
10+
let setException;
11+
const textDecoder = new TextDecoder("utf-8");
12+
const textEncoder = new TextEncoder("utf-8");
13+
14+
let tmpRetString;
15+
let tmpRetBytes;
16+
let tmpRetException;
17+
return {
18+
/** @param {WebAssembly.Imports} importObject */
19+
addImports: (importObject) => {
20+
const bjs = {};
21+
importObject["bjs"] = bjs;
22+
bjs["swift_js_return_string"] = function(ptr, len) {
23+
const bytes = new Uint8Array(memory.buffer, ptr, len);
24+
tmpRetString = textDecoder.decode(bytes);
25+
}
26+
bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) {
27+
const source = swift.memory.getObject(sourceId);
28+
const bytes = new Uint8Array(memory.buffer, bytesPtr);
29+
bytes.set(source);
30+
}
31+
bjs["swift_js_make_js_string"] = function(ptr, len) {
32+
const bytes = new Uint8Array(memory.buffer, ptr, len);
33+
return swift.memory.retain(textDecoder.decode(bytes));
34+
}
35+
bjs["swift_js_init_memory_with_result"] = function(ptr, len) {
36+
const target = new Uint8Array(memory.buffer, ptr, len);
37+
target.set(tmpRetBytes);
38+
tmpRetBytes = undefined;
39+
}
40+
bjs["swift_js_throw"] = function(id) {
41+
tmpRetException = swift.memory.retainByRef(id);
42+
}
43+
bjs["swift_js_retain"] = function(id) {
44+
return swift.memory.retainByRef(id);
45+
}
46+
bjs["swift_js_release"] = function(id) {
47+
swift.memory.release(id);
48+
}
49+
const TestModule = importObject["TestModule"] = {};
50+
TestModule["bjs_createDatabaseConnection"] = function bjs_createDatabaseConnection(config) {
51+
try {
52+
let ret = options.imports.createDatabaseConnection(swift.memory.getObject(config));
53+
return swift.memory.retain(ret);
54+
} catch (error) {
55+
setException(error);
56+
return 0
57+
}
58+
}
59+
TestModule["bjs_createLogger"] = function bjs_createLogger(level) {
60+
try {
61+
const levelObject = swift.memory.getObject(level);
62+
swift.memory.release(level);
63+
let ret = options.imports.createLogger(levelObject);
64+
return swift.memory.retain(ret);
65+
} catch (error) {
66+
setException(error);
67+
return 0
68+
}
69+
}
70+
TestModule["bjs_getConfigManager"] = function bjs_getConfigManager() {
71+
try {
72+
let ret = options.imports.getConfigManager();
73+
return swift.memory.retain(ret);
74+
} catch (error) {
75+
setException(error);
76+
return 0
77+
}
78+
}
79+
TestModule["bjs_DatabaseConnection_isConnected_get"] = function bjs_DatabaseConnection_isConnected_get(self) {
80+
try {
81+
let ret = swift.memory.getObject(self).isConnected;
82+
return ret !== 0;
83+
} catch (error) {
84+
setException(error);
85+
return 0
86+
}
87+
}
88+
TestModule["bjs_DatabaseConnection_connectionTimeout_get"] = function bjs_DatabaseConnection_connectionTimeout_get(self) {
89+
try {
90+
let ret = swift.memory.getObject(self).connectionTimeout;
91+
return ret;
92+
} catch (error) {
93+
setException(error);
94+
return 0
95+
}
96+
}
97+
TestModule["bjs_DatabaseConnection_connectionTimeout_set"] = function bjs_DatabaseConnection_connectionTimeout_set(self, newValue) {
98+
try {
99+
swift.memory.getObject(self).connectionTimeout = newValue;
100+
} catch (error) {
101+
setException(error);
102+
return 0
103+
}
104+
}
105+
TestModule["bjs_DatabaseConnection_connect"] = function bjs_DatabaseConnection_connect(self, url) {
106+
try {
107+
const urlObject = swift.memory.getObject(url);
108+
swift.memory.release(url);
109+
swift.memory.getObject(self).connect(urlObject);
110+
} catch (error) {
111+
setException(error);
112+
}
113+
}
114+
TestModule["bjs_DatabaseConnection_execute"] = function bjs_DatabaseConnection_execute(self, query) {
115+
try {
116+
const queryObject = swift.memory.getObject(query);
117+
swift.memory.release(query);
118+
let ret = swift.memory.getObject(self).execute(queryObject);
119+
return swift.memory.retain(ret);
120+
} catch (error) {
121+
setException(error);
122+
return 0
123+
}
124+
}
125+
TestModule["bjs_Logger_level_get"] = function bjs_Logger_level_get(self) {
126+
try {
127+
let ret = swift.memory.getObject(self).level;
128+
tmpRetBytes = textEncoder.encode(ret);
129+
return tmpRetBytes.length;
130+
} catch (error) {
131+
setException(error);
132+
}
133+
}
134+
TestModule["bjs_Logger_log"] = function bjs_Logger_log(self, message) {
135+
try {
136+
const messageObject = swift.memory.getObject(message);
137+
swift.memory.release(message);
138+
swift.memory.getObject(self).log(messageObject);
139+
} catch (error) {
140+
setException(error);
141+
}
142+
}
143+
TestModule["bjs_Logger_error"] = function bjs_Logger_error(self, message, error) {
144+
try {
145+
const messageObject = swift.memory.getObject(message);
146+
swift.memory.release(message);
147+
swift.memory.getObject(self).error(messageObject, swift.memory.getObject(error));
148+
} catch (error) {
149+
setException(error);
150+
}
151+
}
152+
TestModule["bjs_ConfigManager_configPath_get"] = function bjs_ConfigManager_configPath_get(self) {
153+
try {
154+
let ret = swift.memory.getObject(self).configPath;
155+
tmpRetBytes = textEncoder.encode(ret);
156+
return tmpRetBytes.length;
157+
} catch (error) {
158+
setException(error);
159+
}
160+
}
161+
TestModule["bjs_ConfigManager_get"] = function bjs_ConfigManager_get(self, key) {
162+
try {
163+
const keyObject = swift.memory.getObject(key);
164+
swift.memory.release(key);
165+
let ret = swift.memory.getObject(self).get(keyObject);
166+
return swift.memory.retain(ret);
167+
} catch (error) {
168+
setException(error);
169+
return 0
170+
}
171+
}
172+
TestModule["bjs_ConfigManager_set"] = function bjs_ConfigManager_set(self, key, value) {
173+
try {
174+
const keyObject = swift.memory.getObject(key);
175+
swift.memory.release(key);
176+
swift.memory.getObject(self).set(keyObject, swift.memory.getObject(value));
177+
} catch (error) {
178+
setException(error);
179+
}
180+
}
181+
},
182+
setInstance: (i) => {
183+
instance = i;
184+
memory = instance.exports.memory;
185+
setException = (error) => {
186+
instance.exports._swift_js_exception.value = swift.memory.retain(error)
187+
}
188+
},
189+
/** @param {WebAssembly.Instance} instance */
190+
createExports: (instance) => {
191+
const js = swift.memory.heap;
192+
193+
return {
194+
195+
};
196+
},
197+
}
198+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit,
2+
// DO NOT EDIT.
3+
//
4+
// To update this file, just rebuild your project or run
5+
// `swift package bridge-js`.
6+
7+
export interface TypeScriptProcessor {
8+
convert(ts: string): string;
9+
validate(ts: string): boolean;
10+
readonly version: string;
11+
}
12+
export interface CodeGenerator {
13+
generate(input: any): string;
14+
readonly outputFormat: string;
15+
}
16+
export type Exports = {
17+
}
18+
export type Imports = {
19+
createTS2Skeleton(): TypeScriptProcessor;
20+
createCodeGenerator(format: string): CodeGenerator;
21+
}
22+
export function createInstantiator(options: {
23+
imports: Imports;
24+
}, swift: any): Promise<{
25+
addImports: (importObject: WebAssembly.Imports) => void;
26+
setInstance: (instance: WebAssembly.Instance) => void;
27+
createExports: (instance: WebAssembly.Instance) => Exports;
28+
}>;

0 commit comments

Comments
 (0)