-
Notifications
You must be signed in to change notification settings - Fork 567
feat(api-markdown-documenter): Add limited support for inherited type member docs #25471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Josmithr
wants to merge
50
commits into
microsoft:main
from
Josmithr:api-markdown-documenter/inherit-members
Closed
Changes from all commits
Commits
Show all changes
50 commits
Select commit
Hold shift + click to select a range
05ea790
refactor: Remove helper and improve typing of another
Josmithr 30cbc79
feat(api-markdown-documenter): Class and interface items now inherit …
Josmithr a6fb1c0
refactor: Add utilities dir
Josmithr 8342b3d
refactor: Move API item utils into directory
Josmithr c330692
refactor: Move test module
Josmithr 1f4253b
Merge branch 'api-markdown-documenter/reorganize-utilities' into api-…
Josmithr 29e77e8
refactor: Extract type inheritance utilities into their own module
Josmithr 987894d
docs: Add module comment
Josmithr 9b56a70
docs: TODOs
Josmithr 4b3062e
refactor: Simplify section creation
Josmithr 14e5840
refactor: Don't export helpers
Josmithr 3c14079
refactor: Add new table creation utils
Josmithr 8315d09
refactor: Use new table creation utils
Josmithr ca8f5e5
refactor: Use new table creation utils
Josmithr b9ec5f1
fix: `align` handling
Josmithr 17b7641
refactor: More consistent table handling
Josmithr 5569cc9
refactor: Make table creation utilities more general
Josmithr c788295
refactor: Use new table creation utils
Josmithr 29d2fbf
refactor: Use new table creation utils
Josmithr 5026b75
refactor: Use new table creation utils
Josmithr ba389c5
refactor: Rename utility function
Josmithr 69e5b9e
refactor: Don't export functions
Josmithr f56c2cd
refactor: Use new table creation utility
Josmithr a55eb7e
refactor: Rename utility function
Josmithr b732f78
refactor: Cleanup
Josmithr 262071f
Merge branch 'main' into api-markdown-documenter/improve-table-helpers
Josmithr bf26110
Merge branch 'main' into api-markdown-documenter/inherit-members
Josmithr 88b2168
Merge branch 'api-markdown-documenter/improve-table-helpers' into api…
Josmithr 7ead26b
bump: Version for publishing
Josmithr b76f140
refactor: Fix typo
Josmithr 1dfda98
fix: Don't generate empty cells
Josmithr 9ea079f
Merge branch 'api-markdown-documenter/improve-table-helpers' into api…
Josmithr a936ea8
docs: Add comments
Josmithr 8939d41
docs: Add property docs
Josmithr 4a2f6cd
fix: Inheritance resolution
Josmithr 94ead60
docs: TODO
Josmithr 99f8928
fix: Don't inherit static members
Josmithr 8796129
refactor: Add new utility type
Josmithr c4bcfe9
refactor: Extract function
Josmithr 22835c6
Merge branch 'main' into api-markdown-documenter/inherit-members
Josmithr a8896cc
docs: TODOs
Josmithr 9d2ac4b
refactor: Use explicit table helpers
Josmithr 4b11b21
Merge branch 'main' into api-markdown-documenter/inherit-members
Josmithr e697a54
WIP: Display more information
Josmithr 1e3c2f1
refactor: Rename property
Josmithr 448ef69
refactor: Cleanup
Josmithr 8a14cd2
refactor: Extract function
Josmithr 4134f1e
Merge branch 'main' into api-markdown-documenter/inherit-members
Josmithr 68fcc03
Merge branch 'main' into api-markdown-documenter/inherit-members
Josmithr fb120a5
fix: Table column labeling
Josmithr File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
541 changes: 404 additions & 137 deletions
541
...rkdown-documenter/src/api-item-transforms/default-implementations/TransformApiTypeLike.ts
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
204 changes: 204 additions & 0 deletions
204
tools/api-markdown-documenter/src/api-item-transforms/utilities/InheritanceUtilities.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,204 @@ | ||
| /*! | ||
| * Copyright (c) Microsoft Corporation and contributors. All rights reserved. | ||
| * Licensed under the MIT License. | ||
| */ | ||
|
|
||
| import { | ||
| type ApiClass, | ||
| type ApiInterface, | ||
| type ApiItem, | ||
| ApiItemKind, | ||
| type HeritageType, | ||
| } from "@microsoft/api-extractor-model"; | ||
|
|
||
| import { | ||
| getApiItemKind, | ||
| type ApiTypeLike, | ||
| isTypeLike, | ||
| isStatic, | ||
| } from "../../utilities/index.js"; | ||
| import type { ApiItemTransformationConfiguration } from "../configuration/index.js"; | ||
|
|
||
| import { filterItems } from "./ApiItemTransformUtilities.js"; | ||
|
|
||
| /** | ||
| * API-Extractor does not directly provide a way to get inherited members of an API item. | ||
| * These utilities work around that limitation to provide a best-effort approximation of inherited members. | ||
| * | ||
| * If, in the future, API-Extractor provides a better way to get inherited members, these utilities should be updated or removed as appropriate. | ||
| */ | ||
|
|
||
| /** | ||
| * {@link TypeMember} base interface. | ||
| */ | ||
| export interface TypeMemberBase<TApiItem extends ApiItem = ApiItem> { | ||
| /** | ||
| * The kind of type member. | ||
| * "own" if the member is directly declared on the API item. | ||
| * "inherited" if the member is inherited from a base type. | ||
| */ | ||
| readonly kind: "own" | "inherited"; | ||
|
|
||
| /** | ||
| * The API item that is the member. | ||
| */ | ||
| readonly item: TApiItem; | ||
| } | ||
|
|
||
| /** | ||
| * Represents a type member that is directly declared on the API item. | ||
| */ | ||
| export interface OwnTypeMember<TApiItem extends ApiItem = ApiItem> | ||
| extends TypeMemberBase<TApiItem> { | ||
| readonly kind: "own"; | ||
|
|
||
| /** | ||
| * The member on some base type that this member overrides, if any. | ||
| * @remarks For example, if this member is a method that overrides a method on a base class, this would be that base method. | ||
| */ | ||
| readonly baseDefinition: ApiItem | undefined; | ||
| } | ||
|
|
||
| /** | ||
| * Represents a type member that is inherited from a base type. | ||
| */ | ||
| export interface InheritedTypeMember<TApiItem extends ApiItem = ApiItem> | ||
| extends TypeMemberBase<TApiItem> { | ||
| readonly kind: "inherited"; | ||
|
|
||
| /** | ||
| * The API item from which this member is inherited. | ||
| * @remarks For example, if this member is a method inherited from a base class, this would be that base class. | ||
| */ | ||
| readonly baseDefinition: ApiItem; | ||
| } | ||
|
|
||
| /** | ||
| * A type member, which may be either directly declared on the API item or inherited from a base type. | ||
| */ | ||
| export type TypeMember<TApiItem extends ApiItem = ApiItem> = | ||
| | OwnTypeMember<TApiItem> | ||
| | InheritedTypeMember<TApiItem>; | ||
|
|
||
| /** | ||
| * Gets the members of the specified API item, including inherited members where applicable. | ||
| * @param apiItem - The API item being queried. | ||
| * @param config - See {@link ApiItemTransformationConfiguration}. | ||
| */ | ||
| export function getTypeMembers<TApiItem extends ApiTypeLike>( | ||
| apiItem: TApiItem, | ||
| config: ApiItemTransformationConfiguration, | ||
| ): TypeMember[] { | ||
| // Get inherited members | ||
| const inheritedMembers: InheritedTypeMember[] = []; | ||
| if (apiItem.kind === ApiItemKind.Class) { | ||
| const apiClass = apiItem as ApiClass; | ||
|
|
||
| // Inherit members from `extends` clause | ||
| if (apiClass.extendsType !== undefined) { | ||
| inheritedMembers.push(...getInheritedMembers(apiClass, apiClass.extendsType, config)); | ||
| } | ||
|
|
||
| // Inherit members from `implements` clauses | ||
| for (const implementsType of apiClass.implementsTypes) { | ||
| inheritedMembers.push(...getInheritedMembers(apiClass, implementsType, config)); | ||
| } | ||
| } else if (apiItem.kind === ApiItemKind.Interface) { | ||
| const apiInterface = apiItem as ApiInterface; | ||
|
|
||
| // Inherit members from `extends` clauses | ||
| for (const extendsType of apiInterface.extendsTypes) { | ||
| inheritedMembers.push(...getInheritedMembers(apiInterface, extendsType, config)); | ||
| } | ||
| } else { | ||
| // TODO: type-alias | ||
| } | ||
|
|
||
| const ownMemberItems = filterItems(apiItem.members, config); | ||
|
|
||
| const ownMembers: OwnTypeMember[] = []; | ||
| for (const ownMemberItem of ownMemberItems) { | ||
| const override = inheritedMembers.find( | ||
| // TODO: verify this is a sufficient check for API match | ||
| (inherited) => inherited.item.containerKey === ownMemberItem.containerKey, | ||
| ); | ||
|
|
||
| // If this member overrides a base member, remove that base member from the inherited members list. | ||
| // We only want to display our override. | ||
| if (override) { | ||
| inheritedMembers.splice(inheritedMembers.indexOf(override), 1); | ||
| } | ||
|
|
||
| ownMembers.push({ | ||
| kind: "own", | ||
| item: ownMemberItem, | ||
| baseDefinition: override?.item, | ||
| }); | ||
| } | ||
|
|
||
| // TODO: document ordering | ||
| return [...inheritedMembers, ...ownMembers]; | ||
| } | ||
|
|
||
| function getInheritedMembers( | ||
| apiItem: ApiItem, | ||
| extendsType: HeritageType, | ||
| config: ApiItemTransformationConfiguration, | ||
| ): InheritedTypeMember[] { | ||
| const referencedItem = resolveHeritageTypeToItem(apiItem, extendsType, config); | ||
| if (referencedItem === undefined || !isTypeLike(referencedItem)) { | ||
| return []; | ||
| } | ||
| const referencedItemMembers: InheritedTypeMember[] = getTypeMembers( | ||
| referencedItem, | ||
| config, | ||
| ).map((inherited) => ({ | ||
| kind: "inherited", | ||
| item: inherited.item, | ||
| // If the item we're inheriting is itself inherited, preserve the original source. | ||
| // Otherwise, the source is the item we're inheriting from. | ||
| baseDefinition: inherited.kind === "inherited" ? inherited.baseDefinition : referencedItem, | ||
| })); | ||
|
|
||
| // Don't inherit constructors or static members | ||
| return referencedItemMembers.filter((inherited) => { | ||
| const itemKind = getApiItemKind(inherited.item); | ||
| if (itemKind === ApiItemKind.Constructor || itemKind === ApiItemKind.ConstructSignature) { | ||
| return false; | ||
| } | ||
| if (isStatic(inherited.item)) { | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
| } | ||
|
|
||
| function resolveHeritageTypeToItem( | ||
| contextApiItem: ApiItem, | ||
| heritageType: HeritageType, | ||
| config: ApiItemTransformationConfiguration, | ||
| ): ApiItem | undefined { | ||
| const excerpt = heritageType.excerpt; | ||
| if (excerpt.spannedTokens.length === 0) { | ||
| return undefined; | ||
| } | ||
|
|
||
| if (excerpt.spannedTokens.length > 1) { | ||
| // If there are multiple tokens, then the type expression is more complex than a single reference. | ||
| // This is a case we don't currently support. | ||
| return undefined; | ||
| } | ||
|
|
||
| const token = excerpt.spannedTokens[0]; | ||
| if (token.kind !== "Reference" || token.canonicalReference === undefined) { | ||
| // If the single token is not a reference, then there is nothing to resolve. | ||
| return undefined; | ||
| } | ||
|
|
||
| const resolvedReference = config.apiModel.resolveDeclarationReference( | ||
| token.canonicalReference, | ||
| contextApiItem, | ||
| ); | ||
|
|
||
| return resolvedReference.resolvedApiItem; | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: document this as a future change