Skip to content

Commit 2bc5e36

Browse files
alexisintechclaude
andauthored
fix(repo,shared): backfill inline-object JSDoc on Omit/Pick-resolved types (#9052)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f42aad9 commit 2bc5e36

4 files changed

Lines changed: 135 additions & 10 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.typedoc/custom-plugin.mjs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,119 @@ function collectPropertiesFromType(type, reflectionsByName) {
509509
return [];
510510
}
511511

512+
/**
513+
* Structural fingerprint for a `Type`. Recurses into composite shapes so two types that only differ in their type arguments (`Foo<string>` vs `Foo<number>`) or in their nested property types (`{ x: string }` vs `{ x: number }`) get distinct fingerprints. Two shapes that produce the same fingerprint are treated as structurally identical and so eligible for cross-pollinating JSDoc comments.
514+
*
515+
* Recursion guard: a single shared `Set` of visited reflection ids threads through every nested call to avoid infinite loops on cyclic types (e.g. a type literal that ultimately references itself).
516+
*
517+
* @param {import('typedoc').SomeType | undefined} type
518+
* @param {Set<number>} [seen]
519+
* @returns {string}
520+
*/
521+
function typeFingerprint(type, seen = new Set()) {
522+
if (!type) return '?';
523+
const t =
524+
/** @type {{ type?: string; name?: string; value?: unknown; elementType?: import('typedoc').SomeType; types?: import('typedoc').SomeType[]; typeArguments?: import('typedoc').SomeType[]; declaration?: import('typedoc').DeclarationReflection }} */ (
525+
/** @type {unknown} */ (type)
526+
);
527+
switch (t.type) {
528+
case 'intrinsic':
529+
return `i:${t.name ?? ''}`;
530+
case 'literal':
531+
return `l:${JSON.stringify(t.value)}`;
532+
case 'reference': {
533+
const args = t.typeArguments?.length ? `<${t.typeArguments.map(a => typeFingerprint(a, seen)).join(',')}>` : '';
534+
return `r:${t.name ?? ''}${args}`;
535+
}
536+
case 'array':
537+
return `a:${typeFingerprint(t.elementType, seen)}`;
538+
case 'optional':
539+
return `o:${typeFingerprint(t.elementType, seen)}`;
540+
case 'union':
541+
return `u:[${(t.types ?? [])
542+
.map(a => typeFingerprint(a, seen))
543+
.sort()
544+
.join(',')}]`;
545+
case 'intersection':
546+
return `n:[${(t.types ?? [])
547+
.map(a => typeFingerprint(a, seen))
548+
.sort()
549+
.join(',')}]`;
550+
case 'reflection': {
551+
const decl = t.declaration;
552+
if (decl?.id != null) {
553+
if (seen.has(decl.id)) return `rf:<cycle>`;
554+
seen.add(decl.id);
555+
}
556+
const kids = decl?.children?.filter(c => c.kindOf?.(ReflectionKind.Property)) ?? [];
557+
return `rf:[${kids
558+
.map(c => `${c.name}${c.flags?.isOptional ? '?' : ''}:${typeFingerprint(c.type, seen)}`)
559+
.sort()
560+
.join(',')}]`;
561+
}
562+
default:
563+
return t.type ?? '?';
564+
}
565+
}
566+
567+
/**
568+
* When TypeScript resolves a type through `Omit<...>` / `Pick<...>` (e.g. `ClerkProviderProps = Omit<IsomorphicClerkOptions, …> & { … }`), inline anonymous object literal property types get re-synthesized — and TypeDoc loses the JSDoc on most of their members. Only the first/leading property's comment survives, the rest come through with `comment === undefined`. The same shape elsewhere in the project (e.g. directly under `IsomorphicClerkOptions['telemetry']`) carries all comments correctly.
569+
*
570+
* Match `@kind:typeLiteral` reflections by structural fingerprint (set of `(name, type, optional)` tuples on their property children); within each group, pick the reflection with the most commented children as the source-of-truth and copy missing comments onto its siblings.
571+
*
572+
* @param {import('typedoc').Reflection[]} all
573+
*/
574+
function backfillInlineObjectChildComments(all) {
575+
/** @type {Map<string, import('typedoc').DeclarationReflection[]>} */
576+
const groups = new Map();
577+
for (const r of all) {
578+
if (!r.kindOf?.(ReflectionKind.TypeLiteral)) continue;
579+
const decl = /** @type {import('typedoc').DeclarationReflection} */ (r);
580+
const propChildren = decl.children?.filter(c => c.kindOf?.(ReflectionKind.Property));
581+
if (!propChildren?.length) continue;
582+
const key = propChildren
583+
.map(c => `${c.name}${c.flags?.isOptional ? '?' : ''}:${typeFingerprint(c.type)}`)
584+
.sort()
585+
.join('|');
586+
if (!groups.has(key)) groups.set(key, []);
587+
/** @type {import('typedoc').DeclarationReflection[]} */ (groups.get(key)).push(decl);
588+
}
589+
590+
for (const group of groups.values()) {
591+
if (group.length < 2) continue;
592+
/** @type {import('typedoc').DeclarationReflection | null} */
593+
let best = null;
594+
let bestScore = -1;
595+
for (const refl of group) {
596+
const score =
597+
refl.children?.filter(c => c.kindOf?.(ReflectionKind.Property) && c.comment?.summary?.length).length ?? 0;
598+
if (score > bestScore) {
599+
best = refl;
600+
bestScore = score;
601+
}
602+
}
603+
if (!best || bestScore === 0) continue;
604+
/** @type {Map<string, import('typedoc').DeclarationReflection>} */
605+
const bestByName = new Map();
606+
for (const c of best.children ?? []) {
607+
if (c.kindOf?.(ReflectionKind.Property)) bestByName.set(c.name, c);
608+
}
609+
for (const refl of group) {
610+
if (refl === best) continue;
611+
for (const child of refl.children ?? []) {
612+
if (!child.kindOf?.(ReflectionKind.Property)) continue;
613+
// Any `.comment` object means TypeDoc found a JSDoc block at the source — including
614+
// intentionally empty comments left over from `@generateWithEmptyComment` after the
615+
// modifier tag is stripped in `EVENT_RESOLVE_END`. Only fill in children that have
616+
// no comment at all.
617+
if (child.comment) continue;
618+
const src = bestByName.get(child.name);
619+
if (src?.comment?.summary?.length) child.comment = src.comment;
620+
}
621+
}
622+
}
623+
}
624+
512625
/**
513626
* @param {import('typedoc-plugin-markdown').MarkdownApplication} app
514627
*/
@@ -565,6 +678,8 @@ export function load(app) {
565678
}
566679
}
567680
}
681+
682+
backfillInlineObjectChildComments(all);
568683
});
569684

570685
app.renderer.on(MarkdownPageEvent.END, output => {

packages/shared/src/types/billing.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,15 @@ export interface BillingNamespace {
8787

8888
/**
8989
* Gets the credit balance for the current payer.
90+
* @returns A [`BillingCreditBalanceResource`](https://clerk.com/docs/reference/types/billing-credit-balance-resource) object.
9091
*
9192
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
9293
*/
9394
getCreditBalance: (params: GetCreditBalanceParams) => Promise<BillingCreditBalanceResource>;
9495

9596
/**
9697
* Gets the credit history for the current payer.
98+
* @returns A [`ClerkPaginatedResponse`](https://clerk.com/docs/reference/types/clerk-paginated-response) of [`BillingCreditLedgerResource`](https://clerk.com/docs/reference/types/billing-credit-ledger-resource) objects.
9799
*
98100
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
99101
*/
@@ -913,19 +915,19 @@ export interface BillingSubscriptionResource extends ClerkResource {
913915
}
914916

915917
/**
918+
* The `BillingCreditBalanceResource` type represents the credit balance for a payer.
916919
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
917920
*/
918921
export interface BillingCreditBalanceResource {
919-
/**
920-
* The balance of the credit.
921-
*/
922+
/** The balance of the credit. */
922923
balance: BillingMoneyAmount | null;
923924
}
924925

926+
/**
927+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
928+
*/
925929
export type GetCreditBalanceParams = {
926-
/**
927-
* The ID of the Organization to get the credit balance for.
928-
*/
930+
/** The ID of the Organization to get the credit balance for. */
929931
orgId?: string;
930932
};
931933

@@ -940,13 +942,19 @@ export type GetCreditHistoryParams = {
940942
};
941943

942944
/**
945+
* The `BillingCreditLedgerResource` type represents a credit ledger entry for the current payer or given Organization.
943946
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. It is advised to [pin](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
944947
*/
945948
export interface BillingCreditLedgerResource {
949+
/** The ID of the credit ledger entry. */
946950
id: string;
951+
/** The amount of the credit ledger entry. */
947952
amount: BillingMoneyAmount;
953+
/** The type of the source of the credit ledger entry. */
948954
sourceType: string;
955+
/** The ID of the source of the credit ledger entry. */
949956
sourceId: string;
957+
/** The date when the credit ledger entry was created. */
950958
createdAt: Date;
951959
}
952960

packages/shared/src/types/organizationDomain.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,13 @@ export interface OrganizationDomainResource extends ClerkResource {
173173
totalPendingSuggestions: number;
174174
/**
175175
* Begins the verification process of a created Organization domain by sending a verification code to the provided email address.
176-
* @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/nextjs/reference/types/organization-domain-resource) object.
176+
* @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain-resource) object.
177177
*/
178178
prepareAffiliationVerification: (params: PrepareAffiliationVerificationParams) => Promise<OrganizationDomainResource>;
179179

180180
/**
181-
* Completes the verification process started by [`prepareAffiliationVerification()`](https://clerk.com/docs/nextjs/reference/types/organization-domain-resource#prepare-affiliation-verification), by validating the provided verification code.
182-
* @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/nextjs/reference/types/organization-domain-resource) object.
181+
* Completes the verification process started by [`prepareAffiliationVerification()`](https://clerk.com/docs/reference/types/organization-domain-resource#prepare-affiliation-verification), by validating the provided verification code.
182+
* @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain-resource) object.
183183
*/
184184
attemptAffiliationVerification: (params: AttemptAffiliationVerificationParams) => Promise<OrganizationDomainResource>;
185185
/**
@@ -189,7 +189,7 @@ export interface OrganizationDomainResource extends ClerkResource {
189189
delete: () => Promise<void>;
190190
/**
191191
* Updates the enrollment mode of the Verified Domain.
192-
* @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/nextjs/reference/types/organization-domain-resource) object.
192+
* @returns The updated [`OrganizationDomainResource`](https://clerk.com/docs/reference/types/organization-domain-resource) object.
193193
*/
194194
updateEnrollmentMode: (params: UpdateEnrollmentModeParams) => Promise<OrganizationDomainResource>;
195195
}

0 commit comments

Comments
 (0)