Skip to content

Commit 63d506d

Browse files
authored
Merge branch 'main' into brett/rnd-5794
2 parents 05184e2 + 0f867e5 commit 63d506d

File tree

9 files changed

+79
-29
lines changed

9 files changed

+79
-29
lines changed

.changeset/cold-buckets-divide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
fix nested a tag causing hydration error

bun.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@
145145
"dependencies": {
146146
"@gitbook/api": "^0.115.0",
147147
"@gitbook/cache-tags": "workspace:*",
148-
"@opennextjs/cloudflare": "1.0.4",
148+
"@opennextjs/cloudflare": "1.1.0",
149149
"@sindresorhus/fnv1a": "^3.1.0",
150150
"assert-never": "^1.2.1",
151151
"jwt-decode": "^4.0.0",
@@ -791,9 +791,9 @@
791791

792792
"@nodelib/fs.walk": ["@nodelib/[email protected]", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
793793

794-
"@opennextjs/aws": ["@opennextjs/[email protected].2", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "esbuild": "0.25.4", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-26/3GSoj7mKN7XpQFikYM2Lrwal5jlMc4fJO//QAdd5bCSnBaWBAkzr7+VvXAFVIC6eBeDLdtlWiQuUQVEAPZQ=="],
794+
"@opennextjs/aws": ["@opennextjs/[email protected].4", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "esbuild": "0.25.4", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-/bn9N/6dVu9+sC7AptaGJylKUzyDDgqe3yKfvUxXOpy7whIq/+3Bw+q2/bilyQg6FcskbCWUt9nLvNf1hAVmfA=="],
795795

796-
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.4", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "^3.6.2", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^4.14.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-DVudGpOSJ91ruPiBJ0kuFmPhEQbXIXLTbvUjAx1OlbwFskG2gvdNIAmF3ZXV6z1VGDO7Q/u2W2ybMZLf7avlrA=="],
796+
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.1.0", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.6.4", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^4.14.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-8DOzpLiewivA1pwRNGVPDXbrF79bzJiHscY+M1tekwvKqEqM26CRYafhrA5IDCeyteNaL9AZD6X4DLuCn/eeYQ=="],
797797

798798
"@opentelemetry/api": ["@opentelemetry/[email protected]", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
799799

packages/gitbook-v2/open-next.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export default {
66
wrapper: 'cloudflare-node',
77
converter: 'edge',
88
proxyExternalRequest: 'fetch',
9-
queue: () => import('./openNext/queue/server').then((m) => m.default),
9+
queue: () => import('./openNext/queue/middleware').then((m) => m.default),
1010
incrementalCache: () => import('./openNext/incrementalCache').then((m) => m.default),
1111
tagCache: () => import('./openNext/tagCache/middleware').then((m) => m.default),
1212
},

packages/gitbook-v2/openNext/queue/middleware.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,13 @@ import { trace } from '@/lib/tracing';
22
import type { Queue } from '@opennextjs/aws/types/overrides.js';
33
import { getCloudflareContext } from '@opennextjs/cloudflare';
44
import doQueue from '@opennextjs/cloudflare/overrides/queue/do-queue';
5-
import memoryQueue from '@opennextjs/cloudflare/overrides/queue/memory-queue';
6-
7-
interface Env {
8-
STAGE?: string;
9-
}
105

116
export default {
127
name: 'GitbookISRQueue',
138
send: async (msg) => {
149
return trace({ operation: 'gitbookISRQueueSend', name: msg.MessageBody.url }, async () => {
15-
const { ctx, env } = getCloudflareContext();
16-
const hasDurableObject =
17-
(env as Env).STAGE !== 'dev' && (env as Env).STAGE !== 'preview';
18-
ctx.waitUntil(hasDurableObject ? memoryQueue.send(msg) : doQueue.send(msg));
10+
const { ctx } = getCloudflareContext();
11+
ctx.waitUntil(doQueue.send(msg));
1912
});
2013
},
2114
} satisfies Queue;

packages/gitbook-v2/openNext/tagCache/middleware.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ const originalTagCache = doShardedTagCache({
1111
shardReplication: {
1212
numberOfSoftReplicas: 2,
1313
numberOfHardReplicas: 1,
14+
regionalReplication: {
15+
defaultRegion: 'enam',
16+
},
1417
},
1518
});
1619

packages/gitbook-v2/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"dependencies": {
66
"@gitbook/api": "^0.115.0",
77
"@gitbook/cache-tags": "workspace:*",
8-
"@opennextjs/cloudflare": "1.0.4",
8+
"@opennextjs/cloudflare": "1.1.0",
99
"@sindresorhus/fnv1a": "^3.1.0",
1010
"assert-never": "^1.2.1",
1111
"jwt-decode": "^4.0.0",

packages/gitbook/src/components/DocumentView/Table/RecordCard.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
SiteInsightsLinkPosition,
55
} from '@gitbook/api';
66

7-
import { Link } from '@/components/primitives';
7+
import { LinkBox, LinkOverlay } from '@/components/primitives';
88
import { Image } from '@/components/utils';
99
import { resolveContentRef } from '@/lib/references';
1010
import { type ClassValue, tcls } from '@/lib/tailwind';
@@ -44,7 +44,6 @@ export async function RecordCard(
4444
<div
4545
className={tcls(
4646
'grid-area-1-1',
47-
'z-0',
4847
'relative',
4948
'grid',
5049
'bg-tint-base',
@@ -151,7 +150,6 @@ export async function RecordCard(
151150
'rounded-md',
152151
'straight-corners:rounded-none',
153152
'dark:shadow-transparent',
154-
'z-0',
155153

156154
'before:pointer-events-none',
157155
'before:grid-area-1-1',
@@ -167,19 +165,22 @@ export async function RecordCard(
167165

168166
if (target && targetRef) {
169167
return (
170-
<Link
171-
href={target.href}
172-
className={tcls(style, 'hover:before:ring-tint-12/5')}
173-
insights={{
174-
type: 'link_click',
175-
link: {
176-
target: targetRef,
177-
position: SiteInsightsLinkPosition.Content,
178-
},
179-
}}
180-
>
168+
// We don't use `Link` directly here because we could end up in a situation where
169+
// a link is rendered inside a link, which is not allowed in HTML.
170+
// It causes an hydration error in React.
171+
<LinkBox href={target.href} className={tcls(style, 'hover:before:ring-tint-12/5')}>
172+
<LinkOverlay
173+
href={target.href}
174+
insights={{
175+
type: 'link_click',
176+
link: {
177+
target: targetRef,
178+
position: SiteInsightsLinkPosition.Content,
179+
},
180+
}}
181+
/>
181182
{body}
182-
</Link>
183+
</LinkBox>
183184
);
184185
}
185186

packages/gitbook/src/components/RootLayout/globals.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,13 @@
148148
width: 100%;
149149
}
150150
}
151+
152+
.elevate-link {
153+
& a[href]:not(.link-overlay) {
154+
position: relative;
155+
z-index: 20;
156+
}
157+
}
151158
}
152159

153160
html {

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import NextLink, { type LinkProps as NextLinkProps } from 'next/link';
44
import React from 'react';
55

6+
import { tcls } from '@/lib/tailwind';
67
import { type TrackEventInput, useTrackEvent } from '../Insights';
78

89
// Props from Next, which includes NextLinkProps and all the things anchor elements support.
@@ -75,6 +76,46 @@ export const Link = React.forwardRef(function Link(
7576
);
7677
});
7778

79+
/**
80+
* A box used to contain a link overlay.
81+
* It is used to create a clickable area that can contain other elements.
82+
*/
83+
export const LinkBox = React.forwardRef(function LinkBox(
84+
props: React.BaseHTMLAttributes<HTMLDivElement>,
85+
ref: React.Ref<HTMLDivElement>
86+
) {
87+
const { children, className, ...domProps } = props;
88+
return (
89+
<div ref={ref} {...domProps} className={tcls('elevate-link relative', className)}>
90+
{children}
91+
</div>
92+
);
93+
});
94+
95+
/**
96+
* A link overlay that can be used to create a clickable area on top of other elements.
97+
* It is used to create a link that covers the entire area of the element without encapsulating it in a link tag.
98+
* This is useful to avoid nesting links inside links.
99+
*/
100+
export const LinkOverlay = React.forwardRef(function LinkOverlay(
101+
props: LinkProps,
102+
ref: React.Ref<HTMLAnchorElement>
103+
) {
104+
const { children, className, ...domProps } = props;
105+
return (
106+
<Link
107+
ref={ref}
108+
{...domProps}
109+
className={tcls(
110+
'link-overlay static before:absolute before:top-0 before:left-0 before:z-10 before:h-full before:w-full',
111+
className
112+
)}
113+
>
114+
{children}
115+
</Link>
116+
);
117+
});
118+
78119
/**
79120
* Check if a link is external, compared to an origin.
80121
*/

0 commit comments

Comments
 (0)