diff --git a/packages/fdr-sdk/src/navigation/versions/v1/NavigationNodeLeaf.ts b/packages/fdr-sdk/src/navigation/versions/v1/NavigationNodeLeaf.ts index 132c8e7558..d2d7c55439 100644 --- a/packages/fdr-sdk/src/navigation/versions/v1/NavigationNodeLeaf.ts +++ b/packages/fdr-sdk/src/navigation/versions/v1/NavigationNodeLeaf.ts @@ -1,3 +1,4 @@ +import { LinkNode } from "."; import type { NavigationNode } from "./NavigationNode"; import { isApiLeaf, type NavigationNodeApiLeaf } from "./NavigationNodeApiLeaf"; import { isMarkdownLeaf, type NavigationNodeMarkdownLeaf } from "./NavigationNodePageLeaf"; @@ -5,8 +6,8 @@ import { isMarkdownLeaf, type NavigationNodeMarkdownLeaf } from "./NavigationNod /** * A navigation node that represents a leaf in the navigation tree (i.e. a node that does not have children) */ -export type NavigationNodeLeaf = NavigationNodeApiLeaf | NavigationNodeMarkdownLeaf; +export type NavigationNodeLeaf = NavigationNodeApiLeaf | NavigationNodeMarkdownLeaf | LinkNode; export function isLeaf(node: NavigationNode): node is NavigationNodeLeaf { - return isApiLeaf(node) || isMarkdownLeaf(node); + return isApiLeaf(node) || isMarkdownLeaf(node) || node.type === "link"; } diff --git a/packages/fdr-sdk/src/navigation/versions/v1/converters/NavigationConfigConverter.ts b/packages/fdr-sdk/src/navigation/versions/v1/converters/NavigationConfigConverter.ts index fed94ae4f0..bad6f13682 100644 --- a/packages/fdr-sdk/src/navigation/versions/v1/converters/NavigationConfigConverter.ts +++ b/packages/fdr-sdk/src/navigation/versions/v1/converters/NavigationConfigConverter.ts @@ -79,7 +79,7 @@ export class NavigationConfigConverter { }; // tag all children of hidden nodes as hidden - FernNavigation.V1.traverseNavigation(toRet, (node, _index, parents) => { + FernNavigation.V1.traverseDF(toRet, (node, parents) => { if ( FernNavigation.V1.hasMetadata(node) && parents.some((p) => FernNavigation.V1.hasMetadata(p) && p.hidden === true) diff --git a/packages/fdr-sdk/src/navigation/versions/v1/getChildren.ts b/packages/fdr-sdk/src/navigation/versions/v1/getChildren.ts new file mode 100644 index 0000000000..23731be0f0 --- /dev/null +++ b/packages/fdr-sdk/src/navigation/versions/v1/getChildren.ts @@ -0,0 +1,37 @@ +import { UnreachableCaseError } from "ts-essentials"; +import { NavigationNode } from "./NavigationNode"; +import { isLeaf } from "./NavigationNodeLeaf"; + +export function getChildren(node: NavigationNode): readonly NavigationNode[] { + if (isLeaf(node)) { + return []; + } + + switch (node.type) { + case "apiPackage": + case "changelog": + case "changelogMonth": + case "changelogYear": + case "section": + case "sidebarGroup": + case "sidebarRoot": + case "tabbed": + case "versioned": + return node.children; + case "product": + case "root": + case "tab": + return [node.child]; + case "apiReference": + return [...node.children, ...(node.changelog ? [node.changelog] : [])]; + case "endpointPair": + return [node.nonStream, node.stream]; + case "productgroup": + return [...(node.landingPage ? [node.landingPage] : []), ...node.children]; + case "unversioned": + case "version": + return [...(node.landingPage ? [node.landingPage] : []), node.child]; + default: + throw new UnreachableCaseError(node); + } +} diff --git a/packages/fdr-sdk/src/navigation/versions/v1/index.ts b/packages/fdr-sdk/src/navigation/versions/v1/index.ts index 964859d2c1..e357e5af9e 100644 --- a/packages/fdr-sdk/src/navigation/versions/v1/index.ts +++ b/packages/fdr-sdk/src/navigation/versions/v1/index.ts @@ -1,10 +1,5 @@ export * from "../../../client/generated/api/resources/commons"; export * from "../../../client/generated/api/resources/navigation/resources/v1/types"; -export * from "./convertAvailability"; -export * from "./converters/ApiReferenceNavigationConverter"; -export * from "./converters/toRootNode"; -export * from "./followRedirect"; -export * from "./getPageId"; export * from "./NavigationNode"; export * from "./NavigationNodeApiLeaf"; export * from "./NavigationNodeLeaf"; @@ -16,6 +11,11 @@ export * from "./NavigationNodeSection"; export * from "./NavigationNodeSectionOverview"; export * from "./NavigationNodeWithMetadata"; export * from "./NavigationNodeWithRedirect"; +export * from "./convertAvailability"; +export * from "./converters/ApiReferenceNavigationConverter"; +export * from "./converters/toRootNode"; +export * from "./followRedirect"; +export * from "./getPageId"; export * from "./slugjoin"; export * from "./toDefaultSlug"; -export * from "./traverseNavigation"; +export * from "./traverseDF"; diff --git a/packages/fdr-sdk/src/navigation/versions/v1/traverseDF.ts b/packages/fdr-sdk/src/navigation/versions/v1/traverseDF.ts new file mode 100644 index 0000000000..5e8467e302 --- /dev/null +++ b/packages/fdr-sdk/src/navigation/versions/v1/traverseDF.ts @@ -0,0 +1,8 @@ +import { dfs } from "../../../utils/traversers/dfs"; +import { TraverserVisit } from "../../../utils/traversers/types"; +import { NavigationNode } from "./NavigationNode"; +import { getChildren } from "./getChildren"; + +export function traverseDF(node: NavigationNode, visit: TraverserVisit): void { + return dfs(node, visit, getChildren); +} diff --git a/packages/fdr-sdk/src/navigation/versions/v1/traverseNavigation.ts b/packages/fdr-sdk/src/navigation/versions/v1/traverseNavigation.ts deleted file mode 100644 index 53994bcf8e..0000000000 --- a/packages/fdr-sdk/src/navigation/versions/v1/traverseNavigation.ts +++ /dev/null @@ -1,107 +0,0 @@ -import visitDiscriminatedUnion from "@fern-ui/core-utils/visitDiscriminatedUnion"; -import { noop } from "ts-essentials"; -import { NavigationNode } from "./NavigationNode"; - -const SKIP = "skip" as const; -// const CONTINUE = true as const; -const STOP = false as const; - -export function traverseNavigation( - node: NavigationNode, - visit: (node: NavigationNode, index: number | undefined, parents: NavigationNode[]) => boolean | typeof SKIP | void, -): void { - function internalChildrenTraverser(nodes: NavigationNode[], parents: NavigationNode[]): boolean | void { - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - if (node == null) { - throw new Error(`Failed to index into nodes. Index: ${i} Length: ${nodes.length}`); - } - const result = internalTraverser(node, i, parents); - if (result === STOP) { - return STOP; - } - } - return; - } - function internalTraverser( - node: NavigationNode, - index: number | undefined, - parents: NavigationNode[], - ): boolean | void { - const v = visit(node, index, parents); - if (v === SKIP) { - return; - } else if (v === STOP) { - return STOP; - } - return visitDiscriminatedUnion(node)._visit({ - root: (root) => internalTraverser(root.child, undefined, [...parents, root]), - product: (product) => internalTraverser(product.child, undefined, [...parents, product]), - productgroup: (productgroup) => { - if (productgroup.landingPage != null) { - const result = internalTraverser(productgroup.landingPage, undefined, [...parents, productgroup]); - if (result === STOP) { - return STOP; - } - } - return internalChildrenTraverser(productgroup.children, [...parents, productgroup]); - }, - versioned: (versioned) => internalChildrenTraverser(versioned.children, [...parents, versioned]), - tabbed: (tabbed) => internalChildrenTraverser(tabbed.children, [...parents, tabbed]), - sidebarRoot: (sidebar) => internalChildrenTraverser(sidebar.children, [...parents, sidebar]), - sidebarGroup: (sidebarGroup) => - internalChildrenTraverser(sidebarGroup.children, [...parents, sidebarGroup]), - version: (version) => { - if (version.landingPage != null) { - const result = internalTraverser(version.landingPage, undefined, [...parents, version]); - if (result === STOP) { - return STOP; - } - } - return internalTraverser(version.child, undefined, [...parents, version]); - }, - tab: (tab) => internalTraverser(tab.child, undefined, [...parents, tab]), - link: noop, - page: noop, - landingPage: noop, - section: (section) => internalChildrenTraverser(section.children, [...parents, section]), - apiReference: (apiReference) => { - const result = internalChildrenTraverser(apiReference.children, [...parents, apiReference]); - if (result === STOP) { - return STOP; - } - if (apiReference.changelog != null) { - return internalTraverser(apiReference.changelog, undefined, [...parents, apiReference]); - } - }, - changelog: (changelog) => internalChildrenTraverser(changelog.children, [...parents, changelog]), - changelogYear: (changelogYear) => - internalChildrenTraverser(changelogYear.children, [...parents, changelogYear]), - changelogMonth: (changelogMonth) => - internalChildrenTraverser(changelogMonth.children, [...parents, changelogMonth]), - changelogEntry: noop, - endpoint: noop, - webSocket: noop, - webhook: noop, - apiPackage: (apiPackage) => internalChildrenTraverser(apiPackage.children, [...parents, apiPackage]), - endpointPair: (endpointPair) => { - const result = internalTraverser(endpointPair.nonStream, undefined, [...parents, endpointPair]); - if (result === STOP) { - return STOP; - } - return internalTraverser(endpointPair.stream, undefined, [...parents, endpointPair]); - }, - unversioned: (unversioned) => { - if (unversioned.landingPage != null) { - const result = internalTraverser(unversioned.landingPage, undefined, [...parents, unversioned]); - if (result === STOP) { - return STOP; - } - } - - return internalTraverser(unversioned.child, undefined, [...parents, unversioned]); - }, - }); - } - internalTraverser(node, undefined, []); -} diff --git a/servers/fdr/src/services/algolia/AlgoliaSearchRecordGenerator.ts b/servers/fdr/src/services/algolia/AlgoliaSearchRecordGenerator.ts index ea437e6bcd..3fc5bba119 100644 --- a/servers/fdr/src/services/algolia/AlgoliaSearchRecordGenerator.ts +++ b/servers/fdr/src/services/algolia/AlgoliaSearchRecordGenerator.ts @@ -252,7 +252,7 @@ export class AlgoliaSearchRecordGenerator { } satisfies Algolia.AlgoliaRecordVersionV3) : undefined; - function toBreadcrumbs(parents: FernNavigation.V1.NavigationNode[]): string[] { + function toBreadcrumbs(parents: readonly FernNavigation.V1.NavigationNode[]): string[] { return [ ...breadcrumbs, ...parents @@ -268,7 +268,7 @@ export class AlgoliaSearchRecordGenerator { ]; } - FernNavigation.V1.traverseNavigation(root, (node, _index, parents) => { + FernNavigation.V1.traverseDF(root, (node, parents) => { if (!FernNavigation.V1.hasMetadata(node)) { return; } @@ -698,7 +698,7 @@ export class AlgoliaSearchRecordGenerator { } : undefined; - function toBreadcrumbs(parents: FernNavigation.V1.NavigationNode[]): string[] { + function toBreadcrumbs(parents: readonly FernNavigation.V1.NavigationNode[]): string[] { return [ ...breadcrumbs, ...parents @@ -714,7 +714,7 @@ export class AlgoliaSearchRecordGenerator { ]; } - FernNavigation.V1.traverseNavigation(root, (node, _index, parents) => { + FernNavigation.V1.traverseDF(root, (node, parents) => { if (!FernNavigation.V1.hasMetadata(node)) { return; } diff --git a/servers/fdr/src/services/algolia/AlgoliaSearchRecordGeneratorV2.ts b/servers/fdr/src/services/algolia/AlgoliaSearchRecordGeneratorV2.ts index a8e0e52536..78390b5c27 100644 --- a/servers/fdr/src/services/algolia/AlgoliaSearchRecordGeneratorV2.ts +++ b/servers/fdr/src/services/algolia/AlgoliaSearchRecordGeneratorV2.ts @@ -467,7 +467,7 @@ export class AlgoliaSearchRecordGeneratorV2 extends AlgoliaSearchRecordGenerator } satisfies Algolia.AlgoliaRecordVersionV3) : undefined; - FernNavigation.V1.traverseNavigation(root, (node, _index, parents) => { + FernNavigation.V1.traverseDF(root, (node, parents) => { if (!FernNavigation.V1.hasMetadata(node)) { return; } @@ -1359,7 +1359,7 @@ export class AlgoliaSearchRecordGeneratorV2 extends AlgoliaSearchRecordGenerator slug: part.urlSlug, })); - FernNavigation.V1.traverseNavigation(root, (node, _index, parents) => { + FernNavigation.V1.traverseDF(root, (node) => { if (!FernNavigation.V1.hasMetadata(node)) { return; } @@ -1449,7 +1449,7 @@ function toBreadcrumbs( title: string; slug: string; }[], - parents: FernNavigation.V1.NavigationNode[], + parents: readonly FernNavigation.V1.NavigationNode[], ): BreadcrumbsInfo[] { return [ ...breadcrumbs,