@openwebf/webf 0.22.9 → 0.22.11
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/analyzer.js +98 -4
- package/dist/generator.js +4 -56
- package/dist/react.js +12 -1
- package/package.json +1 -1
- package/src/analyzer.ts +101 -5
- package/src/generator.ts +4 -59
- package/src/react.ts +15 -1
package/dist/analyzer.js
CHANGED
|
@@ -66,11 +66,17 @@ const TYPE_REFERENCE_MAP = {
|
|
|
66
66
|
};
|
|
67
67
|
function analyzer(blob, definedPropertyCollector, unionTypeCollector) {
|
|
68
68
|
try {
|
|
69
|
-
// Check cache first
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
// Check cache first - consider both file path and content
|
|
70
|
+
const cacheEntry = sourceFileCache.get(blob.source);
|
|
71
|
+
let sourceFile;
|
|
72
|
+
if (cacheEntry && cacheEntry.content === blob.raw) {
|
|
73
|
+
// Cache hit with same content
|
|
74
|
+
sourceFile = cacheEntry.sourceFile;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
// Cache miss or content changed - parse and update cache
|
|
72
78
|
sourceFile = typescript_1.default.createSourceFile(blob.source, blob.raw, typescript_1.ScriptTarget.ES2020);
|
|
73
|
-
sourceFileCache.set(blob.source, sourceFile);
|
|
79
|
+
sourceFileCache.set(blob.source, { content: blob.raw, sourceFile });
|
|
74
80
|
}
|
|
75
81
|
blob.objects = sourceFile.statements
|
|
76
82
|
.map(statement => {
|
|
@@ -292,6 +298,8 @@ function getParameterBaseType(type, mode) {
|
|
|
292
298
|
if (mode)
|
|
293
299
|
mode.staticMethod = true;
|
|
294
300
|
return handleGenericWrapper(typeReference, mode);
|
|
301
|
+
case 'CustomEvent':
|
|
302
|
+
return handleCustomEventType(typeReference);
|
|
295
303
|
default:
|
|
296
304
|
if (identifier.includes('SupportAsync')) {
|
|
297
305
|
return handleSupportAsyncType(identifier, typeReference, mode);
|
|
@@ -344,6 +352,92 @@ function handleGenericWrapper(typeReference, mode) {
|
|
|
344
352
|
const argument = typeReference.typeArguments[0];
|
|
345
353
|
return getParameterBaseType(argument, mode);
|
|
346
354
|
}
|
|
355
|
+
function handleCustomEventType(typeReference) {
|
|
356
|
+
// Handle CustomEvent<T> by returning the full type with generic parameter
|
|
357
|
+
if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
|
|
358
|
+
return 'CustomEvent';
|
|
359
|
+
}
|
|
360
|
+
const argument = typeReference.typeArguments[0];
|
|
361
|
+
let genericType;
|
|
362
|
+
if (typescript_1.default.isTypeReferenceNode(argument) && typescript_1.default.isIdentifier(argument.typeName)) {
|
|
363
|
+
const typeName = argument.typeName.text;
|
|
364
|
+
// Check if it's a mapped type reference like 'int' or 'double'
|
|
365
|
+
const mappedType = TYPE_REFERENCE_MAP[typeName];
|
|
366
|
+
if (mappedType !== undefined) {
|
|
367
|
+
switch (mappedType) {
|
|
368
|
+
case declaration_1.FunctionArgumentType.boolean:
|
|
369
|
+
genericType = 'boolean';
|
|
370
|
+
break;
|
|
371
|
+
case declaration_1.FunctionArgumentType.dom_string:
|
|
372
|
+
genericType = 'string';
|
|
373
|
+
break;
|
|
374
|
+
case declaration_1.FunctionArgumentType.double:
|
|
375
|
+
case declaration_1.FunctionArgumentType.int:
|
|
376
|
+
genericType = 'number';
|
|
377
|
+
break;
|
|
378
|
+
case declaration_1.FunctionArgumentType.any:
|
|
379
|
+
genericType = 'any';
|
|
380
|
+
break;
|
|
381
|
+
case declaration_1.FunctionArgumentType.void:
|
|
382
|
+
genericType = 'void';
|
|
383
|
+
break;
|
|
384
|
+
case declaration_1.FunctionArgumentType.function:
|
|
385
|
+
genericType = 'Function';
|
|
386
|
+
break;
|
|
387
|
+
case declaration_1.FunctionArgumentType.promise:
|
|
388
|
+
genericType = 'Promise<any>';
|
|
389
|
+
break;
|
|
390
|
+
default:
|
|
391
|
+
genericType = typeName;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
// For other type references, use the type name directly
|
|
396
|
+
genericType = typeName;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else if (typescript_1.default.isLiteralTypeNode(argument) && typescript_1.default.isStringLiteral(argument.literal)) {
|
|
400
|
+
genericType = argument.literal.text;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
// Handle basic types (boolean, string, number, etc.)
|
|
404
|
+
const basicType = BASIC_TYPE_MAP[argument.kind];
|
|
405
|
+
if (basicType !== undefined) {
|
|
406
|
+
switch (basicType) {
|
|
407
|
+
case declaration_1.FunctionArgumentType.boolean:
|
|
408
|
+
genericType = 'boolean';
|
|
409
|
+
break;
|
|
410
|
+
case declaration_1.FunctionArgumentType.dom_string:
|
|
411
|
+
genericType = 'string';
|
|
412
|
+
break;
|
|
413
|
+
case declaration_1.FunctionArgumentType.double:
|
|
414
|
+
case declaration_1.FunctionArgumentType.int:
|
|
415
|
+
genericType = 'number';
|
|
416
|
+
break;
|
|
417
|
+
case declaration_1.FunctionArgumentType.any:
|
|
418
|
+
genericType = 'any';
|
|
419
|
+
break;
|
|
420
|
+
case declaration_1.FunctionArgumentType.void:
|
|
421
|
+
genericType = 'void';
|
|
422
|
+
break;
|
|
423
|
+
case declaration_1.FunctionArgumentType.null:
|
|
424
|
+
genericType = 'null';
|
|
425
|
+
break;
|
|
426
|
+
case declaration_1.FunctionArgumentType.undefined:
|
|
427
|
+
genericType = 'undefined';
|
|
428
|
+
break;
|
|
429
|
+
default:
|
|
430
|
+
genericType = 'any';
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
// For truly complex types, fallback to 'any' to avoid errors
|
|
435
|
+
console.warn('Complex generic type in CustomEvent, using any');
|
|
436
|
+
genericType = 'any';
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
return `CustomEvent<${genericType}>`;
|
|
440
|
+
}
|
|
347
441
|
function handleSupportAsyncType(identifier, typeReference, mode) {
|
|
348
442
|
if (mode) {
|
|
349
443
|
mode.supportAsync = true;
|
package/dist/generator.js
CHANGED
|
@@ -287,64 +287,12 @@ function reactGen(_a) {
|
|
|
287
287
|
(0, logger_1.error)(`Error generating React component for ${blob.filename}`, err);
|
|
288
288
|
}
|
|
289
289
|
}));
|
|
290
|
-
// Generate
|
|
290
|
+
// Generate index file (always regenerate to avoid duplicates)
|
|
291
291
|
const indexFilePath = path_1.default.join(normalizedTarget, 'src', 'index.ts');
|
|
292
292
|
const newExports = (0, react_1.generateReactIndex)(blobs);
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const existingLines = existingContent.split('\n');
|
|
297
|
-
// Parse new exports
|
|
298
|
-
const newExportLines = newExports.split('\n').filter(line => line.startsWith('export '));
|
|
299
|
-
// Find which exports are missing
|
|
300
|
-
const missingExports = [];
|
|
301
|
-
for (const newExport of newExportLines) {
|
|
302
|
-
// Extract the export statement to check if it exists
|
|
303
|
-
const exportMatch = newExport.match(/export\s*{\s*([^}]+)\s*}\s*from\s*["']([^"']+)["']/);
|
|
304
|
-
if (exportMatch) {
|
|
305
|
-
const [, exportNames, modulePath] = exportMatch;
|
|
306
|
-
const exportedItems = exportNames.split(',').map(s => s.trim());
|
|
307
|
-
// Check if this exact export exists
|
|
308
|
-
const exists = existingLines.some(line => {
|
|
309
|
-
if (!line.startsWith('export '))
|
|
310
|
-
return false;
|
|
311
|
-
const lineMatch = line.match(/export\s*{\s*([^}]+)\s*}\s*from\s*["']([^"']+)["']/);
|
|
312
|
-
if (!lineMatch)
|
|
313
|
-
return false;
|
|
314
|
-
const [, lineExportNames, lineModulePath] = lineMatch;
|
|
315
|
-
const lineExportedItems = lineExportNames.split(',').map(s => s.trim());
|
|
316
|
-
// Check if same module and same exports
|
|
317
|
-
return lineModulePath === modulePath &&
|
|
318
|
-
exportedItems.every(item => lineExportedItems.includes(item)) &&
|
|
319
|
-
lineExportedItems.every(item => exportedItems.includes(item));
|
|
320
|
-
});
|
|
321
|
-
if (!exists) {
|
|
322
|
-
missingExports.push(newExport);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
// If there are missing exports, append them
|
|
327
|
-
if (missingExports.length > 0) {
|
|
328
|
-
let updatedContent = existingContent.trimRight();
|
|
329
|
-
if (!updatedContent.endsWith('\n')) {
|
|
330
|
-
updatedContent += '\n';
|
|
331
|
-
}
|
|
332
|
-
updatedContent += missingExports.join('\n') + '\n';
|
|
333
|
-
if (writeFileIfChanged(indexFilePath, updatedContent)) {
|
|
334
|
-
filesChanged++;
|
|
335
|
-
(0, logger_1.debug)(`Updated: index.ts (added ${missingExports.length} exports)`);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
(0, logger_1.debug)(`Skipped: index.ts (all exports already exist)`);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
// File doesn't exist, create it
|
|
344
|
-
if (writeFileIfChanged(indexFilePath, newExports)) {
|
|
345
|
-
filesChanged++;
|
|
346
|
-
(0, logger_1.debug)(`Generated: index.ts`);
|
|
347
|
-
}
|
|
293
|
+
if (writeFileIfChanged(indexFilePath, newExports)) {
|
|
294
|
+
filesChanged++;
|
|
295
|
+
(0, logger_1.debug)(`Generated: index.ts`);
|
|
348
296
|
}
|
|
349
297
|
(0, logger_1.timeEnd)('reactGen');
|
|
350
298
|
(0, logger_1.success)(`React code generation completed. ${filesChanged} files changed.`);
|
package/dist/react.js
CHANGED
|
@@ -63,6 +63,10 @@ function getEventType(type) {
|
|
|
63
63
|
return 'Event';
|
|
64
64
|
}
|
|
65
65
|
const pointerType = (0, utils_1.getPointerType)(type);
|
|
66
|
+
// Handle CustomEvent with generic parameter
|
|
67
|
+
if (pointerType.startsWith('CustomEvent<') && pointerType.endsWith('>')) {
|
|
68
|
+
return pointerType;
|
|
69
|
+
}
|
|
66
70
|
if (pointerType === 'CustomEvent') {
|
|
67
71
|
return 'CustomEvent';
|
|
68
72
|
}
|
|
@@ -319,8 +323,15 @@ function generateReactIndex(blobs) {
|
|
|
319
323
|
}).filter(component => {
|
|
320
324
|
return component.className.length > 0;
|
|
321
325
|
});
|
|
326
|
+
// Deduplicate components by className, keeping the first occurrence
|
|
327
|
+
const deduplicatedComponents = new Map();
|
|
328
|
+
components.forEach(component => {
|
|
329
|
+
if (!deduplicatedComponents.has(component.className)) {
|
|
330
|
+
deduplicatedComponents.set(component.className, component);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
322
333
|
const content = lodash_1.default.template(readTemplate('react.index.ts'))({
|
|
323
|
-
components,
|
|
334
|
+
components: Array.from(deduplicatedComponents.values()),
|
|
324
335
|
});
|
|
325
336
|
return content.split('\n').filter(str => {
|
|
326
337
|
return str.trim().length > 0;
|
package/package.json
CHANGED
package/src/analyzer.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface UnionTypeCollector {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// Cache for parsed source files to avoid re-parsing
|
|
29
|
-
const sourceFileCache = new Map<string, ts.SourceFile>();
|
|
29
|
+
const sourceFileCache = new Map<string, { content: string; sourceFile: ts.SourceFile }>();
|
|
30
30
|
|
|
31
31
|
// Cache for type conversions to avoid redundant processing
|
|
32
32
|
const typeConversionCache = new Map<string, ParameterType>();
|
|
@@ -56,11 +56,17 @@ const TYPE_REFERENCE_MAP: Record<string, FunctionArgumentType> = {
|
|
|
56
56
|
|
|
57
57
|
export function analyzer(blob: IDLBlob, definedPropertyCollector: DefinedPropertyCollector, unionTypeCollector: UnionTypeCollector) {
|
|
58
58
|
try {
|
|
59
|
-
// Check cache first
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
// Check cache first - consider both file path and content
|
|
60
|
+
const cacheEntry = sourceFileCache.get(blob.source);
|
|
61
|
+
let sourceFile: ts.SourceFile;
|
|
62
|
+
|
|
63
|
+
if (cacheEntry && cacheEntry.content === blob.raw) {
|
|
64
|
+
// Cache hit with same content
|
|
65
|
+
sourceFile = cacheEntry.sourceFile;
|
|
66
|
+
} else {
|
|
67
|
+
// Cache miss or content changed - parse and update cache
|
|
62
68
|
sourceFile = ts.createSourceFile(blob.source, blob.raw, ScriptTarget.ES2020);
|
|
63
|
-
sourceFileCache.set(blob.source, sourceFile);
|
|
69
|
+
sourceFileCache.set(blob.source, { content: blob.raw, sourceFile });
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
blob.objects = sourceFile.statements
|
|
@@ -324,6 +330,9 @@ function getParameterBaseType(type: ts.TypeNode, mode?: ParameterMode): Paramete
|
|
|
324
330
|
if (mode) mode.staticMethod = true;
|
|
325
331
|
return handleGenericWrapper(typeReference, mode);
|
|
326
332
|
|
|
333
|
+
case 'CustomEvent':
|
|
334
|
+
return handleCustomEventType(typeReference);
|
|
335
|
+
|
|
327
336
|
default:
|
|
328
337
|
if (identifier.includes('SupportAsync')) {
|
|
329
338
|
return handleSupportAsyncType(identifier, typeReference, mode);
|
|
@@ -386,6 +395,93 @@ function handleGenericWrapper(typeReference: ts.TypeReferenceNode, mode?: Parame
|
|
|
386
395
|
return getParameterBaseType(argument, mode);
|
|
387
396
|
}
|
|
388
397
|
|
|
398
|
+
function handleCustomEventType(typeReference: ts.TypeReferenceNode): ParameterBaseType {
|
|
399
|
+
// Handle CustomEvent<T> by returning the full type with generic parameter
|
|
400
|
+
if (!typeReference.typeArguments || !typeReference.typeArguments[0]) {
|
|
401
|
+
return 'CustomEvent';
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const argument = typeReference.typeArguments[0];
|
|
405
|
+
let genericType: string;
|
|
406
|
+
|
|
407
|
+
if (ts.isTypeReferenceNode(argument) && ts.isIdentifier(argument.typeName)) {
|
|
408
|
+
const typeName = argument.typeName.text;
|
|
409
|
+
|
|
410
|
+
// Check if it's a mapped type reference like 'int' or 'double'
|
|
411
|
+
const mappedType = TYPE_REFERENCE_MAP[typeName];
|
|
412
|
+
if (mappedType !== undefined) {
|
|
413
|
+
switch (mappedType) {
|
|
414
|
+
case FunctionArgumentType.boolean:
|
|
415
|
+
genericType = 'boolean';
|
|
416
|
+
break;
|
|
417
|
+
case FunctionArgumentType.dom_string:
|
|
418
|
+
genericType = 'string';
|
|
419
|
+
break;
|
|
420
|
+
case FunctionArgumentType.double:
|
|
421
|
+
case FunctionArgumentType.int:
|
|
422
|
+
genericType = 'number';
|
|
423
|
+
break;
|
|
424
|
+
case FunctionArgumentType.any:
|
|
425
|
+
genericType = 'any';
|
|
426
|
+
break;
|
|
427
|
+
case FunctionArgumentType.void:
|
|
428
|
+
genericType = 'void';
|
|
429
|
+
break;
|
|
430
|
+
case FunctionArgumentType.function:
|
|
431
|
+
genericType = 'Function';
|
|
432
|
+
break;
|
|
433
|
+
case FunctionArgumentType.promise:
|
|
434
|
+
genericType = 'Promise<any>';
|
|
435
|
+
break;
|
|
436
|
+
default:
|
|
437
|
+
genericType = typeName;
|
|
438
|
+
}
|
|
439
|
+
} else {
|
|
440
|
+
// For other type references, use the type name directly
|
|
441
|
+
genericType = typeName;
|
|
442
|
+
}
|
|
443
|
+
} else if (ts.isLiteralTypeNode(argument) && ts.isStringLiteral(argument.literal)) {
|
|
444
|
+
genericType = argument.literal.text;
|
|
445
|
+
} else {
|
|
446
|
+
// Handle basic types (boolean, string, number, etc.)
|
|
447
|
+
const basicType = BASIC_TYPE_MAP[argument.kind];
|
|
448
|
+
if (basicType !== undefined) {
|
|
449
|
+
switch (basicType) {
|
|
450
|
+
case FunctionArgumentType.boolean:
|
|
451
|
+
genericType = 'boolean';
|
|
452
|
+
break;
|
|
453
|
+
case FunctionArgumentType.dom_string:
|
|
454
|
+
genericType = 'string';
|
|
455
|
+
break;
|
|
456
|
+
case FunctionArgumentType.double:
|
|
457
|
+
case FunctionArgumentType.int:
|
|
458
|
+
genericType = 'number';
|
|
459
|
+
break;
|
|
460
|
+
case FunctionArgumentType.any:
|
|
461
|
+
genericType = 'any';
|
|
462
|
+
break;
|
|
463
|
+
case FunctionArgumentType.void:
|
|
464
|
+
genericType = 'void';
|
|
465
|
+
break;
|
|
466
|
+
case FunctionArgumentType.null:
|
|
467
|
+
genericType = 'null';
|
|
468
|
+
break;
|
|
469
|
+
case FunctionArgumentType.undefined:
|
|
470
|
+
genericType = 'undefined';
|
|
471
|
+
break;
|
|
472
|
+
default:
|
|
473
|
+
genericType = 'any';
|
|
474
|
+
}
|
|
475
|
+
} else {
|
|
476
|
+
// For truly complex types, fallback to 'any' to avoid errors
|
|
477
|
+
console.warn('Complex generic type in CustomEvent, using any');
|
|
478
|
+
genericType = 'any';
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return `CustomEvent<${genericType}>`;
|
|
483
|
+
}
|
|
484
|
+
|
|
389
485
|
function handleSupportAsyncType(identifier: string, typeReference: ts.TypeReferenceNode, mode?: ParameterMode): ParameterBaseType {
|
|
390
486
|
if (mode) {
|
|
391
487
|
mode.supportAsync = true;
|
package/src/generator.ts
CHANGED
|
@@ -321,68 +321,13 @@ export async function reactGen({ source, target, exclude, packageName }: Generat
|
|
|
321
321
|
}
|
|
322
322
|
});
|
|
323
323
|
|
|
324
|
-
// Generate
|
|
324
|
+
// Generate index file (always regenerate to avoid duplicates)
|
|
325
325
|
const indexFilePath = path.join(normalizedTarget, 'src', 'index.ts');
|
|
326
326
|
const newExports = generateReactIndex(blobs);
|
|
327
327
|
|
|
328
|
-
if (
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const existingLines = existingContent.split('\n');
|
|
332
|
-
|
|
333
|
-
// Parse new exports
|
|
334
|
-
const newExportLines = newExports.split('\n').filter(line => line.startsWith('export '));
|
|
335
|
-
|
|
336
|
-
// Find which exports are missing
|
|
337
|
-
const missingExports: string[] = [];
|
|
338
|
-
for (const newExport of newExportLines) {
|
|
339
|
-
// Extract the export statement to check if it exists
|
|
340
|
-
const exportMatch = newExport.match(/export\s*{\s*([^}]+)\s*}\s*from\s*["']([^"']+)["']/);
|
|
341
|
-
if (exportMatch) {
|
|
342
|
-
const [, exportNames, modulePath] = exportMatch;
|
|
343
|
-
const exportedItems = exportNames.split(',').map(s => s.trim());
|
|
344
|
-
|
|
345
|
-
// Check if this exact export exists
|
|
346
|
-
const exists = existingLines.some(line => {
|
|
347
|
-
if (!line.startsWith('export ')) return false;
|
|
348
|
-
const lineMatch = line.match(/export\s*{\s*([^}]+)\s*}\s*from\s*["']([^"']+)["']/);
|
|
349
|
-
if (!lineMatch) return false;
|
|
350
|
-
const [, lineExportNames, lineModulePath] = lineMatch;
|
|
351
|
-
const lineExportedItems = lineExportNames.split(',').map(s => s.trim());
|
|
352
|
-
|
|
353
|
-
// Check if same module and same exports
|
|
354
|
-
return lineModulePath === modulePath &&
|
|
355
|
-
exportedItems.every(item => lineExportedItems.includes(item)) &&
|
|
356
|
-
lineExportedItems.every(item => exportedItems.includes(item));
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
if (!exists) {
|
|
360
|
-
missingExports.push(newExport);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// If there are missing exports, append them
|
|
366
|
-
if (missingExports.length > 0) {
|
|
367
|
-
let updatedContent = existingContent.trimRight();
|
|
368
|
-
if (!updatedContent.endsWith('\n')) {
|
|
369
|
-
updatedContent += '\n';
|
|
370
|
-
}
|
|
371
|
-
updatedContent += missingExports.join('\n') + '\n';
|
|
372
|
-
|
|
373
|
-
if (writeFileIfChanged(indexFilePath, updatedContent)) {
|
|
374
|
-
filesChanged++;
|
|
375
|
-
debug(`Updated: index.ts (added ${missingExports.length} exports)`);
|
|
376
|
-
}
|
|
377
|
-
} else {
|
|
378
|
-
debug(`Skipped: index.ts (all exports already exist)`);
|
|
379
|
-
}
|
|
380
|
-
} else {
|
|
381
|
-
// File doesn't exist, create it
|
|
382
|
-
if (writeFileIfChanged(indexFilePath, newExports)) {
|
|
383
|
-
filesChanged++;
|
|
384
|
-
debug(`Generated: index.ts`);
|
|
385
|
-
}
|
|
328
|
+
if (writeFileIfChanged(indexFilePath, newExports)) {
|
|
329
|
+
filesChanged++;
|
|
330
|
+
debug(`Generated: index.ts`);
|
|
386
331
|
}
|
|
387
332
|
|
|
388
333
|
timeEnd('reactGen');
|
package/src/react.ts
CHANGED
|
@@ -61,6 +61,12 @@ function getEventType(type: ParameterType) {
|
|
|
61
61
|
return 'Event';
|
|
62
62
|
}
|
|
63
63
|
const pointerType = getPointerType(type);
|
|
64
|
+
|
|
65
|
+
// Handle CustomEvent with generic parameter
|
|
66
|
+
if (pointerType.startsWith('CustomEvent<') && pointerType.endsWith('>')) {
|
|
67
|
+
return pointerType;
|
|
68
|
+
}
|
|
69
|
+
|
|
64
70
|
if (pointerType === 'CustomEvent') {
|
|
65
71
|
return 'CustomEvent';
|
|
66
72
|
}
|
|
@@ -360,8 +366,16 @@ export function generateReactIndex(blobs: IDLBlob[]) {
|
|
|
360
366
|
return component.className.length > 0;
|
|
361
367
|
});
|
|
362
368
|
|
|
369
|
+
// Deduplicate components by className, keeping the first occurrence
|
|
370
|
+
const deduplicatedComponents = new Map<string, { className: string; fileName: string; relativeDir: string }>();
|
|
371
|
+
components.forEach(component => {
|
|
372
|
+
if (!deduplicatedComponents.has(component.className)) {
|
|
373
|
+
deduplicatedComponents.set(component.className, component);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
363
377
|
const content = _.template(readTemplate('react.index.ts'))({
|
|
364
|
-
components,
|
|
378
|
+
components: Array.from(deduplicatedComponents.values()),
|
|
365
379
|
});
|
|
366
380
|
|
|
367
381
|
return content.split('\n').filter(str => {
|