From 47a98a1d6ed1b03fb2292d1a3798a9369a98a0bf Mon Sep 17 00:00:00 2001 From: Matthew Cheung <70357013+Mcheung7272@users.noreply.github.com> Date: Mon, 22 Dec 2025 10:54:03 -0500 Subject: [PATCH 1/2] feat(create-docusaurus): enable creation in current directory (#11611) --- packages/create-docusaurus/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/create-docusaurus/src/index.ts b/packages/create-docusaurus/src/index.ts index fadb644680b1..ae9180746bc3 100755 --- a/packages/create-docusaurus/src/index.ts +++ b/packages/create-docusaurus/src/index.ts @@ -273,7 +273,10 @@ async function getSiteName( return 'A website name is required.'; } const dest = path.resolve(rootDir, siteName); - if (await fs.pathExists(dest)) { + if (siteName === '.' && (await fs.readdir(dest)).length > 0) { + return logger.interpolate`Directory not empty at path=${dest}!`; + } + if (siteName !== '.' && (await fs.pathExists(dest))) { return logger.interpolate`Directory already exists at path=${dest}!`; } return true; From 7f5d6122d258086fcbc4e6b9863a34da471506b6 Mon Sep 17 00:00:00 2001 From: Cesar Garcia <128240629+Chesars@users.noreply.github.com> Date: Mon, 22 Dec 2025 13:04:14 -0300 Subject: [PATCH 2/2] fix(docs): breadcrumb APIs only return category/docs items, ignoring links (#11616) Co-authored-by: sebastien --- .../src/client/__tests__/docsUtils.test.tsx | 148 ++++++++++++++---- .../src/client/docsUtils.tsx | 21 ++- 2 files changed, 134 insertions(+), 35 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx b/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx index 00263ad5907d..5d8b30a0cc74 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx +++ b/packages/docusaurus-plugin-content-docs/src/client/__tests__/docsUtils.test.tsx @@ -568,13 +568,28 @@ describe('useSidebarBreadcrumbs', () => { it('returns first level link', () => { const pathname = '/somePathName'; - const sidebar = [testCategory(), testLink({href: pathname})]; + const sidebar = [testCategory(), testLink({href: pathname, docId: 'doc1'})]; expect(createUseSidebarBreadcrumbsMock(sidebar)(pathname)).toEqual([ sidebar[1], ]); }); + it('returns doc links only', () => { + const pathname = '/somePathName'; + + // A link that is not a doc link should not appear in the breadcrumbs + // See https://github.com/facebook/docusaurus/pull/11616 + const nonDocLink = testLink({href: pathname}); + const docLink = testLink({href: pathname, docId: 'doc1'}); + + const sidebar = [testCategory(), nonDocLink, docLink]; + + expect(createUseSidebarBreadcrumbsMock(sidebar)(pathname)).toEqual([ + docLink, + ]); + }); + it('returns nested category', () => { const pathname = '/somePathName'; @@ -613,7 +628,7 @@ describe('useSidebarBreadcrumbs', () => { it('returns nested link', () => { const pathname = '/somePathName'; - const link = testLink({href: pathname}); + const link = testLink({href: pathname, docId: 'docNested'}); const categoryLevel3 = testCategory({ items: [testLink(), link, testLink()], @@ -657,6 +672,35 @@ describe('useSidebarBreadcrumbs', () => { createUseSidebarBreadcrumbsMock(undefined, false)('/foo'), ).toBeNull(); }); + + // Regression test for https://github.com/facebook/docusaurus/issues/11612 + it('returns the category that owns the URL, not a category with a link pointing to it', () => { + const categoryA: PropSidebarItemCategory = testCategory({ + label: 'Category A', + href: '/category-a', + items: [ + testLink({href: '/category-a/doc1', label: 'Doc 1'}), + testLink({href: '/category-a/doc2', label: 'Doc 2'}), + // This link points to Category B's generated-index + testLink({href: '/category-b', label: 'Go to Category B'}), + ], + }); + + const categoryB: PropSidebarItemCategory = testCategory({ + label: 'Category B', + href: '/category-b', + items: [ + testLink({href: '/category-b/item1', label: 'Item 1'}), + testLink({href: '/category-b/item2', label: 'Item 2'}), + ], + }); + + const sidebar: PropSidebar = [categoryA, categoryB]; + + expect(createUseSidebarBreadcrumbsMock(sidebar)('/category-b')).toEqual([ + categoryB, + ]); + }); }); describe('useCurrentSidebarCategory', () => { @@ -708,12 +752,16 @@ describe('useCurrentSidebarCategory', () => { expect(mockUseCurrentSidebarCategory('/cat2')).toEqual(category2); }); - it('works for category link item', () => { - const link = testLink({href: '/my/link/path'}); + it('works for category doc link item', () => { + const pathname = '/my/link/path'; + const nonDocLink = testLink({href: pathname}); + const docLink = testLink({href: pathname, docId: 'doc1'}); + const category: PropSidebarItemCategory = testCategory({ href: '/cat1', - items: [testLink(), testLink(), link, testCategory()], + items: [testLink(), testLink(), nonDocLink, docLink, testCategory()], }); + const sidebar: PropSidebar = [ testLink(), testLink(), @@ -724,18 +772,28 @@ describe('useCurrentSidebarCategory', () => { const mockUseCurrentSidebarCategory = createUseCurrentSidebarCategoryMock(sidebar); - expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual(category); + expect(mockUseCurrentSidebarCategory(pathname)).toEqual(category); }); it('works for nested category link item', () => { - const link = testLink({href: '/my/link/path'}); + const pathname = '/my/link/path'; + const nonDocLink = testLink({href: pathname}); + const docLink = testLink({href: pathname, docId: 'doc1'}); + const category2: PropSidebarItemCategory = testCategory({ href: '/cat2', - items: [testLink(), testLink(), link, testCategory()], + items: [ + testLink(), + testLink(), + testCategory({items: [nonDocLink]}), + nonDocLink, + docLink, + testCategory(), + ], }); const category1: PropSidebarItemCategory = testCategory({ href: '/cat1', - items: [testLink(), testLink(), category2, testCategory()], + items: [testLink(), nonDocLink, testLink(), category2, testCategory()], }); const sidebar: PropSidebar = [ testLink(), @@ -780,6 +838,38 @@ describe('useCurrentSidebarCategory', () => { `"Unexpected: cant find current sidebar in context"`, ); }); + + // Regression test for https://github.com/facebook/docusaurus/issues/11612 + it('returns the category that owns the URL, not a category with a link pointing to it', () => { + const categoryA: PropSidebarItemCategory = testCategory({ + label: 'Category A', + href: '/category-a', + items: [ + testLink({href: '/category-a/doc1', label: 'Doc 1'}), + testLink({href: '/category-a/doc2', label: 'Doc 2'}), + // This link points to Category B's generated-index + testLink({href: '/category-b', label: 'Go to Category B'}), + ], + }); + + const categoryB: PropSidebarItemCategory = testCategory({ + label: 'Category B', + href: '/category-b', + items: [ + testLink({href: '/category-b/item1', label: 'Item 1'}), + testLink({href: '/category-b/item2', label: 'Item 2'}), + ], + }); + + const sidebar: PropSidebar = [categoryA, categoryB]; + + const mockUseCurrentSidebarCategory = + createUseCurrentSidebarCategoryMock(sidebar); + + // When visiting /category-b, we should get Category B (the owner), + // not Category A (which just has a link to it) + expect(mockUseCurrentSidebarCategory('/category-b')).toEqual(categoryB); + }); }); describe('useCurrentSidebarSiblings', () => { @@ -805,10 +895,10 @@ describe('useCurrentSidebarSiblings', () => { testCategory(), ]; - const mockUseCurrentSidebarCategory = + const mockUseCurrentSidebarSiblings = createUseCurrentSidebarSiblingsMock(sidebar); - expect(mockUseCurrentSidebarCategory('/cat')).toEqual(category.items); + expect(mockUseCurrentSidebarSiblings('/cat')).toEqual(category.items); }); it('works for sidebar root', () => { @@ -823,10 +913,10 @@ describe('useCurrentSidebarSiblings', () => { testCategory(), ]; - const mockUseCurrentSidebarCategory = + const mockUseCurrentSidebarSiblings = createUseCurrentSidebarSiblingsMock(sidebar); - expect(mockUseCurrentSidebarCategory('/rootLink')).toEqual(sidebar); + expect(mockUseCurrentSidebarSiblings('/rootLink')).toEqual(sidebar); }); it('works for nested sidebar category', () => { @@ -852,10 +942,13 @@ describe('useCurrentSidebarSiblings', () => { }); it('works for category link item', () => { - const link = testLink({href: '/my/link/path'}); + const pathname = '/my/link/path'; + const nonDocLink = testLink({href: pathname}); + const docLink = testLink({href: pathname, docId: 'doc1'}); + const category: PropSidebarItemCategory = testCategory({ href: '/cat1', - items: [testLink(), testLink(), link, testCategory()], + items: [testLink(), testLink(), nonDocLink, docLink, testCategory()], }); const sidebar: PropSidebar = [ testLink(), @@ -864,23 +957,24 @@ describe('useCurrentSidebarSiblings', () => { testCategory(), ]; - const mockUseCurrentSidebarCategory = + const mockUseCurrentSidebarSiblings = createUseCurrentSidebarSiblingsMock(sidebar); - expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual( - category.items, - ); + expect(mockUseCurrentSidebarSiblings(pathname)).toEqual(category.items); }); it('works for nested category link item', () => { - const link = testLink({href: '/my/link/path'}); + const pathname = '/my/link/path'; + const nonDocLink = testLink({href: pathname}); + const docLink = testLink({href: pathname, docId: 'doc1'}); + const category2: PropSidebarItemCategory = testCategory({ href: '/cat2', - items: [testLink(), testLink(), link, testCategory()], + items: [testLink(), testLink(), nonDocLink, testCategory()], }); const category1: PropSidebarItemCategory = testCategory({ href: '/cat1', - items: [testLink(), testLink(), category2, testCategory()], + items: [testLink(), testLink(), category2, docLink, testCategory()], }); const sidebar: PropSidebar = [ testLink(), @@ -889,18 +983,16 @@ describe('useCurrentSidebarSiblings', () => { testCategory(), ]; - const mockUseCurrentSidebarCategory = + const mockUseCurrentSidebarSiblings = createUseCurrentSidebarSiblingsMock(sidebar); - expect(mockUseCurrentSidebarCategory('/my/link/path')).toEqual( - category2.items, - ); + expect(mockUseCurrentSidebarSiblings(pathname)).toEqual(category1.items); }); it('throws when sidebar is missing', () => { - const mockUseCurrentSidebarCategory = createUseCurrentSidebarSiblingsMock(); + const mockUseCurrentSidebarSiblings = createUseCurrentSidebarSiblingsMock(); expect(() => - mockUseCurrentSidebarCategory('/cat'), + mockUseCurrentSidebarSiblings('/cat'), ).toThrowErrorMatchingInlineSnapshot( `"Unexpected: cant find current sidebar in context"`, ); diff --git a/packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx b/packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx index aa00df851034..474a58895ade 100644 --- a/packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx +++ b/packages/docusaurus-plugin-content-docs/src/client/docsUtils.tsx @@ -234,15 +234,22 @@ function getSidebarBreadcrumbs({ }): PropSidebarBreadcrumbsItem[] { const breadcrumbs: PropSidebarBreadcrumbsItem[] = []; - function extract(items: PropSidebarItem[]) { + function extract(items: PropSidebarItem[]): boolean { for (const item of items) { - if ( - (item.type === 'category' && - (isSamePath(item.href, pathname) || extract(item.items))) || - (item.type === 'link' && isSamePath(item.href, pathname)) + // Extract category item + if (item.type === 'category') { + if (isSamePath(item.href, pathname) || extract(item.items)) { + breadcrumbs.unshift(item); + return true; + } + } + // Extract doc item + else if ( + item.type === 'link' && + item.docId && + isSamePath(item.href, pathname) ) { - const filtered = onlyCategories && item.type !== 'category'; - if (!filtered) { + if (!onlyCategories) { breadcrumbs.unshift(item); } return true;