Skip to content
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

refactor(api-markdown-documenter): Add and use a ValidApiItemKind type alias which excludes the None value #23384

Merged
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import { type ApiItem, ApiItemKind, ReleaseTag } from "@microsoft/api-extractor-

import type { Heading } from "../Heading.js";
import type { Link } from "../Link.js";
import { getQualifiedApiItemName, getReleaseTag } from "../utilities/index.js";
import {
getQualifiedApiItemName,
getReleaseTag,
getApiItemKind,
type ValidApiItemKind,
} from "../utilities/index.js";

import type {
ApiItemTransformationConfiguration,
Expand Down Expand Up @@ -268,7 +273,7 @@ function getHeadingIdForApiItem(
config: ApiItemTransformationConfiguration,
): string {
let baseName: string | undefined;
const apiItemKind: ApiItemKind = apiItem.kind;
const apiItemKind = getApiItemKind(apiItem);

// Walk parentage up until we reach the ancestor into whose document we're being rendered.
// Generate ID information for everything back to that point
Expand Down Expand Up @@ -367,7 +372,7 @@ export function getAncestralHierarchy(
* @returns `true` if the item should be rendered to its own document. `false` otherwise.
*/
export function doesItemKindRequireOwnDocument(
kind: ApiItemKind,
kind: ValidApiItemKind,
documentBoundaries: DocumentBoundaries,
): boolean {
if (
Expand Down Expand Up @@ -404,7 +409,7 @@ export function doesItemRequireOwnDocument(
apiItem: ApiItem,
documentBoundaries: DocumentBoundaries,
): boolean {
return doesItemKindRequireOwnDocument(apiItem.kind, documentBoundaries);
return doesItemKindRequireOwnDocument(getApiItemKind(apiItem), documentBoundaries);
}

/**
Expand All @@ -430,7 +435,7 @@ export function doesItemRequireOwnDocument(
* @returns `true` if the item should contribute to directory-wise hierarchy in the output. `false` otherwise.
*/
function doesItemKindGenerateHierarchy(
kind: ApiItemKind,
kind: ValidApiItemKind,
hierarchyBoundaries: HierarchyBoundaries,
): boolean {
if (kind === ApiItemKind.Package) {
Expand Down Expand Up @@ -458,7 +463,7 @@ function doesItemGenerateHierarchy(
apiItem: ApiItem,
hierarchyBoundaries: HierarchyBoundaries,
): boolean {
return doesItemKindGenerateHierarchy(apiItem.kind, hierarchyBoundaries);
return doesItemKindGenerateHierarchy(getApiItemKind(apiItem), hierarchyBoundaries);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from "@microsoft/api-extractor-model";

import type { DocumentNode, SectionNode } from "../documentation-domain/index.js";
import { getApiItemKind } from "../utilities/index.js";

import { doesItemRequireOwnDocument, shouldItemBeIncluded } from "./ApiItemTransformUtilities.js";
import { createDocument } from "./Utilities.js";
Expand Down Expand Up @@ -54,16 +55,14 @@ export function apiItemToDocument(
apiItem: ApiItem,
config: ApiItemTransformationConfiguration,
): DocumentNode {
if (apiItem.kind === ApiItemKind.None) {
throw new Error(`Encountered API item "${apiItem.displayName}" with a kind of "None".`);
}
const itemKind = getApiItemKind(apiItem);

if (
apiItem.kind === ApiItemKind.Model ||
apiItem.kind === ApiItemKind.Package ||
apiItem.kind === ApiItemKind.EntryPoint
itemKind === ApiItemKind.Model ||
itemKind === ApiItemKind.Package ||
itemKind === ApiItemKind.EntryPoint
) {
throw new Error(`Provided API item kind must be handled specially: "${apiItem.kind}".`);
throw new Error(`Provided API item kind must be handled specially: "${itemKind}".`);
}

if (!shouldItemBeIncluded(apiItem, config)) {
Expand All @@ -74,13 +73,13 @@ export function apiItemToDocument(

if (!doesItemRequireOwnDocument(apiItem, config.documentBoundaries)) {
throw new Error(
`"renderApiDocument" called for an API item kind that is not intended to be rendered to its own document. Provided item kind: "${apiItem.kind}".`,
`"renderApiDocument" called for an API item kind that is not intended to be rendered to its own document. Provided item kind: "${itemKind}".`,
);
}

const logger = config.logger;

logger.verbose(`Generating document for ${apiItem.displayName} (${apiItem.kind})...`);
logger.verbose(`Generating document for ${apiItem.displayName} (${itemKind})...`);

const sections: SectionNode[] = [];

Expand Down Expand Up @@ -112,16 +111,14 @@ export function apiItemToSections(
apiItem: ApiItem,
config: ApiItemTransformationConfiguration,
): SectionNode[] {
if (apiItem.kind === ApiItemKind.None) {
throw new Error(`Encountered API item "${apiItem.displayName}" with a kind of "None".`);
}
const itemKind = getApiItemKind(apiItem);

if (
apiItem.kind === ApiItemKind.Model ||
apiItem.kind === ApiItemKind.Package ||
apiItem.kind === ApiItemKind.EntryPoint
itemKind === ApiItemKind.Model ||
itemKind === ApiItemKind.Package ||
itemKind === ApiItemKind.EntryPoint
) {
throw new Error(`Provided API item kind must be handled specially: "${apiItem.kind}".`);
throw new Error(`Provided API item kind must be handled specially: "${itemKind}".`);
}

if (!shouldItemBeIncluded(apiItem, config)) {
Expand All @@ -135,7 +132,7 @@ export function apiItemToSections(
logger.verbose(`Rendering section for ${apiItem.displayName}...`);

let sections: SectionNode[];
switch (apiItem.kind) {
switch (itemKind) {
case ApiItemKind.CallSignature: {
sections = config.transformApiCallSignature(apiItem as ApiCallSignature, config);
break;
Expand Down Expand Up @@ -221,7 +218,7 @@ export function apiItemToSections(
}

default: {
throw new Error(`Unrecognized API item kind: "${apiItem.kind}".`);
throw new Error(`Unrecognized API item kind: "${itemKind}".`);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
getSingleLineExcerptText,
isDeprecated,
getReleaseTag,
getApiItemKind,
} from "../../utilities/index.js";

/**
Expand Down Expand Up @@ -279,7 +280,8 @@ export namespace DefaultDocumentationSuiteOptions {
* - Package: Uses the unscoped package name.
*/
export function defaultGetFileNameForItem(apiItem: ApiItem): string {
switch (apiItem.kind) {
const itemKind = getApiItemKind(apiItem);
switch (itemKind) {
case ApiItemKind.Model: {
return "index";
}
Expand Down Expand Up @@ -307,7 +309,8 @@ export namespace DefaultDocumentationSuiteOptions {
* Uses the item's `displayName`, except for `Model` items, in which case the text "API Overview" is displayed.
*/
export function defaultGetHeadingTextForItem(apiItem: ApiItem): string {
switch (apiItem.kind) {
const itemKind = getApiItemKind(apiItem);
switch (itemKind) {
case ApiItemKind.Model: {
return "API Overview";
}
Expand All @@ -332,7 +335,8 @@ export namespace DefaultDocumentationSuiteOptions {
* Uses the item's signature, except for `Model` items, in which case the text "Packages" is displayed.
*/
export function defaultGetLinkTextForItem(apiItem: ApiItem): string {
switch (apiItem.kind) {
const itemKind = getApiItemKind(apiItem);
switch (itemKind) {
case ApiItemKind.Model: {
return "Packages";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import {
} from "@microsoft/api-extractor-model";

import type { SectionNode } from "../../documentation-domain/index.js";
import { ApiModifier, getScopedMemberNameForDiagnostics, isStatic } from "../../utilities/index.js";
import {
ApiModifier,
getApiItemKind,
getScopedMemberNameForDiagnostics,
isStatic,
} from "../../utilities/index.js";
import { filterChildMembers } from "../ApiItemTransformUtilities.js";
import type { ApiItemTransformationConfiguration } from "../configuration/index.js";
import { createChildDetailsSection, createMemberTables } from "../helpers/index.js";
Expand Down Expand Up @@ -75,7 +80,8 @@ export function transformApiClass(
const indexSignatures: ApiIndexSignature[] = [];
const allMethods: ApiMethod[] = [];
for (const child of filteredChildren) {
switch (child.kind) {
const childKind = getApiItemKind(child);
switch (childKind) {
case ApiItemKind.Constructor: {
constructors.push(child as ApiConstructor);
break;
Expand All @@ -102,7 +108,7 @@ export function transformApiClass(
child.displayName
}" of Class "${getScopedMemberNameForDiagnostics(
apiClass,
)}" is of unsupported API item kind: "${child.kind}"`,
)}" is of unsupported API item kind: "${childKind}"`,
);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from "@microsoft/api-extractor-model";

import type { DocumentationNode, SectionNode } from "../../documentation-domain/index.js";
import { getScopedMemberNameForDiagnostics } from "../../utilities/index.js";
import { getApiItemKind, getScopedMemberNameForDiagnostics } from "../../utilities/index.js";
import { filterChildMembers } from "../ApiItemTransformUtilities.js";
import type { ApiItemTransformationConfiguration } from "../configuration/index.js";
import { createMemberTables, wrapInSection } from "../helpers/index.js";
Expand All @@ -31,7 +31,8 @@ export function transformApiEnum(
// Accumulate child items
const flags: ApiEnumMember[] = [];
for (const child of filteredChildren) {
switch (child.kind) {
const childKind = getApiItemKind(child);
switch (childKind) {
case ApiItemKind.EnumMember: {
flags.push(child as ApiEnumMember);
break;
Expand All @@ -42,7 +43,7 @@ export function transformApiEnum(
child.displayName
}" of Enum "${getScopedMemberNameForDiagnostics(
apiEnum,
)}" is of unsupported API item kind: "${child.kind}"`,
)}" is of unsupported API item kind: "${childKind}"`,
);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from "@microsoft/api-extractor-model";

import type { SectionNode } from "../../documentation-domain/index.js";
import { getScopedMemberNameForDiagnostics } from "../../utilities/index.js";
import { getApiItemKind, getScopedMemberNameForDiagnostics } from "../../utilities/index.js";
import { filterChildMembers } from "../ApiItemTransformUtilities.js";
import type { ApiItemTransformationConfiguration } from "../configuration/index.js";
import { createChildDetailsSection, createMemberTables } from "../helpers/index.js";
Expand Down Expand Up @@ -69,7 +69,8 @@ export function transformApiInterface(
const indexSignatures: ApiIndexSignature[] = [];
const methods: ApiMethodSignature[] = [];
for (const child of filteredChildren) {
switch (child.kind) {
const childKind = getApiItemKind(child);
switch (childKind) {
case ApiItemKind.ConstructSignature: {
constructSignatures.push(child as ApiConstructSignature);
break;
Expand Down Expand Up @@ -97,7 +98,7 @@ export function transformApiInterface(
child.displayName
}" of Interface "${getScopedMemberNameForDiagnostics(
apiInterface,
)}" is of unsupported API item kind: "${child.kind}"`,
)}" is of unsupported API item kind: "${childKind}"`,
);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {

import type { SectionNode } from "../../documentation-domain/index.js";
import type { ApiModuleLike } from "../../utilities/index.js";
import { getScopedMemberNameForDiagnostics } from "../../utilities/index.js";
import { getApiItemKind, getScopedMemberNameForDiagnostics } from "../../utilities/index.js";
import { filterItems } from "../ApiItemTransformUtilities.js";
import type { ApiItemTransformationConfiguration } from "../configuration/index.js";
import { createChildDetailsSection, createMemberTables } from "../helpers/index.js";
Expand Down Expand Up @@ -77,7 +77,8 @@ export function transformApiModuleLike(
const enums: ApiEnum[] = [];
const variables: ApiVariable[] = [];
for (const child of filteredChildren) {
switch (child.kind) {
const childKind = getApiItemKind(child);
switch (childKind) {
case ApiItemKind.Interface: {
interfaces.push(child as ApiInterface);
break;
Expand Down Expand Up @@ -108,11 +109,11 @@ export function transformApiModuleLike(
}
default: {
config.logger?.error(
`Child item "${child.displayName}" of ${
apiItem.kind
} "${getScopedMemberNameForDiagnostics(
`Child item "${
child.displayName
}" of ${childKind} "${getScopedMemberNameForDiagnostics(
apiItem,
)}" is of unsupported API item kind: "${child.kind}"`,
)}" is of unsupported API item kind: "${childKind}"`,
);
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
type ApiEntryPoint,
ApiInterface,
type ApiItem,
type ApiItemKind,
ApiReturnTypeMixin,
ApiTypeParameterListMixin,
type Excerpt,
Expand Down Expand Up @@ -56,6 +55,7 @@ import {
getDeprecatedBlock,
getExampleBlocks,
getReturnsBlock,
type ValidApiItemKind,
} from "../../utilities/index.js";
import {
doesItemKindRequireOwnDocument,
Expand Down Expand Up @@ -960,7 +960,7 @@ export interface ChildSectionProperties {
/**
* The API item kind of all child items.
*/
itemKind: ApiItemKind;
itemKind: ValidApiItemKind;

/**
* The child items to be rendered.
Expand Down
22 changes: 22 additions & 0 deletions tools/api-markdown-documenter/src/utilities/ApiItemUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,30 @@ import type { Logger } from "../Logging.js";

/**
* This module contains general `ApiItem`-related types and utilities.
* @remarks Note: the utilities here should not require any specific context or configuration.
*/

/**
* Represents "valid" API item kinds. I.e., not `None`.
*/
export type ValidApiItemKind = Exclude<ApiItemKind, ApiItemKind.None>;

/**
* Gets the {@link ValidApiItemKind} for the provided API item.
*
* @throws If the item's kind is "None".
*/
export function getApiItemKind(apiItem: ApiItem): ValidApiItemKind {
switch (apiItem.kind) {
case ApiItemKind.None: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious: what would have an item kind of None?

Copy link
Contributor Author

@Josmithr Josmithr Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For our purposes, nothing, and would signal that we encountered corrupted input or something. My guess as to why the value exists at all is for use in the various mixin "ApiItem" types they export - they aren't real API item types, so they would be tagged as None.

throw new Error(`Encountered an API item with kind "None": "${apiItem.displayName}".`);
}
default: {
return apiItem.kind;
}
}
}

/**
* Represents "member" API item kinds.
* These are the kinds of items the system supports generally for rendering, file-system configuration, etc.
Expand Down
Loading