Skip to content

Commit 04c8026

Browse files
conico974Nicolas Dorseuil
andauthored
Make sure to use the correct version for adaptive content (#3487)
Co-authored-by: Nicolas Dorseuil <[email protected]>
1 parent 56d1b8f commit 04c8026

File tree

7 files changed

+60
-5
lines changed

7 files changed

+60
-5
lines changed

packages/gitbook/next.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ const nextConfig = {
1414
dynamic: 3600, // 1 hour
1515
static: 3600, // 1 hour
1616
},
17+
18+
// Since content is fully static, we don't want to fetch on hover again
19+
optimisticClientCache: false,
1720
},
1821

1922
env: {

packages/gitbook/src/components/SiteLayout/ClientContexts.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
import type { CustomizationThemeMode, SiteExternalLinksTarget } from '@gitbook/api';
44
import { ThemeProvider } from 'next-themes';
55
import type React from 'react';
6+
import { useClearRouterCache } from '../hooks/useClearRouterCache';
67
import { LinkSettingsContext } from '../primitives';
78

89
export function ClientContexts(props: {
910
nonce?: string;
1011
forcedTheme: CustomizationThemeMode | undefined;
1112
externalLinksTarget: SiteExternalLinksTarget;
13+
contextId: string | undefined;
1214
children: React.ReactNode;
1315
}) {
14-
const { children, forcedTheme, externalLinksTarget } = props;
16+
const { children, forcedTheme, externalLinksTarget, contextId } = props;
17+
18+
useClearRouterCache(contextId);
1519

1620
/**
1721
* A bug in ThemeProvider is causing the nonce to be included incorrectly

packages/gitbook/src/components/SiteLayout/SiteLayout.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import { AdminToolbar } from '@/components/AdminToolbar';
99
import { CookiesToast } from '@/components/Cookies';
1010
import { LoadIntegrations } from '@/components/Integrations';
1111
import { SpaceLayout } from '@/components/SpaceLayout';
12-
import { buildVersion } from '@/lib/build';
13-
import { isSiteIndexable } from '@/lib/seo';
14-
1512
import type { VisitorAuthClaims } from '@/lib/adaptive';
13+
import { buildVersion } from '@/lib/build';
1614
import { GITBOOK_API_PUBLIC_URL, GITBOOK_ASSETS_URL, GITBOOK_ICONS_URL } from '@/lib/env';
1715
import { getResizedImageURL } from '@/lib/images';
16+
import { isSiteIndexable } from '@/lib/seo';
1817
import { ClientContexts } from './ClientContexts';
1918
import { RocketLoaderDetector } from './RocketLoaderDetector';
2019

@@ -50,6 +49,7 @@ export async function SiteLayout(props: {
5049
<NuqsAdapter>
5150
<ClientContexts
5251
nonce={nonce}
52+
contextId={context.contextId}
5353
forcedTheme={
5454
forcedTheme ??
5555
(customization.themes.toggeable ? undefined : customization.themes.default)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client';
2+
import { useRouter } from 'next/navigation';
3+
import { useEffect } from 'react';
4+
5+
// We cannot use a ref here because the contextId gets reset on navigation
6+
// Probably because of this bug https://github.com/vercel/next.js/issues/67542
7+
let previousContextId: string | undefined;
8+
9+
/**
10+
* A custom hook that clears the router cache on contextId change.
11+
* This is useful for ensuring that the router does not cache stale data for adaptive content.
12+
*/
13+
export function useClearRouterCache(contextId: string | undefined) {
14+
const router = useRouter();
15+
useEffect(() => {
16+
if (previousContextId === undefined) {
17+
// On the first run, we set the previousContextId to the current contextId
18+
previousContextId = contextId;
19+
return; // Skip the first run to avoid unnecessary reload
20+
}
21+
// Initially, previousContextId will be undefined, so we only clear the cache
22+
// if contextId has changed from a defined value to a new value.
23+
// This prevents unnecessary cache clearing on the first render.
24+
if (contextId !== previousContextId && previousContextId !== undefined) {
25+
previousContextId = contextId;
26+
// Trigger a full reload to clear the in-memory cache
27+
router.refresh(); // This will clear the cache and re-fetch the data
28+
}
29+
}, [contextId, router]);
30+
}

packages/gitbook/src/components/primitives/Link.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,15 @@ export const Link = React.forwardRef(function Link(
123123
);
124124
}
125125

126+
// Not sure why yet, but it seems necessary to force prefetch to true
127+
// default behavior doesn't seem to properly use the client router cache.
128+
const _prefetch = prefetch === null || prefetch === undefined ? true : prefetch;
129+
126130
return (
127131
<NextLink
128132
ref={ref}
129133
href={href}
130-
prefetch={prefetch}
134+
prefetch={_prefetch}
131135
className={tcls(...forwardedClassNames, className)}
132136
{...domProps}
133137
onClick={onClick}

packages/gitbook/src/lib/context.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export type SiteURLData = Pick<
4444
| 'siteSection'
4545
| 'siteBasePath'
4646
| 'basePath'
47+
| 'contextId'
4748
> & {
4849
/**
4950
* Identifier used for image resizing.
@@ -123,6 +124,9 @@ export type GitBookSiteContext = GitBookSpaceContext & {
123124

124125
/** Scripts to load for the site. */
125126
scripts: SiteIntegrationScript[];
127+
128+
/** Context ID used by adaptive content. It represents an unique identifier for the authentication context */
129+
contextId?: string;
126130
};
127131

128132
/**
@@ -200,6 +204,7 @@ export async function fetchSiteContextByURLLookup(
200204
shareKey: data.shareKey,
201205
changeRequest: data.changeRequest,
202206
revision: data.revision,
207+
contextId: data.contextId,
203208
});
204209
}
205210

@@ -217,6 +222,7 @@ export async function fetchSiteContextByIds(
217222
shareKey: string | undefined;
218223
changeRequest: string | undefined;
219224
revision: string | undefined;
225+
contextId?: string;
220226
}
221227
): Promise<GitBookSiteContext> {
222228
const { dataFetcher } = baseContext;
@@ -314,6 +320,7 @@ export async function fetchSiteContextByIds(
314320
structure: siteStructure,
315321
sections,
316322
scripts,
323+
contextId: ids.contextId,
317324
};
318325
}
319326

packages/gitbook/src/middleware.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
249249
shareKey: siteURLData.shareKey,
250250
apiToken: siteURLData.apiToken,
251251
imagesContextId: imagesContextId,
252+
contextId: siteURLData.contextId,
252253
};
253254

254255
const requestHeaders = new Headers(request.headers);
@@ -328,6 +329,12 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
328329
response.headers.set('x-gitbook-route-type', routeType);
329330
response.headers.set('x-gitbook-route-site', siteURLWithoutProtocol);
330331

332+
// When we use adaptive content, we want to ensure that the cache is not used at all on the client side.
333+
// Vercel already set this header, this is needed in OpenNext.
334+
if (siteURLData.contextId) {
335+
response.headers.set('cache-control', 'public, max-age=0, must-revalidate');
336+
}
337+
331338
return writeResponseCookies(response, cookies);
332339
};
333340

0 commit comments

Comments
 (0)