From 1e765b3ffbe79df73fb0da841435f820168688f5 Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 18 Jun 2026 14:05:07 +0300 Subject: [PATCH 1/7] fix(typedoc): Fix merge properties from base classes and remove obsolete base class name rosolving. --- .../src/main.ts | 146 ++++++++---------- 1 file changed, 65 insertions(+), 81 deletions(-) diff --git a/plugins/typedoc-plugin-react-components/src/main.ts b/plugins/typedoc-plugin-react-components/src/main.ts index 21682a7..394df18 100644 --- a/plugins/typedoc-plugin-react-components/src/main.ts +++ b/plugins/typedoc-plugin-react-components/src/main.ts @@ -6,7 +6,6 @@ import { type Context, Converter, DeclarationReflection, - IntersectionType, IntrinsicType, ParameterReflection, PredicateType, @@ -21,7 +20,13 @@ import { } from 'typedoc'; import ts, { ModifierFlags, SymbolFlags } from 'typescript'; -const excludeBaseTypes = ['LitElement', 'HTMLElement']; +const excludeBaseTypes = [ + 'LitElement', + 'HTMLElement', + 'FormRequiredInterface', + 'FormAssociatedElementInterface', +]; +const excludeProperties = ['emitEvent']; export function load(app: Application) { app.converter.on( @@ -90,9 +95,6 @@ export function load(app: Application) { // The 3rd element is for the `renderProps` prop of the `createComponent` method, but this is only declaration of them. // Definition of them should be handled in the 1st part. - // Fill out "extendedTypes" section in TypeDoc with base types - extractBaseType(type.aliasTypeArguments[0], context, reflection); - // Save a reference to the wc component name for reference in case of deeper WC types being resolved. reflection.implementationOf = ReferenceType.createResolvedReference( type.aliasTypeArguments[0].symbol.name, @@ -179,55 +181,80 @@ export function load(app: Application) { } function parseTypeProperties(type: any, context: Context) { - if (excludeBaseTypes.includes(type.symbol?.name)) { + if ( + type.symbol?.name.includes('EventEmitterInterface') || + excludeBaseTypes.includes(type.symbol?.name) + ) { return; } const props = type.declaredProperties || type.symbol?.members; - props?.forEach((value: ts.Symbol, key: ts.__String) => { + props?.forEach((value: ts.Symbol) => { const memberDeclaration = value?.declarations?.length ? (value.declarations[0] as any) : ((value.valueDeclaration as any) ?? null); const modifiers = memberDeclaration ? ts.getCombinedModifierFlags(memberDeclaration) : ModifierFlags.None; + const memberName = value.name; if ( - !key.toString().startsWith('_') && - (modifiers === ModifierFlags.None || + memberName.toString().startsWith('_') || + excludeProperties.includes(memberName) || + !( + modifiers === ModifierFlags.None || modifiers === ModifierFlags.Public || - modifiers === ModifierFlags.Static) + modifiers === ModifierFlags.Static + ) ) { - let reflectionKind = ReflectionKind.Property; - let category = 'Other'; - switch (value.flags) { - case SymbolFlags.GetAccessor: - category = 'Accessors'; - reflectionKind = ReflectionKind.Accessor; - break; - case SymbolFlags.Method: - category = 'Methods'; - reflectionKind = ReflectionKind.Method; - break; - case SymbolFlags.Accessor: - case SymbolFlags.Property: - category = 'Properties'; - } - if (value.flags.toString() === '16777220') { - // For some reason optional properties get flagged to this number, even though the optional is 16777216 - category = 'Properties'; - } - if (category === 'Other') { - // Other types we just ignore creating. - return; - } + return; + } - createMemberDeclaration(context, value, reflectionKind, category); + let reflectionKind = ReflectionKind.Property; + let category = 'Other'; + switch (value.flags) { + case SymbolFlags.GetAccessor: + category = 'Accessors'; + reflectionKind = ReflectionKind.Accessor; + break; + case SymbolFlags.Method: + category = 'Methods'; + reflectionKind = ReflectionKind.Method; + break; + case SymbolFlags.Accessor: + case SymbolFlags.Property: + category = 'Properties'; } + if (value.flags.toString() === '16777220') { + // For some reason optional properties get flagged to this number, even though the optional is 16777216 + category = 'Properties'; + } + if (category === 'Other') { + // Other types we just ignore creating. + return; + } + + createMemberDeclaration(context, value, reflectionKind, category); }); - if (type.resolvedBaseTypes) { - for (const baseType of type.resolvedBaseTypes) { - parseTypeProperties(baseType, context); - } + resolveBaseTypeProps(type, context); +} + +function resolveBaseTypeProps(type: any, context: Context) { + if (!type.baseTypesResolved || type.resolvedBaseTypes?.length === 0) { + return; + } + + let baseTypes = []; + if (type.resolvedBaseTypes[0].symbol) { + baseTypes = type.resolvedBaseTypes; + } else { + baseTypes = type.resolvedBaseTypes[0].types; + } + if (!baseTypes) { + return; + } + + for (const baseType of baseTypes) { + parseTypeProperties(baseType, context); } } @@ -354,49 +381,6 @@ function createEventDeclaration(context: Context, value: ts.Symbol) { return reflection; } -function extractBaseType(type: any, context: Context, reflection: SignatureReflection) { - if (!type.baseTypesResolved) { - return; - } - assert(context.scope instanceof DeclarationReflection); - // Expected only 1 item so far? - const resolvedBaseTypeString = context.checker.typeToString(type.resolvedBaseTypes[0]); - const intersectTypes = resolvedBaseTypeString.split('&').map((t) => t.trim()); - if (intersectTypes.length > 1) { - for (const [index, typeName] of intersectTypes.entries()) { - if (typeName.includes('EventEmitterInterface') || excludeBaseTypes.includes(typeName)) { - continue; - } - - const baseType = type.resolvedBaseTypes[0].types[index]; - const refType = ReferenceType.createResolvedReference( - baseType.symbol.name, - reflection, - context.project, - ); - if (context.scope.extendedTypes && context.scope.extendedTypes[0].type === 'intersection') { - (context.scope.extendedTypes[0] as IntersectionType).types.push(refType); - } else if (context.scope.extendedTypes) { - const intersectionType = new IntersectionType(context.scope.extendedTypes); - intersectionType.types.push(refType); - context.scope.extendedTypes = [intersectionType]; - } else { - context.scope.extendedTypes = [refType]; - } - } - } else if (!excludeBaseTypes.includes(resolvedBaseTypeString)) { - const baseType = type.resolvedBaseTypes[0]; - const refType = ReferenceType.createResolvedReference( - baseType.symbol.name, - reflection, - context.project, - ); - context.scope.extendedTypes = context.scope.extendedTypes - ? [...context.scope.extendedTypes, refType] - : [refType]; - } -} - //#region Taken from typedoc source for creating a generic signature of a Symbol. function removeUndefined(type: SomeType): SomeType { if (type instanceof UnionType) { From 9a502fab891851693f0edb40bc18d758c2ef1eeb Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 18 Jun 2026 15:41:36 +0300 Subject: [PATCH 2/7] chore(*): Add a few more excluded interfaces from WC. --- plugins/typedoc-plugin-react-components/src/main.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/typedoc-plugin-react-components/src/main.ts b/plugins/typedoc-plugin-react-components/src/main.ts index 394df18..f8cb6f3 100644 --- a/plugins/typedoc-plugin-react-components/src/main.ts +++ b/plugins/typedoc-plugin-react-components/src/main.ts @@ -24,7 +24,9 @@ const excludeBaseTypes = [ 'LitElement', 'HTMLElement', 'FormRequiredInterface', + 'FormAssociatedCheckboxElementInterface', 'FormAssociatedElementInterface', + 'MaskBehaviorElementInterface', ]; const excludeProperties = ['emitEvent']; From 05ce238091d7bdcafd9f759cc1d98758b4c4da19 Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 16 Jul 2026 13:58:13 +0300 Subject: [PATCH 3/7] chore(*): Code tweaks and better checks --- plugins/typedoc-plugin-react-components/src/main.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/typedoc-plugin-react-components/src/main.ts b/plugins/typedoc-plugin-react-components/src/main.ts index f8cb6f3..3c766bb 100644 --- a/plugins/typedoc-plugin-react-components/src/main.ts +++ b/plugins/typedoc-plugin-react-components/src/main.ts @@ -184,7 +184,7 @@ export function load(app: Application) { function parseTypeProperties(type: any, context: Context) { if ( - type.symbol?.name.includes('EventEmitterInterface') || + type.symbol?.name?.includes('EventEmitterInterface') || excludeBaseTypes.includes(type.symbol?.name) ) { return; @@ -225,7 +225,7 @@ function parseTypeProperties(type: any, context: Context) { case SymbolFlags.Property: category = 'Properties'; } - if (value.flags.toString() === '16777220') { + if (value.flags & ts.SymbolFlags.Optional) { // For some reason optional properties get flagged to this number, even though the optional is 16777216 category = 'Properties'; } @@ -241,7 +241,7 @@ function parseTypeProperties(type: any, context: Context) { } function resolveBaseTypeProps(type: any, context: Context) { - if (!type.baseTypesResolved || type.resolvedBaseTypes?.length === 0) { + if (!type.baseTypesResolved || !type.resolvedBaseTypes?.length) { return; } From 4ee401bc0dbc6ffe27cd19a9eceb7fb9214b2e57 Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 16 Jul 2026 16:07:16 +0300 Subject: [PATCH 4/7] refactor(typedoc): Use ts types. --- .../src/main.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/plugins/typedoc-plugin-react-components/src/main.ts b/plugins/typedoc-plugin-react-components/src/main.ts index 3c766bb..257f350 100644 --- a/plugins/typedoc-plugin-react-components/src/main.ts +++ b/plugins/typedoc-plugin-react-components/src/main.ts @@ -182,14 +182,15 @@ export function load(app: Application) { }); } -function parseTypeProperties(type: any, context: Context) { +function parseTypeProperties(type: ts.Type, context: Context) { if ( type.symbol?.name?.includes('EventEmitterInterface') || excludeBaseTypes.includes(type.symbol?.name) ) { return; } - const props = type.declaredProperties || type.symbol?.members; + const props = + (type as ts.InterfaceTypeWithDeclaredMembers)?.declaredProperties ?? type.symbol?.members; props?.forEach((value: ts.Symbol) => { const memberDeclaration = value?.declarations?.length ? (value.declarations[0] as any) @@ -240,18 +241,13 @@ function parseTypeProperties(type: any, context: Context) { resolveBaseTypeProps(type, context); } -function resolveBaseTypeProps(type: any, context: Context) { - if (!type.baseTypesResolved || !type.resolvedBaseTypes?.length) { - return; - } +function resolveBaseTypeProps(type: ts.Type, context: Context) { + if (!type.isClassOrInterface()) return; - let baseTypes = []; - if (type.resolvedBaseTypes[0].symbol) { - baseTypes = type.resolvedBaseTypes; - } else { - baseTypes = type.resolvedBaseTypes[0].types; - } - if (!baseTypes) { + const baseTypes = context.checker + .getBaseTypes(type) + .flatMap((base) => (base.isUnionOrIntersection() ? base.types : base)); + if (!baseTypes?.length) { return; } From 12b053bf9e3a7953edaf012ac5d6aa088742fb6f Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 16 Jul 2026 16:13:30 +0300 Subject: [PATCH 5/7] chore(*): Remove extra check --- plugins/typedoc-plugin-react-components/src/main.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/typedoc-plugin-react-components/src/main.ts b/plugins/typedoc-plugin-react-components/src/main.ts index 257f350..f8bdb3e 100644 --- a/plugins/typedoc-plugin-react-components/src/main.ts +++ b/plugins/typedoc-plugin-react-components/src/main.ts @@ -247,10 +247,6 @@ function resolveBaseTypeProps(type: ts.Type, context: Context) { const baseTypes = context.checker .getBaseTypes(type) .flatMap((base) => (base.isUnionOrIntersection() ? base.types : base)); - if (!baseTypes?.length) { - return; - } - for (const baseType of baseTypes) { parseTypeProperties(baseType, context); } From 727014bc1f4ffc662fc893cecaccf2839c976c24 Mon Sep 17 00:00:00 2001 From: skrustev Date: Thu, 16 Jul 2026 16:30:04 +0300 Subject: [PATCH 6/7] chore(*): Apply type to initializer --- plugins/typedoc-plugin-react-components/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/typedoc-plugin-react-components/src/main.ts b/plugins/typedoc-plugin-react-components/src/main.ts index f8bdb3e..922312d 100644 --- a/plugins/typedoc-plugin-react-components/src/main.ts +++ b/plugins/typedoc-plugin-react-components/src/main.ts @@ -299,7 +299,7 @@ function createMemberDeclaration( // To Do: Better handling of default values? reflection.defaultValue = declaration?.initializer - ? (declaration as any).initializer.getText() + ? (declaration.initializer as ts.Expression)?.getText() : undefined; const categoryTag = new CommentTag('@category', [{ kind: 'text', text: category }]); From 16c8b36deeea524c423288fd8d8d8719fcb24c24 Mon Sep 17 00:00:00 2001 From: Damyan Petev Date: Thu, 16 Jul 2026 16:47:38 +0300 Subject: [PATCH 7/7] chore: leftover comment --- plugins/typedoc-plugin-react-components/src/main.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/typedoc-plugin-react-components/src/main.ts b/plugins/typedoc-plugin-react-components/src/main.ts index 922312d..37d350a 100644 --- a/plugins/typedoc-plugin-react-components/src/main.ts +++ b/plugins/typedoc-plugin-react-components/src/main.ts @@ -227,7 +227,6 @@ function parseTypeProperties(type: ts.Type, context: Context) { category = 'Properties'; } if (value.flags & ts.SymbolFlags.Optional) { - // For some reason optional properties get flagged to this number, even though the optional is 16777216 category = 'Properties'; } if (category === 'Other') {