diff --git a/.changeset/rotten-pianos-cheat.md b/.changeset/rotten-pianos-cheat.md new file mode 100644 index 000000000..e1bad53fe --- /dev/null +++ b/.changeset/rotten-pianos-cheat.md @@ -0,0 +1,5 @@ +--- +"gitbook": patch +--- + +Fix links to other spaces within a section. diff --git a/packages/gitbook/src/components/Search/server-actions.tsx b/packages/gitbook/src/components/Search/server-actions.tsx index 525ff91e2..8b356f2cb 100644 --- a/packages/gitbook/src/components/Search/server-actions.tsx +++ b/packages/gitbook/src/components/Search/server-actions.tsx @@ -3,7 +3,7 @@ import type { GitBookBaseContext, GitBookSiteContext } from '@/lib/context'; import { resolvePageId } from '@/lib/pages'; import { fetchServerActionSiteContext, getServerActionBaseContext } from '@/lib/server-actions'; -import { findSiteSpaceById, getSiteStructureSections } from '@/lib/sites'; +import { findSiteSpaceBy } from '@/lib/sites'; import { filterOutNullable } from '@/lib/typescript'; import type { Revision, @@ -256,24 +256,20 @@ async function searchSiteContent( return ( await Promise.all( searchResults.map(async (spaceItem) => { - const sections = getSiteStructureSections(structure).flatMap((item) => - item.object === 'site-section-group' ? [item, ...item.sections] : item + const found = findSiteSpaceBy( + structure, + (siteSpace) => siteSpace.space.id === spaceItem.id ); - const siteSpace = findSiteSpaceById(structure, spaceItem.id); - const siteSection = sections.find( - (section) => section.id === siteSpace?.section - ) as SiteSection; - const siteSectionGroup = siteSection?.sectionGroup - ? sections.find((sectionGroup) => sectionGroup.id === siteSection.sectionGroup) - : null; + const siteSection = found?.siteSection; + const siteSectionGroup = found?.siteSectionGroup; return Promise.all( spaceItem.pages.map((pageItem) => transformSitePageResult(context, { pageItem, spaceItem, - space: siteSpace?.space, - spaceURL: siteSpace?.urls.published, + space: found?.siteSpace.space, + spaceURL: found?.siteSpace.urls.published, siteSection: siteSection ?? undefined, siteSectionGroup: (siteSectionGroup as SiteSectionGroup) ?? undefined, }) @@ -313,8 +309,11 @@ async function transformAnswer( } // Find the siteSpace in case it is nested in a site section so we can resolve the URL appropriately - const siteSpace = findSiteSpaceById(context.structure, source.space); - const spaceURL = siteSpace?.urls.published; + const found = findSiteSpaceBy( + context.structure, + (siteSpace) => siteSpace.space.id === source.space + ); + const spaceURL = found?.siteSpace.urls.published; const href = spaceURL ? joinPathWithBaseURL(spaceURL, page.page.path) diff --git a/packages/gitbook/src/lib/references.tsx b/packages/gitbook/src/lib/references.tsx index 9d70a60c4..4d08c7646 100644 --- a/packages/gitbook/src/lib/references.tsx +++ b/packages/gitbook/src/lib/references.tsx @@ -33,7 +33,7 @@ import { PageIcon } from '@/components/PageIcon'; import { getGitBookAppHref } from './app'; import { getBlockById, getBlockTitle } from './document'; import { resolvePageId } from './pages'; -import { findSiteSpaceById, getFallbackSiteSpacePath } from './sites'; +import { findSiteSpaceBy, getFallbackSiteSpacePath } from './sites'; import type { ClassValue } from './tailwind'; import { filterOutNullable } from './typescript'; @@ -315,9 +315,12 @@ async function getBestTargetSpace( // In the context of sites, we try to find our target space in the site structure. // because the url of this space will be in the same site. if ('site' in context) { - const siteSpace = findSiteSpaceById(context.structure, spaceId); - if (siteSpace) { - return { space: siteSpace.space, siteSpace }; + const found = findSiteSpaceBy( + context.structure, + (siteSpace) => siteSpace.space.id === spaceId + ); + if (found) { + return { space: found.siteSpace.space, siteSpace: found.siteSpace }; } } diff --git a/packages/gitbook/src/lib/sites.ts b/packages/gitbook/src/lib/sites.ts index 2b1afe104..f773e9238 100644 --- a/packages/gitbook/src/lib/sites.ts +++ b/packages/gitbook/src/lib/sites.ts @@ -48,18 +48,46 @@ export function listAllSiteSpaces(siteStructure: SiteStructure) { /** * Find a site space by its spaceId in a site structure. */ -export function findSiteSpaceById(siteStructure: SiteStructure, spaceId: string): SiteSpace | null { +export function findSiteSpaceBy( + siteStructure: SiteStructure, + predicate: (siteSpace: SiteSpace) => boolean +): { + siteSpace: SiteSpace; + siteSection: SiteSection | null; + siteSectionGroup: SiteSectionGroup | null; +} | null { if (siteStructure.type === 'siteSpaces') { - return siteStructure.structure.find((siteSpace) => siteSpace.space.id === spaceId) ?? null; + const siteSpace = siteStructure.structure.find(predicate) ?? null; + if (siteSpace) { + return { + siteSpace, + siteSection: null, + siteSectionGroup: null, + }; + } + + return null; } - for (const section of siteStructure.structure) { - const siteSpace = - section.object === 'site-section' - ? findSiteSpaceByIdInSiteSpaces(section.siteSpaces, spaceId) - : findSiteSpaceByIdInSections(section.sections, spaceId); - if (siteSpace) { - return siteSpace; + for (const sectionOrGroup of siteStructure.structure) { + if (sectionOrGroup.object === 'site-section') { + const siteSpace = findSiteSpaceByIdInSiteSpaces(sectionOrGroup.siteSpaces, predicate); + if (siteSpace) { + return { + siteSpace, + siteSection: sectionOrGroup, + siteSectionGroup: null, + }; + } + } else { + const found = findSiteSpaceByIdInSections(sectionOrGroup.sections, predicate); + if (found) { + return { + siteSpace: found.siteSpace, + siteSection: found.siteSection, + siteSectionGroup: sectionOrGroup, + }; + } } } @@ -94,25 +122,37 @@ export function getSiteSpaceURL(context: GitBookSiteContext, siteSpace: SiteSpac /** * Get the path of a site space in the current site. - * This doesn't return the most optimized path, as it doesn't take into account which one is the default one. */ export function getFallbackSiteSpacePath(context: GitBookSiteContext, siteSpace: SiteSpace) { - const { sections } = context; - return sections?.current ? joinPath(sections.current.path, siteSpace.path) : siteSpace.path; + const found = findSiteSpaceBy(context.structure, (entry) => entry.id === siteSpace.id); + // don't include the path for the default site space + const siteSpacePath = siteSpace.default ? '' : siteSpace.path; + + // for non-default site sections, include the section path. + if (found?.siteSection && !found?.siteSection.default) { + return joinPath(found.siteSection.path, siteSpacePath); + } + + return siteSpacePath; } -function findSiteSpaceByIdInSections(sections: SiteSection[], spaceId: string): SiteSpace | null { - for (const section of sections) { - const siteSpace = - section.siteSpaces.find((siteSpace) => siteSpace.space.id === spaceId) ?? null; +function findSiteSpaceByIdInSections( + sections: SiteSection[], + predicate: (siteSpace: SiteSpace) => boolean +): { siteSpace: SiteSpace; siteSection: SiteSection } | null { + for (const siteSection of sections) { + const siteSpace = siteSection.siteSpaces.find(predicate) ?? null; if (siteSpace) { - return siteSpace; + return { siteSpace, siteSection }; } } return null; } -function findSiteSpaceByIdInSiteSpaces(siteSpaces: SiteSpace[], spaceId: string): SiteSpace | null { - return siteSpaces.find((siteSpace) => siteSpace.space.id === spaceId) ?? null; +function findSiteSpaceByIdInSiteSpaces( + siteSpaces: SiteSpace[], + predicate: (siteSpace: SiteSpace) => boolean +): SiteSpace | null { + return siteSpaces.find(predicate) ?? null; }