Skip to content

Commit 5dd86f9

Browse files
committed
Introduce slim documents
We introduce slim documents, it's a lighter version of the document computed in GitBook Open to lightweight to document stored in cache and transferred to client. The idea is to store in this document only what is needed to display a document in GitBook Open.
1 parent a3e1125 commit 5dd86f9

29 files changed

+416
-173
lines changed

Diff for: packages/gitbook-v2/src/lib/data/api.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getSlimJSONDocument } from '@/lib/slim-document';
12
import { trace } from '@/lib/tracing';
23
import {
34
type ComputedContentSource,
@@ -779,7 +780,7 @@ const getDocumentUncached = async (
779780
cacheTag(...getCacheTagsFromResponse(res));
780781
cacheLife('max');
781782
}
782-
return res.data;
783+
return getSlimJSONDocument(res.data);
783784
});
784785
});
785786
};
@@ -872,7 +873,7 @@ const getComputedDocumentUncached = async (
872873
cacheTag(...getCacheTagsFromResponse(res));
873874
cacheLife('max');
874875
}
875-
return res.data;
876+
return getSlimJSONDocument(res.data);
876877
});
877878
}
878879
);

Diff for: packages/gitbook-v2/src/lib/data/pages.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { JSONDocument, RevisionPageDocument, Space } from '@gitbook/api';
1+
import type { SlimJSONDocument } from '@/lib/slim-document';
2+
import type { RevisionPageDocument, Space } from '@gitbook/api';
23
import { getDataOrNull } from './errors';
34
import type { GitBookDataFetcher } from './types';
45

@@ -9,7 +10,7 @@ export async function getPageDocument(
910
dataFetcher: GitBookDataFetcher,
1011
space: Space,
1112
page: RevisionPageDocument
12-
): Promise<JSONDocument | null> {
13+
): Promise<SlimJSONDocument | null> {
1314
if (page.documentId) {
1415
return getDataOrNull(
1516
dataFetcher.getDocument({ spaceId: space.id, documentId: page.documentId })

Diff for: packages/gitbook-v2/src/lib/data/types.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { SlimJSONDocument } from '@/lib/slim-document';
12
import type * as api from '@gitbook/api';
23

34
export type DataFetcherErrorData = {
@@ -110,7 +111,7 @@ export interface GitBookDataFetcher {
110111
* Get a document by its space ID and document ID.
111112
*/
112113
getDocument(params: { spaceId: string; documentId: string }): Promise<
113-
DataFetcherResponse<api.JSONDocument>
114+
DataFetcherResponse<SlimJSONDocument>
114115
>;
115116

116117
/**
@@ -121,7 +122,7 @@ export interface GitBookDataFetcher {
121122
spaceId: string;
122123
source: api.ComputedContentSource;
123124
seed: string;
124-
}): Promise<DataFetcherResponse<api.JSONDocument>>;
125+
}): Promise<DataFetcherResponse<SlimJSONDocument>>;
125126

126127
/**
127128
* Get a reusable content by its space ID, revision ID and reusable content ID.

Diff for: packages/gitbook/src/components/DocumentView/Block.tsx

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { DocumentBlock, JSONDocument } from '@gitbook/api';
21
import React from 'react';
32

43
import {
@@ -10,6 +9,7 @@ import {
109
} from '@/components/primitives';
1110
import type { ClassValue } from '@/lib/tailwind';
1211

12+
import type { SlimDocumentBlock, SlimJSONDocument } from '@/lib/slim-document';
1313
import { BlockContentRef } from './BlockContentRef';
1414
import { CodeBlock } from './CodeBlock';
1515
import { Divider } from './Divider';
@@ -34,10 +34,10 @@ import { StepperStep } from './StepperStep';
3434
import { Table } from './Table';
3535
import { Tabs } from './Tabs';
3636

37-
export interface BlockProps<Block extends DocumentBlock> extends DocumentContextProps {
37+
export interface BlockProps<Block extends SlimDocumentBlock> extends DocumentContextProps {
3838
block: Block;
39-
document: JSONDocument;
40-
ancestorBlocks: DocumentBlock[];
39+
document: SlimJSONDocument;
40+
ancestorBlocks: SlimDocumentBlock[];
4141
/** If true, we estimate that the block will be outside the initial viewport */
4242
isEstimatedOffscreen: boolean;
4343
/** Class names to be passed to the underlying DOM element */
@@ -51,7 +51,7 @@ function nullIfNever(_value: never): null {
5151
return null;
5252
}
5353

54-
export function Block<T extends DocumentBlock>(props: BlockProps<T>) {
54+
export function Block<T extends SlimDocumentBlock>(props: BlockProps<T>) {
5555
const { block, style, isEstimatedOffscreen, context } = props;
5656

5757
const content = (() => {
@@ -132,7 +132,7 @@ export function Block<T extends DocumentBlock>(props: BlockProps<T>) {
132132
/**
133133
* Skeleton for a block while it is being loaded.
134134
*/
135-
export function BlockSkeleton(props: { block: DocumentBlock; style: ClassValue }) {
135+
export function BlockSkeleton(props: { block: SlimDocumentBlock; style: ClassValue }) {
136136
const { block, style } = props;
137137
const id = 'meta' in block && block.meta && 'id' in block.meta ? block.meta.id : undefined;
138138

Diff for: packages/gitbook/src/components/DocumentView/Blocks.tsx

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
import type { DocumentBlock, JSONDocument } from '@gitbook/api';
2-
31
import { type ClassValue, tcls } from '@/lib/tailwind';
42

3+
import type { SlimDocumentBlock, SlimJSONDocument } from '@/lib/slim-document';
54
import { Block } from './Block';
65
import type { DocumentContextProps } from './DocumentView';
76
import { isBlockOffscreen } from './utils';
87

98
/**
109
* Renders a list of blocks with a wrapper element.
1110
*/
12-
export function Blocks<TBlock extends DocumentBlock, Tag extends React.ElementType = 'div'>(
11+
export function Blocks<TBlock extends SlimDocumentBlock, Tag extends React.ElementType = 'div'>(
1312
props: UnwrappedBlocksProps<TBlock> & {
1413
/** HTML tag to use for the wrapper */
1514
tag?: Tag;
@@ -30,15 +29,15 @@ export function Blocks<TBlock extends DocumentBlock, Tag extends React.ElementTy
3029
);
3130
}
3231

33-
type UnwrappedBlocksProps<TBlock extends DocumentBlock> = DocumentContextProps & {
32+
type UnwrappedBlocksProps<TBlock extends SlimDocumentBlock> = DocumentContextProps & {
3433
/** Blocks to render */
3534
nodes: TBlock[];
3635

3736
/** Document being rendered */
38-
document: JSONDocument;
37+
document: SlimJSONDocument;
3938

4039
/** Ancestors of the blocks */
41-
ancestorBlocks: DocumentBlock[];
40+
ancestorBlocks: SlimDocumentBlock[];
4241

4342
/** Style passed to all blocks */
4443
blockStyle?: ClassValue;
@@ -50,7 +49,9 @@ type UnwrappedBlocksProps<TBlock extends DocumentBlock> = DocumentContextProps &
5049
/**
5150
* Renders a list of blocks without a wrapper element.
5251
*/
53-
export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBlocksProps<TBlock>) {
52+
export function UnwrappedBlocks<TBlock extends SlimDocumentBlock>(
53+
props: UnwrappedBlocksProps<TBlock>
54+
) {
5455
const { nodes, blockStyle, isOffscreen: defaultIsOffscreen = false, ...contextProps } = props;
5556

5657
let isOffscreen = defaultIsOffscreen;
@@ -69,7 +70,7 @@ export function UnwrappedBlocks<TBlock extends DocumentBlock>(props: UnwrappedBl
6970
block={node}
7071
style={[
7172
'mx-auto w-full decoration-primary/6',
72-
node.data && 'fullWidth' in node.data && node.data.fullWidth
73+
'data' in node && node.data && 'fullWidth' in node.data && node.data.fullWidth
7374
? 'max-w-screen-xl'
7475
: 'max-w-3xl',
7576
blockStyle,

Diff for: packages/gitbook/src/components/DocumentView/Caption.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import type {
33
DocumentBlockEmbed,
44
DocumentBlockFile,
55
DocumentBlockImage,
6-
JSONDocument,
76
} from '@gitbook/api';
87

98
import { getNodeFragmentByName, isNodeEmpty } from '@/lib/document';
109
import { type ClassValue, tcls } from '@/lib/tailwind';
1110

11+
import type { SlimJSONDocument } from '@/lib/slim-document';
1212
import type { DocumentContextProps } from './DocumentView';
1313
import { Inlines } from './Inlines';
1414

@@ -18,7 +18,7 @@ import { Inlines } from './Inlines';
1818
export function Caption(
1919
props: {
2020
children: React.ReactNode;
21-
document: JSONDocument;
21+
document: SlimJSONDocument;
2222
style?: ClassValue;
2323
fit?: boolean;
2424
wrapperStyle?: ClassValue;

Diff for: packages/gitbook/src/components/DocumentView/CodeBlock/ClientCodeBlock.tsx

+17-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
'use client';
22

3-
import type { DocumentBlockCode } from '@gitbook/api';
43
import { useEffect, useMemo, useRef, useState } from 'react';
54

65
import { useInViewportListener } from '@/components/hooks/useInViewportListener';
76
import { useScrollListener } from '@/components/hooks/useScrollListener';
7+
import type { SlimDocumentBlockCode } from '@/lib/slim-document';
88
import { useDebounceCallback } from 'usehooks-ts';
9-
import type { BlockProps } from '../Block';
10-
import { CodeBlockRenderer } from './CodeBlockRenderer';
9+
import { CodeBlockRenderer, type CodeBlockRendererProps } from './CodeBlockRenderer';
1110
import type { HighlightLine, RenderedInline } from './highlight';
1211
import { plainHighlight } from './plain-highlight';
1312

14-
type ClientBlockProps = Pick<BlockProps<DocumentBlockCode>, 'block' | 'style'> & {
13+
interface ClientBlockProps extends Omit<CodeBlockRendererProps, 'lines'> {
14+
block: SlimDocumentBlockCode;
1515
inlines: RenderedInline[];
16-
};
16+
}
1717

1818
/**
1919
* Render a code-block client-side by loading the highlighter asynchronously.
@@ -24,13 +24,13 @@ export function ClientCodeBlock(props: ClientBlockProps) {
2424
const blockRef = useRef<HTMLDivElement>(null);
2525
const isInViewportRef = useRef(false);
2626
const [isInViewport, setIsInViewport] = useState(false);
27-
const plainLines = useMemo(() => plainHighlight(block, []), [block]);
27+
const plainLines = useMemo(() => plainHighlight({ block, inlines: [] }), [block]);
2828
const [lines, setLines] = useState<null | HighlightLine[]>(null);
2929

3030
// Preload the highlighter when the block is mounted.
3131
useEffect(() => {
32-
import('./highlight').then(({ preloadHighlight }) => preloadHighlight(block));
33-
}, [block]);
32+
import('./highlight').then(({ preloadHighlight }) => preloadHighlight(block.data.syntax));
33+
}, [block.data.syntax]);
3434

3535
// When user scrolls, we need to wait for the scroll to finish before running the highlight
3636
const isScrollingRef = useRef(false);
@@ -78,7 +78,7 @@ export function ClientCodeBlock(props: ClientBlockProps) {
7878

7979
if (typeof window !== 'undefined') {
8080
import('./highlight').then(({ highlight }) => {
81-
highlight(block, inlines).then((lines) => {
81+
highlight({ block, inlines }).then((lines) => {
8282
if (cancelled) {
8383
return;
8484
}
@@ -98,6 +98,13 @@ export function ClientCodeBlock(props: ClientBlockProps) {
9898
}, [isInViewport, block, inlines]);
9999

100100
return (
101-
<CodeBlockRenderer ref={blockRef} block={block} style={style} lines={lines ?? plainLines} />
101+
<CodeBlockRenderer
102+
ref={blockRef}
103+
title={props.title}
104+
withLineNumbers={props.withLineNumbers}
105+
withWrap={props.withWrap}
106+
style={style}
107+
lines={lines ?? plainLines}
108+
/>
102109
);
103110
}

Diff for: packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlock.tsx

+28-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import type { DocumentBlockCode } from '@gitbook/api';
2-
31
import { getNodeFragmentByType } from '@/lib/document';
42
import { isV2 } from '@/lib/v2';
53

4+
import type { SlimDocumentBlockCode } from '@/lib/slim-document';
65
import type { BlockProps } from '../Block';
76
import { Blocks } from '../Blocks';
87
import { ClientCodeBlock } from './ClientCodeBlock';
@@ -12,8 +11,11 @@ import { type RenderedInline, getInlines, highlight } from './highlight';
1211
/**
1312
* Render a code block, can be client-side or server-side.
1413
*/
15-
export async function CodeBlock(props: BlockProps<DocumentBlockCode>) {
14+
export async function CodeBlock(props: BlockProps<SlimDocumentBlockCode>) {
1615
const { block, document, style, isEstimatedOffscreen, context } = props;
16+
const withLineNumbers = Boolean(block.data.lineNumbers) && block.nodes.length > 1;
17+
const withWrap = block.data.overflow === 'wrap';
18+
const title = block.data.title ?? '';
1719
const inlines = getInlines(block);
1820
const richInlines: RenderedInline[] = inlines.map((inline, index) => {
1921
const body = (() => {
@@ -38,9 +40,29 @@ export async function CodeBlock(props: BlockProps<DocumentBlockCode>) {
3840

3941
if (isV2() && !isEstimatedOffscreen) {
4042
// In v2, we render the code block server-side
41-
const lines = await highlight(block, richInlines);
42-
return <CodeBlockRenderer block={block} style={style} lines={lines} />;
43+
const lines = await highlight({
44+
inlines: richInlines,
45+
block,
46+
});
47+
return (
48+
<CodeBlockRenderer
49+
withLineNumbers={withLineNumbers}
50+
withWrap={withWrap}
51+
title={title}
52+
style={style}
53+
lines={lines}
54+
/>
55+
);
4356
}
4457

45-
return <ClientCodeBlock block={block} style={style} inlines={richInlines} />;
58+
return (
59+
<ClientCodeBlock
60+
block={block}
61+
withLineNumbers={withLineNumbers}
62+
withWrap={withWrap}
63+
title={title}
64+
style={style}
65+
inlines={richInlines}
66+
/>
67+
);
4668
}

Diff for: packages/gitbook/src/components/DocumentView/CodeBlock/CodeBlockRenderer.tsx

+6-7
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ import type { HighlightLine, HighlightToken } from './highlight';
1212
import './theme.css';
1313
import './CodeBlockRenderer.css';
1414

15-
type CodeBlockRendererProps = Pick<BlockProps<DocumentBlockCode>, 'block' | 'style'> & {
15+
export interface CodeBlockRendererProps extends Pick<BlockProps<DocumentBlockCode>, 'style'> {
1616
lines: HighlightLine[];
17-
};
17+
withLineNumbers: boolean;
18+
withWrap: boolean;
19+
title: string;
20+
}
1821

1922
/**
2023
* The logic of rendering a code block from lines.
@@ -23,12 +26,8 @@ export const CodeBlockRenderer = forwardRef(function CodeBlockRenderer(
2326
props: CodeBlockRendererProps,
2427
ref: React.ForwardedRef<HTMLDivElement>
2528
) {
26-
const { block, style, lines } = props;
27-
29+
const { style, lines, withLineNumbers, withWrap, title } = props;
2830
const id = useId();
29-
const withLineNumbers = Boolean(block.data.lineNumbers) && block.nodes.length > 1;
30-
const withWrap = block.data.overflow === 'wrap';
31-
const title = block.data.title;
3231

3332
return (
3433
<div ref={ref} className={tcls('group/codeblock grid grid-flow-col', style)}>

Diff for: packages/gitbook/src/components/DocumentView/CodeBlock/PlainCodeBlock.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { DocumentBlockCode, JSONDocument } from '@gitbook/api';
21
import { useId } from 'react';
32

3+
import type { SlimDocumentBlockCode, SlimJSONDocument } from '@/lib/slim-document';
44
import { CodeBlock } from './CodeBlock';
55

66
/**
@@ -11,7 +11,7 @@ export function PlainCodeBlock(props: { code: string; syntax: string }) {
1111
const { code, syntax } = props;
1212
const id = useId();
1313

14-
const block: DocumentBlockCode = {
14+
const block: SlimDocumentBlockCode = {
1515
key: id,
1616
object: 'block',
1717
type: 'code',
@@ -37,7 +37,7 @@ export function PlainCodeBlock(props: { code: string; syntax: string }) {
3737
})),
3838
};
3939

40-
const document: JSONDocument = {
40+
const document: SlimJSONDocument = {
4141
object: 'document',
4242
data: {},
4343
nodes: [block],

0 commit comments

Comments
 (0)