diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap index 3072419e03ef..244c608c1c0b 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/index.test.ts.snap @@ -1243,214 +1243,228 @@ exports[`simple website content: global data 1`] = ` exports[`simple website content: route config 1`] = ` [ { - "component": "@theme/DocTagsListPage", - "exact": true, - "modules": { - "tags": "~docs/tags-list-current-prop-15a.json", - }, - "path": "/docs/tags", - }, - { - "component": "@theme/DocTagDocListPage", - "exact": true, - "modules": { - "tag": "~docs/tag-docs-tags-tag-1-b3f.json", - }, - "path": "/docs/tags/tag-1", - }, - { - "component": "@theme/DocTagDocListPage", - "exact": true, - "modules": { - "tag": "~docs/tag-docs-tags-tag-3-ab5.json", - }, - "path": "/docs/tags/tag-3", - }, - { - "component": "@theme/DocTagDocListPage", - "exact": true, - "modules": { - "tag": "~docs/tag-docs-tags-tag-2-custom-permalink-825.json", - }, - "path": "/docs/tags/tag2-custom-permalink", - }, - { - "component": "@theme/DocPage", + "component": "@theme/DocsRoot", "exact": false, - "modules": { - "versionMetadata": "~docs/version-current-metadata-prop-751.json", - }, "path": "/docs", - "priority": -1, "routes": [ { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/hello.md", - }, - "path": "/docs/", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/slugs/absoluteSlug.md", - }, - "path": "/docs/absoluteSlug", - }, - { - "component": "@theme/DocCategoryGeneratedIndexPage", - "exact": true, - "modules": { - "categoryGeneratedIndex": "~docs/category-docs-docs-category-slugs-0fe.json", - }, - "path": "/docs/category/slugs", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/customLastUpdate.md", - }, - "path": "/docs/customLastUpdate", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/doc with space.md", - }, - "path": "/docs/doc with space", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/doc-draft.md", - }, - "path": "/docs/doc-draft", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/foo/bar.md", - }, - "path": "/docs/foo/bar", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/foo/baz.md", - }, - "path": "/docs/foo/bazSlug.html", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/headingAsTitle.md", - }, - "path": "/docs/headingAsTitle", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/rootResolvedSlug.md", - }, - "path": "/docs/hey/rootResolvedSlug", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/ipsum.md", - }, - "path": "/docs/ipsum", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/lastUpdateAuthorOnly.md", - }, - "path": "/docs/lastUpdateAuthorOnly", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/lastUpdateDateOnly.md", - }, - "path": "/docs/lastUpdateDateOnly", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/lorem.md", - }, - "path": "/docs/lorem", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/rootAbsoluteSlug.md", - }, - "path": "/docs/rootAbsoluteSlug", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/rootRelativeSlug.md", - }, - "path": "/docs/rootRelativeSlug", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/rootTryToEscapeSlug.md", - }, - "path": "/docs/rootTryToEscapeSlug", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/slugs/resolvedSlug.md", - }, - "path": "/docs/slugs/hey/resolvedSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/slugs/relativeSlug.md", - }, - "path": "/docs/slugs/relativeSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, + "component": "@theme/DocVersionRoot", + "exact": false, "modules": { - "content": "@site/docs/slugs/tryToEscapeSlug.md", + "version": "~docs/version-current-metadata-prop-751.json", }, - "path": "/docs/tryToEscapeSlug", + "path": "/docs", + "priority": -1, + "routes": [ + { + "component": "@theme/DocTagsListPage", + "exact": true, + "modules": { + "tags": "~docs/tags-list-current-prop-15a.json", + }, + "path": "/docs/tags", + }, + { + "component": "@theme/DocTagDocListPage", + "exact": true, + "modules": { + "tag": "~docs/tag-docs-tags-tag-1-b3f.json", + }, + "path": "/docs/tags/tag-1", + }, + { + "component": "@theme/DocTagDocListPage", + "exact": true, + "modules": { + "tag": "~docs/tag-docs-tags-tag-3-ab5.json", + }, + "path": "/docs/tags/tag-3", + }, + { + "component": "@theme/DocTagDocListPage", + "exact": true, + "modules": { + "tag": "~docs/tag-docs-tags-tag-2-custom-permalink-825.json", + }, + "path": "/docs/tags/tag2-custom-permalink", + }, + { + "component": "@theme/DocRoot", + "exact": false, + "path": "/docs", + "routes": [ + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/hello.md", + }, + "path": "/docs/", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/slugs/absoluteSlug.md", + }, + "path": "/docs/absoluteSlug", + }, + { + "component": "@theme/DocCategoryGeneratedIndexPage", + "exact": true, + "modules": { + "categoryGeneratedIndex": "~docs/category-docs-docs-category-slugs-0fe.json", + }, + "path": "/docs/category/slugs", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/customLastUpdate.md", + }, + "path": "/docs/customLastUpdate", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/doc with space.md", + }, + "path": "/docs/doc with space", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/doc-draft.md", + }, + "path": "/docs/doc-draft", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/foo/bar.md", + }, + "path": "/docs/foo/bar", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/foo/baz.md", + }, + "path": "/docs/foo/bazSlug.html", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/headingAsTitle.md", + }, + "path": "/docs/headingAsTitle", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/rootResolvedSlug.md", + }, + "path": "/docs/hey/rootResolvedSlug", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/ipsum.md", + }, + "path": "/docs/ipsum", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/lastUpdateAuthorOnly.md", + }, + "path": "/docs/lastUpdateAuthorOnly", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/lastUpdateDateOnly.md", + }, + "path": "/docs/lastUpdateDateOnly", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/lorem.md", + }, + "path": "/docs/lorem", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/rootAbsoluteSlug.md", + }, + "path": "/docs/rootAbsoluteSlug", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/rootRelativeSlug.md", + }, + "path": "/docs/rootRelativeSlug", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/rootTryToEscapeSlug.md", + }, + "path": "/docs/rootTryToEscapeSlug", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/slugs/resolvedSlug.md", + }, + "path": "/docs/slugs/hey/resolvedSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/slugs/relativeSlug.md", + }, + "path": "/docs/slugs/relativeSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/slugs/tryToEscapeSlug.md", + }, + "path": "/docs/tryToEscapeSlug", + }, + ], + }, + ], }, ], }, @@ -2724,42 +2738,63 @@ exports[`versioned website (community) content: global data 1`] = ` exports[`versioned website (community) content: route config 1`] = ` [ { - "component": "@theme/DocPage", + "component": "@theme/DocsRoot", "exact": false, - "modules": { - "versionMetadata": "~docs/version-current-metadata-prop-751.json", - }, - "path": "/community/next", - "priority": undefined, + "path": "/community", "routes": [ { - "component": "@theme/DocItem", - "exact": true, + "component": "@theme/DocVersionRoot", + "exact": false, "modules": { - "content": "@site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md", + "version": "~docs/version-current-metadata-prop-751.json", }, - "path": "/community/next/team", - "sidebar": "community", + "path": "/community/next", + "priority": undefined, + "routes": [ + { + "component": "@theme/DocRoot", + "exact": false, + "path": "/community/next", + "routes": [ + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/i18n/en/docusaurus-plugin-content-docs-community/current/team.md", + }, + "path": "/community/next/team", + "sidebar": "community", + }, + ], + }, + ], }, - ], - }, - { - "component": "@theme/DocPage", - "exact": false, - "modules": { - "versionMetadata": "~docs/version-1-0-0-metadata-prop-608.json", - }, - "path": "/community", - "priority": -1, - "routes": [ { - "component": "@theme/DocItem", - "exact": true, + "component": "@theme/DocVersionRoot", + "exact": false, "modules": { - "content": "@site/community_versioned_docs/version-1.0.0/team.md", + "version": "~docs/version-1-0-0-metadata-prop-608.json", }, - "path": "/community/team", - "sidebar": "version-1.0.0/community", + "path": "/community", + "priority": -1, + "routes": [ + { + "component": "@theme/DocRoot", + "exact": false, + "path": "/community", + "routes": [ + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/community_versioned_docs/version-1.0.0/team.md", + }, + "path": "/community/team", + "sidebar": "version-1.0.0/community", + }, + ], + }, + ], }, ], }, @@ -3931,238 +3966,273 @@ exports[`versioned website content: global data 1`] = ` exports[`versioned website content: route config 1`] = ` [ { - "component": "@theme/DocTagsListPage", - "exact": true, - "modules": { - "tags": "~docs/tags-list-current-prop-15a.json", - }, - "path": "/docs/next/tags", - }, - { - "component": "@theme/DocTagDocListPage", - "exact": true, - "modules": { - "tag": "~docs/tag-docs-next-tags-bar-tag-1-a8f.json", - }, - "path": "/docs/next/tags/bar-tag-1", - }, - { - "component": "@theme/DocTagDocListPage", - "exact": true, - "modules": { - "tag": "~docs/tag-docs-next-tags-bar-tag-2-216.json", - }, - "path": "/docs/next/tags/bar-tag-2", - }, - { - "component": "@theme/DocTagDocListPage", - "exact": true, - "modules": { - "tag": "~docs/tag-docs-next-tags-bar-tag-3-permalink-94a.json", - }, - "path": "/docs/next/tags/barTag-3-permalink", - }, - { - "component": "@theme/DocPage", - "exact": false, - "modules": { - "versionMetadata": "~docs/version-1-0-0-metadata-prop-608.json", - }, - "path": "/docs/1.0.0", - "priority": undefined, - "routes": [ - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md", - }, - "path": "/docs/1.0.0/", - "sidebar": "version-1.0.0/docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/versioned_docs/version-1.0.0/foo/bar.md", - }, - "path": "/docs/1.0.0/foo/barSlug", - "sidebar": "version-1.0.0/docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/versioned_docs/version-1.0.0/foo/baz.md", - }, - "path": "/docs/1.0.0/foo/baz", - "sidebar": "version-1.0.0/docs", - }, - ], - }, - { - "component": "@theme/DocPage", + "component": "@theme/DocsRoot", "exact": false, - "modules": { - "versionMetadata": "~docs/version-current-metadata-prop-751.json", - }, - "path": "/docs/next", - "priority": undefined, - "routes": [ - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/hello.md", - }, - "path": "/docs/next/", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/slugs/absoluteSlug.md", - }, - "path": "/docs/next/absoluteSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/foo/bar.md", - }, - "path": "/docs/next/foo/barSlug", - "sidebar": "docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/slugs/resolvedSlug.md", - }, - "path": "/docs/next/slugs/hey/resolvedSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/slugs/relativeSlug.md", - }, - "path": "/docs/next/slugs/relativeSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/docs/slugs/tryToEscapeSlug.md", - }, - "path": "/docs/next/tryToEscapeSlug", - }, - ], - }, - { - "component": "@theme/DocPage", - "exact": false, - "modules": { - "versionMetadata": "~docs/version-with-slugs-metadata-prop-2bf.json", - }, - "path": "/docs/withSlugs", - "priority": undefined, + "path": "/docs", "routes": [ { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md", - }, - "path": "/docs/withSlugs/absoluteSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/versioned_docs/version-withSlugs/rootResolvedSlug.md", - }, - "path": "/docs/withSlugs/hey/rootResolvedSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md", - }, - "path": "/docs/withSlugs/rootAbsoluteSlug", - "sidebar": "version-1.0.1/docs", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/versioned_docs/version-withSlugs/rootRelativeSlug.md", - }, - "path": "/docs/withSlugs/rootRelativeSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md", - }, - "path": "/docs/withSlugs/rootTryToEscapeSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, - "modules": { - "content": "@site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md", - }, - "path": "/docs/withSlugs/slugs/hey/resolvedSlug", - }, - { - "component": "@theme/DocItem", - "exact": true, + "component": "@theme/DocVersionRoot", + "exact": false, "modules": { - "content": "@site/versioned_docs/version-withSlugs/slugs/relativeSlug.md", + "version": "~docs/version-1-0-0-metadata-prop-608.json", }, - "path": "/docs/withSlugs/slugs/relativeSlug", + "path": "/docs/1.0.0", + "priority": undefined, + "routes": [ + { + "component": "@theme/DocRoot", + "exact": false, + "path": "/docs/1.0.0", + "routes": [ + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md", + }, + "path": "/docs/1.0.0/", + "sidebar": "version-1.0.0/docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-1.0.0/foo/bar.md", + }, + "path": "/docs/1.0.0/foo/barSlug", + "sidebar": "version-1.0.0/docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-1.0.0/foo/baz.md", + }, + "path": "/docs/1.0.0/foo/baz", + "sidebar": "version-1.0.0/docs", + }, + ], + }, + ], }, { - "component": "@theme/DocItem", - "exact": true, + "component": "@theme/DocVersionRoot", + "exact": false, "modules": { - "content": "@site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md", + "version": "~docs/version-current-metadata-prop-751.json", }, - "path": "/docs/withSlugs/tryToEscapeSlug", + "path": "/docs/next", + "priority": undefined, + "routes": [ + { + "component": "@theme/DocTagsListPage", + "exact": true, + "modules": { + "tags": "~docs/tags-list-current-prop-15a.json", + }, + "path": "/docs/next/tags", + }, + { + "component": "@theme/DocTagDocListPage", + "exact": true, + "modules": { + "tag": "~docs/tag-docs-next-tags-bar-tag-1-a8f.json", + }, + "path": "/docs/next/tags/bar-tag-1", + }, + { + "component": "@theme/DocTagDocListPage", + "exact": true, + "modules": { + "tag": "~docs/tag-docs-next-tags-bar-tag-2-216.json", + }, + "path": "/docs/next/tags/bar-tag-2", + }, + { + "component": "@theme/DocTagDocListPage", + "exact": true, + "modules": { + "tag": "~docs/tag-docs-next-tags-bar-tag-3-permalink-94a.json", + }, + "path": "/docs/next/tags/barTag-3-permalink", + }, + { + "component": "@theme/DocRoot", + "exact": false, + "path": "/docs/next", + "routes": [ + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/hello.md", + }, + "path": "/docs/next/", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/slugs/absoluteSlug.md", + }, + "path": "/docs/next/absoluteSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/foo/bar.md", + }, + "path": "/docs/next/foo/barSlug", + "sidebar": "docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/slugs/resolvedSlug.md", + }, + "path": "/docs/next/slugs/hey/resolvedSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/slugs/relativeSlug.md", + }, + "path": "/docs/next/slugs/relativeSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/slugs/tryToEscapeSlug.md", + }, + "path": "/docs/next/tryToEscapeSlug", + }, + ], + }, + ], }, - ], - }, - { - "component": "@theme/DocPage", - "exact": false, - "modules": { - "versionMetadata": "~docs/version-1-0-1-metadata-prop-e87.json", - }, - "path": "/docs", - "priority": -1, - "routes": [ { - "component": "@theme/DocItem", - "exact": true, + "component": "@theme/DocVersionRoot", + "exact": false, "modules": { - "content": "@site/versioned_docs/version-1.0.1/hello.md", + "version": "~docs/version-with-slugs-metadata-prop-2bf.json", }, - "path": "/docs/", - "sidebar": "VersionedSideBarNameDoesNotMatter/docs", + "path": "/docs/withSlugs", + "priority": undefined, + "routes": [ + { + "component": "@theme/DocRoot", + "exact": false, + "path": "/docs/withSlugs", + "routes": [ + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-withSlugs/slugs/absoluteSlug.md", + }, + "path": "/docs/withSlugs/absoluteSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-withSlugs/rootResolvedSlug.md", + }, + "path": "/docs/withSlugs/hey/rootResolvedSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-withSlugs/rootAbsoluteSlug.md", + }, + "path": "/docs/withSlugs/rootAbsoluteSlug", + "sidebar": "version-1.0.1/docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-withSlugs/rootRelativeSlug.md", + }, + "path": "/docs/withSlugs/rootRelativeSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-withSlugs/rootTryToEscapeSlug.md", + }, + "path": "/docs/withSlugs/rootTryToEscapeSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-withSlugs/slugs/resolvedSlug.md", + }, + "path": "/docs/withSlugs/slugs/hey/resolvedSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-withSlugs/slugs/relativeSlug.md", + }, + "path": "/docs/withSlugs/slugs/relativeSlug", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-withSlugs/slugs/tryToEscapeSlug.md", + }, + "path": "/docs/withSlugs/tryToEscapeSlug", + }, + ], + }, + ], }, { - "component": "@theme/DocItem", - "exact": true, + "component": "@theme/DocVersionRoot", + "exact": false, "modules": { - "content": "@site/versioned_docs/version-1.0.1/foo/bar.md", + "version": "~docs/version-1-0-1-metadata-prop-e87.json", }, - "path": "/docs/foo/bar", - "sidebar": "VersionedSideBarNameDoesNotMatter/docs", + "path": "/docs", + "priority": -1, + "routes": [ + { + "component": "@theme/DocRoot", + "exact": false, + "path": "/docs", + "routes": [ + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-1.0.1/hello.md", + }, + "path": "/docs/", + "sidebar": "VersionedSideBarNameDoesNotMatter/docs", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/versioned_docs/version-1.0.1/foo/bar.md", + }, + "path": "/docs/foo/bar", + "sidebar": "VersionedSideBarNameDoesNotMatter/docs", + }, + ], + }, + ], }, ], }, diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts index 0cfa5aa02637..47535f746ce0 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/options.test.ts @@ -49,7 +49,9 @@ describe('normalizeDocsPluginOptions', () => { sidebarPath: 'my-sidebar', // Path to sidebar configuration for showing a list of markdown pages. sidebarItemsGenerator: DefaultSidebarItemsGenerator, numberPrefixParser: DefaultNumberPrefixParser, - docLayoutComponent: '@theme/DocPage', + docsRootComponent: '@theme/DocsRoot', + docVersionRootComponent: '@theme/DocVersionRoot', + docRootComponent: '@theme/DocRoot', docItemComponent: '@theme/DocItem', docTagDocListComponent: '@theme/DocTagDocListPage', docTagsListComponent: '@theme/DocTagsListPage', diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index 0b03d48a0542..69bb8077aa7d 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -27,22 +27,18 @@ import { addDocNavigation, type DocEnv, } from './docs'; -import {readVersionsMetadata} from './versions'; +import {readVersionsMetadata, toFullVersion} from './versions'; import {cliDocsVersionCommand} from './cli'; import {VERSIONS_JSON_FILE} from './constants'; import {toGlobalDataVersion} from './globalData'; -import {toTagDocListProp} from './props'; -import {getCategoryGeneratedIndexMetadataList} from './categoryGeneratedIndex'; import { translateLoadedContent, getLoadedContentTranslationFiles, } from './translations'; -import {getVersionTags} from './tags'; -import {createVersionRoutes} from './routes'; +import {createAllRoutes} from './routes'; import {createSidebarsUtils} from './sidebars/utils'; import type { - PropTagsListPage, PluginOptions, DocMetadataBase, VersionMetadata, @@ -55,7 +51,6 @@ import type { SourceToPermalink, DocFile, DocsMarkdownOption, - VersionTag, FullVersion, } from './types'; import type {RuleSetRule} from 'webpack'; @@ -209,102 +204,20 @@ export default async function pluginContentDocs( }, async contentLoaded({content, actions}) { - const {loadedVersions} = content; - const { - docLayoutComponent, - docItemComponent, - docCategoryGeneratedIndexComponent, - breadcrumbs, - } = options; - const {addRoute, createData, setGlobalData} = actions; - const versions: FullVersion[] = loadedVersions.map((version) => { - const sidebarsUtils = createSidebarsUtils(version.sidebars); - return { - ...version, - sidebarsUtils, - categoryGeneratedIndices: getCategoryGeneratedIndexMetadataList({ - docs: version.docs, - sidebarsUtils, - }), - }; + const versions: FullVersion[] = content.loadedVersions.map(toFullVersion); + + await createAllRoutes({ + baseUrl, + versions, + options, + actions, + aliasedSource, }); - async function createVersionTagsRoutes(version: FullVersion) { - const versionTags = getVersionTags(version.docs); - - // TODO tags should be a sub route of the version route - async function createTagsListPage() { - const tagsProp: PropTagsListPage['tags'] = Object.values( - versionTags, - ).map((tagValue) => ({ - label: tagValue.label, - permalink: tagValue.permalink, - count: tagValue.docIds.length, - })); - - // Only create /tags page if there are tags. - if (tagsProp.length > 0) { - const tagsPropPath = await createData( - `${docuHash(`tags-list-${version.versionName}-prop`)}.json`, - JSON.stringify(tagsProp, null, 2), - ); - addRoute({ - path: version.tagsPath, - exact: true, - component: options.docTagsListComponent, - modules: { - tags: aliasedSource(tagsPropPath), - }, - }); - } - } - - // TODO tags should be a sub route of the version route - async function createTagDocListPage(tag: VersionTag) { - const tagProps = toTagDocListProp({ - allTagsPath: version.tagsPath, - tag, - docs: version.docs, - }); - const tagPropPath = await createData( - `${docuHash(`tag-${tag.permalink}`)}.json`, - JSON.stringify(tagProps, null, 2), - ); - addRoute({ - path: tag.permalink, - component: options.docTagDocListComponent, - exact: true, - modules: { - tag: aliasedSource(tagPropPath), - }, - }); - } - - await createTagsListPage(); - await Promise.all(Object.values(versionTags).map(createTagDocListPage)); - } - - await Promise.all( - versions.map((version) => - createVersionRoutes({ - version, - docItemComponent, - docLayoutComponent, - docCategoryGeneratedIndexComponent, - pluginId, - aliasedSource, - actions, - }), - ), - ); - - // TODO tags should be a sub route of the version route - await Promise.all(versions.map(createVersionTagsRoutes)); - - setGlobalData({ + actions.setGlobalData({ path: normalizeUrl([baseUrl, options.routeBasePath]), versions: versions.map(toGlobalDataVersion), - breadcrumbs, + breadcrumbs: options.breadcrumbs, }); }, diff --git a/packages/docusaurus-plugin-content-docs/src/options.ts b/packages/docusaurus-plugin-content-docs/src/options.ts index 7ef8c417a65d..01d97d7684ec 100644 --- a/packages/docusaurus-plugin-content-docs/src/options.ts +++ b/packages/docusaurus-plugin-content-docs/src/options.ts @@ -30,7 +30,9 @@ export const DEFAULT_OPTIONS: Omit = { exclude: GlobExcludeDefault, sidebarItemsGenerator: DefaultSidebarItemsGenerator, numberPrefixParser: DefaultNumberPrefixParser, - docLayoutComponent: '@theme/DocPage', + docsRootComponent: '@theme/DocsRoot', + docVersionRootComponent: '@theme/DocVersionRoot', + docRootComponent: '@theme/DocRoot', docItemComponent: '@theme/DocItem', docTagDocListComponent: '@theme/DocTagDocListPage', docTagsListComponent: '@theme/DocTagsListPage', @@ -104,7 +106,11 @@ const OptionsSchema = Joi.object({ }), ) .default(() => DEFAULT_OPTIONS.numberPrefixParser), - docLayoutComponent: Joi.string().default(DEFAULT_OPTIONS.docLayoutComponent), + docsRootComponent: Joi.string().default(DEFAULT_OPTIONS.docsRootComponent), + docVersionRootComponent: Joi.string().default( + DEFAULT_OPTIONS.docVersionRootComponent, + ), + docRootComponent: Joi.string().default(DEFAULT_OPTIONS.docRootComponent), docItemComponent: Joi.string().default(DEFAULT_OPTIONS.docItemComponent), docTagsListComponent: Joi.string().default( DEFAULT_OPTIONS.docTagsListComponent, diff --git a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts index 7bd8bbf3cbf0..3ae1e0fb7acc 100644 --- a/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts +++ b/packages/docusaurus-plugin-content-docs/src/plugin-content-docs.d.ts @@ -198,10 +198,24 @@ declare module '@docusaurus/plugin-content-docs' { */ exclude: string[]; /** - * Root layout component of each doc page. Provides the version data - * context, and is not unmounted when switching docs. + * Parent component of all the docs plugin pages (including all versions). + * Stays mounted when navigation between docs pages and versions. */ - docLayoutComponent: string; + docsRootComponent: string; + /** + * Parent component of all docs pages of an individual version: + * - docs pages with sidebars + * - tags pages + * Stays mounted when navigation between pages of that specific version. + */ + docVersionRootComponent: string; + /** + * Parent component of all docs pages with sidebars: + * - regular docs pages + * - category generated index pages + * Stays mounted when navigation between such pages. + */ + docRootComponent: string; /** Main doc container, with TOC, pagination, etc. */ docItemComponent: string; /** Root component of the "docs containing tag X" page. */ @@ -610,14 +624,32 @@ declare module '@theme/DocBreadcrumbs' { export default function DocBreadcrumbs(): JSX.Element; } -declare module '@theme/DocPage' { +declare module '@theme/DocsRoot' { + import type {RouteConfigComponentProps} from 'react-router-config'; + import type {Required} from 'utility-types'; + + export interface Props extends Required {} + + export default function DocsRoot(props: Props): JSX.Element; +} + +declare module '@theme/DocVersionRoot' { import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs'; import type {RouteConfigComponentProps} from 'react-router-config'; import type {Required} from 'utility-types'; export interface Props extends Required { - readonly versionMetadata: PropVersionMetadata; + readonly version: PropVersionMetadata; } - export default function DocPage(props: Props): JSX.Element; + export default function DocVersionRoot(props: Props): JSX.Element; +} + +declare module '@theme/DocRoot' { + import type {RouteConfigComponentProps} from 'react-router-config'; + import type {Required} from 'utility-types'; + + export interface Props extends Required {} + + export default function DocRoot(props: Props): JSX.Element; } diff --git a/packages/docusaurus-plugin-content-docs/src/props.ts b/packages/docusaurus-plugin-content-docs/src/props.ts index e8f05ba4ebe4..c9f58096ac50 100644 --- a/packages/docusaurus-plugin-content-docs/src/props.ts +++ b/packages/docusaurus-plugin-content-docs/src/props.ts @@ -7,7 +7,7 @@ import _ from 'lodash'; import {createDocsByIdIndex} from './docs'; -import type {VersionTag} from './types'; +import type {VersionTag, VersionTags} from './types'; import type { SidebarItemDoc, SidebarItem, @@ -21,6 +21,7 @@ import type { PropSidebarItemCategory, PropTagDocList, PropTagDocListDoc, + PropTagsListPage, PropSidebarItemLink, PropVersionDocs, DocMetadata, @@ -181,3 +182,13 @@ export function toTagDocListProp({ items: toDocListProp(), }; } + +export function toTagsListTagsProp( + versionTags: VersionTags, +): PropTagsListPage['tags'] { + return Object.values(versionTags).map((tagValue) => ({ + label: tagValue.label, + permalink: tagValue.permalink, + count: tagValue.docIds.length, + })); +} diff --git a/packages/docusaurus-plugin-content-docs/src/routes.ts b/packages/docusaurus-plugin-content-docs/src/routes.ts index 9a9205bee1c5..0b7a6425594f 100644 --- a/packages/docusaurus-plugin-content-docs/src/routes.ts +++ b/packages/docusaurus-plugin-content-docs/src/routes.ts @@ -5,30 +5,32 @@ * LICENSE file in the root directory of this source tree. */ +import _ from 'lodash'; import logger from '@docusaurus/logger'; -import {docuHash, createSlugger} from '@docusaurus/utils'; -import {toVersionMetadataProp} from './props'; +import {docuHash, createSlugger, normalizeUrl} from '@docusaurus/utils'; +import { + toTagDocListProp, + toTagsListTagsProp, + toVersionMetadataProp, +} from './props'; +import {getVersionTags} from './tags'; import type {PluginContentLoadedActions, RouteConfig} from '@docusaurus/types'; -import type {FullVersion} from './types'; +import type {FullVersion, VersionTag} from './types'; import type { CategoryGeneratedIndexMetadata, - DocMetadata, + PluginOptions, + PropTagsListPage, } from '@docusaurus/plugin-content-docs'; -export async function createCategoryGeneratedIndexRoutes({ +async function buildVersionCategoryGeneratedIndexRoutes({ version, actions, - docCategoryGeneratedIndexComponent, + options, aliasedSource, -}: { - version: FullVersion; - actions: PluginContentLoadedActions; - docCategoryGeneratedIndexComponent: string; - aliasedSource: (str: string) => string; -}): Promise { +}: BuildVersionRoutesParam): Promise { const slugs = createSlugger(); - async function createCategoryGeneratedIndexRoute( + async function buildCategoryGeneratedIndexRoute( categoryGeneratedIndex: CategoryGeneratedIndexMetadata, ): Promise { const {sidebar, ...prop} = categoryGeneratedIndex; @@ -44,7 +46,7 @@ export async function createCategoryGeneratedIndexRoutes({ return { path: categoryGeneratedIndex.permalink, - component: docCategoryGeneratedIndexComponent, + component: options.docCategoryGeneratedIndexComponent, exact: true, modules: { categoryGeneratedIndex: aliasedSource(propData), @@ -56,21 +58,17 @@ export async function createCategoryGeneratedIndexRoutes({ } return Promise.all( - version.categoryGeneratedIndices.map(createCategoryGeneratedIndexRoute), + version.categoryGeneratedIndices.map(buildCategoryGeneratedIndexRoute), ); } -export async function createDocRoutes({ - docs, +async function buildVersionDocRoutes({ + version, actions, - docItemComponent, -}: { - docs: DocMetadata[]; - actions: PluginContentLoadedActions; - docItemComponent: string; -}): Promise { + options, +}: BuildVersionRoutesParam): Promise { return Promise.all( - docs.map(async (metadataItem) => { + version.docs.map(async (metadataItem) => { await actions.createData( // Note that this created data path must be in sync with // metadataPath provided to mdx-loader. @@ -80,12 +78,12 @@ export async function createDocRoutes({ const docRoute: RouteConfig = { path: metadataItem.permalink, - component: docItemComponent, + component: options.docItemComponent, exact: true, modules: { content: metadataItem.source, }, - // Because the parent (DocPage) comp need to access it easily + // Because the parent (DocRoot) comp need to access it easily // This permits to render the sidebar once without unmount/remount when // navigating (and preserve sidebar state) ...(metadataItem.sidebar && { @@ -98,62 +96,160 @@ export async function createDocRoutes({ ); } -export async function createVersionRoutes({ - version, - actions, - docItemComponent, - docLayoutComponent, - docCategoryGeneratedIndexComponent, - pluginId, - aliasedSource, -}: { - version: FullVersion; - actions: PluginContentLoadedActions; - docLayoutComponent: string; - docItemComponent: string; - docCategoryGeneratedIndexComponent: string; - pluginId: string; - aliasedSource: (str: string) => string; -}): Promise { - async function doCreateVersionRoutes(): Promise { - const versionMetadata = toVersionMetadataProp(pluginId, version); - const versionMetadataPropPath = await actions.createData( - `${docuHash(`version-${version.versionName}-metadata-prop`)}.json`, - JSON.stringify(versionMetadata, null, 2), - ); +async function buildVersionSidebarRoute(param: BuildVersionRoutesParam) { + const [docRoutes, categoryGeneratedIndexRoutes] = await Promise.all([ + buildVersionDocRoutes(param), + buildVersionCategoryGeneratedIndexRoutes(param), + ]); + const subRoutes = [...docRoutes, ...categoryGeneratedIndexRoutes]; + return { + path: param.version.path, + exact: false, + component: param.options.docRootComponent, + routes: subRoutes, + }; +} - async function createVersionSubRoutes() { - const [docRoutes, sidebarsRoutes] = await Promise.all([ - createDocRoutes({docs: version.docs, actions, docItemComponent}), - createCategoryGeneratedIndexRoutes({ - version, - actions, - docCategoryGeneratedIndexComponent, - aliasedSource, - }), - ]); +async function buildVersionTagsRoutes( + param: BuildVersionRoutesParam, +): Promise { + const {version, options, actions, aliasedSource} = param; + const versionTags = getVersionTags(version.docs); - const routes = [...docRoutes, ...sidebarsRoutes]; - return routes.sort((a, b) => a.path.localeCompare(b.path)); + async function buildTagsListRoute(): Promise { + // Don't create a tags list page if there's no tag + if (Object.keys(versionTags).length === 0) { + return null; } + const tagsProp: PropTagsListPage['tags'] = toTagsListTagsProp(versionTags); + const tagsPropPath = await actions.createData( + `${docuHash(`tags-list-${version.versionName}-prop`)}.json`, + JSON.stringify(tagsProp, null, 2), + ); + return { + path: version.tagsPath, + exact: true, + component: options.docTagsListComponent, + modules: { + tags: aliasedSource(tagsPropPath), + }, + }; + } + + async function buildTagDocListRoute(tag: VersionTag): Promise { + const tagProps = toTagDocListProp({ + allTagsPath: version.tagsPath, + tag, + docs: version.docs, + }); + const tagPropPath = await actions.createData( + `${docuHash(`tag-${tag.permalink}`)}.json`, + JSON.stringify(tagProps, null, 2), + ); + return { + path: tag.permalink, + component: options.docTagDocListComponent, + exact: true, + modules: { + tag: aliasedSource(tagPropPath), + }, + }; + } + + const [tagsListRoute, allTagsDocListRoutes] = await Promise.all([ + buildTagsListRoute(), + Promise.all(Object.values(versionTags).map(buildTagDocListRoute)), + ]); + + return _.compact([tagsListRoute, ...allTagsDocListRoutes]); +} + +type BuildVersionRoutesParam = Omit & { + version: FullVersion; +}; + +async function buildVersionRoutes( + param: BuildVersionRoutesParam, +): Promise { + const {version, actions, options, aliasedSource} = param; - actions.addRoute({ + async function buildVersionSubRoutes() { + const [sidebarRoute, tagsRoutes] = await Promise.all([ + buildVersionSidebarRoute(param), + buildVersionTagsRoutes(param), + ]); + + return [sidebarRoute, ...tagsRoutes]; + } + + async function doBuildVersionRoutes(): Promise { + const versionProp = toVersionMetadataProp(options.id, version); + const versionPropPath = await actions.createData( + `${docuHash(`version-${version.versionName}-metadata-prop`)}.json`, + JSON.stringify(versionProp, null, 2), + ); + const subRoutes = await buildVersionSubRoutes(); + return { path: version.path, - // Allow matching /docs/* since this is the wrapping route exact: false, - component: docLayoutComponent, - routes: await createVersionSubRoutes(), + component: options.docVersionRootComponent, + routes: subRoutes, modules: { - versionMetadata: aliasedSource(versionMetadataPropPath), + version: aliasedSource(versionPropPath), }, priority: version.routePriority, - }); + }; } try { - return await doCreateVersionRoutes(); + return await doBuildVersionRoutes(); } catch (err) { logger.error`Can't create version routes for version name=${version.versionName}`; throw err; } } + +type BuildAllRoutesParam = Omit & { + actions: Omit; +}; + +// TODO we want this buildAllRoutes function to be easily testable +// Ideally, we should avoid side effects here (ie not injecting actions) +export async function buildAllRoutes( + param: BuildAllRoutesParam, +): Promise { + const subRoutes = await Promise.all( + param.versions.map((version) => + buildVersionRoutes({ + ...param, + version, + }), + ), + ); + + // all docs routes are wrapped under a single parent route, this ensures + // the theme layout never unmounts/remounts when navigating between versions + return [ + { + path: normalizeUrl([param.baseUrl, param.options.routeBasePath]), + exact: false, + component: param.options.docsRootComponent, + routes: subRoutes, + }, + ]; +} + +type CreateAllRoutesParam = { + baseUrl: string; + versions: FullVersion[]; + options: PluginOptions; + actions: PluginContentLoadedActions; + aliasedSource: (str: string) => string; +}; + +export async function createAllRoutes( + param: CreateAllRoutesParam, +): Promise { + const routes = await buildAllRoutes(param); + routes.forEach(param.actions.addRoute); +} diff --git a/packages/docusaurus-plugin-content-docs/src/versions/index.ts b/packages/docusaurus-plugin-content-docs/src/versions/index.ts index 39edeba0e092..f9be8149a853 100644 --- a/packages/docusaurus-plugin-content-docs/src/versions/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/versions/index.ts @@ -14,12 +14,16 @@ import { getVersionMetadataPaths, readVersionNames, } from './files'; +import {createSidebarsUtils} from '../sidebars/utils'; +import {getCategoryGeneratedIndexMetadataList} from '../categoryGeneratedIndex'; +import type {FullVersion} from '../types'; +import type {LoadContext} from '@docusaurus/types'; import type { + LoadedVersion, PluginOptions, VersionBanner, VersionMetadata, } from '@docusaurus/plugin-content-docs'; -import type {LoadContext} from '@docusaurus/types'; export type VersionContext = { /** The version name to get banner of. */ @@ -252,3 +256,15 @@ export async function readVersionsMetadata({ ); return versionsMetadata; } + +export function toFullVersion(version: LoadedVersion): FullVersion { + const sidebarsUtils = createSidebarsUtils(version.sidebars); + return { + ...version, + sidebarsUtils, + categoryGeneratedIndices: getCategoryGeneratedIndexMetadataList({ + docs: version.docs, + sidebarsUtils, + }), + }; +} diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 5c6db4e29825..8cf475b8400c 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -379,17 +379,17 @@ declare module '@theme/DocItem/Footer' { export default function DocItemFooter(): JSX.Element; } -declare module '@theme/DocPage/Layout' { +declare module '@theme/DocRoot/Layout' { import type {ReactNode} from 'react'; export interface Props { readonly children: ReactNode; } - export default function DocPageLayout(props: Props): JSX.Element; + export default function DocRootLayout(props: Props): JSX.Element; } -declare module '@theme/DocPage/Layout/Sidebar' { +declare module '@theme/DocRoot/Layout/Sidebar' { import type {Dispatch, SetStateAction} from 'react'; import type {PropSidebar} from '@docusaurus/plugin-content-docs'; @@ -399,20 +399,20 @@ declare module '@theme/DocPage/Layout/Sidebar' { readonly setHiddenSidebarContainer: Dispatch>; } - export default function DocPageLayoutSidebar(props: Props): JSX.Element; + export default function DocRootLayoutSidebar(props: Props): JSX.Element; } -declare module '@theme/DocPage/Layout/Sidebar/ExpandButton' { +declare module '@theme/DocRoot/Layout/Sidebar/ExpandButton' { export interface Props { toggleSidebar: () => void; } - export default function DocPageLayoutSidebarExpandButton( + export default function DocRootLayoutSidebarExpandButton( props: Props, ): JSX.Element; } -declare module '@theme/DocPage/Layout/Main' { +declare module '@theme/DocRoot/Layout/Main' { import type {ReactNode} from 'react'; export interface Props { @@ -420,7 +420,7 @@ declare module '@theme/DocPage/Layout/Main' { readonly children: ReactNode; } - export default function DocPageLayoutMain(props: Props): JSX.Element; + export default function DocRootLayoutMain(props: Props): JSX.Element; } declare module '@theme/DocPaginator' { @@ -662,6 +662,14 @@ declare module '@theme/Heading' { export default function Heading(props: Props): JSX.Element; } +declare module '@theme/NotFound/Content' { + export interface Props { + readonly className?: string; + } + + export default function NotFoundContent(props: Props): JSX.Element; +} + declare module '@theme/Layout' { import type {ReactNode} from 'react'; diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx deleted file mode 100644 index 61a289c30f3d..000000000000 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import clsx from 'clsx'; -import { - HtmlClassNameProvider, - ThemeClassNames, - PageMetadata, -} from '@docusaurus/theme-common'; -import { - docVersionSearchTag, - DocsSidebarProvider, - DocsVersionProvider, - useDocRouteMetadata, -} from '@docusaurus/theme-common/internal'; -import DocPageLayout from '@theme/DocPage/Layout'; -import NotFound from '@theme/NotFound'; -import SearchMetadata from '@theme/SearchMetadata'; -import type {Props} from '@theme/DocPage'; - -function DocPageMetadata(props: Props): JSX.Element { - const {versionMetadata} = props; - return ( - <> - - - {versionMetadata.noIndex && ( - - )} - - - ); -} - -export default function DocPage(props: Props): JSX.Element { - const {versionMetadata} = props; - const currentDocRouteMetadata = useDocRouteMetadata(props); - if (!currentDocRouteMetadata) { - return ; - } - const {docElement, sidebarName, sidebarItems} = currentDocRouteMetadata; - return ( - <> - - - - - {docElement} - - - - - ); -} diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Main/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Main/index.tsx similarity index 89% rename from packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Main/index.tsx rename to packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Main/index.tsx index eb19b3549c89..b8432909ef37 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Main/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Main/index.tsx @@ -8,11 +8,11 @@ import React from 'react'; import clsx from 'clsx'; import {useDocsSidebar} from '@docusaurus/theme-common/internal'; -import type {Props} from '@theme/DocPage/Layout/Main'; +import type {Props} from '@theme/DocRoot/Layout/Main'; import styles from './styles.module.css'; -export default function DocPageLayoutMain({ +export default function DocRootLayoutMain({ hiddenSidebarContainer, children, }: Props): JSX.Element { diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Main/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Main/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Main/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Main/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/ExpandButton/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/ExpandButton/index.tsx similarity index 89% rename from packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/ExpandButton/index.tsx rename to packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/ExpandButton/index.tsx index 688501a0ace5..564d27475ccb 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/ExpandButton/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/ExpandButton/index.tsx @@ -8,11 +8,11 @@ import React from 'react'; import {translate} from '@docusaurus/Translate'; import IconArrow from '@theme/Icon/Arrow'; -import type {Props} from '@theme/DocPage/Layout/Sidebar/ExpandButton'; +import type {Props} from '@theme/DocRoot/Layout/Sidebar/ExpandButton'; import styles from './styles.module.css'; -export default function DocPageLayoutSidebarExpandButton({ +export default function DocRootLayoutSidebarExpandButton({ toggleSidebar, }: Props): JSX.Element { return ( diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/ExpandButton/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/ExpandButton/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/ExpandButton/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/ExpandButton/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/index.tsx similarity index 92% rename from packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/index.tsx rename to packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/index.tsx index a88302612113..243511230c0d 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/index.tsx @@ -11,8 +11,8 @@ import {ThemeClassNames} from '@docusaurus/theme-common'; import {useDocsSidebar} from '@docusaurus/theme-common/internal'; import {useLocation} from '@docusaurus/router'; import DocSidebar from '@theme/DocSidebar'; -import ExpandButton from '@theme/DocPage/Layout/Sidebar/ExpandButton'; -import type {Props} from '@theme/DocPage/Layout/Sidebar'; +import ExpandButton from '@theme/DocRoot/Layout/Sidebar/ExpandButton'; +import type {Props} from '@theme/DocRoot/Layout/Sidebar'; import styles from './styles.module.css'; @@ -28,7 +28,7 @@ function ResetOnSidebarChange({children}: {children: ReactNode}) { ); } -export default function DocPageLayoutSidebar({ +export default function DocRootLayoutSidebar({ sidebar, hiddenSidebarContainer, setHiddenSidebarContainer, diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/styles.module.css similarity index 100% rename from packages/docusaurus-theme-classic/src/theme/DocPage/Layout/Sidebar/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/Sidebar/styles.module.css diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/index.tsx similarity index 61% rename from packages/docusaurus-theme-classic/src/theme/DocPage/Layout/index.tsx rename to packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/index.tsx index 0ff53b286d16..45a12d28cfe5 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/index.tsx @@ -7,32 +7,31 @@ import React, {useState} from 'react'; import {useDocsSidebar} from '@docusaurus/theme-common/internal'; -import Layout from '@theme/Layout'; import BackToTopButton from '@theme/BackToTopButton'; -import DocPageLayoutSidebar from '@theme/DocPage/Layout/Sidebar'; -import DocPageLayoutMain from '@theme/DocPage/Layout/Main'; -import type {Props} from '@theme/DocPage/Layout'; +import DocRootLayoutSidebar from '@theme/DocRoot/Layout/Sidebar'; +import DocRootLayoutMain from '@theme/DocRoot/Layout/Main'; +import type {Props} from '@theme/DocRoot/Layout'; import styles from './styles.module.css'; -export default function DocPageLayout({children}: Props): JSX.Element { +export default function DocRootLayout({children}: Props): JSX.Element { const sidebar = useDocsSidebar(); const [hiddenSidebarContainer, setHiddenSidebarContainer] = useState(false); return ( - +
-
+
{sidebar && ( - )} - + {children} - +
- +
); } diff --git a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/styles.module.css similarity index 95% rename from packages/docusaurus-theme-classic/src/theme/DocPage/Layout/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/styles.module.css index 2516de55e3d4..d80285c4c1c0 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocPage/Layout/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/DocRoot/Layout/styles.module.css @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -.docPage { +.docRoot { display: flex; width: 100%; } diff --git a/packages/docusaurus-theme-classic/src/theme/DocRoot/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocRoot/index.tsx new file mode 100644 index 000000000000..8c5eb805d13f --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/DocRoot/index.tsx @@ -0,0 +1,34 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import clsx from 'clsx'; +import {HtmlClassNameProvider, ThemeClassNames} from '@docusaurus/theme-common'; +import { + DocsSidebarProvider, + useDocRootMetadata, +} from '@docusaurus/theme-common/internal'; +import DocRootLayout from '@theme/DocRoot/Layout'; +import NotFoundContent from '@theme/NotFound/Content'; +import type {Props} from '@theme/DocRoot'; + +export default function DocRoot(props: Props): JSX.Element { + const currentDocRouteMetadata = useDocRootMetadata(props); + if (!currentDocRouteMetadata) { + // We only render the not found content to avoid a double layout + // see https://github.com/facebook/docusaurus/pull/7966#pullrequestreview-1077276692 + return ; + } + const {docElement, sidebarName, sidebarItems} = currentDocRouteMetadata; + return ( + + + {docElement} + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocTagDocListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocTagDocListPage/index.tsx index a928644a9bd6..f32b32ffdba4 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocTagDocListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocTagDocListPage/index.tsx @@ -15,7 +15,6 @@ import { usePluralForm, } from '@docusaurus/theme-common'; import Translate, {translate} from '@docusaurus/Translate'; -import Layout from '@theme/Layout'; import SearchMetadata from '@theme/SearchMetadata'; import type {Props} from '@theme/DocTagDocListPage'; @@ -37,6 +36,18 @@ function useNDocsTaggedPlural() { ); } +function usePageTitle(props: Props): string { + const nDocsTaggedPlural = useNDocsTaggedPlural(); + return translate( + { + id: 'theme.docs.tagDocListPageTitle', + description: 'The title of the page for a docs tag', + message: '{nDocsTagged} with "{tagName}"', + }, + {nDocsTagged: nDocsTaggedPlural(props.tag.count), tagName: props.tag.label}, + ); +} + function DocItem({doc}: {doc: Props['tag']['items'][number]}): JSX.Element { return (
@@ -48,48 +59,55 @@ function DocItem({doc}: {doc: Props['tag']['items'][number]}): JSX.Element { ); } -export default function DocTagDocListPage({tag}: Props): JSX.Element { - const nDocsTaggedPlural = useNDocsTaggedPlural(); - const title = translate( - { - id: 'theme.docs.tagDocListPageTitle', - description: 'The title of the page for a docs tag', - message: '{nDocsTagged} with "{tagName}"', - }, - {nDocsTagged: nDocsTaggedPlural(tag.count), tagName: tag.label}, +function DocTagDocListPageMetadata({ + title, +}: Props & {title: string}): JSX.Element { + return ( + <> + + + ); +} +function DocTagDocListPageContent({ + tag, + title, +}: Props & {title: string}): JSX.Element { return ( - - - -
-
-
-
-

{title}

- - - View All Tags - - -
-
- {tag.items.map((doc) => ( - - ))} -
-
-
+ className={clsx(ThemeClassNames.page.docsTagDocListPage)}> +
+
+
+
+

{title}

+ + + View All Tags + + +
+
+ {tag.items.map((doc) => ( + + ))} +
+
- +
); } + +export default function DocTagDocListPage(props: Props): JSX.Element { + const title = usePageTitle(props); + return ( + <> + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocTagsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocTagsListPage/index.tsx index e848128fedf0..5603797ab6bf 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocTagsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocTagsListPage/index.tsx @@ -13,31 +13,46 @@ import { ThemeClassNames, translateTagsPageTitle, } from '@docusaurus/theme-common'; -import Layout from '@theme/Layout'; import TagsListByLetter from '@theme/TagsListByLetter'; import SearchMetadata from '@theme/SearchMetadata'; import type {Props} from '@theme/DocTagsListPage'; -export default function DocTagsListPage({tags}: Props): JSX.Element { - const title = translateTagsPageTitle(); +function DocTagsListPageMetadata({ + title, +}: Props & {title: string}): JSX.Element { return ( - + <> - -
-
-
-

{title}

- -
-
+ + ); +} + +function DocTagsListPageContent({ + tags, + title, +}: Props & {title: string}): JSX.Element { + return ( + +
+
+
+

{title}

+ +
- +
); } + +export default function DocTagsListPage(props: Props): JSX.Element { + const title = translateTagsPageTitle(); + return ( + <> + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocVersionRoot/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocVersionRoot/index.tsx new file mode 100644 index 000000000000..9c1952ce1b5c --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/DocVersionRoot/index.tsx @@ -0,0 +1,51 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import {HtmlClassNameProvider, PageMetadata} from '@docusaurus/theme-common'; +import { + docVersionSearchTag, + DocsVersionProvider, +} from '@docusaurus/theme-common/internal'; +import renderRoutes from '@docusaurus/renderRoutes'; +import SearchMetadata from '@theme/SearchMetadata'; + +import type {Props} from '@theme/DocVersionRoot'; + +function DocVersionRootMetadata(props: Props): JSX.Element { + const {version} = props; + return ( + <> + + + {version.noIndex && } + + + ); +} + +function DocVersionRootContent(props: Props): JSX.Element { + const {version, route} = props; + return ( + + + {renderRoutes(route.routes!)} + + + ); +} +export default function DocVersionRoot(props: Props): JSX.Element { + return ( + <> + + + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/DocsRoot/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocsRoot/index.tsx new file mode 100644 index 000000000000..ea65d7414f17 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/DocsRoot/index.tsx @@ -0,0 +1,22 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import clsx from 'clsx'; +import {ThemeClassNames, HtmlClassNameProvider} from '@docusaurus/theme-common'; +import renderRoutes from '@docusaurus/renderRoutes'; +import Layout from '@theme/Layout'; + +import type {Props} from '@theme/DocVersionRoot'; + +export default function DocsRoot(props: Props): JSX.Element { + return ( + + {renderRoutes(props.route.routes!)} + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/NotFound.tsx b/packages/docusaurus-theme-classic/src/theme/NotFound.tsx deleted file mode 100644 index e6b9542f3d50..000000000000 --- a/packages/docusaurus-theme-classic/src/theme/NotFound.tsx +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; -import Translate, {translate} from '@docusaurus/Translate'; -import {PageMetadata} from '@docusaurus/theme-common'; -import Layout from '@theme/Layout'; - -export default function NotFound(): JSX.Element { - return ( - <> - - -
-
-
-

- - Page Not Found - -

-

- - We could not find what you were looking for. - -

-

- - Please contact the owner of the site that linked you to the - original URL and let them know their link is broken. - -

-
-
-
-
- - ); -} diff --git a/packages/docusaurus-theme-classic/src/theme/NotFound/Content/index.tsx b/packages/docusaurus-theme-classic/src/theme/NotFound/Content/index.tsx new file mode 100644 index 000000000000..f5288dec00e6 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/NotFound/Content/index.tsx @@ -0,0 +1,44 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import clsx from 'clsx'; +import Translate from '@docusaurus/Translate'; +import type {Props} from '@theme/NotFound/Content'; + +export default function NotFoundContent({className}: Props): JSX.Element { + return ( +
+
+
+

+ + Page Not Found + +

+

+ + We could not find what you were looking for. + +

+

+ + Please contact the owner of the site that linked you to the + original URL and let them know their link is broken. + +

+
+
+
+ ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/NotFound/index.tsx b/packages/docusaurus-theme-classic/src/theme/NotFound/index.tsx new file mode 100644 index 000000000000..7be3617f7154 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/NotFound/index.tsx @@ -0,0 +1,27 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import {translate} from '@docusaurus/Translate'; +import {PageMetadata} from '@docusaurus/theme-common'; +import Layout from '@theme/Layout'; +import NotFoundContent from '@theme/NotFound/Content'; + +export default function Index(): JSX.Element { + const title = translate({ + id: 'theme.NotFound.title', + message: 'Page Not Found', + }); + return ( + <> + + + + + + ); +} diff --git a/packages/docusaurus-theme-common/src/internal.ts b/packages/docusaurus-theme-common/src/internal.ts index 79812c968634..8415cc967bfe 100644 --- a/packages/docusaurus-theme-common/src/internal.ts +++ b/packages/docusaurus-theme-common/src/internal.ts @@ -73,7 +73,7 @@ export { useDocsVersionCandidates, useLayoutDoc, useLayoutDocsSidebar, - useDocRouteMetadata, + useDocRootMetadata, } from './utils/docsUtils'; export {useTitleFormatter} from './utils/generalUtils'; diff --git a/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts b/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts index 9b4623214b6f..8f82f5213bbb 100644 --- a/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts +++ b/packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts @@ -27,6 +27,9 @@ export const ThemeClassNames = { }, wrapper: { main: 'main-wrapper', + // TODO these wrapper class names are now quite useless + // TODO do breaking change later in 3.0 + // we already add plugin name/id class on : that's enough blogPages: 'blog-wrapper', docsPages: 'docs-wrapper', mdxPages: 'mdx-wrapper', diff --git a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx index 7ce3222df3e1..d480172b87c8 100644 --- a/packages/docusaurus-theme-common/src/utils/docsUtils.tsx +++ b/packages/docusaurus-theme-common/src/utils/docsUtils.tsx @@ -17,7 +17,7 @@ import { type GlobalSidebar, type GlobalDoc, } from '@docusaurus/plugin-content-docs/client'; -import type {Props as DocPageProps} from '@theme/DocPage'; +import type {Props as DocRootProps} from '@theme/DocRoot'; import {useDocsPreferredVersion} from '../contexts/docsPreferredVersion'; import {useDocsVersion} from '../contexts/docsVersion'; import {useDocsSidebar} from '../contexts/docsSidebar'; @@ -290,14 +290,11 @@ Available doc ids are: * version metadata, and the subroutes creating individual doc pages. This hook * will match the current location against all known sub-routes. * - * @param props The props received by `@theme/DocPage` + * @param props The props received by `@theme/DocRoot` * @returns The data of the relevant document at the current location, or `null` * if no document associated with the current location can be found. */ -export function useDocRouteMetadata({ - route, - versionMetadata, -}: DocPageProps): null | { +export function useDocRootMetadata({route}: DocRootProps): null | { /** The element that should be rendered at the current location. */ docElement: JSX.Element; /** @@ -309,6 +306,7 @@ export function useDocRouteMetadata({ sidebarItems: PropSidebar | undefined; } { const location = useLocation(); + const versionMetadata = useDocsVersion(); const docRoutes = route.routes!; const currentDocRoute = docRoutes.find((docRoute) => matchPath(location.pathname, docRoute), diff --git a/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap b/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap index 391f3f0530c9..9fe2fabb5ff4 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap @@ -60,7 +60,7 @@ exports[`loadRoutes loads nested route config 1`] = ` { "registry": { "__comp---theme-doc-item-178-a40": "@theme/DocItem", - "__comp---theme-doc-page-1-be-9be": "@theme/DocPage", + "__comp---theme-doc-roota-94-67a": "@theme/DocRoot", "content---docs-foo-baz-8-ce-61e": "docs/foo/baz.md", "content---docs-helloaff-811": "docs/hello.md", "docsMetadata---docs-routef-34-881": "docs-b5f.json", @@ -77,8 +77,8 @@ exports[`loadRoutes loads nested route config 1`] = ` "content": "content---docs-helloaff-811", "metadata": "metadata---docs-hello-956-741", }, - "/docs:route-502": { - "__comp": "__comp---theme-doc-page-1-be-9be", + "/docs:route-9d0": { + "__comp": "__comp---theme-doc-roota-94-67a", "docsMetadata": "docsMetadata---docs-routef-34-881", }, "docs/foo/baz-eb2": { @@ -96,7 +96,7 @@ import ComponentCreator from '@docusaurus/ComponentCreator'; export default [ { path: '/docs:route', - component: ComponentCreator('/docs:route', '502'), + component: ComponentCreator('/docs:route', '9d0'), routes: [ { path: '/docs/hello', diff --git a/packages/docusaurus/src/server/__tests__/routes.test.ts b/packages/docusaurus/src/server/__tests__/routes.test.ts index a2f22058da4b..ada2f9be9d59 100644 --- a/packages/docusaurus/src/server/__tests__/routes.test.ts +++ b/packages/docusaurus/src/server/__tests__/routes.test.ts @@ -103,7 +103,7 @@ describe('handleDuplicateRoutes', () => { describe('loadRoutes', () => { it('loads nested route config', () => { const nestedRouteConfig: RouteConfig = { - component: '@theme/DocPage', + component: '@theme/DocRoot', path: '/docs:route', modules: { docsMetadata: 'docs-b5f.json', diff --git a/packages/docusaurus/src/server/plugins/__tests__/__snapshots__/routeConfig.test.ts.snap b/packages/docusaurus/src/server/plugins/__tests__/__snapshots__/routeConfig.test.ts.snap index e38931b90952..9d4fb06e800a 100644 --- a/packages/docusaurus/src/server/plugins/__tests__/__snapshots__/routeConfig.test.ts.snap +++ b/packages/docusaurus/src/server/plugins/__tests__/__snapshots__/routeConfig.test.ts.snap @@ -103,3 +103,45 @@ exports[`sortConfig sorts route config given a baseURL 1`] = ` }, ] `; + +exports[`sortConfig sorts route config recursively 1`] = ` +[ + { + "component": "", + "exact": true, + "path": "/some/page", + }, + { + "component": "", + "path": "/docs", + "routes": [ + { + "component": "", + "exact": true, + "path": "/docs/tags", + }, + { + "component": "", + "exact": true, + "path": "/docs/tags/someTag", + }, + { + "component": "", + "path": "/docs", + "routes": [ + { + "component": "", + "exact": true, + "path": "/docs/doc1", + }, + { + "component": "", + "exact": true, + "path": "/docs/doc2", + }, + ], + }, + ], + }, +] +`; diff --git a/packages/docusaurus/src/server/plugins/__tests__/routeConfig.test.ts b/packages/docusaurus/src/server/plugins/__tests__/routeConfig.test.ts index 3d0e6a5a8ec7..64c8034eb520 100644 --- a/packages/docusaurus/src/server/plugins/__tests__/routeConfig.test.ts +++ b/packages/docusaurus/src/server/plugins/__tests__/routeConfig.test.ts @@ -207,6 +207,52 @@ describe('sortConfig', () => { expect(routes).toMatchSnapshot(); }); + it('sorts route config recursively', () => { + const routes: RouteConfig[] = [ + { + path: '/docs', + component: '', + routes: [ + { + path: '/docs/tags', + component: '', + exact: true, + }, + { + path: '/docs', + component: '', + routes: [ + { + path: '/docs/doc1', + component: '', + exact: true, + }, + { + path: '/docs/doc2', + component: '', + exact: true, + }, + ], + }, + { + path: '/docs/tags/someTag', + component: '', + exact: true, + }, + ], + }, + { + path: '/some/page', + component: '', + exact: true, + }, + ]; + + sortConfig(routes); + + expect(routes).toMatchSnapshot(); + }); + it('sorts route config given a baseURL', () => { const baseURL = '/latest/'; const routes: RouteConfig[] = [ diff --git a/packages/docusaurus/src/server/plugins/routeConfig.ts b/packages/docusaurus/src/server/plugins/routeConfig.ts index 3731eb993d74..d757406bd09b 100644 --- a/packages/docusaurus/src/server/plugins/routeConfig.ts +++ b/packages/docusaurus/src/server/plugins/routeConfig.ts @@ -63,6 +63,8 @@ export function sortConfig( }); routeConfigs.forEach((routeConfig) => { - routeConfig.routes?.sort((a, b) => a.path.localeCompare(b.path)); + if (routeConfig.routes) { + sortConfig(routeConfig.routes, baseUrl); + } }); } diff --git a/website/docs/api/plugins/plugin-content-docs.md b/website/docs/api/plugins/plugin-content-docs.md index 6e60b02e9176..9b9a124e0c47 100644 --- a/website/docs/api/plugins/plugin-content-docs.md +++ b/website/docs/api/plugins/plugin-content-docs.md @@ -46,7 +46,9 @@ Accepted fields: | `sidebarCollapsed` | `boolean` | `true` | Whether sidebar categories are collapsed by default. See also [Expanded categories by default](/docs/sidebar#expanded-categories-by-default) | | `sidebarItemsGenerator` | SidebarGenerator | _Omitted_ | Function used to replace the sidebar items of type `'autogenerated'` with real sidebar items (docs, categories, links...). See also [Customize the sidebar items generator](/docs/sidebar#customize-the-sidebar-items-generator) | | `numberPrefixParser` | boolean \| PrefixParser | _Omitted_ | Custom parsing logic to extract number prefixes from file names. Use `false` to disable this behavior and leave the docs untouched, and `true` to use the default parser. See also [Using number prefixes](/docs/sidebar#using-number-prefixes) | -| `docLayoutComponent` | `string` | `'@theme/DocPage'` | Root layout component of each doc page. Provides the version data context, and is not unmounted when switching docs. | +| `docsRootComponent` | `string` | `'@theme/DocsRoot'` | Parent component of all the docs plugin pages (including all versions). Stays mounted when navigation between docs pages and versions. | +| `docVersionRootComponent` | `string` | `'@theme/DocVersionLayout'` | Parent component of all docs pages of an individual version (doc pages with sidebars, tags pages). Stays mounted when navigation between pages of that specific version. | +| `docRootComponent` | `string` | `'@theme/DocPage'` | Parent component of all doc pages with sidebars (regular docs pages, category generated index pages). Stays mounted when navigation between such pages. | | `docItemComponent` | `string` | `'@theme/DocItem'` | Main doc container, with TOC, pagination, etc. | | `docTagsListComponent` | `string` | `'@theme/DocTagsListPage'` | Root component of the tags list page | | `docTagDocListComponent` | `string` | `'@theme/DocTagDocListPage'` | Root component of the "docs containing tag X" page. |