Use newer APIs to optimize data loading and fix resolution of links in the TOC (#11)

* Update api@0.16.0

* Fetch pages and files independantly

* Correctly resolve content ref in TOC

* Format

* Format

* Cleanup
This commit is contained in:
Samy Pessé
2023-11-26 23:46:51 +01:00
committed by GitHub
parent 2a03461a21
commit fbd17713fe
22 changed files with 282 additions and 154 deletions
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -14,7 +14,7 @@
},
"dependencies": {
"@geist-ui/icons": "^1.0.2",
"@gitbook/api": "^0.15.0",
"@gitbook/api": "^0.16.0",
"@readme/openapi-parser": "^2.5.0",
"@upstash/redis": "^1.25.1",
"ajv": "^8.12.0",
+5 -3
View File
@@ -5,10 +5,11 @@ import shadesOf from 'tailwind-shades';
import colors from 'tailwindcss/colors';
import { hexToRgb } from '@/components/utils/HexToRgb';
import { getSpaceCustomization } from '@/lib/api';
import { tcls } from '@/lib/tailwind';
import { ClientLayout } from './ClientLayout';
import { PagePathParams, fetchPageData } from '../fetch';
import { PagePathParams } from '../fetch';
const inter = Inter({ subsets: ['latin'] });
@@ -16,8 +17,9 @@ export default async function SpaceRootLayout(props: {
children: React.ReactNode;
params: PagePathParams;
}) {
const { children, params } = props;
const { customization } = await fetchPageData(params);
const { params, children } = props;
const customization = await getSpaceCustomization(params.spaceId);
const headerTheme = generateHeaderTheme(customization);
return (
+4 -3
View File
@@ -17,7 +17,7 @@ export const runtime = 'nodejs';
export default async function Page(props: { params: PagePathParams }) {
const { params } = props;
const { space, customization, revision, page, collection, collectionSpaces, ancestors } =
const { content, space, customization, pages, page, collection, collectionSpaces, ancestors } =
await fetchPageData(params);
const linksContext: PageHrefContext = {};
@@ -27,13 +27,14 @@ export default async function Page(props: { params: PagePathParams }) {
redirect(pageHref(page, linksContext));
}
const document = await getPageDocument(space.id, revision.id, page.id);
const document = await getPageDocument(content, page.id);
return (
<SpaceContent
content={content}
space={space}
customization={customization}
revision={revision}
pages={pages}
page={page}
ancestors={ancestors}
document={document}
+15 -9
View File
@@ -2,16 +2,15 @@ import { ContentVisibility, Space } from '@gitbook/api';
import {
getSpace,
getCurrentRevision,
getSpaceCustomization,
getCollectionSpaces,
getCollection,
ContentPointer,
getRevisionPages,
} from '@/lib/api';
import { resolvePagePath, resolvePageId } from '@/lib/pages';
export interface SpaceParams {
spaceId: string;
}
export type SpaceParams = ContentPointer;
export interface PagePathParams extends SpaceParams {
pathname?: string[];
@@ -27,21 +26,28 @@ export interface PageIdParams extends SpaceParams {
export async function fetchPageData(params: PagePathParams | PageIdParams) {
const { spaceId } = params;
const [space, revision, customization] = await Promise.all([
const content: ContentPointer = {
spaceId: params.spaceId,
changeRequestId: params.changeRequestId,
revisionId: params.revisionId,
};
const [space, pages, customization] = await Promise.all([
getSpace(spaceId),
getCurrentRevision(spaceId),
getRevisionPages(content),
getSpaceCustomization(spaceId),
]);
const collection = await fetchParentCollection(space);
const page =
'pageId' in params && params.pageId
? resolvePageId(revision, params.pageId)
: resolvePagePath(revision, getPagePath(params));
? resolvePageId(pages, params.pageId)
: resolvePagePath(pages, getPagePath(params));
return {
content,
space,
revision,
pages,
customization,
ancestors: [],
...page,
+6 -9
View File
@@ -1,8 +1,8 @@
import { Revision, RevisionPageDocument, RevisionPageGroup } from '@gitbook/api';
import { RevisionPage, RevisionPageDocument, RevisionPageGroup } from '@gitbook/api';
import jsontoxml from 'jsontoxml';
import { NextRequest } from 'next/server';
import { api } from '@/lib/api';
import { getRevisionPages } from '@/lib/api';
import { pageHref } from '@/lib/links';
import { SpaceParams } from '../fetch';
@@ -15,11 +15,8 @@ export const runtime = 'nodejs';
* Generate a sitemap.xml for the current space.
*/
export async function GET(req: NextRequest, { params }: { params: SpaceParams }) {
const { spaceId } = params;
const { data: revision } = await api().spaces.getCurrentRevision(spaceId);
const pages = flattenPages(revision);
const rootPages = await getRevisionPages(params);
const pages = flattenPages(rootPages);
const urls = pages.map(({ page, depth }) => {
// Decay priority with depth
const priority = Math.pow(2, -0.25 * depth);
@@ -70,7 +67,7 @@ export async function GET(req: NextRequest, { params }: { params: SpaceParams })
type FlatPageEntry = { page: RevisionPageDocument; depth: number };
function flattenPages(revision: Revision): FlatPageEntry[] {
function flattenPages(rootPags: RevisionPage[]): FlatPageEntry[] {
const flattenPage = (
page: RevisionPageDocument | RevisionPageGroup,
depth: number,
@@ -83,5 +80,5 @@ function flattenPages(revision: Revision): FlatPageEntry[] {
];
};
return revision.pages.flatMap((page) => (page.type === 'link' ? [] : flattenPage(page, 0)));
return rootPags.flatMap((page) => (page.type === 'link' ? [] : flattenPage(page, 0)));
}
+3 -2
View File
@@ -1,14 +1,15 @@
import IconDownload from '@geist-ui/icons/download';
import { DocumentBlockFile } from '@gitbook/api';
import { getRevisionFile } from '@/lib/api';
import { tcls } from '@/lib/tailwind';
import { BlockProps } from './Block';
export function File(props: BlockProps<DocumentBlockFile>) {
export async function File(props: BlockProps<DocumentBlockFile>) {
const { block, context, style } = props;
const file = context.revision.files.find((f) => f.id === block.data.ref.file);
const file = await getRevisionFile(context.content, block.data.ref.file);
if (!file) {
return null;
}
+4 -8
View File
@@ -2,6 +2,7 @@ import { CustomizationSettings, Revision, RevisionPageDocument, Space } from '@g
import React from 'react';
import { Image } from '@/components/utils';
import { ContentRefContext } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import { FooterLinksGroup } from './FooterLinksGroup';
@@ -10,12 +11,11 @@ import { ThemeToggler } from '../ThemeToggler';
export function Footer(props: {
space: Space;
revision: Revision;
page: RevisionPageDocument;
context: ContentRefContext;
customization: CustomizationSettings;
asFullWidth: boolean;
}) {
const { space, revision, page, customization, asFullWidth } = props;
const { space, context, customization, asFullWidth } = props;
return (
<div
@@ -53,11 +53,7 @@ export function Footer(props: {
) : null}
{customization.footer.groups.map((group, index) => (
<FooterLinksGroup
key={index}
group={group}
context={{ space, revision, page }}
/>
<FooterLinksGroup key={index} group={group} context={context} />
))}
</div>
) : null}
+5 -18
View File
@@ -1,14 +1,9 @@
import {
Collection,
CustomizationSettings,
Revision,
RevisionPageDocument,
Space,
} from '@gitbook/api';
import { Collection, CustomizationSettings, Space } from '@gitbook/api';
import { Suspense } from 'react';
import { CONTAINER_MAX_WIDTH_NORMAL, CONTAINER_PADDING } from '@/components/layout';
import { t } from '@/lib/intl';
import { ContentRefContext } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import { CollectionSpacesDropdown } from './CollectionSpacesDropdown';
@@ -23,13 +18,11 @@ export function Header(props: {
space: Space;
collection: Collection | null;
collectionSpaces: Space[];
revision: Revision;
page: RevisionPageDocument;
context: ContentRefContext;
asFullWidth: boolean;
customization: CustomizationSettings;
}) {
const { space, collection, collectionSpaces, revision, page, asFullWidth, customization } =
props;
const { context, space, collection, collectionSpaces, asFullWidth, customization } = props;
return (
<header
@@ -93,13 +86,7 @@ export function Header(props: {
)}
>
{customization.header.links.map((link, index) => (
<HeaderLink
key={index}
link={link}
space={space}
revision={revision}
page={page}
/>
<HeaderLink key={index} link={link} context={context} />
))}
</div>
<div className={tcls('flex', 'basis-56', 'grow-0', 'shrink-0')}>
+8 -26
View File
@@ -1,13 +1,7 @@
import {
CustomizationContentLink,
CustomizationHeaderLink,
Revision,
RevisionPageDocument,
Space,
} from '@gitbook/api';
import { CustomizationContentLink, CustomizationHeaderLink } from '@gitbook/api';
import Link from 'next/link';
import { resolveContentRef } from '@/lib/references';
import { ContentRefContext, resolveContentRef } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import {
@@ -19,18 +13,12 @@ import {
} from './Dropdown';
export async function HeaderLink(props: {
space: Space;
revision: Revision;
page: RevisionPageDocument;
context: ContentRefContext;
link: CustomizationHeaderLink;
}) {
const { space, revision, page, link } = props;
const { context, link } = props;
const target = await resolveContentRef(link.to, {
space,
revision,
page,
});
const target = await resolveContentRef(link.to, context);
if (!target) {
return null;
@@ -73,18 +61,12 @@ export async function HeaderLink(props: {
}
async function SubHeaderLink(props: {
space: Space;
revision: Revision;
page: RevisionPageDocument;
context: ContentRefContext;
link: CustomizationContentLink;
}) {
const { space, revision, page, link } = props;
const { context, link } = props;
const target = await resolveContentRef(link.to, {
space,
revision,
page,
});
const target = await resolveContentRef(link.to, context);
if (!target) {
return null;
+7 -14
View File
@@ -1,5 +1,6 @@
import { Revision, RevisionPageDocument, Space } from '@gitbook/api';
import { JSONDocument, Revision, RevisionPageDocument, Space } from '@gitbook/api';
import { ContentRefContext } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import { PageFooterNavigation } from './PageFooterNavigation';
@@ -8,25 +9,17 @@ import { DocumentView } from '../DocumentView';
export function PageBody(props: {
space: Space;
revision: Revision;
page: RevisionPageDocument;
document: any;
context: ContentRefContext;
document: JSONDocument;
}) {
const { space, revision, page, document } = props;
const { space, context, page, document } = props;
return (
<main className={tcls('py-8', 'px-4', 'lg:px-12', 'flex-1')}>
<PageHeader page={page} />
<DocumentView
document={document}
style={['space-y-6']}
context={{
space,
revision,
page,
}}
/>
<PageFooterNavigation space={space} revision={revision} page={page} />
<DocumentView document={document} style={['space-y-6']} context={context} />
<PageFooterNavigation space={space} pages={context.pages} page={page} />
</main>
);
}
@@ -13,11 +13,11 @@ import { tcls } from '@/lib/tailwind';
*/
export function PageFooterNavigation(props: {
space: Space;
revision: Revision;
pages: Revision['pages'];
page: RevisionPageDocument;
}) {
const { space, revision, page } = props;
const { previous, next } = resolvePrevNextPages(revision, page);
const { space, pages, page } = props;
const { previous, next } = resolvePrevNextPages(pages, page);
return (
<div className={tcls('flex', 'flex-row', 'mt-6', 'gap-2', 'max-w-3xl', 'mx-auto')}>
+24 -8
View File
@@ -15,8 +15,10 @@ import { CONTAINER_MAX_WIDTH_NORMAL, CONTAINER_PADDING } from '@/components/layo
import { PageBody } from '@/components/PageBody';
import { SearchModal } from '@/components/Search';
import { TableOfContents } from '@/components/TableOfContents';
import { ContentPointer } from '@/lib/api';
import { hasFullWidthBlock } from '@/lib/document';
import { tString } from '@/lib/intl';
import { ContentRefContext } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import { PageAside } from '../PageAside';
@@ -25,11 +27,12 @@ import { PageAside } from '../PageAside';
* Render the entire content of the space (header, table of contents, footer, and page content).
*/
export function SpaceContent(props: {
content: ContentPointer;
space: Space;
collection: Collection | null;
collectionSpaces: Space[];
customization: CustomizationSettings;
revision: Revision;
pages: Revision['pages'];
page: RevisionPageDocument;
ancestors: Array<RevisionPageDocument | RevisionPageGroup>;
document: any;
@@ -38,7 +41,8 @@ export function SpaceContent(props: {
space,
collection,
collectionSpaces,
revision,
content,
pages,
customization,
page,
ancestors,
@@ -48,6 +52,13 @@ export function SpaceContent(props: {
const asFullWidth = hasFullWidthBlock(document);
const withTopHeader = customization.header.preset !== CustomizationHeaderPreset.None;
const contentRefContext: ContentRefContext = {
space,
pages,
page,
content,
};
return (
<div>
{withTopHeader ? (
@@ -55,8 +66,7 @@ export function SpaceContent(props: {
space={space}
collection={collection}
collectionSpaces={collectionSpaces}
revision={revision}
page={page}
context={contentRefContext}
customization={customization}
asFullWidth={asFullWidth}
/>
@@ -72,9 +82,11 @@ export function SpaceContent(props: {
>
<TableOfContents
space={space}
revision={revision}
content={content}
pages={pages}
activePage={page}
ancestors={ancestors}
context={contentRefContext}
header={
withTopHeader ? null : (
<CompactHeader
@@ -87,7 +99,12 @@ export function SpaceContent(props: {
}
withHeaderOffset={withTopHeader}
/>
<PageBody space={space} revision={revision} page={page} document={document} />
<PageBody
space={space}
context={contentRefContext}
page={page}
document={document}
/>
<PageAside
space={space}
page={page}
@@ -102,8 +119,7 @@ export function SpaceContent(props: {
customization.footer.groups?.length ? (
<Footer
space={space}
revision={revision}
page={page}
context={contentRefContext}
customization={customization}
asFullWidth={asFullWidth}
/>
@@ -2,6 +2,7 @@ import { RevisionPageDocument, RevisionPageGroup } from '@gitbook/api';
import Link from 'next/link';
import { pageHref } from '@/lib/links';
import { ContentRefContext } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import { PagesList } from './PagesList';
@@ -11,8 +12,9 @@ export function PageDocumentItem(props: {
page: RevisionPageDocument;
activePage: RevisionPageDocument;
ancestors: Array<RevisionPageDocument | RevisionPageGroup>;
context: ContentRefContext;
}) {
const { page, activePage, ancestors } = props;
const { page, activePage, ancestors, context } = props;
const hasActiveDescendant = ancestors.some((ancestor) => ancestor.id === page.id);
@@ -64,6 +66,7 @@ export function PageDocumentItem(props: {
)}
activePage={activePage}
ancestors={ancestors}
context={context}
/>
}
defaultOpen={hasActiveDescendant || activePage.id === page.id}
@@ -1,5 +1,6 @@
import { RevisionPageDocument, RevisionPageGroup } from '@gitbook/api';
import { ContentRefContext } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import { PagesList } from './PagesList';
@@ -8,14 +9,20 @@ export function PageGroupItem(props: {
page: RevisionPageGroup;
activePage: RevisionPageDocument;
ancestors: Array<RevisionPageDocument | RevisionPageGroup>;
context: ContentRefContext;
}) {
const { page, activePage, ancestors } = props;
const { page, activePage, ancestors, context } = props;
return (
<li className={tcls('flex', 'flex-col', 'my-3')}>
<div className={tcls('px-2', 'py-2', 'text', 'font-medium')}>{page.title}</div>
{page.pages && page.pages.length ? (
<PagesList pages={page.pages} activePage={activePage} ancestors={ancestors} />
<PagesList
pages={page.pages}
activePage={activePage}
ancestors={ancestors}
context={context}
/>
) : null}
</li>
);
@@ -1,17 +1,18 @@
import { RevisionPageLink } from '@gitbook/api';
import Link from 'next/link';
import { ContentRefContext, resolveContentRef } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
export function PageLinkItem(props: { page: RevisionPageLink }) {
const { page } = props;
export async function PageLinkItem(props: { page: RevisionPageLink; context: ContentRefContext }) {
const { page, context } = props;
const resolved = await resolveContentRef(page.target, context);
return (
<li className={tcls('flex', 'flex-col', 'mb-0.5')}>
<Link
href={
page.href ?? '#todo' // TODO: Will be fixed soon as the `target`
}
href={resolved?.href ?? '#'}
className={tcls(
'flex',
'flex-row',
+6 -2
View File
@@ -1,5 +1,6 @@
import { RevisionPage, RevisionPageDocument, RevisionPageGroup } from '@gitbook/api';
import { ContentRefContext } from '@/lib/references';
import { ClassValue, tcls } from '@/lib/tailwind';
import { PageDocumentItem } from './PageDocumentItem';
@@ -10,9 +11,10 @@ export function PagesList(props: {
pages: RevisionPage[];
activePage: RevisionPageDocument;
ancestors: Array<RevisionPageDocument | RevisionPageGroup>;
context: ContentRefContext;
style?: ClassValue;
}) {
const { pages, activePage, ancestors, style } = props;
const { pages, activePage, ancestors, context, style } = props;
return (
<ul className={tcls('flex', 'flex-col', style)}>
@@ -24,10 +26,11 @@ export function PagesList(props: {
page={page}
activePage={activePage}
ancestors={ancestors}
context={context}
/>
);
} else if (page.type === 'link') {
return <PageLinkItem key={page.id} page={page} />;
return <PageLinkItem key={page.id} page={page} context={context} />;
}
return (
@@ -36,6 +39,7 @@ export function PagesList(props: {
page={page}
activePage={activePage}
ancestors={ancestors}
context={context}
/>
);
})}
@@ -1,7 +1,9 @@
import { Revision, RevisionPageDocument, RevisionPageGroup } from '@gitbook/api';
import React from 'react';
import { ContentPointer } from '@/lib/api';
import { IntlContext } from '@/lib/intl';
import { ContentRefContext } from '@/lib/references';
import { tcls } from '@/lib/tailwind';
import { PagesList } from './PagesList';
@@ -10,14 +12,24 @@ import { SIDE_COLUMN_WITHOUT_HEADER, SIDE_COLUMN_WITH_HEADER } from '../layout';
export function TableOfContents(
props: IntlContext & {
revision: Revision;
content: ContentPointer;
context: ContentRefContext;
pages: Revision['pages'];
activePage: RevisionPageDocument;
ancestors: Array<RevisionPageDocument | RevisionPageGroup>;
header?: React.ReactNode;
withHeaderOffset?: boolean;
},
) {
const { space, revision, activePage, ancestors, header, withHeaderOffset = false } = props;
const {
space,
pages,
activePage,
ancestors,
header,
context,
withHeaderOffset = false,
} = props;
return (
<aside
@@ -42,7 +54,12 @@ export function TableOfContents(
'pr-4',
)}
>
<PagesList pages={revision.pages} activePage={activePage} ancestors={ancestors} />
<PagesList
pages={pages}
activePage={activePage}
ancestors={ancestors}
context={context}
/>
</div>
<Trademark space={space} />
</aside>
+136 -23
View File
@@ -5,6 +5,12 @@ import { headers } from 'next/headers';
import { cache } from './cache';
export interface ContentPointer {
spaceId: string;
changeRequestId?: string;
revisionId?: string;
}
/**
* Create an API client for the current request.
*/
@@ -36,6 +42,72 @@ export const getSpace = cache('api.getSpace', async (spaceId: string) => {
return data;
});
/**
* Get all the pages in the space.
*/
export const getRevisionPages = cache('api.getRevisionPages', async (pointer: ContentPointer) => {
const { data } = await (async () => {
if (pointer.revisionId) {
return api().spaces.listPagesInRevisionById(pointer.spaceId, pointer.revisionId, {
cache: 'no-store',
});
}
if (pointer.changeRequestId) {
return api().spaces.listPagesInChangeRequest(spaceId, pointer.changeRequestId, {
cache: 'no-store',
});
}
return api().spaces.listPages(pointer.spaceId, {
cache: 'no-store',
});
})();
return data.pages!;
});
/**
* Resolve a file by its ID.
*/
export const getRevisionFile = cache(
'api.getRevisionFile',
async (pointer: ContentPointer, fileId: string) => {
try {
const { data } = await (async () => {
if (pointer.revisionId) {
return api().spaces.getFileInRevisionById(
pointer.spaceId,
pointer.revisionId,
fileId,
{
cache: 'no-store',
},
);
}
if (pointer.changeRequestId) {
return api().spaces.getFileInChangeRequestById(
spaceId,
pointer.changeRequestId,
fileId,
{
cache: 'no-store',
},
);
}
return api().spaces.getFileById(pointer.spaceId, fileId, {
cache: 'no-store',
});
})();
return data;
} catch (error) {
// TODO: improve error handling to throw non-404
return null;
}
},
);
/**
* Get the current revision of a space
*/
@@ -51,21 +123,59 @@ export const getCurrentRevision = cache('api.getCurrentRevision', async (spaceId
*/
export const getPageDocument = cache(
'api.getPageDocument',
async (spaceId: string, revisionId: string, pageId: string) => {
const { data } = await api().spaces.getPageInRevisionById(
spaceId,
revisionId,
pageId,
{},
{
cache: 'no-store',
},
);
async (pointer: ContentPointer, pageId: string) => {
const { data } = await (async () => {
if (pointer.revisionId) {
return api().spaces.getPageInRevisionById(
pointer.spaceId,
pointer.revisionId,
pageId,
{
format: 'document',
},
{
cache: 'no-store',
},
);
}
if (pointer.changeRequestId) {
return api().spaces.getPageInChangeRequestById(
pointer.spaceId,
pointer.changeRequestId,
pageId,
{
format: 'document',
},
{
cache: 'no-store',
},
);
}
return api().spaces.getPageById(
pointer.spaceId,
pageId,
{
format: 'document',
},
{
cache: 'no-store',
},
);
})();
// @ts-ignore
return data.document as JSONDocument;
},
);
/**
* Get a document by its ID.
*/
export const getDocument = cache('api.getDocument', async (spaceId: string, documentId: string) => {
// TODO
});
/**
* Get the customization settings for a space.
*/
@@ -79,8 +189,8 @@ export const getSpaceCustomization = cache('api.getSpaceCustomization', async (s
/**
* Get the infos about a collection by its ID.
*/
export const getCollection = cache('api.getCollection', async (spaceId: string) => {
const { data } = await api().collections.getCollectionById(spaceId, {
export const getCollection = cache('api.getCollection', async (collectionId: string) => {
const { data } = await api().collections.getCollectionById(collectionId, {
cache: 'no-store',
});
return data;
@@ -89,14 +199,17 @@ export const getCollection = cache('api.getCollection', async (spaceId: string)
/**
* List all the spaces variants published in a collection.
*/
export const getCollectionSpaces = cache('api.getCollectionSpaces', async (spaceId: string) => {
const { data } = await api().collections.listSpacesInCollectionById(
spaceId,
{},
{
cache: 'no-store',
},
);
// TODO: do this filtering on the API side
return data.items.filter((space) => space.visibility === ContentVisibility.InCollection);
});
export const getCollectionSpaces = cache(
'api.getCollectionSpaces',
async (collectionId: string) => {
const { data } = await api().collections.listSpacesInCollectionById(
collectionId,
{},
{
cache: 'no-store',
},
);
// TODO: do this filtering on the API side
return data.items.filter((space) => space.visibility === ContentVisibility.InCollection);
},
);
+3 -3
View File
@@ -15,7 +15,7 @@ const memoryCache = new Map<string, any>();
* Cache data from an async function.
* We don't use the next.js cache because it has a 2MB limit.
*/
export function cache<Args extends string[], Result>(
export function cache<Args extends any[], Result>(
fnName: string,
fn: (...args: Args) => Promise<Result>,
): (...args: Args) => Promise<Result> {
@@ -38,8 +38,8 @@ export function cache<Args extends string[], Result>(
/**
* Create a cache key from a function name and its arguments.
*/
function getCacheKey(fnName: string, args: string[]) {
return `${fnName}(${args.join(',')})`;
function getCacheKey(fnName: string, args: any[]) {
return `${fnName}(${args.map((arg) => JSON.stringify(arg)).join(',')})`;
}
/**
+7 -7
View File
@@ -6,7 +6,7 @@ export type AncestorRevisionPage = RevisionPageDocument | RevisionPageGroup;
* Resolve a page path to a page document.
*/
export function resolvePagePath(
revision: Revision,
rootPages: Revision['pages'],
pagePath: string,
): { page: RevisionPageDocument; ancestors: AncestorRevisionPage[] } | undefined {
const iteratePages = (
@@ -33,7 +33,7 @@ export function resolvePagePath(
};
if (!pagePath) {
const firstPage = resolveFirstDocument(revision.pages, []);
const firstPage = resolveFirstDocument(rootPages, []);
if (!firstPage) {
return undefined;
}
@@ -41,14 +41,14 @@ export function resolvePagePath(
return firstPage;
}
return iteratePages(revision.pages, []);
return iteratePages(rootPages, []);
}
/**
* Find a page by its ID in a revision.
*/
export function resolvePageId(
revision: Revision,
rootPages: Revision['pages'],
pageId: string,
): { page: RevisionPageDocument; ancestors: AncestorRevisionPage[] } | undefined {
const iteratePages = (
@@ -70,17 +70,17 @@ export function resolvePageId(
}
}
};
return iteratePages(revision.pages, []);
return iteratePages(rootPages, []);
}
/**
* Resolve the next/previous page before another one.
*/
export function resolvePrevNextPages(
revision: Revision,
rootPages: Revision['pages'],
page: RevisionPageDocument,
): { previous?: RevisionPageDocument; next?: RevisionPageDocument } {
const flat = flattenPages(revision.pages);
const flat = flattenPages(rootPages);
const currentIndex = flat.findIndex((p) => p.id === page.id);
if (currentIndex === -1) {
+6 -4
View File
@@ -1,5 +1,6 @@
import { ContentRef, Revision, RevisionPageDocument, Space } from '@gitbook/api';
import { ContentPointer, getRevisionFile } from './api';
import { pageHref, PageHrefContext } from './links';
import { resolvePageId } from './pages';
@@ -13,8 +14,9 @@ export interface ResolvedContentRef {
}
export interface ContentRefContext extends PageHrefContext {
content: ContentPointer;
space: Space;
revision: Revision;
pages: Revision['pages'];
page: RevisionPageDocument;
}
@@ -23,7 +25,7 @@ export interface ContentRefContext extends PageHrefContext {
*/
export async function resolveContentRef(
contentRef: ContentRef,
{ space, revision, page: activePage, ...linksContext }: ContentRefContext,
{ content, space, pages, page: activePage, ...linksContext }: ContentRefContext,
): Promise<ResolvedContentRef | null> {
// Try to resolve a local ref in the current space
if (contentRef.kind === 'url') {
@@ -33,7 +35,7 @@ export async function resolveContentRef(
active: false,
};
} else if (contentRef.kind === 'file') {
const file = revision.files.find((file) => file.id === contentRef.file);
const file = await getRevisionFile(content, contentRef.file);
if (file) {
return {
href: file.downloadURL,
@@ -50,7 +52,7 @@ export async function resolveContentRef(
const page =
!contentRef.page || contentRef.page === activePage.id
? activePage
: resolvePageId(revision, contentRef.page)?.page;
: resolvePageId(pages, contentRef.page)?.page;
if (!page) {
return null;
}