diff options
author | HiDeoo | 2025-02-25 15:28:04 +0100 |
---|---|---|
committer | GitHub | 2025-02-25 15:28:04 +0100 |
commit | 790c000c4761f68b2782f1eef74568f210d4c619 (patch) | |
tree | 52ba24e11ea2d7a02d2c50d1ec184e8318f35fd6 | |
parent | f9a4e30ad9352ed1c96f148a88c7d9ed2734e81a (diff) | |
download | IT.starlight-790c000c4761f68b2782f1eef74568f210d4c619.tar.gz IT.starlight-790c000c4761f68b2782f1eef74568f210d4c619.tar.bz2 IT.starlight-790c000c4761f68b2782f1eef74568f210d4c619.zip |
Fix trailing slash inconsistency in sidebar links (#2918)
12 files changed, 226 insertions, 37 deletions
diff --git a/.changeset/large-chairs-refuse.md b/.changeset/large-chairs-refuse.md new file mode 100644 index 00000000..28f53b08 --- /dev/null +++ b/.changeset/large-chairs-refuse.md @@ -0,0 +1,5 @@ +--- +'@astrojs/starlight': patch +--- + +Fixes a trailing slash inconsistency in generated sidebar links when using the [`trailingSlash: 'ignore'`](https://docs.astro.build/en/reference/configuration-reference/#trailingslash) Astro option (the default) between [internal](https://starlight.astro.build/guides/sidebar/#internal-links) and [auto-generated](https://starlight.astro.build/guides/sidebar/#autogenerated-groups) links. Starlight behavior for this configuration value is to use a trailing slash as many common hosting providers redirect to URLs with a trailing slash by default. diff --git a/packages/starlight/__tests__/basics/starlight-page-route-data.test.ts b/packages/starlight/__tests__/basics/starlight-page-route-data.test.ts index b9a23232..972dbfe4 100644 --- a/packages/starlight/__tests__/basics/starlight-page-route-data.test.ts +++ b/packages/starlight/__tests__/basics/starlight-page-route-data.test.ts @@ -238,7 +238,7 @@ test('uses provided sidebar if any', async () => { { "attrs": {}, "badge": undefined, - "href": "/reference/frontmatter", + "href": "/reference/frontmatter/", "isCurrent": false, "label": "Frontmatter Reference", "type": "link", diff --git a/packages/starlight/__tests__/i18n-sidebar/i18n-sidebar-fallback-slug.test.ts b/packages/starlight/__tests__/i18n-sidebar/i18n-sidebar-fallback-slug.test.ts index 750c4244..725a312d 100644 --- a/packages/starlight/__tests__/i18n-sidebar/i18n-sidebar-fallback-slug.test.ts +++ b/packages/starlight/__tests__/i18n-sidebar/i18n-sidebar-fallback-slug.test.ts @@ -30,7 +30,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/getting-started", + "href": "/getting-started/", "isCurrent": false, "label": "Getting Started", "type": "link", @@ -41,7 +41,7 @@ describe('getSidebar', () => { "text": "New", "variant": "default", }, - "href": "/manual-setup", + "href": "/manual-setup/", "isCurrent": false, "label": "Do it yourself", "type": "link", @@ -52,7 +52,7 @@ describe('getSidebar', () => { "text": "Eco-friendly", "variant": "success", }, - "href": "/environmental-impact", + "href": "/environmental-impact/", "isCurrent": false, "label": "Eco-friendly docs", "type": "link", @@ -64,7 +64,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/guides/pages", + "href": "/guides/pages/", "isCurrent": false, "label": "Pages", "type": "link", @@ -75,7 +75,7 @@ describe('getSidebar', () => { "text": "Deprecated", "variant": "default", }, - "href": "/guides/authoring-content", + "href": "/guides/authoring-content/", "isCurrent": false, "label": "Authoring Content in Markdown", "type": "link", @@ -87,7 +87,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/resources/plugins", + "href": "/resources/plugins/", "isCurrent": false, "label": "Plugins and Integrations", "type": "link", @@ -101,7 +101,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr", + "href": "/fr/", "isCurrent": true, "label": "Starlight 🌟 Build documentation sites with Astro", "type": "link", @@ -109,7 +109,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/getting-started", + "href": "/fr/getting-started/", "isCurrent": false, "label": "Getting Started", "type": "link", @@ -120,7 +120,7 @@ describe('getSidebar', () => { "text": "Nouveau", "variant": "default", }, - "href": "/fr/manual-setup", + "href": "/fr/manual-setup/", "isCurrent": false, "label": "Fait maison", "type": "link", @@ -131,7 +131,7 @@ describe('getSidebar', () => { "text": "Écologique", "variant": "success", }, - "href": "/fr/environmental-impact", + "href": "/fr/environmental-impact/", "isCurrent": false, "label": "Eco-friendly docs", "type": "link", @@ -143,7 +143,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/guides/pages", + "href": "/fr/guides/pages/", "isCurrent": false, "label": "Pages", "type": "link", @@ -154,7 +154,7 @@ describe('getSidebar', () => { "text": "Deprecated", "variant": "default", }, - "href": "/fr/guides/authoring-content", + "href": "/fr/guides/authoring-content/", "isCurrent": false, "label": "Authoring Content in Markdown", "type": "link", @@ -166,7 +166,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/resources/plugins", + "href": "/fr/resources/plugins/", "isCurrent": false, "label": "Plugins and Integrations", "type": "link", diff --git a/packages/starlight/__tests__/i18n-sidebar/i18n-sidebar.test.ts b/packages/starlight/__tests__/i18n-sidebar/i18n-sidebar.test.ts index ee62e268..9d7e005e 100644 --- a/packages/starlight/__tests__/i18n-sidebar/i18n-sidebar.test.ts +++ b/packages/starlight/__tests__/i18n-sidebar/i18n-sidebar.test.ts @@ -43,7 +43,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/getting-started", + "href": "/getting-started/", "isCurrent": false, "label": "Getting Started", "type": "link", @@ -54,7 +54,7 @@ describe('getSidebar', () => { "text": "New", "variant": "default", }, - "href": "/manual-setup", + "href": "/manual-setup/", "isCurrent": false, "label": "Do it yourself", "type": "link", @@ -65,7 +65,7 @@ describe('getSidebar', () => { "text": "Eco-friendly", "variant": "success", }, - "href": "/environmental-impact", + "href": "/environmental-impact/", "isCurrent": false, "label": "Eco-friendly docs", "type": "link", @@ -82,7 +82,7 @@ describe('getSidebar', () => { "text": "Test", "variant": "default", }, - "href": "/guides/pages", + "href": "/guides/pages/", "isCurrent": false, "label": "Pages Guide", "type": "link", @@ -93,7 +93,7 @@ describe('getSidebar', () => { "text": "Deprecated", "variant": "default", }, - "href": "/guides/authoring-content", + "href": "/guides/authoring-content/", "isCurrent": false, "label": "Authoring Content in Markdown", "type": "link", @@ -105,7 +105,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/resources/plugins", + "href": "/resources/plugins/", "isCurrent": false, "label": "Plugins and Integrations", "type": "link", @@ -119,7 +119,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr", + "href": "/fr/", "isCurrent": true, "label": "Starlight 🌟 Construire des sites de documentation avec Astro", "type": "link", @@ -127,7 +127,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/getting-started", + "href": "/fr/getting-started/", "isCurrent": false, "label": "Mise en route", "type": "link", @@ -138,7 +138,7 @@ describe('getSidebar', () => { "text": "Nouveau", "variant": "default", }, - "href": "/fr/manual-setup", + "href": "/fr/manual-setup/", "isCurrent": false, "label": "Fait maison", "type": "link", @@ -149,7 +149,7 @@ describe('getSidebar', () => { "text": "Écologique", "variant": "success", }, - "href": "/fr/environmental-impact", + "href": "/fr/environmental-impact/", "isCurrent": false, "label": "Documents écologiques", "type": "link", @@ -161,7 +161,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/guides/pages", + "href": "/fr/guides/pages/", "isCurrent": false, "label": "Pages", "type": "link", @@ -172,7 +172,7 @@ describe('getSidebar', () => { "text": "Deprecated", "variant": "default", }, - "href": "/fr/guides/authoring-content", + "href": "/fr/guides/authoring-content/", "isCurrent": false, "label": "Création de contenu en Markdown", "type": "link", @@ -184,7 +184,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/resources/plugins", + "href": "/fr/resources/plugins/", "isCurrent": false, "label": "Modules d'extension et outils", "type": "link", @@ -198,7 +198,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr", + "href": "/fr/", "isCurrent": false, "label": "Starlight 🌟 Construire des sites de documentation avec Astro", "type": "link", @@ -206,7 +206,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/getting-started", + "href": "/fr/getting-started/", "isCurrent": true, "label": "Mise en route", "type": "link", @@ -217,7 +217,7 @@ describe('getSidebar', () => { "text": "Nouveau", "variant": "default", }, - "href": "/fr/manual-setup", + "href": "/fr/manual-setup/", "isCurrent": false, "label": "Fait maison", "type": "link", @@ -228,7 +228,7 @@ describe('getSidebar', () => { "text": "Écologique", "variant": "success", }, - "href": "/fr/environmental-impact", + "href": "/fr/environmental-impact/", "isCurrent": false, "label": "Documents écologiques", "type": "link", @@ -240,7 +240,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/guides/pages", + "href": "/fr/guides/pages/", "isCurrent": false, "label": "Pages", "type": "link", @@ -251,7 +251,7 @@ describe('getSidebar', () => { "text": "Deprecated", "variant": "default", }, - "href": "/fr/guides/authoring-content", + "href": "/fr/guides/authoring-content/", "isCurrent": false, "label": "Création de contenu en Markdown", "type": "link", @@ -263,7 +263,7 @@ describe('getSidebar', () => { { "attrs": {}, "badge": undefined, - "href": "/fr/resources/plugins", + "href": "/fr/resources/plugins/", "isCurrent": false, "label": "Modules d'extension et outils", "type": "link", @@ -273,12 +273,12 @@ describe('getSidebar', () => { }); test('uses label from config for internal links', () => { const sidebar = getSidebar('/', undefined); - const entry = sidebar.find((item) => item.type === 'link' && item.href === '/manual-setup'); + const entry = sidebar.find((item) => item.type === 'link' && item.href === '/manual-setup/'); expect(entry?.label).toBe('Do it yourself'); }); test('uses translation from config for internal links', () => { const sidebar = getSidebar('/fr', 'fr'); - const entry = sidebar.find((item) => item.type === 'link' && item.href === '/fr/manual-setup'); + const entry = sidebar.find((item) => item.type === 'link' && item.href === '/fr/manual-setup/'); expect(entry?.label).toBe('Fait maison'); }); test('uses intermediate sidebars cached by locales', async () => { diff --git a/packages/starlight/__tests__/sidebar/navigation-attributes.test.ts b/packages/starlight/__tests__/sidebar/navigation-attributes.test.ts index 2dda3324..60d24a04 100644 --- a/packages/starlight/__tests__/sidebar/navigation-attributes.test.ts +++ b/packages/starlight/__tests__/sidebar/navigation-attributes.test.ts @@ -6,6 +6,8 @@ vi.mock('astro:content', async () => docs: [ ['index.mdx', { title: 'Home Page' }], ['environmental-impact.md', { title: 'Eco-friendly docs' }], + ['resources/plugins.mdx', { title: 'Plugins' }], + ['resources/themes.mdx', { title: 'Themes' }], [ 'reference/frontmatter.md', { @@ -73,6 +75,30 @@ describe('getSidebar', () => { "type": "group", }, { + "badge": undefined, + "collapsed": false, + "entries": [ + { + "attrs": {}, + "badge": undefined, + "href": "/resources/plugins/", + "isCurrent": false, + "label": "Plugins", + "type": "link", + }, + { + "attrs": {}, + "badge": undefined, + "href": "/resources/themes/", + "isCurrent": false, + "label": "Themes", + "type": "link", + }, + ], + "label": "Resources", + "type": "group", + }, + { "badge": { "text": "Experimental", "variant": "default", diff --git a/packages/starlight/__tests__/sidebar/navigation-badges.test.ts b/packages/starlight/__tests__/sidebar/navigation-badges.test.ts index d0963c2c..1a4e9e25 100644 --- a/packages/starlight/__tests__/sidebar/navigation-badges.test.ts +++ b/packages/starlight/__tests__/sidebar/navigation-badges.test.ts @@ -6,6 +6,8 @@ vi.mock('astro:content', async () => docs: [ ['index.mdx', { title: 'Home Page' }], ['environmental-impact.md', { title: 'Eco-friendly docs' }], + ['resources/plugins.mdx', { title: 'Plugins' }], + ['resources/themes.mdx', { title: 'Themes' }], [ 'reference/configuration.mdx', { @@ -80,6 +82,30 @@ describe('getSidebar', () => { "type": "group", }, { + "badge": undefined, + "collapsed": false, + "entries": [ + { + "attrs": {}, + "badge": undefined, + "href": "/resources/plugins/", + "isCurrent": false, + "label": "Plugins", + "type": "link", + }, + { + "attrs": {}, + "badge": undefined, + "href": "/resources/themes/", + "isCurrent": false, + "label": "Themes", + "type": "link", + }, + ], + "label": "Resources", + "type": "group", + }, + { "badge": { "text": "Experimental", "variant": "default", diff --git a/packages/starlight/__tests__/sidebar/navigation-hidden.test.ts b/packages/starlight/__tests__/sidebar/navigation-hidden.test.ts index ee5cc0c5..d2d92024 100644 --- a/packages/starlight/__tests__/sidebar/navigation-hidden.test.ts +++ b/packages/starlight/__tests__/sidebar/navigation-hidden.test.ts @@ -6,6 +6,8 @@ vi.mock('astro:content', async () => docs: [ ['index.mdx', { title: 'Home Page' }], ['environmental-impact.md', { title: 'Eco-friendly docs' }], + ['resources/plugins.mdx', { title: 'Plugins' }], + ['resources/themes.mdx', { title: 'Themes' }], ['reference/configuration.mdx', { title: 'Config Reference' }], ['reference/frontmatter.md', { title: 'Frontmatter Reference', sidebar: { hidden: true } }], ['api/v1/users.md', { title: 'Users API' }], @@ -69,6 +71,30 @@ describe('getSidebar', () => { "type": "group", }, { + "badge": undefined, + "collapsed": false, + "entries": [ + { + "attrs": {}, + "badge": undefined, + "href": "/resources/plugins/", + "isCurrent": false, + "label": "Plugins", + "type": "link", + }, + { + "attrs": {}, + "badge": undefined, + "href": "/resources/themes/", + "isCurrent": false, + "label": "Themes", + "type": "link", + }, + ], + "label": "Resources", + "type": "group", + }, + { "badge": { "text": "Experimental", "variant": "default", diff --git a/packages/starlight/__tests__/sidebar/navigation-order.test.ts b/packages/starlight/__tests__/sidebar/navigation-order.test.ts index cce9a694..31e64413 100644 --- a/packages/starlight/__tests__/sidebar/navigation-order.test.ts +++ b/packages/starlight/__tests__/sidebar/navigation-order.test.ts @@ -6,6 +6,8 @@ vi.mock('astro:content', async () => docs: [ ['index.mdx', { title: 'Home Page' }], ['environmental-impact.md', { title: 'Eco-friendly docs' }], + ['resources/plugins.mdx', { title: 'Plugins' }], + ['resources/themes.mdx', { title: 'Themes' }], ['reference/configuration.mdx', { title: 'Config Reference' }], ['reference/frontmatter.md', { title: 'Frontmatter Reference', sidebar: { order: 1 } }], ['api/v1/users.md', { title: 'Users API' }], @@ -69,6 +71,30 @@ describe('getSidebar', () => { "type": "group", }, { + "badge": undefined, + "collapsed": false, + "entries": [ + { + "attrs": {}, + "badge": undefined, + "href": "/resources/plugins/", + "isCurrent": false, + "label": "Plugins", + "type": "link", + }, + { + "attrs": {}, + "badge": undefined, + "href": "/resources/themes/", + "isCurrent": false, + "label": "Themes", + "type": "link", + }, + ], + "label": "Resources", + "type": "group", + }, + { "badge": { "text": "Experimental", "variant": "default", diff --git a/packages/starlight/__tests__/sidebar/navigation-unicode.test.ts b/packages/starlight/__tests__/sidebar/navigation-unicode.test.ts index 02c16bb4..c5bb2643 100644 --- a/packages/starlight/__tests__/sidebar/navigation-unicode.test.ts +++ b/packages/starlight/__tests__/sidebar/navigation-unicode.test.ts @@ -6,6 +6,8 @@ vi.mock('astro:content', async () => docs: [ ['index.mdx', { title: 'Home Page' }], ['environmental-impact.md', { title: 'Eco-friendly docs' }], + ['resources/plugins.mdx', { title: 'Plugins' }], + ['resources/themes.mdx', { title: 'Themes' }], ['reference/configuration.mdx', { title: 'Config Reference' }], ['reference/frontmatter.md', { title: 'Frontmatter Reference' }], ['api/v1/用户.md', { title: 'Path with non-ASCII characters' }], @@ -69,6 +71,30 @@ describe('getSidebar', () => { "type": "group", }, { + "badge": undefined, + "collapsed": false, + "entries": [ + { + "attrs": {}, + "badge": undefined, + "href": "/resources/plugins/", + "isCurrent": false, + "label": "Plugins", + "type": "link", + }, + { + "attrs": {}, + "badge": undefined, + "href": "/resources/themes/", + "isCurrent": false, + "label": "Themes", + "type": "link", + }, + ], + "label": "Resources", + "type": "group", + }, + { "badge": { "text": "Experimental", "variant": "default", diff --git a/packages/starlight/__tests__/sidebar/navigation.test.ts b/packages/starlight/__tests__/sidebar/navigation.test.ts index f113d431..a2291819 100644 --- a/packages/starlight/__tests__/sidebar/navigation.test.ts +++ b/packages/starlight/__tests__/sidebar/navigation.test.ts @@ -1,11 +1,14 @@ import { describe, expect, test, vi } from 'vitest'; import { getSidebar } from '../../utils/navigation'; +import type { SidebarEntry } from '../../utils/routing/types'; vi.mock('astro:content', async () => (await import('../test-utils')).mockedAstroContent({ docs: [ ['index.mdx', { title: 'Home Page' }], ['environmental-impact.md', { title: 'Eco-friendly docs' }], + ['resources/plugins.mdx', { title: 'Plugins' }], + ['resources/themes.mdx', { title: 'Themes' }], ['reference/configuration.mdx', { title: 'Config Reference' }], ['reference/frontmatter.md', { title: 'Frontmatter Reference' }], ['reference/frontmatter/foo.mdx', { title: 'Foo' }], @@ -70,6 +73,30 @@ describe('getSidebar', () => { "type": "group", }, { + "badge": undefined, + "collapsed": false, + "entries": [ + { + "attrs": {}, + "badge": undefined, + "href": "/resources/plugins/", + "isCurrent": false, + "label": "Plugins", + "type": "link", + }, + { + "attrs": {}, + "badge": undefined, + "href": "/resources/themes/", + "isCurrent": false, + "label": "Themes", + "type": "link", + }, + ], + "label": "Resources", + "type": "group", + }, + { "badge": { "text": "Experimental", "variant": "default", @@ -147,4 +174,21 @@ describe('getSidebar', () => { ] `); }); + + test("ensures trailing slash consistency between internal and auto-generated sidebar links when using `trailingSlash: 'ignore'`", () => { + const sidebar = getSidebar('/', undefined); + const internalLinksGroup = sidebar.at(2); + const autoGeneratedLinksGroup = sidebar.at(3); + + function includesOnlyLinksWithTrailingSlash(entry: SidebarEntry | undefined): boolean { + return ( + entry !== undefined && + ((entry.type === 'link' && entry.href.endsWith('/')) || + (entry.type === 'group' && entry.entries.every(includesOnlyLinksWithTrailingSlash))) + ); + } + + expect(includesOnlyLinksWithTrailingSlash(internalLinksGroup)).toBe(true); + expect(includesOnlyLinksWithTrailingSlash(autoGeneratedLinksGroup)).toBe(true); + }); }); diff --git a/packages/starlight/__tests__/sidebar/vitest.config.ts b/packages/starlight/__tests__/sidebar/vitest.config.ts index 8eceebbc..205238c9 100644 --- a/packages/starlight/__tests__/sidebar/vitest.config.ts +++ b/packages/starlight/__tests__/sidebar/vitest.config.ts @@ -25,6 +25,11 @@ export default defineVitestConfig({ }, ], }, + // A group containing internal links using the `slug` property or its shorthand string syntax. + { + label: 'Resources', + items: ['resources/plugins', { slug: 'resources/themes' }], + }, // A group linking to all pages in the `reference` directory. { label: 'Reference', diff --git a/packages/starlight/utils/navigation.ts b/packages/starlight/utils/navigation.ts index cfcd2e83..2870d619 100644 --- a/packages/starlight/utils/navigation.ts +++ b/packages/starlight/utils/navigation.ts @@ -165,7 +165,12 @@ function linkFromInternalSidebarLinkItem( frontmatter.title; const badge = item.badge ?? frontmatter.sidebar?.badge; const attrs = { ...frontmatter.sidebar?.attrs, ...item.attrs }; - return makeSidebarLink(route.slug, label, getSidebarBadge(badge, locale, label), attrs); + return makeSidebarLink( + slugToPathname(route.slug), + label, + getSidebarBadge(badge, locale, label), + attrs + ); } /** Process sidebar link options to create a link entry. */ |