diff --git a/packages/gitbook/src/lib/sitemap.test.ts b/packages/gitbook/src/lib/sitemap.test.ts new file mode 100644 index 000000000..8e5f05e86 --- /dev/null +++ b/packages/gitbook/src/lib/sitemap.test.ts @@ -0,0 +1,90 @@ +import { describe, expect, it } from 'bun:test'; +import type { RevisionPage } from '@gitbook/api'; + +import { getIndexablePages } from './sitemap'; + +describe('getIndexablePages', () => { + it('includes hidden pages when they remain indexable', () => { + const pages: RevisionPage[] = [ + { + id: 'visible-page', + type: 'document', + title: 'Visible page', + path: 'visible-page', + pages: [], + hidden: false, + }, + { + id: 'hidden-page', + type: 'document', + title: 'Hidden page', + path: 'hidden-page', + pages: [], + hidden: true, + }, + ] as RevisionPage[]; + + expect(getIndexablePages(pages).map(({ page }) => page.id)).toEqual([ + 'visible-page', + 'hidden-page', + ]); + }); + + it('excludes descendants of pages blocked from indexing', () => { + const pages: RevisionPage[] = [ + { + id: 'parent', + type: 'group', + title: 'Parent', + path: 'parent', + hidden: false, + noRobotsIndex: true, + pages: [ + { + id: 'child', + type: 'document', + title: 'Child', + path: 'parent/child', + pages: [], + hidden: false, + }, + ], + }, + ] as RevisionPage[]; + + expect(getIndexablePages(pages)).toEqual([]); + }); + + it('includes documents nested under groups', () => { + const pages: RevisionPage[] = [ + { + id: 'outer-group', + type: 'group', + title: 'Outer group', + path: 'outer-group', + hidden: false, + pages: [ + { + id: 'inner-group', + type: 'group', + title: 'Inner group', + path: 'outer-group/inner-group', + hidden: false, + pages: [ + { + id: 'nested-page', + type: 'document', + title: 'Nested page', + path: 'outer-group/inner-group/nested-page', + pages: [], + hidden: false, + }, + ], + }, + ], + }, + ] as RevisionPage[]; + + expect(getIndexablePages(pages).map(({ page }) => page.id)).toEqual(['nested-page']); + }); +}); diff --git a/packages/gitbook/src/lib/sitemap.ts b/packages/gitbook/src/lib/sitemap.ts index c34268b61..9af0d6a0a 100644 --- a/packages/gitbook/src/lib/sitemap.ts +++ b/packages/gitbook/src/lib/sitemap.ts @@ -9,27 +9,35 @@ export type FlatPageEntry = { page: RevisionPageDocument; depth: number }; */ function flattenPages( rootPages: RevisionPage[], - filter: (page: RevisionPageDocument | RevisionPageGroup) => boolean + filter: ( + page: RevisionPageDocument | RevisionPageGroup, + ancestors: Array + ) => boolean ): FlatPageEntry[] { const flattenPage = ( page: RevisionPageDocument | RevisionPageGroup, - depth: number + depth: number, + ancestors: Array ): FlatPageEntry[] => { - const allowed = filter(page); + const allowed = filter(page, ancestors); if (!allowed) { return []; } - return [ - ...(page.type === 'document' ? [{ page, depth }] : []), - ...page.pages.flatMap((child) => - child.type === 'document' ? flattenPage(child, depth + 1) : [] - ), - ]; + const children: FlatPageEntry[] = []; + for (const child of page.pages) { + if (child.type === 'link' || child.type === 'computed') { + continue; + } + + children.push(...flattenPage(child, depth + 1, [...ancestors, page])); + } + + return [...(page.type === 'document' ? [{ page, depth }] : []), ...children]; }; return rootPages.flatMap((page) => - page.type === 'group' || page.type === 'document' ? flattenPage(page, 0) : [] + page.type === 'group' || page.type === 'document' ? flattenPage(page, 0, []) : [] ); } @@ -37,5 +45,5 @@ function flattenPages( * Get all indexable pages from a revision in a flat list. */ export function getIndexablePages(rootPages: RevisionPage[]) { - return flattenPages(rootPages, (page) => !page.hidden && isPageIndexable([], page)); + return flattenPages(rootPages, (page, ancestors) => isPageIndexable(ancestors, page)); }