diff --git a/app/[[...path]]/page.tsx b/app/[[...path]]/page.tsx index 2042ec90e14b7..7c02fdca36d14 100644 --- a/app/[[...path]]/page.tsx +++ b/app/[[...path]]/page.tsx @@ -22,7 +22,7 @@ import {isDeveloperDocs} from 'sentry-docs/isDeveloperDocs'; import { getDevDocsFrontMatter, getDocsFrontMatter, - getFileBySlug, + getFileBySlugWithCache, getVersionsFromDoc, } from 'sentry-docs/mdx'; import {mdxComponents} from 'sentry-docs/mdxComponents'; @@ -106,9 +106,9 @@ export default async function Page(props: {params: Promise<{path?: string[]}>}) if (isDeveloperDocs) { // get the MDX for the current doc and render it - let doc: Awaited> | null = null; + let doc: Awaited>; try { - doc = await getFileBySlug(`develop-docs/${params.path?.join('/') ?? ''}`); + doc = await getFileBySlugWithCache(`develop-docs/${params.path?.join('/') ?? ''}`); } catch (e) { if (e.code === 'ENOENT') { // eslint-disable-next-line no-console @@ -144,9 +144,9 @@ export default async function Page(props: {params: Promise<{path?: string[]}>}) } // get the MDX for the current doc and render it - let doc: Awaited> | null = null; + let doc: Awaited>; try { - doc = await getFileBySlug(`docs/${pageNode.path}`); + doc = await getFileBySlugWithCache(`docs/${pageNode.path}`); } catch (e) { if (e.code === 'ENOENT') { // eslint-disable-next-line no-console diff --git a/src/components/include.tsx b/src/components/include.tsx index 8ec009d4b57e9..ad5b73dda97df 100644 --- a/src/components/include.tsx +++ b/src/components/include.tsx @@ -1,7 +1,7 @@ import {useMemo} from 'react'; import {getMDXComponent} from 'mdx-bundler/client'; -import {getFileBySlug} from 'sentry-docs/mdx'; +import {getFileBySlugWithCache} from 'sentry-docs/mdx'; import {mdxComponents} from 'sentry-docs/mdxComponents'; import {PlatformContent} from './platformContent'; @@ -10,13 +10,18 @@ type Props = { name: string; }; +function MDXLayoutRenderer({mdxSource: source, ...rest}) { + const MDXLayout = useMemo(() => getMDXComponent(source), [source]); + return ; +} + export async function Include({name}: Props) { - let doc: Awaited> | null = null; + let doc: Awaited>; if (name.endsWith('.mdx')) { name = name.slice(0, name.length - '.mdx'.length); } try { - doc = await getFileBySlug(`includes/${name}`); + doc = await getFileBySlugWithCache(`includes/${name}`); } catch (e) { if (e.code === 'ENOENT') { return null; @@ -24,9 +29,5 @@ export async function Include({name}: Props) { throw e; } const {mdxSource} = doc; - function MDXLayoutRenderer({mdxSource: source, ...rest}) { - const MDXLayout = useMemo(() => getMDXComponent(source), [source]); - return ; - } return ; } diff --git a/src/components/platformContent.tsx b/src/components/platformContent.tsx index fccb7554e1aa6..1ff6e6de34902 100644 --- a/src/components/platformContent.tsx +++ b/src/components/platformContent.tsx @@ -1,10 +1,10 @@ import fs from 'fs'; -import {useMemo} from 'react'; +import {cache, useMemo} from 'react'; import {getMDXComponent} from 'mdx-bundler/client'; import {getCurrentGuide, getDocsRootNode, getPlatform} from 'sentry-docs/docTree'; -import {getFileBySlug} from 'sentry-docs/mdx'; +import {getFileBySlugWithCache} from 'sentry-docs/mdx'; import {mdxComponents} from 'sentry-docs/mdxComponents'; import {serverContext} from 'sentry-docs/serverContext'; import { @@ -24,7 +24,7 @@ type Props = { platform?: string; }; -const udpatePathIfVersionedFileDoesNotExist = (path: string): string => { +const updatePathIfVersionedFileDoesNotExist = (path: string): string => { if (!isVersioned(path)) { return path; } @@ -39,6 +39,21 @@ const udpatePathIfVersionedFileDoesNotExist = (path: string): string => { return path; }; +/** + * Cache the result of updatePathIfVersionedFileDoesNotExist + * to avoid calling it multiple times for the same path. + * + * This is important because we want to skip the `fs.existsSync` call if possible. + */ +const updatePathIfVersionedFileDoesNotExistWithCache = cache( + updatePathIfVersionedFileDoesNotExist +); + +function MDXLayoutRenderer({mdxSource: source, ...rest}) { + const MDXLayout = useMemo(() => getMDXComponent(source), [source]); + return ; +} + export async function PlatformContent({includePath, platform, noGuides}: Props) { const {path} = serverContext(); @@ -54,15 +69,15 @@ export async function PlatformContent({includePath, platform, noGuides}: Props) guide = `${platform}.${path[3]}`; } - let doc: Awaited> | null = null; + let doc: Awaited> | undefined; if (guide) { - const guidePath = udpatePathIfVersionedFileDoesNotExist( + const guidePath = updatePathIfVersionedFileDoesNotExistWithCache( `platform-includes/${includePath}/${guide}` ); try { - doc = await getFileBySlug(guidePath); + doc = await getFileBySlugWithCache(guidePath); } catch (e) { // It's fine - keep looking. } @@ -72,13 +87,13 @@ export async function PlatformContent({includePath, platform, noGuides}: Props) const rootNode = await getDocsRootNode(); const guideObject = getCurrentGuide(rootNode, path); - const fallbackGuidePath = udpatePathIfVersionedFileDoesNotExist( + const fallbackGuidePath = updatePathIfVersionedFileDoesNotExistWithCache( `platform-includes/${includePath}/${guideObject?.fallbackGuide}${VERSION_INDICATOR}${getVersion(guide || '')}` ); if (guideObject?.fallbackGuide) { try { - doc = await getFileBySlug(fallbackGuidePath); + doc = await getFileBySlugWithCache(fallbackGuidePath); } catch (e) { // It's fine - keep looking. } @@ -87,11 +102,11 @@ export async function PlatformContent({includePath, platform, noGuides}: Props) if (!doc) { try { - const platformPath = udpatePathIfVersionedFileDoesNotExist( + const platformPath = updatePathIfVersionedFileDoesNotExistWithCache( `platform-includes/${includePath}/${platform}` ); - doc = await getFileBySlug(platformPath); + doc = await getFileBySlugWithCache(platformPath); } catch (e) { // It's fine - keep looking. } @@ -101,13 +116,13 @@ export async function PlatformContent({includePath, platform, noGuides}: Props) const rootNode = await getDocsRootNode(); const platformObject = getPlatform(rootNode, platform); - const fallbackPlatformPath = udpatePathIfVersionedFileDoesNotExist( + const fallbackPlatformPath = updatePathIfVersionedFileDoesNotExistWithCache( `platform-includes/${includePath}/${platformObject?.fallbackPlatform}` ); if (platformObject?.fallbackPlatform) { try { - doc = await getFileBySlug(fallbackPlatformPath); + doc = await getFileBySlugWithCache(fallbackPlatformPath); } catch (e) { // It's fine - keep looking. } @@ -116,7 +131,7 @@ export async function PlatformContent({includePath, platform, noGuides}: Props) if (!doc) { try { - doc = await getFileBySlug(`platform-includes/${includePath}/_default`); + doc = await getFileBySlugWithCache(`platform-includes/${includePath}/_default`); } catch (e) { // Couldn't find anything. return null; @@ -124,10 +139,6 @@ export async function PlatformContent({includePath, platform, noGuides}: Props) } const {mdxSource} = doc; - function MDXLayoutRenderer({mdxSource: source, ...rest}) { - const MDXLayout = useMemo(() => getMDXComponent(source), [source]); - return ; - } return ; } diff --git a/src/mdx.ts b/src/mdx.ts index 6a9ebe3989334..2c22c28bebafd 100644 --- a/src/mdx.ts +++ b/src/mdx.ts @@ -1,6 +1,7 @@ import fs from 'fs'; import path from 'path'; +import {cache} from 'react'; import matter from 'gray-matter'; import {s} from 'hastscript'; import yaml from 'js-yaml'; @@ -474,3 +475,10 @@ export async function getFileBySlug(slug: string) { }, }; } + +/** + * Cache the result of {@link getFileBySlug}. + * + * This is useful for performance when rendering the same file multiple times. + */ +export const getFileBySlugWithCache = cache(getFileBySlug);