Skip to content

Commit e2ef0cc

Browse files
authored
refactor(docs): Enable ts-check in API Documentation generation modules and fix typing violations (#23730)
Also exposed a couple of issues that should be addressed in `api-markdown-documenter`.
1 parent 6200ca4 commit e2ef0cc

File tree

6 files changed

+106
-13
lines changed

6 files changed

+106
-13
lines changed

docs/infra/api-markdown-documenter/admonition-node.mjs

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
* Licensed under the MIT License.
44
*/
55

6+
//@ts-check
7+
/** @typedef {import("@fluid-tools/api-markdown-documenter").DocumentationNode} DocumentationNode */
8+
69
import { DocumentationParentNodeBase } from "@fluid-tools/api-markdown-documenter";
710

811
/**
@@ -44,7 +47,7 @@ export const admonitionNodeType = "Admonition";
4447
*/
4548
export class AdmonitionNode extends DocumentationParentNodeBase {
4649
/**
47-
* @param {@fluid-tools/api-markdown-documenter#DocumentationNode[]} children - Child node content.
50+
* @param {DocumentationNode[]} children - Child node content.
4851
* @param {string} admonitionKind - The kind of admonition. See {@link https://docusaurus.io/docs/markdown-features/admonitions}.
4952
* @param {string | undefined} title - (Optional) Title text for the admonition.
5053
*/

docs/infra/api-markdown-documenter/api-documentation-layout.mjs

+12-7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
*/
55

66
//@ts-check
7+
/** @typedef {import("@fluid-tools/api-markdown-documenter").ApiItem} ApiItem */
8+
/** @typedef {import("@fluid-tools/api-markdown-documenter").ApiItemTransformationConfiguration} ApiItemTransformationConfiguration */
79

810
import {
911
ApiItemKind,
@@ -45,7 +47,7 @@ const supportDocsLinkSpan = new SpanNode([
4547
* If we later wish to differentiate between release tags of `@legacy` items, this function will need
4648
* to be updated.
4749
*
48-
* @param apiItem {import("@fluid-tools/api-markdown-documenter").ApiItem} - The API item for which the import notice is being created.
50+
* @param {ApiItem} apiItem - The API item for which the import notice is being created.
4951
*/
5052
function createImportNotice(apiItem) {
5153
const containingPackage = apiItem.getAssociatedPackage();
@@ -105,7 +107,7 @@ function createImportNotice(apiItem) {
105107
*
106108
* If the item is tagged as "@system", displays an internal notice with use notes.
107109
*
108-
* @param apiItem {import("@fluid-tools/api-markdown-documenter").ApiItem} - The API item for which the system notice is being created.
110+
* @param {ApiItem} apiItem - The API item for which the system notice is being created.
109111
*/
110112
function createSystemNotice(apiItem) {
111113
if (ApiItemUtilities.ancestryHasModifierTag(apiItem, "@system")) {
@@ -144,9 +146,9 @@ function createSystemNotice(apiItem) {
144146
*
145147
* 1. See (if any)
146148
*
147-
* @param {import("@fluid-tools/api-markdown-documenter").ApiItem} apiItem - The API item being rendered.
148-
* @param {import("@fluid-tools/api-markdown-documenter").SectionNode[] | undefined} itemSpecificContent - API item-specific details to be included in the default layout.
149-
* @param {import("@fluid-tools/api-markdown-documenter").ApiItemTransformationConfiguration} config - Transformation configuration.
149+
* @param {ApiItem} apiItem - The API item being rendered.
150+
* @param {SectionNode[] | undefined} itemSpecificContent - API item-specific details to be included in the default layout.
151+
* @param {ApiItemTransformationConfiguration} config - Transformation configuration.
150152
*
151153
* @returns An array of sections describing the layout. See {@link @fluid-tools/api-markdown-documenter#ApiItemTransformationConfiguration.createDefaultLayout}.
152154
*/
@@ -245,8 +247,8 @@ export function layoutContent(apiItem, itemSpecificContent, config) {
245247
*
246248
* @remarks Displayed as a Docusaurus admonition. See {@link AdmonitionNode} and {@link renderAdmonitionNode}.
247249
*
248-
* @param {import("@fluid-tools/api-markdown-documenter").ApiItem} apiItem - The API item being rendered.
249-
* @param {import("@fluid-tools/api-markdown-documenter").ApiItemTransformationConfiguration} config - Transformation configuration.
250+
* @param {ApiItem} apiItem - The API item being rendered.
251+
* @param {ApiItemTransformationConfiguration} config - Transformation configuration.
250252
*
251253
* @returns The doc section if the API item had a `@remarks` comment, otherwise `undefined`.
252254
*/
@@ -257,6 +259,9 @@ function createDeprecationNoticeSection(apiItem, config) {
257259
}
258260

259261
const transformedDeprecatedBlock = transformTsdocNode(deprecatedBlock, apiItem, config);
262+
if (transformedDeprecatedBlock === undefined) {
263+
throw new Error("Failed to transform deprecated block.");
264+
}
260265

261266
return new AdmonitionNode(
262267
[transformedDeprecatedBlock],

docs/infra/api-markdown-documenter/custom-renderers.mjs

+25-1
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,25 @@
33
* Licensed under the MIT License.
44
*/
55

6+
//@ts-check
7+
/** @typedef {import("@fluid-tools/api-markdown-documenter").DocumentationNode} DocumentationNode */
8+
/** @typedef {import("@fluid-tools/api-markdown-documenter").MarkdownRenderContext} MarkdownRenderContext */
9+
610
import {
11+
BlockQuoteNode,
712
documentationNodeToHtml,
13+
DocumentWriter,
814
HtmlRenderer,
915
MarkdownRenderer,
16+
TableNode,
1017
} from "@fluid-tools/api-markdown-documenter";
18+
import { AdmonitionNode } from "./admonition-node.mjs";
1119

1220
/**
1321
* Renders a Docusaurus Admonition from the given parameters.
1422
* @param {string} admonitionKind -
1523
* @param {string | undefined} title
16-
* @param {object[]} children - Child contents to render in the admonition body.
24+
* @param {DocumentationNode[]} children - Child contents to render in the admonition body.
1725
* @param {DocumentWriter} writer - Writer context object into which the document contents will be written.
1826
* @param {MarkdownRenderContext} context - See {@link @fluid-tools/api-markdown-documenter#MarkdownRenderContext}.
1927
*/
@@ -59,6 +67,15 @@ export function renderBlockQuoteNode(blockQuoteNode, writer, context) {
5967
renderAdmonition("note", undefined, blockQuoteNode.children, writer, context);
6068
}
6169

70+
/**
71+
* Type guard for element HAST nodes.
72+
* @param {import("hast").Nodes} node - The node to check.
73+
* @returns {node is import("hast").Element} Whether the node is an element.
74+
*/
75+
function isElement(node) {
76+
return node.type === "element";
77+
}
78+
6279
/**
6380
* Renders a {@link TableNode} using HTML syntax, and applies the desired CSS class to it.
6481
*
@@ -68,11 +85,18 @@ export function renderBlockQuoteNode(blockQuoteNode, writer, context) {
6885
*/
6986
export function renderTableNode(tableNode, writer, context) {
7087
// Generate HTML AST for the table node.
88+
7189
const htmlTree = documentationNodeToHtml(tableNode, {
7290
rootFormatting: context,
7391
startingHeadingLevel: context.headingLevel,
92+
// @ts-ignore TODO: Fix this in the API-Markdown-Documenter package
7493
logger: context.logger,
7594
});
95+
96+
if (!isElement(htmlTree)) {
97+
throw new Error("Expected an HTML element as output from table node transformation.");
98+
}
99+
76100
htmlTree.properties.class = "table table-striped table-hover";
77101

78102
// Convert the HTML AST to a string.

docs/infra/api-markdown-documenter/render-api-documentation.mjs

+42-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
* Licensed under the MIT License.
44
*/
55

6+
//@ts-check
7+
/** @typedef {import("@fluid-tools/api-markdown-documenter").ApiItem} ApiItem */
8+
/** @typedef {import("@fluid-tools/api-markdown-documenter").ApiItemTransformationConfiguration} ApiItemTransformationConfiguration */
9+
/** @typedef {import("@fluid-tools/api-markdown-documenter").ApiPackage} ApiPackage */
10+
611
import {
712
ApiItemKind,
813
ApiItemUtilities,
@@ -31,8 +36,19 @@ import {
3136
const generatedContentNotice =
3237
"<!-- Do not edit this file. It is automatically generated by @fluidtools/api-markdown-documenter. -->";
3338

39+
/**
40+
* Type guard for identifying a package API item.
41+
*
42+
* @param {ApiItem} apiItem - The API item being type-checked.
43+
* @returns {apiItem is ApiPackage} Whether the API item is a package.
44+
*/
45+
function isPackage(apiItem) {
46+
return apiItem.kind === ApiItemKind.Package;
47+
}
48+
3449
/**
3550
* Generates a documentation suite for the API model saved under `inputDir`, saving the output to `outputDir`.
51+
*
3652
* @param {string} inputDir - The directory path containing the API model to be processed.
3753
* @param {string} outputDir - The directory path under which the generated documentation suite will be saved.
3854
* @param {string} uriRootDir - The base for all links between API members.
@@ -43,6 +59,7 @@ const generatedContentNotice =
4359
export async function renderApiDocumentation(inputDir, outputDir, uriRootDir, apiVersion) {
4460
/**
4561
* Logs a progress message, prefaced with the API version number to help differentiate parallel logging output.
62+
* @param {string} message - The progress message to log.
4663
*/
4764
function logProgress(message) {
4865
console.log(`(v${apiVersion}) ${message}`);
@@ -51,6 +68,11 @@ export async function renderApiDocumentation(inputDir, outputDir, uriRootDir, ap
5168
/**
5269
* Logs the error with the specified message, prefaced with the API version number to help differentiate parallel
5370
* logging output, and re-throws the error.
71+
*
72+
* @param {string} message - The progress message to log.
73+
* @param {unknown} error - The error to log and re-throw.
74+
*
75+
* @returns {never} This function always throws an error.
5476
*/
5577
function logErrorAndRethrow(message, error) {
5678
console.error(chalk.red(`(v${apiVersion}) ${message}:`));
@@ -70,10 +92,16 @@ export async function renderApiDocumentation(inputDir, outputDir, uriRootDir, ap
7092

7193
const apiModel = await loadModel({ modelDirectoryPath: inputDir });
7294

73-
// Custom renderers that utilize Docusaurus syntax for certain kinds of documentation elements.
95+
/**
96+
* Custom renderers that utilize Docusaurus syntax for certain kinds of documentation elements.
97+
* @type {import("@fluid-tools/api-markdown-documenter").MarkdownRenderers}
98+
*/
7499
const customRenderers = {
100+
// @ts-ignore TODO: fix typing in API-Markdown-Documenter package
75101
[DocumentationNodeType.BlockQuote]: renderBlockQuoteNode,
102+
// @ts-ignore TODO: fix typing in API-Markdown-Documenter package
76103
[DocumentationNodeType.Table]: renderTableNode,
104+
// @ts-ignore TODO: fix typing in API-Markdown-Documenter package
77105
[admonitionNodeType]: renderAdmonitionNode,
78106
};
79107

@@ -115,7 +143,6 @@ export async function renderApiDocumentation(inputDir, outputDir, uriRootDir, ap
115143
}
116144
},
117145
},
118-
newlineKind: "lf",
119146
uriRoot: uriRootDir,
120147
includeBreadcrumb: false, // Docusaurus includes this by default based on file hierarchy
121148
includeTopLevelDocumentHeading: false, // We inject `title` front-matter metadata instead
@@ -146,7 +173,7 @@ export async function renderApiDocumentation(inputDir, outputDir, uriRootDir, ap
146173
},
147174
exclude: (apiItem) => {
148175
// Exclude packages that aren't intended for public consumption.
149-
if (apiItem.kind === ApiItemKind.Package) {
176+
if (isPackage(apiItem)) {
150177
const packageName = apiItem.name;
151178
const packageScope = PackageName.getScope(packageName);
152179

@@ -181,6 +208,9 @@ export async function renderApiDocumentation(inputDir, outputDir, uriRootDir, ap
181208
await Promise.all(
182209
documents.map(async (document) => {
183210
const documentApiItem = document.apiItem;
211+
if (documentApiItem === undefined) {
212+
throw new Error("Document does not have an associated API item.");
213+
}
184214

185215
// #region Filter documents based on site-specific requirements
186216

@@ -225,14 +255,22 @@ export async function renderApiDocumentation(inputDir, outputDir, uriRootDir, ap
225255
await fs.writeFile(filePath, fileContents);
226256
} catch (error) {
227257
logErrorAndRethrow(
228-
`Encountered error while writing file output for "${document.apiItem.displayName}"`,
258+
`Encountered error while writing file output for "${documentApiItem.displayName}"`,
229259
error,
230260
);
231261
}
232262
}),
233263
);
234264
}
235265

266+
/**
267+
* Generate `yaml`-formatted front-matter for the API item. For use by Docusaurus.
268+
*
269+
* @param {ApiItem} documentApiItem - The item for which front-matter is being generated.
270+
* @param {ApiItemTransformationConfiguration} config - The transformation configuration.
271+
*
272+
* @returns {string} The front-matter, formatted as a string.
273+
*/
236274
function createFrontMatter(documentApiItem, config) {
237275
let title, sidebarLabel;
238276
if (documentApiItem.kind === ApiItemKind.Model) {

docs/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@
7777
"@microsoft/applicationinsights-web": "^3.3.4",
7878
"@playwright/test": "^1.49.0",
7979
"@rushstack/node-core-library": "^5.9.0",
80+
"@types/fs-extra": "^11.0.4",
81+
"@types/hast": "^3.0.4",
8082
"@types/node": "^22.9.1",
8183
"@wayneferrao/docusaurus-search-local": "^0.48.5",
8284
"chalk": "^5.3.0",

docs/pnpm-lock.yaml

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)