diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/docs.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/docs.test.ts.snap index c7e3163103..b66f4a4929 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/docs.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/docs.test.ts.snap @@ -6,8 +6,8 @@ Object { Object { "id": "doc with space", "next": Object { - "permalink": "/docs/headingAsTitle", - "title": "My heading as title", + "permalink": "/docs/foo/bar", + "title": "Bar", }, "prev": undefined, }, @@ -19,8 +19,8 @@ Object { Object { "id": "foo/baz", "next": Object { - "permalink": "/docs/absoluteSlug", - "title": "absoluteSlug", + "permalink": "/docs/headingAsTitle", + "title": "My heading as title", }, "prev": Object { "permalink": "/docs/foo/bar", @@ -34,8 +34,8 @@ Object { "title": "Hello sidebar_label", }, "prev": Object { - "permalink": "/docs/doc with space", - "title": "Hoo hoo, if this path tricks you...", + "permalink": "/docs/foo/bazSlug.html", + "title": "baz pagination_label", }, }, Object { @@ -122,8 +122,8 @@ Object { "title": "relativeSlug", }, "prev": Object { - "permalink": "/docs/foo/bazSlug.html", - "title": "baz pagination_label", + "permalink": "/docs/rootTryToEscapeSlug", + "title": "rootTryToEscapeSlug", }, }, Object { @@ -163,6 +163,23 @@ Object { "id": "doc with space", "type": "doc", }, + Object { + "collapsed": false, + "collapsible": true, + "items": Array [ + Object { + "id": "foo/bar", + "type": "doc", + }, + Object { + "id": "foo/baz", + "type": "doc", + }, + ], + "label": "foo", + "link": undefined, + "type": "category", + }, Object { "id": "headingAsTitle", "type": "doc", @@ -196,23 +213,6 @@ Object { "id": "rootTryToEscapeSlug", "type": "doc", }, - Object { - "collapsed": false, - "collapsible": true, - "items": Array [ - Object { - "id": "foo/bar", - "type": "doc", - }, - Object { - "id": "foo/baz", - "type": "doc", - }, - ], - "label": "foo", - "link": undefined, - "type": "category", - }, Object { "collapsed": false, "collapsible": true, diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts index dfda57ad80..61f7ba4ab4 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/generator.test.ts @@ -142,7 +142,7 @@ describe('DefaultSidebarItemsGenerator', () => { id: 'intro', source: '@site/docs/intro.md', sourceDirName: '.', - sidebarPosition: 1, + sidebarPosition: 0, frontMatter: {}, }, { @@ -183,7 +183,7 @@ describe('DefaultSidebarItemsGenerator', () => { id: 'guide1', source: '@site/docs/02-Guides/guide1.md', sourceDirName: '02-Guides', - sidebarPosition: 1, + sidebarPosition: 0, frontMatter: { sidebar_class_name: 'foo', }, @@ -406,7 +406,7 @@ describe('DefaultSidebarItemsGenerator', () => { }, { id: 'parent/doc2', - source: '@site/docs/Category/index.md', + source: '@site/docs/Category/doc2.md', sourceDirName: 'Category', frontMatter: {}, }, @@ -451,11 +451,12 @@ describe('DefaultSidebarItemsGenerator', () => { }, items: [ { - id: 'parent/doc1', + id: 'parent/doc2', type: 'doc', }, + // doc1 is below doc2, because its file name is index.md { - id: 'parent/doc2', + id: 'parent/doc1', type: 'doc', }, ], @@ -469,11 +470,11 @@ describe('DefaultSidebarItemsGenerator', () => { type: 'doc', }, { - id: 'parent/doc5', + id: 'parent/doc6', type: 'doc', }, { - id: 'parent/doc6', + id: 'parent/doc5', type: 'doc', }, ], @@ -508,7 +509,7 @@ describe('DefaultSidebarItemsGenerator', () => { id: 'intro', source: '@site/docs/intro.md', sourceDirName: '.', - sidebarPosition: 1, + sidebarPosition: 0, frontMatter: {}, }, { diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts index a0954cef8c..811e092309 100644 --- a/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts +++ b/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts @@ -20,8 +20,6 @@ import path from 'path'; import {createDocsByIdIndex, toCategoryIndexMatcherParam} from '../docs'; const BreadcrumbSeparator = '/'; -// To avoid possible name clashes with a folder of the same name as the ID -const docIdPrefix = '$doc$/'; // Just an alias to the make code more explicit function getLocalDocId(docId: string): string { @@ -31,16 +29,20 @@ function getLocalDocId(docId: string): string { export const CategoryMetadataFilenameBase = '_category_'; export const CategoryMetadataFilenamePattern = '_category_.{json,yml,yaml}'; -type WithPosition = T & {position?: number}; +type WithPosition = T & { + position?: number; + /** The source is the file/folder name */ + source?: string; +}; /** * A representation of the fs structure. For each object entry: * If it's a folder, the key is the directory name, and value is the directory - * content; If it's a doc file, the key is the doc id prefixed with '$doc$/', - * and value is null + * content; If it's a doc file, the key is the doc's source file name, and value + * is the doc ID */ type Dir = { - [item: string]: Dir | null; + [item: string]: Dir | string; }; // Comment for this feature: https://github.com/facebook/docusaurus/issues/3464#issuecomment-818670449 @@ -108,14 +110,16 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ const treeRoot: Dir = {}; docs.forEach((doc) => { const breadcrumb = getRelativeBreadcrumb(doc); - let currentDir = treeRoot; // We walk down the file's path to generate the fs structure + // We walk down the file's path to generate the fs structure + let currentDir = treeRoot; breadcrumb.forEach((dir) => { if (typeof currentDir[dir] === 'undefined') { currentDir[dir] = {}; // Create new folder. } - currentDir = currentDir[dir]!; // Go into the subdirectory. + currentDir = currentDir[dir] as Dir; // Go into the subdirectory. }); - currentDir[`${docIdPrefix}${doc.id}`] = null; // We've walked through the file path. Register the file in this directory. + // We've walked through the path. Register the file in this directory. + currentDir[path.basename(doc.source)] = doc.id; }); return treeRoot; } @@ -126,8 +130,12 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ */ function generateSidebar( fsModel: Dir, - ): Promise[]> { - function createDocItem(id: string): WithPosition { + ): WithPosition[] { + function createDocItem( + id: string, + fullPath: string, + fileName: string, + ): WithPosition { const { sidebarPosition: position, frontMatter: {sidebar_label: label, sidebar_class_name: className}, @@ -136,25 +144,24 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ type: 'doc', id, position, + source: fileName, // We don't want these fields to magically appear in the generated // sidebar ...(label !== undefined && {label}), ...(className !== undefined && {className}), }; } - async function createCategoryItem( + function createCategoryItem( dir: Dir, fullPath: string, folderName: string, - ): Promise> { + ): WithPosition { const categoryMetadata = categoriesMetadata[posixPath(path.join(autogenDir, fullPath))]; const className = categoryMetadata?.className; const {filename, numberPrefix} = numberPrefixParser(folderName); - const allItems = await Promise.all( - Object.entries(dir).map(([key, content]) => - dirToItem(content, key, `${fullPath}/${key}`), - ), + const allItems = Object.entries(dir).map(([key, content]) => + dirToItem(content, key, `${fullPath}/${key}`), ); // Try to match a doc inside the category folder, @@ -211,24 +218,23 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ collapsible: categoryMetadata?.collapsible, collapsed: categoryMetadata?.collapsed, position: categoryMetadata?.position ?? numberPrefix, + source: folderName, ...(className !== undefined && {className}), items, ...(link && {link}), }; } - async function dirToItem( - dir: Dir | null, // The directory item to be transformed. - itemKey: string, // For docs, it's the doc ID; for categories, it's used to generate the next `relativePath`. + function dirToItem( + dir: Dir | string, // The directory item to be transformed. + itemKey: string, // File/folder name; for categories, it's used to generate the next `relativePath`. fullPath: string, // `dir`'s full path relative to the autogen dir. - ): Promise> { - return dir + ): WithPosition { + return typeof dir === 'object' ? createCategoryItem(dir, fullPath, itemKey) - : createDocItem(itemKey.substring(docIdPrefix.length)); + : createDocItem(dir, fullPath, itemKey); } - return Promise.all( - Object.entries(fsModel).map(([key, content]) => - dirToItem(content, key, key), - ), + return Object.entries(fsModel).map(([key, content]) => + dirToItem(content, key, key), ); } @@ -248,16 +254,16 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({ } return item; }); - const sortedSidebarItems = _.sortBy( - processedSidebarItems, - (item) => item.position, - ); - return sortedSidebarItems.map(({position, ...item}) => item); + const sortedSidebarItems = _.sortBy(processedSidebarItems, [ + 'position', + 'source', + ]); + return sortedSidebarItems.map(({position, source, ...item}) => item); } // TODO: the whole code is designed for pipeline operator const docs = getAutogenDocs(); const fsModel = treeify(docs); - const sidebarWithPosition = await generateSidebar(fsModel); + const sidebarWithPosition = generateSidebar(fsModel); const sortedSidebar = sortItems(sidebarWithPosition); return sortedSidebar; };