@openwebf/webf 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CLAUDE.md +206 -0
  2. package/README-zhCN.md +256 -0
  3. package/README.md +232 -0
  4. package/bin/webf.js +25 -0
  5. package/coverage/clover.xml +1295 -0
  6. package/coverage/coverage-final.json +12 -0
  7. package/coverage/lcov-report/IDLBlob.ts.html +142 -0
  8. package/coverage/lcov-report/analyzer.ts.html +2158 -0
  9. package/coverage/lcov-report/analyzer_original.ts.html +1450 -0
  10. package/coverage/lcov-report/base.css +224 -0
  11. package/coverage/lcov-report/block-navigation.js +87 -0
  12. package/coverage/lcov-report/commands.ts.html +700 -0
  13. package/coverage/lcov-report/dart.ts.html +490 -0
  14. package/coverage/lcov-report/declaration.ts.html +337 -0
  15. package/coverage/lcov-report/favicon.png +0 -0
  16. package/coverage/lcov-report/generator.ts.html +1171 -0
  17. package/coverage/lcov-report/index.html +266 -0
  18. package/coverage/lcov-report/logger.ts.html +424 -0
  19. package/coverage/lcov-report/prettify.css +1 -0
  20. package/coverage/lcov-report/prettify.js +2 -0
  21. package/coverage/lcov-report/react.ts.html +619 -0
  22. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  23. package/coverage/lcov-report/sorter.js +196 -0
  24. package/coverage/lcov-report/utils.ts.html +466 -0
  25. package/coverage/lcov-report/vue.ts.html +613 -0
  26. package/coverage/lcov.info +2149 -0
  27. package/global.d.ts +2 -0
  28. package/jest.config.js +24 -0
  29. package/package.json +36 -0
  30. package/src/IDLBlob.ts +20 -0
  31. package/src/analyzer.ts +692 -0
  32. package/src/commands.ts +645 -0
  33. package/src/dart.ts +170 -0
  34. package/src/declaration.ts +84 -0
  35. package/src/generator.ts +454 -0
  36. package/src/logger.ts +114 -0
  37. package/src/react.ts +186 -0
  38. package/src/utils.ts +127 -0
  39. package/src/vue.ts +176 -0
  40. package/templates/class.dart.tpl +86 -0
  41. package/templates/gitignore.tpl +2 -0
  42. package/templates/react.component.tsx.tpl +53 -0
  43. package/templates/react.createComponent.tpl +286 -0
  44. package/templates/react.index.ts.tpl +8 -0
  45. package/templates/react.package.json.tpl +26 -0
  46. package/templates/react.tsconfig.json.tpl +16 -0
  47. package/templates/react.tsup.config.ts.tpl +10 -0
  48. package/templates/tsconfig.json.tpl +8 -0
  49. package/templates/vue.component.partial.tpl +31 -0
  50. package/templates/vue.components.d.ts.tpl +49 -0
  51. package/templates/vue.package.json.tpl +11 -0
  52. package/templates/vue.tsconfig.json.tpl +15 -0
  53. package/test/IDLBlob.test.ts +75 -0
  54. package/test/analyzer.test.ts +370 -0
  55. package/test/commands.test.ts +1253 -0
  56. package/test/generator.test.ts +460 -0
  57. package/test/logger.test.ts +215 -0
  58. package/test/react.test.ts +49 -0
  59. package/test/utils.test.ts +316 -0
  60. package/tsconfig.json +30 -0
@@ -0,0 +1,692 @@
1
+ import ts, {HeritageClause, ScriptTarget, VariableStatement} from 'typescript';
2
+ import {IDLBlob} from './IDLBlob';
3
+ import {
4
+ ClassObject,
5
+ ClassObjectKind,
6
+ FunctionArguments,
7
+ FunctionArgumentType,
8
+ FunctionDeclaration,
9
+ FunctionObject,
10
+ IndexedPropertyDeclaration,
11
+ ParameterMode,
12
+ PropsDeclaration,
13
+ } from './declaration';
14
+ import {isUnionType} from "./utils";
15
+
16
+ interface DefinedPropertyCollector {
17
+ properties: Set<string>;
18
+ files: Set<string>;
19
+ interfaces: Set<string>;
20
+ }
21
+
22
+ export interface UnionTypeCollector {
23
+ types: Set<ParameterType[]>;
24
+ }
25
+
26
+ // Cache for parsed source files to avoid re-parsing
27
+ const sourceFileCache = new Map<string, ts.SourceFile>();
28
+
29
+ // Cache for type conversions to avoid redundant processing
30
+ const typeConversionCache = new Map<string, ParameterType>();
31
+
32
+ // Type mapping constants for better performance
33
+ const BASIC_TYPE_MAP: Partial<Record<ts.SyntaxKind, FunctionArgumentType>> = {
34
+ [ts.SyntaxKind.StringKeyword]: FunctionArgumentType.dom_string,
35
+ [ts.SyntaxKind.NumberKeyword]: FunctionArgumentType.double,
36
+ [ts.SyntaxKind.BooleanKeyword]: FunctionArgumentType.boolean,
37
+ [ts.SyntaxKind.AnyKeyword]: FunctionArgumentType.any,
38
+ [ts.SyntaxKind.ObjectKeyword]: FunctionArgumentType.object,
39
+ [ts.SyntaxKind.VoidKeyword]: FunctionArgumentType.void,
40
+ [ts.SyntaxKind.NullKeyword]: FunctionArgumentType.null,
41
+ [ts.SyntaxKind.UndefinedKeyword]: FunctionArgumentType.undefined,
42
+ };
43
+
44
+ const TYPE_REFERENCE_MAP: Record<string, FunctionArgumentType> = {
45
+ 'Function': FunctionArgumentType.function,
46
+ 'Promise': FunctionArgumentType.promise,
47
+ 'JSArrayProtoMethod': FunctionArgumentType.js_array_proto_methods,
48
+ 'int': FunctionArgumentType.int,
49
+ 'double': FunctionArgumentType.double,
50
+ };
51
+
52
+ export function analyzer(blob: IDLBlob, definedPropertyCollector: DefinedPropertyCollector, unionTypeCollector: UnionTypeCollector) {
53
+ try {
54
+ // Check cache first
55
+ let sourceFile = sourceFileCache.get(blob.source);
56
+ if (!sourceFile) {
57
+ sourceFile = ts.createSourceFile(blob.source, blob.raw, ScriptTarget.ES2020);
58
+ sourceFileCache.set(blob.source, sourceFile);
59
+ }
60
+
61
+ blob.objects = sourceFile.statements
62
+ .map(statement => {
63
+ try {
64
+ return walkProgram(blob, statement, definedPropertyCollector, unionTypeCollector);
65
+ } catch (error) {
66
+ console.error(`Error processing statement in ${blob.source}:`, error);
67
+ return null;
68
+ }
69
+ })
70
+ .filter(o => o instanceof ClassObject || o instanceof FunctionObject) as (FunctionObject | ClassObject)[];
71
+ } catch (error) {
72
+ console.error(`Error analyzing ${blob.source}:`, error);
73
+ throw new Error(`Failed to analyze ${blob.source}: ${error instanceof Error ? error.message : String(error)}`);
74
+ }
75
+ }
76
+
77
+ export function buildClassRelationship() {
78
+ const globalClassMap = ClassObject.globalClassMap;
79
+ const globalClassRelationMap = ClassObject.globalClassRelationMap;
80
+
81
+ // Use more efficient for...in loop
82
+ for (const key in globalClassMap) {
83
+ const obj = globalClassMap[key];
84
+ if (obj.parent) {
85
+ if (!globalClassRelationMap[obj.parent]) {
86
+ globalClassRelationMap[obj.parent] = [];
87
+ }
88
+ globalClassRelationMap[obj.parent].push(obj.name);
89
+ }
90
+ }
91
+ }
92
+
93
+ function getInterfaceName(statement: ts.Statement): string {
94
+ if (!ts.isInterfaceDeclaration(statement)) {
95
+ throw new Error('Statement is not an interface declaration');
96
+ }
97
+ return statement.name.escapedText as string;
98
+ }
99
+
100
+ function getHeritageType(heritage: HeritageClause): string | null {
101
+ if (!heritage.types.length) return null;
102
+
103
+ const expression = heritage.types[0].expression;
104
+ if (ts.isIdentifier(expression)) {
105
+ return expression.escapedText as string;
106
+ }
107
+ return null;
108
+ }
109
+
110
+ function getMixins(heritage: HeritageClause): string[] | null {
111
+ if (heritage.types.length <= 1) return null;
112
+
113
+ const mixins: string[] = [];
114
+ for (let i = 1; i < heritage.types.length; i++) {
115
+ const expression = heritage.types[i].expression;
116
+ if (ts.isIdentifier(expression)) {
117
+ mixins.push(expression.escapedText as string);
118
+ }
119
+ }
120
+ return mixins.length > 0 ? mixins : null;
121
+ }
122
+
123
+ function getPropName(propName: ts.PropertyName, prop?: PropsDeclaration): string {
124
+ switch (propName.kind) {
125
+ case ts.SyntaxKind.Identifier:
126
+ return propName.escapedText.toString();
127
+ case ts.SyntaxKind.StringLiteral:
128
+ return propName.text;
129
+ case ts.SyntaxKind.NumericLiteral:
130
+ return propName.text;
131
+ case ts.SyntaxKind.ComputedPropertyName:
132
+ if (ts.isPropertyAccessExpression(propName.expression)) {
133
+ if (prop) prop.isSymbol = true;
134
+ const expression = propName.expression;
135
+ return `${expression.expression.getText()}_${expression.name.getText()}`;
136
+ }
137
+ throw new Error(`Computed property name of type ${ts.SyntaxKind[propName.expression.kind]} is not supported`);
138
+ default:
139
+ throw new Error(`Property name of type ${ts.SyntaxKind[propName.kind]} is not supported`);
140
+ }
141
+ }
142
+
143
+ function getParameterName(name: ts.BindingName): string {
144
+ if (ts.isIdentifier(name)) {
145
+ return name.escapedText.toString();
146
+ }
147
+ // Handle other binding patterns if needed
148
+ console.warn('Non-identifier parameter names are not fully supported');
149
+ return '';
150
+ }
151
+
152
+ export type ParameterBaseType = FunctionArgumentType | string;
153
+ export type ParameterType = {
154
+ isArray?: boolean;
155
+ value: ParameterType | ParameterType[] | ParameterBaseType;
156
+ };
157
+
158
+ function getParameterBaseType(type: ts.TypeNode, mode?: ParameterMode): ParameterBaseType {
159
+ // Check basic types first (most common case)
160
+ const basicType = BASIC_TYPE_MAP[type.kind];
161
+ if (basicType !== undefined) {
162
+ return basicType;
163
+ }
164
+
165
+ if (type.kind === ts.SyntaxKind.TypeReference) {
166
+ const typeReference = type as ts.TypeReferenceNode;
167
+ const typeName = typeReference.typeName;
168
+
169
+ if (!ts.isIdentifier(typeName)) {
170
+ console.warn('Non-identifier type references are not supported');
171
+ return FunctionArgumentType.any;
172
+ }
173
+
174
+ const identifier = typeName.text;
175
+
176
+ // Check simple type references
177
+ const mappedType = TYPE_REFERENCE_MAP[identifier];
178
+ if (mappedType !== undefined) {
179
+ return mappedType;
180
+ }
181
+
182
+ // Handle special type wrappers
183
+ switch (identifier) {
184
+ case 'NewObject':
185
+ if (mode) mode.newObject = true;
186
+ if (typeReference.typeArguments && typeReference.typeArguments[0]) {
187
+ const argument = typeReference.typeArguments[0];
188
+ if (ts.isTypeReferenceNode(argument) && ts.isIdentifier(argument.typeName)) {
189
+ return argument.typeName.text;
190
+ }
191
+ }
192
+ return FunctionArgumentType.any;
193
+
194
+ case 'DartImpl':
195
+ if (mode) mode.dartImpl = true;
196
+ return handleDartImplType(typeReference, mode);
197
+
198
+ case 'DependentsOnLayout':
199
+ if (mode) mode.layoutDependent = true;
200
+ return handleDependentsOnLayoutType(typeReference, mode);
201
+
202
+ case 'StaticMember':
203
+ if (mode) mode.static = true;
204
+ return handleGenericWrapper(typeReference, mode);
205
+
206
+ case 'StaticMethod':
207
+ if (mode) mode.staticMethod = true;
208
+ return handleGenericWrapper(typeReference, mode);
209
+
210
+ default:
211
+ if (identifier.includes('SupportAsync')) {
212
+ return handleSupportAsyncType(identifier, typeReference, mode);
213
+ }
214
+ return identifier;
215
+ }
216
+ }
217
+
218
+ if (type.kind === ts.SyntaxKind.LiteralType) {
219
+ // Handle literal types - check if it's a null literal
220
+ const literalType = type as ts.LiteralTypeNode;
221
+ if (literalType.literal.kind === ts.SyntaxKind.NullKeyword) {
222
+ return FunctionArgumentType.null;
223
+ }
224
+ return FunctionArgumentType.any;
225
+ }
226
+
227
+ return FunctionArgumentType.any;
228
+ }
229
+
230
+ function handleDartImplType(typeReference: ts.TypeReferenceNode, mode?: ParameterMode): ParameterBaseType {
231
+ if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
232
+ return FunctionArgumentType.any;
233
+ }
234
+
235
+ let argument = typeReference.typeArguments[0];
236
+
237
+ if (ts.isTypeReferenceNode(argument)) {
238
+ const typeName = argument.typeName;
239
+ if (ts.isIdentifier(typeName) && typeName.text === 'DependentsOnLayout') {
240
+ if (mode) mode.layoutDependent = true;
241
+ if (argument.typeArguments && argument.typeArguments[0]) {
242
+ argument = argument.typeArguments[0];
243
+ }
244
+ }
245
+ }
246
+
247
+ return getParameterBaseType(argument, mode);
248
+ }
249
+
250
+ function handleDependentsOnLayoutType(typeReference: ts.TypeReferenceNode, mode?: ParameterMode): ParameterBaseType {
251
+ if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
252
+ return FunctionArgumentType.any;
253
+ }
254
+
255
+ const argument = typeReference.typeArguments[0];
256
+ return getParameterBaseType(argument, mode);
257
+ }
258
+
259
+ function handleGenericWrapper(typeReference: ts.TypeReferenceNode, mode?: ParameterMode): ParameterBaseType {
260
+ if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
261
+ return FunctionArgumentType.any;
262
+ }
263
+
264
+ const argument = typeReference.typeArguments[0];
265
+ return getParameterBaseType(argument, mode);
266
+ }
267
+
268
+ function handleSupportAsyncType(identifier: string, typeReference: ts.TypeReferenceNode, mode?: ParameterMode): ParameterBaseType {
269
+ if (mode) {
270
+ mode.supportAsync = true;
271
+ if (identifier === "SupportAsyncManual") {
272
+ mode.supportAsyncManual = true;
273
+ }
274
+ }
275
+
276
+ if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
277
+ return FunctionArgumentType.any;
278
+ }
279
+
280
+ let argument = typeReference.typeArguments[0];
281
+
282
+ if (ts.isTypeReferenceNode(argument)) {
283
+ const typeName = argument.typeName;
284
+ if (ts.isIdentifier(typeName) && typeName.text === 'DartImpl') {
285
+ if (mode) mode.dartImpl = true;
286
+ if (argument.typeArguments && argument.typeArguments[0]) {
287
+ argument = argument.typeArguments[0];
288
+ }
289
+ }
290
+ } else if (ts.isArrayTypeNode(argument)) {
291
+ if (mode) mode.supportAsyncArrayValue = true;
292
+ return getParameterBaseType(argument.elementType, mode);
293
+ }
294
+
295
+ return getParameterBaseType(argument, mode);
296
+ }
297
+
298
+ function getParameterType(type: ts.TypeNode, unionTypeCollector: UnionTypeCollector, mode?: ParameterMode): ParameterType {
299
+ // Generate cache key
300
+ const cacheKey = `${type.kind}_${type.pos}_${type.end}`;
301
+
302
+ // Check cache first (only for types without mode)
303
+ if (!mode) {
304
+ const cached = typeConversionCache.get(cacheKey);
305
+ if (cached) return cached;
306
+ }
307
+
308
+ let result: ParameterType;
309
+
310
+ if (type.kind === ts.SyntaxKind.ParenthesizedType) {
311
+ const typeNode = type as ts.ParenthesizedTypeNode;
312
+ result = getParameterType(typeNode.type, unionTypeCollector, mode);
313
+ } else if (type.kind === ts.SyntaxKind.ArrayType) {
314
+ const arrayType = type as ts.ArrayTypeNode;
315
+ result = {
316
+ isArray: true,
317
+ value: getParameterType(arrayType.elementType, unionTypeCollector, mode)
318
+ };
319
+ } else if (type.kind === ts.SyntaxKind.UnionType) {
320
+ const unionType = type as ts.UnionTypeNode;
321
+ const types = unionType.types.map(t => getParameterType(t, unionTypeCollector, mode));
322
+ result = {
323
+ isArray: false,
324
+ value: types
325
+ };
326
+ if (isUnionType(result)) {
327
+ unionTypeCollector.types.add(result.value as ParameterType[]);
328
+ }
329
+ } else if (type.kind === ts.SyntaxKind.TypeReference) {
330
+ result = handleTypeReferenceForParameter(type as ts.TypeReferenceNode, unionTypeCollector, mode);
331
+ } else {
332
+ result = {
333
+ isArray: false,
334
+ value: getParameterBaseType(type, mode)
335
+ };
336
+ }
337
+
338
+ // Cache the result if no mode
339
+ if (!mode) {
340
+ typeConversionCache.set(cacheKey, result);
341
+ }
342
+
343
+ return result;
344
+ }
345
+
346
+ function handleTypeReferenceForParameter(typeReference: ts.TypeReferenceNode, unionTypeCollector: UnionTypeCollector, mode?: ParameterMode): ParameterType {
347
+ const typeName = typeReference.typeName;
348
+
349
+ if (!ts.isIdentifier(typeName)) {
350
+ return {
351
+ isArray: false,
352
+ value: FunctionArgumentType.any
353
+ };
354
+ }
355
+
356
+ const identifier = typeName.text;
357
+
358
+ if (identifier.includes('SupportAsync') && typeReference.typeArguments && typeReference.typeArguments[0]) {
359
+ const argument = typeReference.typeArguments[0];
360
+
361
+ if (ts.isUnionTypeNode(argument)) {
362
+ if (mode) {
363
+ mode.supportAsync = true;
364
+ if (identifier === 'SupportAsyncManual') {
365
+ mode.supportAsyncManual = true;
366
+ }
367
+ }
368
+
369
+ const types = argument.types.map(t => getParameterType(t, unionTypeCollector, mode));
370
+ const result = {
371
+ isArray: false,
372
+ value: types
373
+ };
374
+
375
+ if (isUnionType(result)) {
376
+ unionTypeCollector.types.add(result.value as ParameterType[]);
377
+ }
378
+
379
+ return result;
380
+ }
381
+ }
382
+
383
+ return {
384
+ isArray: false,
385
+ value: getParameterBaseType(typeReference, mode)
386
+ };
387
+ }
388
+
389
+ function paramsNodeToArguments(parameter: ts.ParameterDeclaration, unionTypeCollector: UnionTypeCollector): FunctionArguments {
390
+ const args = new FunctionArguments();
391
+ args.name = getParameterName(parameter.name);
392
+
393
+ if (!parameter.type) {
394
+ console.warn(`Parameter ${args.name} has no type annotation, defaulting to any`);
395
+ args.type = { isArray: false, value: FunctionArgumentType.any };
396
+ return args;
397
+ }
398
+
399
+ const typeMode = new ParameterMode();
400
+ args.type = getParameterType(parameter.type, unionTypeCollector, typeMode);
401
+ args.isDotDotDot = !!parameter.dotDotDotToken;
402
+ args.typeMode = typeMode;
403
+ args.required = !parameter.questionToken;
404
+
405
+ return args;
406
+ }
407
+
408
+ function isParamsReadOnly(m: ts.PropertySignature): boolean {
409
+ if (!m.modifiers) return false;
410
+ return m.modifiers.some(k => k.kind === ts.SyntaxKind.ReadonlyKeyword);
411
+ }
412
+
413
+ function walkProgram(blob: IDLBlob, statement: ts.Statement, definedPropertyCollector: DefinedPropertyCollector, unionTypeCollector: UnionTypeCollector) {
414
+ switch(statement.kind) {
415
+ case ts.SyntaxKind.InterfaceDeclaration:
416
+ return processInterfaceDeclaration(statement as ts.InterfaceDeclaration, blob, definedPropertyCollector, unionTypeCollector);
417
+
418
+ case ts.SyntaxKind.VariableStatement:
419
+ return processVariableStatement(statement as VariableStatement, unionTypeCollector);
420
+
421
+ default:
422
+ return null;
423
+ }
424
+ }
425
+
426
+ function processInterfaceDeclaration(
427
+ statement: ts.InterfaceDeclaration,
428
+ blob: IDLBlob,
429
+ definedPropertyCollector: DefinedPropertyCollector,
430
+ unionTypeCollector: UnionTypeCollector
431
+ ): ClassObject | null {
432
+ const interfaceName = statement.name.escapedText.toString();
433
+ const obj = new ClassObject();
434
+
435
+ // Process heritage clauses
436
+ if (statement.heritageClauses) {
437
+ const heritage = statement.heritageClauses[0];
438
+ const heritageType = getHeritageType(heritage);
439
+ const mixins = getMixins(heritage);
440
+
441
+ if (heritageType) obj.parent = heritageType;
442
+ if (mixins) obj.mixinParent = mixins;
443
+ }
444
+
445
+ obj.name = interfaceName;
446
+
447
+ if (obj.kind === ClassObjectKind.interface) {
448
+ definedPropertyCollector.interfaces.add('QJS' + interfaceName);
449
+ definedPropertyCollector.files.add(blob.filename);
450
+ definedPropertyCollector.properties.add(interfaceName);
451
+ }
452
+
453
+ // Process members in batches for better performance
454
+ const members = Array.from(statement.members);
455
+ processMembersBatch(members, obj, definedPropertyCollector, unionTypeCollector);
456
+
457
+ ClassObject.globalClassMap[interfaceName] = obj;
458
+
459
+ return obj;
460
+ }
461
+
462
+ function processMembersBatch(
463
+ members: ts.TypeElement[],
464
+ obj: ClassObject,
465
+ definedPropertyCollector: DefinedPropertyCollector,
466
+ unionTypeCollector: UnionTypeCollector
467
+ ): void {
468
+ for (const member of members) {
469
+ try {
470
+ processMember(member, obj, definedPropertyCollector, unionTypeCollector);
471
+ } catch (error) {
472
+ console.error(`Error processing member:`, error);
473
+ }
474
+ }
475
+ }
476
+
477
+ function processMember(
478
+ member: ts.TypeElement,
479
+ obj: ClassObject,
480
+ definedPropertyCollector: DefinedPropertyCollector,
481
+ unionTypeCollector: UnionTypeCollector
482
+ ): void {
483
+ switch(member.kind) {
484
+ case ts.SyntaxKind.PropertySignature:
485
+ processPropertySignature(member as ts.PropertySignature, obj, definedPropertyCollector, unionTypeCollector);
486
+ break;
487
+
488
+ case ts.SyntaxKind.MethodSignature:
489
+ processMethodSignature(member as ts.MethodSignature, obj, unionTypeCollector);
490
+ break;
491
+
492
+ case ts.SyntaxKind.IndexSignature:
493
+ processIndexSignature(member as ts.IndexSignatureDeclaration, obj, unionTypeCollector);
494
+ break;
495
+
496
+ case ts.SyntaxKind.ConstructSignature:
497
+ processConstructSignature(member as ts.ConstructSignatureDeclaration, obj, unionTypeCollector);
498
+ break;
499
+ }
500
+ }
501
+
502
+ function processPropertySignature(
503
+ member: ts.PropertySignature,
504
+ obj: ClassObject,
505
+ definedPropertyCollector: DefinedPropertyCollector,
506
+ unionTypeCollector: UnionTypeCollector
507
+ ): void {
508
+ const prop = new PropsDeclaration();
509
+ prop.name = getPropName(member.name!, prop);
510
+ prop.readonly = isParamsReadOnly(member);
511
+
512
+ definedPropertyCollector.properties.add(prop.name);
513
+
514
+ if (!member.type) {
515
+ console.warn(`Property ${prop.name} has no type annotation`);
516
+ return;
517
+ }
518
+
519
+ const mode = new ParameterMode();
520
+ prop.type = getParameterType(member.type, unionTypeCollector, mode);
521
+ prop.typeMode = mode;
522
+ prop.optional = !!member.questionToken;
523
+
524
+ if (prop.type.value === FunctionArgumentType.function && ts.isFunctionTypeNode(member.type)) {
525
+ const functionProps = prop as FunctionDeclaration;
526
+ functionProps.args = member.type.parameters.map(params =>
527
+ paramsNodeToArguments(params, unionTypeCollector)
528
+ );
529
+ obj.methods.push(functionProps);
530
+ } else {
531
+ obj.props.push(prop);
532
+
533
+ // Handle async properties
534
+ if (prop.typeMode.supportAsync) {
535
+ const asyncProp = createAsyncProperty(prop, mode);
536
+ definedPropertyCollector.properties.add(asyncProp.name);
537
+ obj.props.push(asyncProp);
538
+ }
539
+ }
540
+ }
541
+
542
+ function createAsyncProperty(prop: PropsDeclaration, mode: ParameterMode): PropsDeclaration {
543
+ const asyncProp = Object.assign({}, prop);
544
+ const syncMode = Object.assign({}, mode);
545
+
546
+ syncMode.supportAsync = false;
547
+ syncMode.supportAsyncManual = false;
548
+ prop.typeMode = syncMode;
549
+
550
+ asyncProp.name = asyncProp.name + '_async';
551
+ asyncProp.async_type = {
552
+ isArray: false,
553
+ value: FunctionArgumentType.promise
554
+ };
555
+
556
+ return asyncProp;
557
+ }
558
+
559
+ function processMethodSignature(
560
+ member: ts.MethodSignature,
561
+ obj: ClassObject,
562
+ unionTypeCollector: UnionTypeCollector
563
+ ): void {
564
+ const f = new FunctionDeclaration();
565
+ f.name = getPropName(member.name!);
566
+ f.args = member.parameters.map(params =>
567
+ paramsNodeToArguments(params, unionTypeCollector)
568
+ );
569
+
570
+ if (member.type) {
571
+ const mode = new ParameterMode();
572
+ f.returnType = getParameterType(member.type, unionTypeCollector, mode);
573
+
574
+ if (mode.supportAsyncArrayValue) {
575
+ f.returnType = {
576
+ isArray: true,
577
+ value: f.returnType
578
+ };
579
+ }
580
+
581
+ f.returnTypeMode = mode;
582
+
583
+ if (f.returnTypeMode.staticMethod) {
584
+ obj.staticMethods.push(f);
585
+ }
586
+ }
587
+
588
+ obj.methods.push(f);
589
+
590
+ // Handle async methods
591
+ if (f.returnTypeMode?.supportAsync) {
592
+ const asyncFunc = createAsyncMethod(member, f, unionTypeCollector);
593
+ obj.methods.push(asyncFunc);
594
+ }
595
+ }
596
+
597
+ function createAsyncMethod(
598
+ member: ts.MethodSignature,
599
+ originalFunc: FunctionDeclaration,
600
+ unionTypeCollector: UnionTypeCollector
601
+ ): FunctionDeclaration {
602
+ const asyncFunc = Object.assign({}, originalFunc);
603
+ const mode = Object.assign({}, originalFunc.returnTypeMode);
604
+
605
+ mode.supportAsync = false;
606
+ mode.supportAsyncManual = false;
607
+ originalFunc.returnTypeMode = mode;
608
+
609
+ asyncFunc.name = getPropName(member.name!) + '_async';
610
+ asyncFunc.args = member.parameters.map(params =>
611
+ paramsNodeToArguments(params, unionTypeCollector)
612
+ );
613
+ asyncFunc.async_returnType = {
614
+ isArray: false,
615
+ value: FunctionArgumentType.promise
616
+ };
617
+
618
+ return asyncFunc;
619
+ }
620
+
621
+ function processIndexSignature(
622
+ member: ts.IndexSignatureDeclaration,
623
+ obj: ClassObject,
624
+ unionTypeCollector: UnionTypeCollector
625
+ ): void {
626
+ const prop = new IndexedPropertyDeclaration();
627
+ const modifier = member.modifiers;
628
+ prop.readonly = !!(modifier && modifier[0].kind === ts.SyntaxKind.ReadonlyKeyword);
629
+
630
+ const params = member.parameters;
631
+ if (params.length > 0 && params[0].type) {
632
+ prop.indexKeyType = params[0].type.kind === ts.SyntaxKind.NumberKeyword ? 'number' : 'string';
633
+ }
634
+
635
+ const mode = new ParameterMode();
636
+ prop.type = getParameterType(member.type, unionTypeCollector, mode);
637
+ prop.typeMode = mode;
638
+ obj.indexedProp = prop;
639
+ }
640
+
641
+ function processConstructSignature(
642
+ member: ts.ConstructSignatureDeclaration,
643
+ obj: ClassObject,
644
+ unionTypeCollector: UnionTypeCollector
645
+ ): void {
646
+ const c = new FunctionDeclaration();
647
+ c.name = 'constructor';
648
+ c.args = member.parameters.map(params =>
649
+ paramsNodeToArguments(params, unionTypeCollector)
650
+ );
651
+
652
+ if (member.type) {
653
+ c.returnType = getParameterType(member.type, unionTypeCollector);
654
+ }
655
+
656
+ obj.construct = c;
657
+ }
658
+
659
+ function processVariableStatement(
660
+ statement: VariableStatement,
661
+ unionTypeCollector: UnionTypeCollector
662
+ ): FunctionObject | null {
663
+ const declaration = statement.declarationList.declarations[0];
664
+
665
+ if (!ts.isIdentifier(declaration.name)) {
666
+ console.warn('Variable declaration with non-identifier name is not supported');
667
+ return null;
668
+ }
669
+
670
+ const methodName = declaration.name.text;
671
+ const type = declaration.type;
672
+
673
+ if (!type || !ts.isFunctionTypeNode(type)) {
674
+ return null;
675
+ }
676
+
677
+ const functionObject = new FunctionObject();
678
+ functionObject.declare = new FunctionDeclaration();
679
+ functionObject.declare.name = methodName;
680
+ functionObject.declare.args = type.parameters.map(param =>
681
+ paramsNodeToArguments(param, unionTypeCollector)
682
+ );
683
+ functionObject.declare.returnType = getParameterType(type.type, unionTypeCollector);
684
+
685
+ return functionObject;
686
+ }
687
+
688
+ // Clear caches when needed (e.g., between runs)
689
+ export function clearCaches() {
690
+ sourceFileCache.clear();
691
+ typeConversionCache.clear();
692
+ }