Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/social-berries-visit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"gitbook": patch
---

Fix links to other spaces and root page in embeddable view.
23 changes: 23 additions & 0 deletions packages/gitbook/src/lib/embeddable.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { describe, expect, it } from 'bun:test';
import { getEmbeddableLinker } from './embeddable';
import { createLinker } from './links';

describe('getEmbeddableLinker', () => {
it('withOtherSiteSpace should resolve future links within the embed namespace', () => {
const root = createLinker({
host: 'docs.company.com',
spaceBasePath: '/',
siteBasePath: '/',
});

const embeddableLinker = getEmbeddableLinker(root);

const otherSpaceEmbeddableLinker = embeddableLinker.withOtherSiteSpace({
spaceBasePath: '/section/variant',
});

expect(otherSpaceEmbeddableLinker.toPathInSpace('some/path')).toBe(
'/section/variant/~gitbook/embed/page/some/path'
);
});
});
7 changes: 7 additions & 0 deletions packages/gitbook/src/lib/embeddable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,12 @@ export function getEmbeddableLinker(linker: GitBookLinker): GitBookLinker {

return linker.toPathInSpace(embedPagePath) + (anchor ? `#${anchor}` : '');
},

withOtherSiteSpace(override: { spaceBasePath: string }): GitBookLinker {
return linker.withOtherSiteSpace({
// We make sure that links in the other site space will be shown in the embeddeable view.
spaceBasePath: joinPath(override.spaceBasePath, '~gitbook/embed/page'),
});
},
};
}
17 changes: 6 additions & 11 deletions packages/gitbook/src/lib/links.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { describe, expect, it } from 'bun:test';
import {
createLinker,
linkerForPublishedURL,
linkerWithAbsoluteURLs,
linkerWithOtherSpaceBasePath,
} from './links';
import { createLinker, linkerForPublishedURL, linkerWithAbsoluteURLs } from './links';

const root = createLinker({
host: 'docs.company.com',
Expand Down Expand Up @@ -122,9 +117,9 @@ describe('linkerWithAbsoluteURLs', () => {
});
});

describe('linkerWithOtherSpaceBasePath', () => {
describe('linker.withOtherSiteSpace', () => {
it('should return a new linker that resolves links relative to a new spaceBasePath in the current site', () => {
const otherSpaceBasePathLinker = linkerWithOtherSpaceBasePath(root, {
const otherSpaceBasePathLinker = root.withOtherSiteSpace({
spaceBasePath: '/section/variant',
});
expect(otherSpaceBasePathLinker.toPathInSpace('some/path')).toBe(
Expand All @@ -133,7 +128,7 @@ describe('linkerWithOtherSpaceBasePath', () => {
});

it('should return a new linker that resolves links relative to a new spaceBasePath in the current site', () => {
const otherSpaceBasePathLinker = linkerWithOtherSpaceBasePath(root, {
const otherSpaceBasePathLinker = root.withOtherSiteSpace({
spaceBasePath: '/section/variant',
});
expect(otherSpaceBasePathLinker.toPathInSpace('some/path')).toBe(
Expand All @@ -142,14 +137,14 @@ describe('linkerWithOtherSpaceBasePath', () => {
});

it('should use a basepath relative to the site', () => {
const otherSpaceBasePathLinker = linkerWithOtherSpaceBasePath(siteGitBookIO, {
const otherSpaceBasePathLinker = siteGitBookIO.withOtherSiteSpace({
spaceBasePath: 'a/b',
});
expect(otherSpaceBasePathLinker.toPathInSpace('some/path')).toBe('/sitename/a/b/some/path');
});

it('should use a basepath relative to the site (with trailing slash)', () => {
const otherSpaceBasePathLinker = linkerWithOtherSpaceBasePath(siteGitBookIO, {
const otherSpaceBasePathLinker = siteGitBookIO.withOtherSiteSpace({
spaceBasePath: '/a/b',
});
expect(otherSpaceBasePathLinker.toPathInSpace('some/path')).toBe('/sitename/a/b/some/path');
Expand Down
53 changes: 24 additions & 29 deletions packages/gitbook/src/lib/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ export interface GitBookLinker {
*/
fork(override: { spaceBasePath: string }): GitBookLinker;

/**
* Create a new linker that resolves links relative to a new space in the current site.
*/
withOtherSiteSpace(override: { spaceBasePath: string }): GitBookLinker;

/**
* Site base path used to create this linker.
*/
Expand Down Expand Up @@ -156,6 +161,25 @@ export function createLinker(

return rawURL;
},

withOtherSiteSpace(override: { spaceBasePath: string }): GitBookLinker {
const newLinker: GitBookLinker = {
...linker,
toPathInSpace(relativePath: string): string {
return linker.toPathInSite(joinPaths(override.spaceBasePath, relativePath));
},
// implementation matches the base linker toPathForPage, but decouples from using `this` to
// ensure we always use the updates `toPathInSpace` method.
toPathForPage({ pages, page, anchor }) {
return (
newLinker.toPathInSpace(getPagePath(pages, page)) +
(anchor ? `#${anchor}` : '')
);
},
};

return newLinker;
},
};

return linker;
Expand Down Expand Up @@ -204,35 +228,6 @@ export function linkerWithAbsoluteURLs(linker: GitBookLinker): GitBookLinker {
};
}

/**
* Create a new linker that resolves links relative to a new spaceBasePath in the current site.
*/
export function linkerWithOtherSpaceBasePath(
linker: GitBookLinker,
{
spaceBasePath,
}: {
/**
* The base path of the space. It should be relative to the root of the site.
*/
spaceBasePath: string;
}
): GitBookLinker {
const newLinker: GitBookLinker = {
...linker,
toPathInSpace(relativePath: string): string {
return linker.toPathInSite(joinPaths(spaceBasePath, relativePath));
},
// implementation matches the base linker toPathForPage, but decouples from using `this` to
// ensure we always use the updates `toPathInSpace` method.
toPathForPage({ pages, page, anchor }) {
return newLinker.toPathInSpace(getPagePath(pages, page)) + (anchor ? `#${anchor}` : '');
},
};

return newLinker;
}

function joinPaths(prefix: string, path: string): string {
const prefixPath = prefix.endsWith('/') ? prefix : `${prefix}/`;
const suffixPath = path.startsWith('/') ? path.slice(1) : path;
Expand Down
9 changes: 2 additions & 7 deletions packages/gitbook/src/lib/references.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@ import {
getRevisionReusableContent,
ignoreDataThrownError,
} from '@/lib/data';
import {
type GitBookLinker,
createLinker,
linkerWithAbsoluteURLs,
linkerWithOtherSpaceBasePath,
} from '@/lib/links';
import { type GitBookLinker, createLinker, linkerWithAbsoluteURLs } from '@/lib/links';
import type {
ContentRef,
RevisionFile,
Expand Down Expand Up @@ -448,7 +443,7 @@ async function createContextForSpace(

if (bestTargetSpace?.siteSpace && 'site' in context) {
// If we found the space ID in the current site context, we can resolve links relative to it in the site.
linker = linkerWithOtherSpaceBasePath(context.linker, {
linker = context.linker.withOtherSiteSpace({
spaceBasePath: getFallbackSiteSpacePath(context, bestTargetSpace.siteSpace),
});
} else {
Expand Down
4 changes: 2 additions & 2 deletions packages/gitbook/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,10 +590,10 @@ function encodePathInSiteContent(rawPathname: string): {
}

// If the pathname is an embedded page
const embedPage = pathname.match(/^~gitbook\/embed\/page\/(\S+)$/);
const embedPage = pathname.match(/^~gitbook\/embed\/page(\/(\S*))?$/);
if (embedPage) {
return {
pathname: `~gitbook/embed/page/${encodeURIComponent(embedPage[1]!)}`,
pathname: `~gitbook/embed/page/${encodeURIComponent(embedPage[1] || '/')}`,
};
}

Expand Down