diff --git a/packages/gitbook-v2/src/lib/data/prefetch.ts b/packages/gitbook-v2/src/lib/data/prefetch.ts index 5f9dd152a..29e4b8f89 100644 --- a/packages/gitbook-v2/src/lib/data/prefetch.ts +++ b/packages/gitbook-v2/src/lib/data/prefetch.ts @@ -42,6 +42,8 @@ export interface PrefetchedPageData { prefetchedRef: Promise>>; } +const cachedInitialDate = cache(() => Date.now()); + /** * Fetches the page data matching the requested pathname and fallback to root page when page is not found. */ @@ -63,22 +65,29 @@ async function getPageDataWithFallback(args: { export const getPrefetchedDataFromLayoutParams = cache( (params: RouteLayoutParams): PrefetchedLayoutData => { - const staticSiteContext = getStaticSiteContext(params); + const startingDate = cachedInitialDate(); + const staticSiteContext = getStaticSiteContext(params).finally(() => { + console.log(`Finished fetching static site context in ${Date.now() - startingDate}ms`); + }); const icons = Promise.all([ staticSiteContext.then(({ context }) => getIcon(context, 'light')), staticSiteContext.then(({ context }) => getIcon(context, 'dark')), - ]).then((urls) => [ - { - url: urls[0], - type: 'image/png', - media: '(prefers-color-scheme: light)', - }, - { - url: urls[1], - type: 'image/png', - media: '(prefers-color-scheme: dark)', - }, - ]); + ]) + .then((urls) => [ + { + url: urls[0], + type: 'image/png', + media: '(prefers-color-scheme: light)', + }, + { + url: urls[1], + type: 'image/png', + media: '(prefers-color-scheme: dark)', + }, + ]) + .finally(() => { + console.log(`Finished fetching icons in ${Date.now() - startingDate}ms`); + }); return { staticSiteContext, @@ -112,33 +121,43 @@ export const prefetchedDocumentRef = ( if (document.nodes && Array.isArray(document.nodes)) { traverseNodes(document.nodes); } - console.log('Prefetched document references:', fetched.size); return fetched; }; export const getPrefetchedDataFromPageParams = cache((params: RouteParams): PrefetchedPageData => { + const startingDate = cachedInitialDate(); const { staticSiteContext } = getPrefetchedDataFromLayoutParams(params); const pathname = getPagePathFromParams(params); - const pageData = staticSiteContext.then(({ context }) => - getPageDataWithFallback({ - context, - pagePathParams: { - pathname, - }, + const pageData = staticSiteContext + .then(({ context }) => + getPageDataWithFallback({ + context, + pagePathParams: { + pathname, + }, + }) + ) + .finally(() => { + console.log(`Finished fetching page data in ${Date.now() - startingDate}ms`); + }); + const document = pageData + .then(({ context, pageTarget }) => { + if (!pageTarget?.page) { + return null; + } + return getPageDocument(context, pageTarget?.page); }) - ); - const document = pageData.then(({ context, pageTarget }) => { - if (!pageTarget?.page) { - return null; - } - return getPageDocument(context, pageTarget?.page); - }); - const prefetchedRef = Promise.all([staticSiteContext, document]).then( - ([{ context }, document]) => { + .finally(() => { + console.log(`Finished fetching document in ${Date.now() - startingDate}ms`); + }); + const prefetchedRef = Promise.all([staticSiteContext, document]) + .then(([{ context }, document]) => { // Prefetch the references in the document return prefetchedDocumentRef(document, context); - } - ); + }) + .finally(() => { + console.log(`Finished prefetching references in ${Date.now() - startingDate}ms`); + }); return { pageData, document, diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/PlainCodeBlock.tsx b/packages/gitbook/src/components/DocumentView/CodeBlock/PlainCodeBlock.tsx index 9c50b95a0..318a61cc6 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/PlainCodeBlock.tsx +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/PlainCodeBlock.tsx @@ -48,6 +48,7 @@ export function PlainCodeBlock(props: { code: string; syntax: string }) { document={document} context={{ mode: 'default', + getContentRef: async () => null, // No content references needed for plain code block }} block={block} ancestorBlocks={[]} diff --git a/packages/gitbook/src/components/DocumentView/DocumentView.tsx b/packages/gitbook/src/components/DocumentView/DocumentView.tsx index f8892080e..abce77120 100644 --- a/packages/gitbook/src/components/DocumentView/DocumentView.tsx +++ b/packages/gitbook/src/components/DocumentView/DocumentView.tsx @@ -2,7 +2,7 @@ import type { ClassValue } from '@/lib/tailwind'; import type { ContentRef, JSONDocument } from '@gitbook/api'; import type { GitBookAnyContext } from '@v2/lib/context'; -import type { ResolvedContentRef } from '@/lib/references'; +import type { ResolveContentRefOptions, ResolvedContentRef } from '@/lib/references'; import { BlockSkeleton } from './Block'; import { Blocks } from './Blocks'; @@ -30,7 +30,10 @@ export interface DocumentContext { */ wrapBlocksInSuspense?: boolean; - contentRef?: Promise>>; + getContentRef: ( + ref: ContentRef, + options?: ResolveContentRefOptions + ) => Promise; } export interface DocumentContextProps { diff --git a/packages/gitbook/src/components/DocumentView/InlineLink.tsx b/packages/gitbook/src/components/DocumentView/InlineLink.tsx index 9f429349d..bc3cc67f0 100644 --- a/packages/gitbook/src/components/DocumentView/InlineLink.tsx +++ b/packages/gitbook/src/components/DocumentView/InlineLink.tsx @@ -1,6 +1,4 @@ import { type DocumentInlineLink, SiteInsightsLinkPosition } from '@gitbook/api'; - -import { resolveContentRef } from '@/lib/references'; import { Icon } from '@gitbook/icons'; import { StyledLink } from '../primitives'; import type { InlineProps } from './Inline'; @@ -10,16 +8,7 @@ import { Inlines } from './Inlines'; export async function InlineLink(props: InlineProps) { const { inline, document, context, ancestorInlines } = props; - const refs = await context.contentRef; - - const resolved = - (await refs?.get(inline.data.ref)) ?? - (context.contentContext - ? await resolveContentRef(inline.data.ref, context.contentContext, { - // We don't want to resolve the anchor text here, as it can be very expensive and will block rendering if there is a lot of anchors link. - resolveAnchorText: false, - }) - : null); + const resolved = await context.getContentRef(inline.data.ref); if (!context.contentContext || !resolved) { return ( diff --git a/packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx b/packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx index c32fb01e9..4a748019b 100644 --- a/packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx +++ b/packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx @@ -6,7 +6,6 @@ import { import { LinkBox, LinkOverlay } from '@/components/primitives'; import { Image } from '@/components/utils'; -import { resolveContentRef } from '@/lib/references'; import { type ClassValue, tcls } from '@/lib/tailwind'; import { RecordColumnValue } from './RecordColumnValue'; @@ -28,12 +27,8 @@ export async function RecordCard( : null; const [cover, target] = await Promise.all([ - coverFile && context.contentContext - ? resolveContentRef({ kind: 'file', file: coverFile }, context.contentContext) - : null, - targetRef && context.contentContext - ? resolveContentRef(targetRef, context.contentContext) - : null, + coverFile ? context.getContentRef({ kind: 'file', file: coverFile }) : null, + targetRef && context.getContentRef(targetRef), ]); const coverIsSquareOrPortrait = diff --git a/packages/gitbook/src/components/PDF/PDFPage.tsx b/packages/gitbook/src/components/PDF/PDFPage.tsx index f70b340b4..201abea15 100644 --- a/packages/gitbook/src/components/PDF/PDFPage.tsx +++ b/packages/gitbook/src/components/PDF/PDFPage.tsx @@ -28,6 +28,7 @@ import { PageControlButtons } from './PageControlButtons'; import { PrintButton } from './PrintButton'; import './pdf.css'; import { sanitizeGitBookAppURL } from '@/lib/app'; +import { resolveContentRef } from '@/lib/references'; import { getPageDocument } from '@v2/lib/data'; const DEFAULT_LIMIT = 100; @@ -244,6 +245,8 @@ async function PDFPageDocument(props: { ...context, page, }, + //TODO: Use prefetchedRef to avoid fetching the same content multiple times + getContentRef: (ref, options) => resolveContentRef(ref, context, options), getId: (id) => getPagePDFContainerId(page, id), }} // We consider all pages as offscreen in PDF mode diff --git a/packages/gitbook/src/components/PageBody/PageBody.tsx b/packages/gitbook/src/components/PageBody/PageBody.tsx index 5e22ea486..bc436506f 100644 --- a/packages/gitbook/src/components/PageBody/PageBody.tsx +++ b/packages/gitbook/src/components/PageBody/PageBody.tsx @@ -6,7 +6,11 @@ import { getSpaceLanguage } from '@/intl/server'; import { t } from '@/intl/translate'; import { hasFullWidthBlock, isNodeEmpty } from '@/lib/document'; import type { AncestorRevisionPage } from '@/lib/pages'; -import type { ResolvedContentRef } from '@/lib/references'; +import { + type ResolveContentRefOptions, + type ResolvedContentRef, + resolveContentRef, +} from '@/lib/references'; import { tcls } from '@/lib/tailwind'; import { DocumentView, DocumentViewSkeleton } from '../DocumentView'; import { TrackPageViewEvent } from '../Insights'; @@ -35,6 +39,19 @@ export function PageBody(props: { const language = getSpaceLanguage(customization); const updatedAt = page.updatedAt ?? page.createdAt; + const getContentRef = async ( + ref?: ContentRef, + options?: ResolveContentRefOptions + ): Promise => { + if (!ref) { + return null; + } + if (!options) { + return resolveContentRef(ref, context, options); + } + return props.prefetchedRef.then((prefetched) => prefetched.get(ref) ?? null); + }; + return ( <>
diff --git a/packages/gitbook/src/components/Search/server-actions.tsx b/packages/gitbook/src/components/Search/server-actions.tsx index b45bc0aba..3b02bbcf3 100644 --- a/packages/gitbook/src/components/Search/server-actions.tsx +++ b/packages/gitbook/src/components/Search/server-actions.tsx @@ -20,6 +20,7 @@ import { createStreamableValue } from 'ai/rsc'; import type * as React from 'react'; import { joinPathWithBaseURL } from '@/lib/paths'; +import { resolveContentRef } from '@/lib/references'; import { isV2 } from '@/lib/v2'; import type { IconName } from '@gitbook/icons'; import { throwIfDataError } from '@v2/lib/data'; @@ -345,6 +346,9 @@ async function transformAnswer( mode: 'default', contentContext: undefined, wrapBlocksInSuspense: false, + // TODO: Use prefetched content references + getContentRef: async (ref, options) => + resolveContentRef(ref, context, options), }} style={['space-y-5']} />