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

fix: noUncheckedIndexedAccess=true on fern docs #1219

Merged
merged 3 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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
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
Copy link
Member

Choose a reason for hiding this comment

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

we should file a ticket if this is important

}
}

return {
Expand Down
Loading
Loading