Skip to content

Commit

Permalink
fix: noUncheckedIndexedAccess=true on fern docs (#1219)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored and sulaiman-fern committed Jul 31, 2024
1 parent afb6e32 commit bd1b33a
Show file tree
Hide file tree
Showing 23 changed files with 179 additions and 114 deletions.
2 changes: 1 addition & 1 deletion packages/configs/tsconfig/nextjs.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
"lib": ["dom", "dom.iterable", "esnext"],
"types": ["node", "next", "vitest/globals"],
"plugins": [{ "name": "next" }],
"noUncheckedIndexedAccess": false
"noUncheckedIndexedAccess": true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const ErrorExampleSelect: FC<PropsWithChildren<ErrorExampleSelect.Props>>
children,
}) => {
const handleValueChange = (value: string) => {
const [errorIndex, exampleIndex] = value.split(":").map((v) => parseInt(v, 10));
const [errorIndex = 0, exampleIndex = 0] = value.split(":").map((v) => parseInt(v, 10));
setSelectedErrorAndExample(errors[errorIndex], errors[errorIndex]?.examples[exampleIndex]);
};
const selectedErrorIndex = selectedError != null ? errors.indexOf(selectedError) : -1;
Expand Down
4 changes: 3 additions & 1 deletion packages/ui/app/src/api-page/examples/CodeSnippetExample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ const CodeSnippetExampleInternal: FC<CodeSnippetExample.Props> = ({
const viewportRef = createRef<ScrollToHandle>();

useResizeObserver(codeBlockRef, ([entry]) => {
measureHeight?.(entry.contentRect.height);
if (entry != null) {
measureHeight?.(entry.contentRect.height);
}
});

const requestHighlightLines = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,9 @@ function HeaderAuthForm({ header, disabled }: { header: APIV1Read.HeaderAuth; di
headers: {
...headers,
[header.headerWireValue]:
typeof change === "function" ? change(headers[header.headerWireValue]) : change,
typeof change === "function"
? change(headers[header.headerWireValue] ?? "")
: change,
},
}));
},
Expand Down Expand Up @@ -405,7 +407,7 @@ function isAuthed(auth: APIV1Read.ApiAuth, authState: PlaygroundAuthState): bool
bearerAuth: () => !isEmpty(authState.bearerAuth?.token.trim()),
basicAuth: () =>
!isEmpty(authState.basicAuth?.username.trim()) && !isEmpty(authState.basicAuth?.password.trim()),
header: (header) => !isEmpty(authState.header?.headers[header.headerWireValue].trim()),
header: (header) => !isEmpty(authState.header?.headers[header.headerWireValue]?.trim()),
_other: () => false,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function PlaygroundEndpointDescription({ endpoint }: PlaygroundEndpointDe
const [descriptionIsClamped, setDescriptionIsClamped] = useState(false);

useResizeObserver(descriptionRef, ([entry]) => {
if (!showFullDescription) {
if (!showFullDescription && entry != null) {
setDescriptionIsClamped(entry.target.scrollHeight > entry.target.clientHeight);
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FernNavigation } from "@fern-api/fdr-sdk";
import { FernButton, FernInput, FernScrollArea, FernTooltip, FernTooltipProvider } from "@fern-ui/components";
import { isNonNullish } from "@fern-ui/core-utils";
import { EMPTY_ARRAY, isNonNullish } from "@fern-ui/core-utils";
import { Cross1Icon, MagnifyingGlassIcon, SlashIcon } from "@radix-ui/react-icons";
import cn, { clsx } from "clsx";
import dynamic from "next/dynamic";
Expand Down Expand Up @@ -77,7 +77,7 @@ export function flattenApiSection(root: FernNavigation.SidebarRootNode | undefin
const allBreadcrumbs = result.map((group) => group.breadcrumbs);
const sharedBreadcrumbs = allBreadcrumbs.reduce((acc, breadcrumbs) => {
return acc.filter((breadcrumb, idx) => breadcrumb === breadcrumbs[idx]);
}, allBreadcrumbs[0]);
}, allBreadcrumbs[0] ?? EMPTY_ARRAY);

return result.map((group) => ({
...group,
Expand Down
14 changes: 10 additions & 4 deletions packages/ui/app/src/api-playground/form/PlaygroundMapForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,22 @@ export const PlaygroundMapForm = memo<PlaygroundMapFormProps>((props) => {

const handleChangeKey = useCallback((idx: number, newKey: unknown) => {
setInternalState((oldState) => {
const nextState = { ...oldState[idx] };
nextState.key = typeof newKey === "function" ? newKey(nextState.key) : newKey;
const nextState = {
key: oldState[idx]?.key ?? "",
value: oldState[idx]?.value ?? "",
};
nextState.key = (typeof newKey === "function" ? newKey(nextState.key) : newKey) ?? "";
return [...oldState.slice(0, idx), nextState, ...oldState.slice(idx + 1)];
});
}, []);

const handleChangeValue = useCallback((idx: number, newValue: unknown) => {
setInternalState((oldState) => {
const nextState = { ...oldState[idx] };
nextState.value = typeof newValue === "function" ? newValue(nextState.value) : newValue;
const nextState = {
key: oldState[idx]?.key ?? "",
value: oldState[idx]?.value ?? "",
};
nextState.value = (typeof newValue === "function" ? newValue(nextState.value) : newValue) ?? "";
return [...oldState.slice(0, idx), nextState, ...oldState.slice(idx + 1)];
});
}, []);
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/app/src/atoms/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,10 @@ export const SIDEBAR_DISMISSABLE_ATOM = atom((get) => {
// this is useful for tabs that only have one page
if (
sidebar.children.length === 1 &&
sidebar.children[0] != null &&
sidebar.children[0].type === "sidebarGroup" &&
sidebar.children[0].children.length === 1 &&
sidebar.children[0].children[0] != null &&
sidebar.children[0].children[0].type === "page"
) {
return true;
Expand Down
11 changes: 7 additions & 4 deletions packages/ui/app/src/docs/ChangelogEntryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ResolvedPath } from "../resolver/ResolvedPath";
export function ChangelogEntryPage({ resolvedPath }: { resolvedPath: ResolvedPath.ChangelogEntryPage }): ReactElement {
const page = resolvedPath.pages[resolvedPath.node.pageId];
const title = typeof page !== "string" ? page?.frontmatter.title : undefined;
const excerpt = typeof page !== "string" ? page?.frontmatter.subtitle ?? page.frontmatter.excerpt : undefined;
const excerpt = typeof page !== "string" ? page?.frontmatter.subtitle ?? page?.frontmatter.excerpt : undefined;
return (
<div className="flex justify-between px-4 md:px-6 lg:pl-8 lg:pr-16 xl:pr-0">
<div className="w-full min-w-0 pt-8">
Expand Down Expand Up @@ -38,9 +38,12 @@ export function ChangelogEntryPage({ resolvedPath }: { resolvedPath: ResolvedPat
</div>
)}
</header>
<div className="prose dark:prose-invert">
<MdxContent mdx={page} />
</div>
{/* TODO: alert if the page is null */}
{page != null && (
<div className="prose dark:prose-invert">
<MdxContent mdx={page} />
</div>
)}

<BottomNavigationButtons />
</div>
Expand Down
6 changes: 4 additions & 2 deletions packages/ui/app/src/docs/ChangelogPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ export function ChangelogPage({ resolvedPath }: { resolvedPath: ResolvedPath.Cha
</div>
<div className="prose dark:prose-invert">
{title != null && <h1>{title}</h1>}
<MdxContent mdx={page} />
{/* TODO: alert if the page is null */}
{page != null && <MdxContent mdx={page} />}
</div>
</div>
</>
Expand All @@ -85,7 +86,8 @@ export function ChangelogPage({ resolvedPath }: { resolvedPath: ResolvedPath.Cha
</div>
<div className="prose dark:prose-invert">
{title != null && <h1>{title}</h1>}
<MdxContent mdx={page} />
{/* TODO: alert if the page is null */}
{page != null && <MdxContent mdx={page} />}
</div>
</div>
<div className="-mt-2 w-72 pl-4 text-right max-xl:hidden">
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/app/src/mdx/components/snippets/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function useSelectedClient(
): [CodeExample | undefined, (nextClient: CodeExample) => void] {
const [selectedLanguage, setSelectedLanguage] = useAtom(FERN_LANGUAGE_ATOM);
const client = clients.find((c) => c.language === selectedLanguage) ?? clients[0];
const selectedClient = exampleName ? client.examples.find((e) => e.name === exampleName) : client.examples[0];
const selectedClient = exampleName ? client?.examples.find((e) => e.name === exampleName) : client?.examples[0];

const handleClickClient = useCallback(
(nextClient: CodeExample) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/app/src/mdx/plugins/rehypeSanitizeJSX.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ export function rehypeSanitizeJSX({ showErrors = false }: { showErrors?: boolean
!SUPPORTED_JSX_TAGS.includes(esnode.openingElement.name.name)
) {
const ancestor = ancestors[ancestors.length - 1];
if (ancestor.type === "ExpressionStatement") {
if (ancestor?.type === "ExpressionStatement") {
ancestor.expression = jsxFragment();
return ESTREE_SKIP;
}
if (ancestor.type === "JSXFragment" && i != null) {
if (ancestor?.type === "JSXFragment" && i != null) {
ancestor.children[i] = jsxFragment();
}
}
Expand Down
33 changes: 18 additions & 15 deletions packages/ui/app/src/next-app/utils/getBreadcrumbList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,27 @@ export function getBreadcrumbList(

if (FernNavigation.isPage(node)) {
const pageId = FernNavigation.utils.getPageId(node);
if (pageId != null && pages[pageId] != null) {
const { data: frontmatter } = getFrontmatter(pages[pageId].markdown);
if (pageId != null) {
const page = pages[pageId];
if (page != null) {
const { data: frontmatter } = getFrontmatter(page.markdown);

// if the frontmatter has a jsonld:breadcrumb, use that
if (frontmatter["jsonld:breadcrumb"] != null) {
const breadcrumb = JsonLd.BreadcrumbListSchema.safeParse(frontmatter["jsonld:breadcrumb"]);
if (breadcrumb.success) {
return breadcrumb.data;
} else {
// eslint-disable-next-line no-console
console.error("Invalid jsonld:breadcrumb", breadcrumb.error.toString());
// if the frontmatter has a jsonld:breadcrumb, use that
if (frontmatter["jsonld:breadcrumb"] != null) {
const breadcrumb = JsonLd.BreadcrumbListSchema.safeParse(frontmatter["jsonld:breadcrumb"]);
if (breadcrumb.success) {
return breadcrumb.data;
} else {
// eslint-disable-next-line no-console
console.error("Invalid jsonld:breadcrumb", breadcrumb.error.toString());
}
}
}

// override the title used in the breadcrumb's last item.
// for example, if the sidebar's title is "Overview" but the page title is "This API Overview"
if (frontmatter.title != null) {
title = frontmatter.title;
// override the title used in the breadcrumb's last item.
// for example, if the sidebar's title is "Overview" but the page title is "This API Overview"
if (frontmatter.title != null) {
title = frontmatter.title;
}
}
}
}
Expand Down
79 changes: 45 additions & 34 deletions packages/ui/app/src/next-app/utils/getSeoProp.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { APIV1Read, DocsV1Read, FernNavigation } from "@fern-api/fdr-sdk";
import { visitDiscriminatedUnion } from "@fern-ui/core-utils";
import { assertNonNullish, visitDiscriminatedUnion } from "@fern-ui/core-utils";
import type { LinkTag, MetaTag, NextSeoProps } from "@fern-ui/next-seo";
import { trim } from "lodash-es";
import { fromMarkdown } from "mdast-util-from-markdown";
Expand All @@ -12,7 +12,11 @@ import { getBreadcrumbList } from "./getBreadcrumbList";

function getFile(fileOrUrl: DocsV1Read.FileIdOrUrl, files: Record<string, DocsV1Read.File_>): DocsV1Read.File_ {
return visitDiscriminatedUnion(fileOrUrl)._visit({
fileId: ({ value: fileId }) => files[fileId],
fileId: ({ value: fileId }) => {
const file = files[fileId];
assertNonNullish(file, `File with id ${fileId} not found`);
return file;
},
url: ({ value: url }) => ({ type: "url", url }),
});
}
Expand Down Expand Up @@ -54,42 +58,48 @@ export function getSeoProps(

let ogMetadata: DocsV1Read.MetadataConfig = metadata ?? {};

if (pageId != null && pages[pageId]) {
const { data: frontmatter } = getFrontmatter(pages[pageId].markdown);
ogMetadata = { ...ogMetadata, ...frontmatter };
if (pageId != null) {
const page = pages[pageId];
if (page != null) {
const { data: frontmatter } = getFrontmatter(page.markdown);
ogMetadata = { ...ogMetadata, ...frontmatter };

// retrofit og:image
if (frontmatter.image != null) {
ogMetadata["og:image"] ??= { type: "url", value: frontmatter.image };
}
// retrofit og:image
if (frontmatter.image != null) {
ogMetadata["og:image"] ??= { type: "url", value: frontmatter.image };
}

seo.title ??= frontmatter.title;
seo.description ??= frontmatter.description ?? frontmatter.subtitle ?? frontmatter.excerpt;
seo.title ??= frontmatter.title;
seo.description ??= frontmatter.description ?? frontmatter.subtitle ?? frontmatter.excerpt;
}
}

if (FernNavigation.isApiLeaf(node) && apis[node.apiDefinitionId] != null) {
const api = FernNavigation.ApiDefinitionHolder.create(apis[node.apiDefinitionId]);

visitDiscriminatedUnion(node)._visit({
endpoint: ({ endpointId }) => {
const endpoint = api.endpoints.get(endpointId);
if (endpoint?.description != null) {
seo.description ??= endpoint.description;
}
},
webSocket: ({ webSocketId }) => {
const webSocket = api.webSockets.get(webSocketId);
if (webSocket?.description != null) {
seo.description ??= webSocket.description;
}
},
webhook: ({ webhookId }) => {
const webhook = api.webhooks.get(webhookId);
if (webhook?.description != null) {
seo.description ??= webhook.description;
}
},
});
const definition = apis[node.apiDefinitionId];
if (definition != null) {
const api = FernNavigation.ApiDefinitionHolder.create(definition);

visitDiscriminatedUnion(node)._visit({
endpoint: ({ endpointId }) => {
const endpoint = api.endpoints.get(endpointId);
if (endpoint?.description != null) {
seo.description ??= endpoint.description;
}
},
webSocket: ({ webSocketId }) => {
const webSocket = api.webSockets.get(webSocketId);
if (webSocket?.description != null) {
seo.description ??= webSocket.description;
}
},
webhook: ({ webhookId }) => {
const webhook = api.webhooks.get(webhookId);
if (webhook?.description != null) {
seo.description ??= webhook.description;
}
},
});
}
}

if (seo.title != null && stringHasMarkdown(seo.title)) {
Expand Down Expand Up @@ -168,7 +178,8 @@ export function getSeoProps(
}

if (favicon != null && files[favicon] != null) {
const image = files[favicon];
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const image = files[favicon]!;
additionalLinkTags.push({
rel: "icon",
href: image.url,
Expand Down
40 changes: 22 additions & 18 deletions packages/ui/app/src/resolver/ApiDefinitionResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,24 +133,28 @@ export class ApiDefinitionResolver {

if (node.overviewPageId != null && this.pages[node.overviewPageId] != null) {
const pageContent = this.pages[node.overviewPageId];
items.unshift({
type: "page",
id: node.overviewPageId,
slug: node.slug,
title: node.title,
markdown: await serializeMdx(pageContent.markdown, {
...this.mdxOptions,
filename: node.overviewPageId,
frontmatterDefaults: {
title: node.title,
breadcrumbs: [], // TODO: implement breadcrumbs
"edit-this-page-url": pageContent.editThisPageUrl,
"hide-nav-links": true,
layout: "reference",
"force-toc": this.featureFlags.isTocDefaultEnabled,
},
}),
});
if (pageContent != null) {
items.unshift({
type: "page",
id: node.overviewPageId,
slug: node.slug,
title: node.title,
markdown: await serializeMdx(pageContent.markdown, {
...this.mdxOptions,
filename: node.overviewPageId,
frontmatterDefaults: {
title: node.title,
breadcrumbs: [], // TODO: implement breadcrumbs
"edit-this-page-url": pageContent.editThisPageUrl,
"hide-nav-links": true,
layout: "reference",
"force-toc": this.featureFlags.isTocDefaultEnabled,
},
}),
});
} else {
// TODO: alert if the page is null
}
}

return {
Expand Down
Loading

0 comments on commit bd1b33a

Please sign in to comment.