@openwebf/webf 0.24.1 → 0.24.3

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.
package/dist/module.js CHANGED
@@ -14,12 +14,12 @@ function parseModuleDefinition(modulePath) {
14
14
  const sourceFile = typescript_1.default.createSourceFile(modulePath, sourceText, typescript_1.default.ScriptTarget.ES2020, true, typescript_1.default.ScriptKind.TS);
15
15
  let interfaceDecl;
16
16
  const supporting = [];
17
+ const webfInterfaceDecls = [];
17
18
  for (const stmt of sourceFile.statements) {
18
19
  if (typescript_1.default.isInterfaceDeclaration(stmt)) {
19
20
  const name = stmt.name.text;
20
- if (!interfaceDecl && name.startsWith('WebF')) {
21
- interfaceDecl = stmt;
22
- }
21
+ if (name.startsWith('WebF'))
22
+ webfInterfaceDecls.push(stmt);
23
23
  supporting.push(stmt);
24
24
  }
25
25
  else if (typescript_1.default.isTypeAliasDeclaration(stmt) ||
@@ -28,6 +28,13 @@ function parseModuleDefinition(modulePath) {
28
28
  supporting.push(stmt);
29
29
  }
30
30
  }
31
+ // Prefer the "main module interface": first WebF* interface that declares methods.
32
+ if (!interfaceDecl) {
33
+ interfaceDecl = webfInterfaceDecls.find(decl => decl.members.some(member => typescript_1.default.isMethodSignature(member)));
34
+ }
35
+ if (!interfaceDecl) {
36
+ interfaceDecl = webfInterfaceDecls[0];
37
+ }
31
38
  if (!interfaceDecl) {
32
39
  throw new Error(`No interface starting with "WebF" found in module interface file: ${modulePath}`);
33
40
  }
@@ -70,10 +77,65 @@ function parseModuleDefinition(modulePath) {
70
77
  documentation,
71
78
  });
72
79
  }
80
+ // Optional module events declaration:
81
+ // `interface WebF<ModuleName>ModuleEvents { scanResult: [Event, Payload]; }`
82
+ const eventsInterfaceName = `${interfaceName}ModuleEvents`;
83
+ const eventsDecl = sourceFile.statements.find(stmt => typescript_1.default.isInterfaceDeclaration(stmt) && stmt.name.text === eventsInterfaceName);
84
+ let events;
85
+ if (eventsDecl) {
86
+ const eventSpecs = [];
87
+ for (const member of eventsDecl.members) {
88
+ if (!typescript_1.default.isPropertySignature(member) || !member.name)
89
+ continue;
90
+ const rawName = member.name.getText(sourceFile);
91
+ const eventName = rawName.replace(/['"]/g, '');
92
+ let eventTypeText = 'Event';
93
+ let extraTypeText = 'any';
94
+ if (member.type) {
95
+ if (typescript_1.default.isTupleTypeNode(member.type) && member.type.elements.length === 2) {
96
+ eventTypeText = printer.printNode(typescript_1.default.EmitHint.Unspecified, member.type.elements[0], sourceFile);
97
+ extraTypeText = printer.printNode(typescript_1.default.EmitHint.Unspecified, member.type.elements[1], sourceFile);
98
+ }
99
+ else {
100
+ eventTypeText = printer.printNode(typescript_1.default.EmitHint.Unspecified, member.type, sourceFile);
101
+ }
102
+ }
103
+ let documentation;
104
+ const jsDocs = member.jsDoc;
105
+ if (jsDocs && jsDocs.length > 0) {
106
+ documentation = jsDocs
107
+ .map(doc => doc.comment)
108
+ .filter(Boolean)
109
+ .join('\n');
110
+ }
111
+ eventSpecs.push({
112
+ name: eventName,
113
+ eventTypeText,
114
+ extraTypeText,
115
+ documentation,
116
+ });
117
+ }
118
+ if (eventSpecs.length > 0) {
119
+ events = {
120
+ interfaceName: eventsInterfaceName,
121
+ eventNameTypeName: `${interfaceName}ModuleEventName`,
122
+ eventArgsTypeName: `${interfaceName}ModuleEventArgs`,
123
+ listenerTypeName: `${interfaceName}ModuleEventListener`,
124
+ events: eventSpecs,
125
+ };
126
+ }
127
+ }
73
128
  if (methods.length === 0) {
74
129
  throw new Error(`Interface ${interfaceName} in ${modulePath} does not declare any methods`);
75
130
  }
76
- return { interfaceName, moduleName, methods, supportingStatements: supporting, sourceFile };
131
+ return {
132
+ interfaceName,
133
+ moduleName,
134
+ methods,
135
+ events,
136
+ supportingStatements: supporting,
137
+ sourceFile,
138
+ };
77
139
  }
78
140
  function buildTypesFile(def) {
79
141
  const printer = typescript_1.default.createPrinter();
@@ -102,6 +164,19 @@ function buildTypesFile(def) {
102
164
  lines.push(printed);
103
165
  }
104
166
  lines.push('');
167
+ if (def.events) {
168
+ const { interfaceName, eventNameTypeName, eventArgsTypeName, listenerTypeName } = def.events;
169
+ lines.push(`export type ${eventNameTypeName} = Extract<keyof ${interfaceName}, string>;`);
170
+ lines.push(`export type ${eventArgsTypeName}<K extends ${eventNameTypeName} = ${eventNameTypeName}> =`);
171
+ lines.push(` ${interfaceName}[K] extends readonly [infer E, infer X]`);
172
+ lines.push(` ? [event: (E & { type: K }), extra: X]`);
173
+ lines.push(` : [event: (${interfaceName}[K] & { type: K }), extra: any];`);
174
+ lines.push('');
175
+ lines.push(`export type ${listenerTypeName} = (...args: {`);
176
+ lines.push(` [K in ${eventNameTypeName}]: ${eventArgsTypeName}<K>;`);
177
+ lines.push(`}[${eventNameTypeName}]) => any;`);
178
+ lines.push('');
179
+ }
105
180
  // Ensure file is treated as a module even if no declarations were emitted.
106
181
  lines.push('export {};');
107
182
  return lines.join('\n');
@@ -128,6 +203,10 @@ function buildIndexFile(def) {
128
203
  typeImportNames.add(name);
129
204
  }
130
205
  }
206
+ if (def.events) {
207
+ typeImportNames.add(def.events.eventNameTypeName);
208
+ typeImportNames.add(def.events.eventArgsTypeName);
209
+ }
131
210
  const typeImportsSorted = Array.from(typeImportNames).sort();
132
211
  if (typeImportsSorted.length > 0) {
133
212
  lines.push(`import type { ${typeImportsSorted.join(', ')} } from './types';`);
@@ -138,6 +217,49 @@ function buildIndexFile(def) {
138
217
  lines.push(" return typeof webf !== 'undefined' && typeof (webf as any).invokeModuleAsync === 'function';");
139
218
  lines.push(' }');
140
219
  lines.push('');
220
+ if (def.events) {
221
+ lines.push(' private static _moduleListenerInstalled = false;');
222
+ lines.push(' private static _listeners: Record<string, Set<(event: Event, extra: any) => any>> = Object.create(null);');
223
+ lines.push('');
224
+ lines.push(` static addListener<K extends ${def.events.eventNameTypeName}>(type: K, listener: (...args: ${def.events.eventArgsTypeName}<K>) => any): () => void {`);
225
+ lines.push(" if (typeof webf === 'undefined' || typeof (webf as any).addWebfModuleListener !== 'function') {");
226
+ lines.push(" throw new Error('WebF module event API is not available. Make sure you are running in WebF runtime.');");
227
+ lines.push(' }');
228
+ lines.push('');
229
+ lines.push(' if (!this._moduleListenerInstalled) {');
230
+ lines.push(` (webf as any).addWebfModuleListener('${def.moduleName}', (event: Event, extra: any) => {`);
231
+ lines.push(' const set = this._listeners[event.type];');
232
+ lines.push(' if (!set) return;');
233
+ lines.push(' for (const fn of set) { fn(event, extra); }');
234
+ lines.push(' });');
235
+ lines.push(' this._moduleListenerInstalled = true;');
236
+ lines.push(' }');
237
+ lines.push('');
238
+ lines.push(' (this._listeners[type] ??= new Set()).add(listener as any);');
239
+ lines.push('');
240
+ lines.push(' const cls = this;');
241
+ lines.push(' return () => {');
242
+ lines.push(' const set = cls._listeners[type];');
243
+ lines.push(' if (!set) return;');
244
+ lines.push(' set.delete(listener as any);');
245
+ lines.push(' if (set.size === 0) { delete cls._listeners[type]; }');
246
+ lines.push('');
247
+ lines.push(' if (Object.keys(cls._listeners).length === 0) {');
248
+ lines.push(' cls.removeListener();');
249
+ lines.push(' }');
250
+ lines.push(' };');
251
+ lines.push(' }');
252
+ lines.push('');
253
+ lines.push(' static removeListener(): void {');
254
+ lines.push(' this._listeners = Object.create(null);');
255
+ lines.push(' this._moduleListenerInstalled = false;');
256
+ lines.push(" if (typeof webf === 'undefined' || typeof (webf as any).removeWebfModuleListener !== 'function') {");
257
+ lines.push(' return;');
258
+ lines.push(' }');
259
+ lines.push(` (webf as any).removeWebfModuleListener('${def.moduleName}');`);
260
+ lines.push(' }');
261
+ lines.push('');
262
+ }
141
263
  for (const method of def.methods) {
142
264
  if (method.documentation) {
143
265
  lines.push(' /**');
@@ -173,6 +295,11 @@ function buildIndexFile(def) {
173
295
  typeExportNames.add(stmt.name.text);
174
296
  }
175
297
  }
298
+ if (def.events) {
299
+ typeExportNames.add(def.events.eventNameTypeName);
300
+ typeExportNames.add(def.events.eventArgsTypeName);
301
+ typeExportNames.add(def.events.listenerTypeName);
302
+ }
176
303
  const sorted = Array.from(typeExportNames).sort();
177
304
  if (sorted.length) {
178
305
  lines.push(' ' + sorted.join(','));
@@ -260,6 +387,7 @@ function mapTsPropertyTypeToDart(type, optional) {
260
387
  }
261
388
  }
262
389
  function buildDartBindings(def, command) {
390
+ var _a;
263
391
  const dartClassBase = `${def.moduleName}Module`;
264
392
  const dartBindingsClass = `${dartClassBase}Bindings`;
265
393
  const lines = [];
@@ -268,6 +396,9 @@ function buildDartBindings(def, command) {
268
396
  lines.push('// Generated by `webf module-codegen`');
269
397
  lines.push('');
270
398
  lines.push("import 'package:webf/module.dart';");
399
+ if (def.events) {
400
+ lines.push("import 'package:webf/dom.dart';");
401
+ }
271
402
  if (def.methods.some(m => m.params.some(p => isTsByteArrayUnion(p.typeText)))) {
272
403
  lines.push("import 'package:webf/bridge.dart';");
273
404
  }
@@ -275,7 +406,9 @@ function buildDartBindings(def, command) {
275
406
  // Generate Dart classes for supporting TS interfaces (compound option types).
276
407
  const optionInterfaces = [];
277
408
  for (const stmt of def.supportingStatements) {
278
- if (typescript_1.default.isInterfaceDeclaration(stmt) && stmt.name.text !== def.interfaceName) {
409
+ if (typescript_1.default.isInterfaceDeclaration(stmt) &&
410
+ stmt.name.text !== def.interfaceName &&
411
+ stmt.name.text !== ((_a = def.events) === null || _a === void 0 ? void 0 : _a.interfaceName)) {
279
412
  optionInterfaces.push(stmt);
280
413
  }
281
414
  }
@@ -365,6 +498,25 @@ function buildDartBindings(def, command) {
365
498
  lines.push(` @override`);
366
499
  lines.push(` String get name => '${def.moduleName}';`);
367
500
  lines.push('');
501
+ if (def.events) {
502
+ for (const evt of def.events.events) {
503
+ const methodName = `emit${lodash_1.default.upperFirst(lodash_1.default.camelCase(evt.name))}`;
504
+ const mappedExtra = mapTsParamTypeToDart(evt.extraTypeText, optionTypeNames);
505
+ const dataParamType = mappedExtra.optionClassName
506
+ ? `${mappedExtra.optionClassName}?`
507
+ : 'dynamic';
508
+ lines.push(` dynamic ${methodName}({Event? event, ${dataParamType} data}) {`);
509
+ if (mappedExtra.optionClassName) {
510
+ lines.push(' final mapped = data?.toMap();');
511
+ lines.push(` return dispatchEvent(event: event ?? Event('${evt.name}'), data: mapped);`);
512
+ }
513
+ else {
514
+ lines.push(` return dispatchEvent(event: event ?? Event('${evt.name}'), data: data);`);
515
+ }
516
+ lines.push(' }');
517
+ lines.push('');
518
+ }
519
+ }
368
520
  for (const method of def.methods) {
369
521
  const dartMethodName = lodash_1.default.camelCase(method.name);
370
522
  let dartReturnType = mapTsReturnTypeToDart(method.returnTypeText);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwebf/webf",
3
- "version": "0.24.1",
3
+ "version": "0.24.3",
4
4
  "description": "Command line tools for WebF",
5
5
  "main": "index.js",
6
6
  "bin": {
package/src/analyzer.ts CHANGED
@@ -410,6 +410,188 @@ function handleGenericWrapper(typeReference: ts.TypeReferenceNode, mode?: Parame
410
410
  return getParameterBaseType(argument, mode);
411
411
  }
412
412
 
413
+ const customEventTypePrinter = ts.createPrinter({ removeComments: true });
414
+
415
+ function mapTypeReferenceIdentifierToTsType(identifier: string): string | null {
416
+ const mappedType = TYPE_REFERENCE_MAP[identifier];
417
+ if (mappedType === undefined) return null;
418
+ switch (mappedType) {
419
+ case FunctionArgumentType.boolean:
420
+ return 'boolean';
421
+ case FunctionArgumentType.dom_string:
422
+ return 'string';
423
+ case FunctionArgumentType.double:
424
+ case FunctionArgumentType.int:
425
+ return 'number';
426
+ case FunctionArgumentType.any:
427
+ return 'any';
428
+ case FunctionArgumentType.void:
429
+ return 'void';
430
+ case FunctionArgumentType.function:
431
+ return 'Function';
432
+ case FunctionArgumentType.promise:
433
+ return 'Promise<any>';
434
+ default:
435
+ return null;
436
+ }
437
+ }
438
+
439
+ function getBasicTypeKindAsTsType(kind: ts.SyntaxKind): string | null {
440
+ const basicType = BASIC_TYPE_MAP[kind];
441
+ if (basicType === undefined) return null;
442
+ switch (basicType) {
443
+ case FunctionArgumentType.boolean:
444
+ return 'boolean';
445
+ case FunctionArgumentType.dom_string:
446
+ return 'string';
447
+ case FunctionArgumentType.double:
448
+ case FunctionArgumentType.int:
449
+ return 'number';
450
+ case FunctionArgumentType.any:
451
+ return 'any';
452
+ case FunctionArgumentType.void:
453
+ return 'void';
454
+ case FunctionArgumentType.null:
455
+ return 'null';
456
+ case FunctionArgumentType.undefined:
457
+ return 'undefined';
458
+ default:
459
+ return null;
460
+ }
461
+ }
462
+
463
+ function stringifyEntityName(name: ts.EntityName): string {
464
+ if (ts.isIdentifier(name)) return name.text;
465
+ return `${stringifyEntityName(name.left)}.${name.right.text}`;
466
+ }
467
+
468
+ function safePrintCustomEventNode(node: ts.Node): string {
469
+ const sourceFile = node.getSourceFile();
470
+ const printed = customEventTypePrinter.printNode(ts.EmitHint.Unspecified, node, sourceFile);
471
+ // Ensure WebF IDL-like aliases used in type definitions do not leak into generated TypeScript packages.
472
+ return printed.replace(/\bint\b/g, 'number').replace(/\bdouble\b/g, 'number');
473
+ }
474
+
475
+ function stringifyCustomEventGenericTypeNode(typeNode: ts.TypeNode): string | null {
476
+ if (ts.isParenthesizedTypeNode(typeNode)) {
477
+ const inner = stringifyCustomEventGenericTypeNode(typeNode.type);
478
+ return inner ? `(${inner})` : null;
479
+ }
480
+
481
+ if (ts.isUnionTypeNode(typeNode)) {
482
+ const parts = typeNode.types.map(t => stringifyCustomEventGenericTypeNode(t)).filter((t): t is string => Boolean(t));
483
+ return parts.length === typeNode.types.length ? parts.join(' | ') : null;
484
+ }
485
+
486
+ if (ts.isIntersectionTypeNode(typeNode)) {
487
+ const parts = typeNode.types.map(t => stringifyCustomEventGenericTypeNode(t)).filter((t): t is string => Boolean(t));
488
+ return parts.length === typeNode.types.length ? parts.join(' & ') : null;
489
+ }
490
+
491
+ if (ts.isArrayTypeNode(typeNode)) {
492
+ const element = stringifyCustomEventGenericTypeNode(typeNode.elementType);
493
+ return element ? `${element}[]` : null;
494
+ }
495
+
496
+ if (ts.isTupleTypeNode(typeNode)) {
497
+ const elements = typeNode.elements.map(e => stringifyCustomEventGenericTypeNode(e)).filter((t): t is string => Boolean(t));
498
+ return elements.length === typeNode.elements.length ? `[${elements.join(', ')}]` : null;
499
+ }
500
+
501
+ if (ts.isLiteralTypeNode(typeNode)) {
502
+ const literal = typeNode.literal;
503
+ if (literal.kind === ts.SyntaxKind.NullKeyword) return 'null';
504
+ if (literal.kind === ts.SyntaxKind.UndefinedKeyword) return 'undefined';
505
+ if (literal.kind === ts.SyntaxKind.TrueKeyword) return 'true';
506
+ if (literal.kind === ts.SyntaxKind.FalseKeyword) return 'false';
507
+ if (ts.isStringLiteral(literal)) return JSON.stringify(literal.text);
508
+ if (ts.isNumericLiteral(literal)) return literal.text;
509
+ return null;
510
+ }
511
+
512
+ const basic = getBasicTypeKindAsTsType(typeNode.kind);
513
+ if (basic) return basic;
514
+
515
+ if (ts.isTypeReferenceNode(typeNode)) {
516
+ const typeName = stringifyEntityName(typeNode.typeName);
517
+
518
+ // Unwrap internal helpers used by WebF typings.
519
+ if (typeName === 'DartImpl' && typeNode.typeArguments && typeNode.typeArguments[0]) {
520
+ return stringifyCustomEventGenericTypeNode(typeNode.typeArguments[0]);
521
+ }
522
+
523
+ if (typeName === 'Promise') {
524
+ if (!typeNode.typeArguments || !typeNode.typeArguments[0]) return 'Promise<any>';
525
+ const inner = stringifyCustomEventGenericTypeNode(typeNode.typeArguments[0]);
526
+ return inner ? `Promise<${inner}>` : null;
527
+ }
528
+
529
+ const mapped = mapTypeReferenceIdentifierToTsType(typeName);
530
+ if (mapped) return mapped;
531
+
532
+ if (!typeNode.typeArguments || typeNode.typeArguments.length === 0) {
533
+ return typeName;
534
+ }
535
+
536
+ const args = typeNode.typeArguments
537
+ .map(arg => stringifyCustomEventGenericTypeNode(arg))
538
+ .filter((t): t is string => Boolean(t));
539
+ if (args.length !== typeNode.typeArguments.length) return null;
540
+ return `${typeName}<${args.join(', ')}>`;
541
+ }
542
+
543
+ if (ts.isTypeLiteralNode(typeNode)) {
544
+ const members: string[] = [];
545
+ for (const member of typeNode.members) {
546
+ if (ts.isPropertySignature(member) && member.type) {
547
+ const typeString = stringifyCustomEventGenericTypeNode(member.type);
548
+ if (!typeString) return null;
549
+ let nameText: string;
550
+ if (ts.isIdentifier(member.name)) nameText = member.name.text;
551
+ else if (ts.isStringLiteral(member.name)) nameText = JSON.stringify(member.name.text);
552
+ else if (ts.isNumericLiteral(member.name)) nameText = member.name.text;
553
+ else nameText = member.name.getText();
554
+ const optional = member.questionToken ? '?' : '';
555
+ members.push(`${nameText}${optional}: ${typeString}`);
556
+ continue;
557
+ }
558
+ if (ts.isIndexSignatureDeclaration(member) && member.type && member.parameters.length === 1) {
559
+ const param = member.parameters[0];
560
+ const paramName = ts.isIdentifier(param.name) ? param.name.text : param.name.getText();
561
+ const paramType = param.type ? stringifyCustomEventGenericTypeNode(param.type) : 'string';
562
+ const valueType = stringifyCustomEventGenericTypeNode(member.type);
563
+ if (!paramType || !valueType) return null;
564
+ members.push(`[${paramName}: ${paramType}]: ${valueType}`);
565
+ continue;
566
+ }
567
+ // Fallback for uncommon members (call signatures, method signatures, etc.).
568
+ members.push(safePrintCustomEventNode(member));
569
+ }
570
+ return `{ ${members.join('; ')} }`;
571
+ }
572
+
573
+ if (ts.isTypeOperatorNode(typeNode)) {
574
+ const inner = stringifyCustomEventGenericTypeNode(typeNode.type);
575
+ if (!inner) return null;
576
+ const operator =
577
+ typeNode.operator === ts.SyntaxKind.KeyOfKeyword ? 'keyof' :
578
+ typeNode.operator === ts.SyntaxKind.ReadonlyKeyword ? 'readonly' :
579
+ typeNode.operator === ts.SyntaxKind.UniqueKeyword ? 'unique' :
580
+ null;
581
+ return operator ? `${operator} ${inner}` : null;
582
+ }
583
+
584
+ if (ts.isIndexedAccessTypeNode(typeNode)) {
585
+ const objectType = stringifyCustomEventGenericTypeNode(typeNode.objectType);
586
+ const indexType = stringifyCustomEventGenericTypeNode(typeNode.indexType);
587
+ if (!objectType || !indexType) return null;
588
+ return `${objectType}[${indexType}]`;
589
+ }
590
+
591
+ // As a last resort, keep the original syntax but normalize known WebF aliases.
592
+ return safePrintCustomEventNode(typeNode);
593
+ }
594
+
413
595
  function handleCustomEventType(typeReference: ts.TypeReferenceNode): ParameterBaseType {
414
596
  // Handle CustomEvent<T> by returning the full type with generic parameter
415
597
  if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
@@ -417,121 +599,11 @@ function handleCustomEventType(typeReference: ts.TypeReferenceNode): ParameterBa
417
599
  }
418
600
 
419
601
  const argument = typeReference.typeArguments[0];
420
- let genericType: string;
421
-
422
- // Preserve simple union/compound generic types (e.g., boolean | null)
423
- if (ts.isUnionTypeNode(argument) || ts.isIntersectionTypeNode(argument)) {
424
- const unionTypes = (argument as ts.UnionTypeNode | ts.IntersectionTypeNode).types ?? [];
425
- const parts = unionTypes.map(t => {
426
- // Literal union members: handle null/undefined explicitly
427
- if (ts.isLiteralTypeNode(t)) {
428
- const lit = t.literal;
429
- if (lit.kind === ts.SyntaxKind.NullKeyword) return 'null';
430
- if (lit.kind === ts.SyntaxKind.UndefinedKeyword) return 'undefined';
431
- if (ts.isStringLiteral(lit)) return JSON.stringify(lit.text);
432
- return 'any';
433
- }
434
- // Basic keywords: boolean, string, number, null, undefined
435
- const basic = BASIC_TYPE_MAP[t.kind];
436
- if (basic !== undefined) {
437
- switch (basic) {
438
- case FunctionArgumentType.boolean:
439
- return 'boolean';
440
- case FunctionArgumentType.dom_string:
441
- return 'string';
442
- case FunctionArgumentType.double:
443
- case FunctionArgumentType.int:
444
- return 'number';
445
- case FunctionArgumentType.null:
446
- return 'null';
447
- case FunctionArgumentType.undefined:
448
- return 'undefined';
449
- default:
450
- return 'any';
451
- }
452
- }
453
- // Literal null/undefined keywords that BASIC_TYPE_MAP may not cover
454
- if (t.kind === ts.SyntaxKind.NullKeyword) return 'null';
455
- if (t.kind === ts.SyntaxKind.UndefinedKeyword) return 'undefined';
456
- // Fallback: rely on toString of node kind
457
- return 'any';
458
- });
459
- genericType = parts.join(' | ');
460
- } else if (ts.isTypeReferenceNode(argument) && ts.isIdentifier(argument.typeName)) {
461
- const typeName = argument.typeName.text;
462
-
463
- // Check if it's a mapped type reference like 'int' or 'double'
464
- const mappedType = TYPE_REFERENCE_MAP[typeName];
465
- if (mappedType !== undefined) {
466
- switch (mappedType) {
467
- case FunctionArgumentType.boolean:
468
- genericType = 'boolean';
469
- break;
470
- case FunctionArgumentType.dom_string:
471
- genericType = 'string';
472
- break;
473
- case FunctionArgumentType.double:
474
- case FunctionArgumentType.int:
475
- genericType = 'number';
476
- break;
477
- case FunctionArgumentType.any:
478
- genericType = 'any';
479
- break;
480
- case FunctionArgumentType.void:
481
- genericType = 'void';
482
- break;
483
- case FunctionArgumentType.function:
484
- genericType = 'Function';
485
- break;
486
- case FunctionArgumentType.promise:
487
- genericType = 'Promise<any>';
488
- break;
489
- default:
490
- genericType = typeName;
491
- }
492
- } else {
493
- // For other type references, use the type name directly
494
- genericType = typeName;
495
- }
496
- } else if (ts.isLiteralTypeNode(argument) && ts.isStringLiteral(argument.literal)) {
497
- genericType = argument.literal.text;
498
- } else {
499
- // Handle basic types (boolean, string, number, etc.)
500
- const basicType = BASIC_TYPE_MAP[argument.kind];
501
- if (basicType !== undefined) {
502
- switch (basicType) {
503
- case FunctionArgumentType.boolean:
504
- genericType = 'boolean';
505
- break;
506
- case FunctionArgumentType.dom_string:
507
- genericType = 'string';
508
- break;
509
- case FunctionArgumentType.double:
510
- case FunctionArgumentType.int:
511
- genericType = 'number';
512
- break;
513
- case FunctionArgumentType.any:
514
- genericType = 'any';
515
- break;
516
- case FunctionArgumentType.void:
517
- genericType = 'void';
518
- break;
519
- case FunctionArgumentType.null:
520
- genericType = 'null';
521
- break;
522
- case FunctionArgumentType.undefined:
523
- genericType = 'undefined';
524
- break;
525
- default:
526
- genericType = 'any';
527
- }
528
- } else {
529
- // For truly complex types, fallback to 'any' to avoid errors
530
- console.warn('Complex generic type in CustomEvent, using any');
531
- genericType = 'any';
532
- }
602
+ const genericType = stringifyCustomEventGenericTypeNode(argument);
603
+ if (!genericType) {
604
+ console.warn('Complex generic type in CustomEvent, using any');
605
+ return 'CustomEvent<any>';
533
606
  }
534
-
535
607
  return `CustomEvent<${genericType}>`;
536
608
  }
537
609