Skip to content

Commit

Permalink
Fix autogenerated sidebar issue (#2688)
Browse files Browse the repository at this point in the history
Co-authored-by: delucis <357379+delucis@users.noreply.github.com>
  • Loading branch information
HiDeoo and delucis authored Dec 14, 2024
1 parent d5719f8 commit 5c6996c
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/wild-laws-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': patch
---

Fixes an issue with autogenerated sidebars when using Starlight with Astro's new Content Layer API where group names would be sluggified.
29 changes: 27 additions & 2 deletions packages/starlight/__tests__/basics/navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ vi.mock('astro:content', async () =>
['guides/authoring-content.mdx', { title: 'Authoring Markdown' }],
['reference/frontmatter.md', { title: 'Frontmatter Reference', sidebar: { hidden: true } }],
['guides/project-structure.mdx', { title: 'Project Structure' }],
['Getting Started/intro.md', { title: 'Introduction' }],
],
})
);
Expand All @@ -33,6 +34,22 @@ describe('getSidebar', () => {
"label": "Eco-friendly docs",
"type": "link",
},
{
"badge": undefined,
"collapsed": false,
"entries": [
{
"attrs": {},
"badge": undefined,
"href": "/getting-started/intro/",
"isCurrent": false,
"label": "Introduction",
"type": "link",
},
],
"label": "Getting Started",
"type": "group",
},
{
"badge": undefined,
"collapsed": false,
Expand Down Expand Up @@ -152,6 +169,14 @@ describe('flattenSidebar', () => {
"label": "Eco-friendly docs",
"type": "link",
},
{
"attrs": {},
"badge": undefined,
"href": "/getting-started/intro/",
"isCurrent": false,
"label": "Introduction",
"type": "link",
},
{
"attrs": {},
"badge": undefined,
Expand Down Expand Up @@ -182,9 +207,9 @@ describe('getPrevNextLinks', () => {
"next": {
"attrs": {},
"badge": undefined,
"href": "/guides/authoring-content/",
"href": "/getting-started/intro/",
"isCurrent": false,
"label": "Authoring Markdown",
"label": "Introduction",
"type": "link",
},
"prev": {
Expand Down
6 changes: 5 additions & 1 deletion packages/starlight/__tests__/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ function mockDoc(
data: z.input<typeof frontmatterSchema>,
body = ''
): StarlightDocsCollectionEntry {
const slug = docsFilePath.replace(/\.[^\.]+$/, '').replace(/\/index$/, '');
const slug = docsFilePath
.replace(/\.[^\.]+$/, '')
.replace(/\s/, '-')
.replace(/\/index$/, '')
.toLowerCase();

const doc: StarlightDocsCollectionEntry = {
id: project.legacyCollections ? docsFilePath : slug,
Expand Down
32 changes: 23 additions & 9 deletions packages/starlight/utils/navigation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AstroError } from 'astro/errors';
import config from 'virtual:starlight/user-config';
import project from 'virtual:starlight/project-context';
import type { Badge, I18nBadge, I18nBadgeConfig } from '../schemas/badge';
import type { PrevNextLinkConfig } from '../schemas/prevNextLink';
import type {
Expand All @@ -14,8 +15,9 @@ import { formatPath } from './format-path';
import { BuiltInDefaultLocale, pickLang } from './i18n';
import { ensureLeadingSlash, ensureTrailingSlash, stripLeadingAndTrailingSlashes } from './path';
import { getLocaleRoutes, routes, type Route } from './routing';
import { localeToLang, slugToPathname } from './slugs';
import { localeToLang, localizedId, slugToPathname } from './slugs';
import type { StarlightConfig } from './user-config';
import { getCollectionPathFromRoot } from './collection';

const DirKey = Symbol('DirKey');
const SlugKey = Symbol('SlugKey');
Expand Down Expand Up @@ -108,7 +110,7 @@ function groupFromAutogenerateConfig(
// Match against `foo/anything/else.md`.
doc.id.startsWith(localeDir + '/')
);
const tree = treeify(dirDocs, localeDir);
const tree = treeify(dirDocs, locale, localeDir);
const label = pickLang(item.translations, localeToLang(locale)) || item.label;
return {
type: 'group',
Expand Down Expand Up @@ -205,8 +207,8 @@ function pathsMatch(pathA: string, pathB: string) {
function getBreadcrumbs(path: string, baseDir: string): string[] {
// Strip extension from path.
const pathWithoutExt = stripExtension(path);
// Index paths will match `baseDir` but we still need to consider them as a single segment.
if (pathWithoutExt === baseDir) return [path];
// Index paths will match `baseDir` and don’t include breadcrumbs.
if (pathWithoutExt === baseDir) return [];
// Ensure base directory ends in a trailing slash.
baseDir = ensureTrailingSlash(baseDir);
// Strip base directory from path if present.
Expand All @@ -218,16 +220,28 @@ function getBreadcrumbs(path: string, baseDir: string): string[] {
}

/** Turn a flat array of routes into a tree structure. */
function treeify(routes: Route[], baseDir: string): Dir {
function treeify(routes: Route[], locale: string | undefined, baseDir: string): Dir {
const treeRoot: Dir = makeDir(baseDir);
const collectionPathFromRoot = getCollectionPathFromRoot('docs', project);
routes
// Remove any entries that should be hidden
.filter((doc) => !doc.entry.data.sidebar.hidden)
// Compute the path of each entry from the root of the collection ahead of time.
.map(
(doc) =>
[
project.legacyCollections
? doc.id
: // For collections with a loader, use a localized filePath relative to the collection
localizedId(doc.entry.filePath.replace(`${collectionPathFromRoot}/`, ''), locale),
doc,
] as const
)
// Sort by depth, to build the tree depth first.
.sort((a, b) => b.id.split('/').length - a.id.split('/').length)
.sort(([a], [b]) => b.split('/').length - a.split('/').length)
// Build the tree
.forEach((doc) => {
const parts = getBreadcrumbs(doc.id, baseDir);
.forEach(([filePathFromContentDir, doc]) => {
const parts = getBreadcrumbs(filePathFromContentDir, baseDir);
let currentNode = treeRoot;

parts.forEach((part, index) => {
Expand Down Expand Up @@ -374,7 +388,7 @@ function getIntermediateSidebarFromConfig(
if (sidebarConfig) {
return sidebarConfig.map((group) => configItemToEntry(group, pathname, locale, routes));
} else {
const tree = treeify(routes, locale || '');
const tree = treeify(routes, locale, locale || '');
return sidebarFromDir(tree, pathname, locale, false);
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/starlight/utils/slugs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ export function localizedSlug(slug: string, locale: string | undefined): string
}

/**
* Convert a legacy collection entry ID to a different locale.
* Convert a legacy collection entry ID or filePath relative to the collection root to a different
* locale.
* For example, passing an ID of `en/home.md` and a locale of `fr` results in `fr/home.md`.
* An undefined locale is treated as the root locale, resulting in `home.md`.
* @param id A collection entry ID
Expand Down

0 comments on commit 5c6996c

Please sign in to comment.