Make sure to use the correct version for adaptive content (#3487)

Co-authored-by: Nicolas Dorseuil <nicolas@gitbook.io>
This commit is contained in:
conico974
2025-07-24 11:40:12 +02:00
committed by GitHub
parent 56d1b8fe2d
commit 04c80262e2
7 changed files with 60 additions and 5 deletions
+3
View File
@@ -14,6 +14,9 @@ const nextConfig = {
dynamic: 3600, // 1 hour
static: 3600, // 1 hour
},
// Since content is fully static, we don't want to fetch on hover again
optimisticClientCache: false,
},
env: {
@@ -3,15 +3,19 @@
import type { CustomizationThemeMode, SiteExternalLinksTarget } from '@gitbook/api';
import { ThemeProvider } from 'next-themes';
import type React from 'react';
import { useClearRouterCache } from '../hooks/useClearRouterCache';
import { LinkSettingsContext } from '../primitives';
export function ClientContexts(props: {
nonce?: string;
forcedTheme: CustomizationThemeMode | undefined;
externalLinksTarget: SiteExternalLinksTarget;
contextId: string | undefined;
children: React.ReactNode;
}) {
const { children, forcedTheme, externalLinksTarget } = props;
const { children, forcedTheme, externalLinksTarget, contextId } = props;
useClearRouterCache(contextId);
/**
* A bug in ThemeProvider is causing the nonce to be included incorrectly
@@ -9,12 +9,11 @@ import { AdminToolbar } from '@/components/AdminToolbar';
import { CookiesToast } from '@/components/Cookies';
import { LoadIntegrations } from '@/components/Integrations';
import { SpaceLayout } from '@/components/SpaceLayout';
import { buildVersion } from '@/lib/build';
import { isSiteIndexable } from '@/lib/seo';
import type { VisitorAuthClaims } from '@/lib/adaptive';
import { buildVersion } from '@/lib/build';
import { GITBOOK_API_PUBLIC_URL, GITBOOK_ASSETS_URL, GITBOOK_ICONS_URL } from '@/lib/env';
import { getResizedImageURL } from '@/lib/images';
import { isSiteIndexable } from '@/lib/seo';
import { ClientContexts } from './ClientContexts';
import { RocketLoaderDetector } from './RocketLoaderDetector';
@@ -50,6 +49,7 @@ export async function SiteLayout(props: {
<NuqsAdapter>
<ClientContexts
nonce={nonce}
contextId={context.contextId}
forcedTheme={
forcedTheme ??
(customization.themes.toggeable ? undefined : customization.themes.default)
@@ -0,0 +1,30 @@
'use client';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
// We cannot use a ref here because the contextId gets reset on navigation
// Probably because of this bug https://github.com/vercel/next.js/issues/67542
let previousContextId: string | undefined;
/**
* A custom hook that clears the router cache on contextId change.
* This is useful for ensuring that the router does not cache stale data for adaptive content.
*/
export function useClearRouterCache(contextId: string | undefined) {
const router = useRouter();
useEffect(() => {
if (previousContextId === undefined) {
// On the first run, we set the previousContextId to the current contextId
previousContextId = contextId;
return; // Skip the first run to avoid unnecessary reload
}
// Initially, previousContextId will be undefined, so we only clear the cache
// if contextId has changed from a defined value to a new value.
// This prevents unnecessary cache clearing on the first render.
if (contextId !== previousContextId && previousContextId !== undefined) {
previousContextId = contextId;
// Trigger a full reload to clear the in-memory cache
router.refresh(); // This will clear the cache and re-fetch the data
}
}, [contextId, router]);
}
@@ -123,11 +123,15 @@ export const Link = React.forwardRef(function Link(
);
}
// Not sure why yet, but it seems necessary to force prefetch to true
// default behavior doesn't seem to properly use the client router cache.
const _prefetch = prefetch === null || prefetch === undefined ? true : prefetch;
return (
<NextLink
ref={ref}
href={href}
prefetch={prefetch}
prefetch={_prefetch}
className={tcls(...forwardedClassNames, className)}
{...domProps}
onClick={onClick}
+7
View File
@@ -44,6 +44,7 @@ export type SiteURLData = Pick<
| 'siteSection'
| 'siteBasePath'
| 'basePath'
| 'contextId'
> & {
/**
* Identifier used for image resizing.
@@ -123,6 +124,9 @@ export type GitBookSiteContext = GitBookSpaceContext & {
/** Scripts to load for the site. */
scripts: SiteIntegrationScript[];
/** Context ID used by adaptive content. It represents an unique identifier for the authentication context */
contextId?: string;
};
/**
@@ -200,6 +204,7 @@ export async function fetchSiteContextByURLLookup(
shareKey: data.shareKey,
changeRequest: data.changeRequest,
revision: data.revision,
contextId: data.contextId,
});
}
@@ -217,6 +222,7 @@ export async function fetchSiteContextByIds(
shareKey: string | undefined;
changeRequest: string | undefined;
revision: string | undefined;
contextId?: string;
}
): Promise<GitBookSiteContext> {
const { dataFetcher } = baseContext;
@@ -314,6 +320,7 @@ export async function fetchSiteContextByIds(
structure: siteStructure,
sections,
scripts,
contextId: ids.contextId,
};
}
+7
View File
@@ -249,6 +249,7 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
shareKey: siteURLData.shareKey,
apiToken: siteURLData.apiToken,
imagesContextId: imagesContextId,
contextId: siteURLData.contextId,
};
const requestHeaders = new Headers(request.headers);
@@ -328,6 +329,12 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
response.headers.set('x-gitbook-route-type', routeType);
response.headers.set('x-gitbook-route-site', siteURLWithoutProtocol);
// When we use adaptive content, we want to ensure that the cache is not used at all on the client side.
// Vercel already set this header, this is needed in OpenNext.
if (siteURLData.contextId) {
response.headers.set('cache-control', 'public, max-age=0, must-revalidate');
}
return writeResponseCookies(response, cookies);
};