diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap index 1eab791d4450..c19351278b5f 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/index.test.ts.snap @@ -8,7 +8,7 @@ exports[`blog plugin works on blog tags without pagination 1`] = ` "/another/tags", "/another/tags2", ], - "name": "tag1", + "label": "tag1", "pages": [ { "items": [ @@ -36,7 +36,7 @@ exports[`blog plugin works on blog tags without pagination 1`] = ` "/another/tags", "/another/tags2", ], - "name": "tag2", + "label": "tag2", "pages": [ { "items": [ @@ -69,7 +69,7 @@ exports[`blog plugin works with blog tags 1`] = ` "/another/tags", "/another/tags2", ], - "name": "tag1", + "label": "tag1", "pages": [ { "items": [ @@ -112,7 +112,7 @@ exports[`blog plugin works with blog tags 1`] = ` "/another/tags", "/another/tags2", ], - "name": "tag2", + "label": "tag2", "pages": [ { "items": [ diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts index b1823bb6d68d..8138d1ab8b00 100644 --- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts +++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts @@ -114,7 +114,7 @@ export function getBlogTags({ ); return _.mapValues(groups, ({tag, items: tagBlogPosts}) => ({ - name: tag.label, + label: tag.label, items: tagBlogPosts.map((item) => item.id), permalink: tag.permalink, pages: paginateBlogPosts({ diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts index 3414d2820a07..c5538be08235 100644 --- a/packages/docusaurus-plugin-content-blog/src/index.ts +++ b/packages/docusaurus-plugin-content-blog/src/index.ts @@ -30,7 +30,13 @@ import type { BlogContentPaths, BlogMarkdownLoaderOptions, } from './types'; -import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types'; +import type { + LoadContext, + Plugin, + HtmlTags, + TagsListItem, + TagModule, +} from '@docusaurus/types'; import { generateBlogPosts, getSourceToPermalink, @@ -43,7 +49,6 @@ import type { BlogPostFrontMatter, BlogPostMetadata, Assets, - TagModule, } from '@docusaurus/plugin-content-blog'; export default async function pluginContentBlog( @@ -117,6 +122,8 @@ export default async function pluginContentBlog( blogSidebarTitle, } = options; + const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]); + const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]); const blogPosts = await generateBlogPosts(contentPaths, context, options); if (!blogPosts.length) { @@ -125,7 +132,7 @@ export default async function pluginContentBlog( blogPosts: [], blogListPaginated: [], blogTags: {}, - blogTagsListPath: null, + blogTagsListPath, blogTagsPaginated: [], }; } @@ -150,8 +157,6 @@ export default async function pluginContentBlog( } }); - const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]); - const blogListPaginated: BlogPaginated[] = paginateBlogPosts({ blogPosts, blogTitle, @@ -167,11 +172,6 @@ export default async function pluginContentBlog( blogTitle, }); - const tagsPath = normalizeUrl([baseBlogUrl, tagsBasePath]); - - const blogTagsListPath = - Object.keys(blogTags).length > 0 ? tagsPath : null; - return { blogSidebarTitle, blogPosts, @@ -307,30 +307,47 @@ export default async function pluginContentBlog( }), ); - // Tags. - if (blogTagsListPath === null) { + // Tags. This is the last part so we early-return if there are no tags. + if (Object.keys(blogTags).length === 0) { return; } - const tagsModule: {[tagName: string]: TagModule} = Object.fromEntries( - Object.entries(blogTags).map(([, tag]) => { - const tagModule: TagModule = { - allTagsPath: blogTagsListPath, - name: tag.name, - count: tag.items.length, - permalink: tag.permalink, - }; - return [tag.name, tagModule]; - }), - ); + async function createTagsListPage() { + const tagsProp: TagsListItem[] = Object.values(blogTags).map((tag) => ({ + label: tag.label, + permalink: tag.permalink, + count: tag.items.length, + })); - async function createTagRoutes(tag: BlogTag): Promise { + const tagsPropPath = await createData( + `${docuHash(`${blogTagsListPath}-tags`)}.json`, + JSON.stringify(tagsProp, null, 2), + ); + + addRoute({ + path: blogTagsListPath, + component: blogTagsListComponent, + exact: true, + modules: { + sidebar: aliasedSource(sidebarProp), + tags: aliasedSource(tagsPropPath), + }, + }); + } + + async function createTagPostsListPage(tag: BlogTag): Promise { await Promise.all( tag.pages.map(async (blogPaginated) => { const {metadata, items} = blogPaginated; - const tagsMetadataPath = await createData( + const tagProp: TagModule = { + label: tag.label, + permalink: tag.permalink, + allTagsPath: blogTagsListPath, + count: tag.items.length, + }; + const tagPropPath = await createData( `${docuHash(metadata.permalink)}.json`, - JSON.stringify(tagsModule[tag.name], null, 2), + JSON.stringify(tagProp, null, 2), ); const listMetadataPath = await createData( @@ -356,7 +373,7 @@ export default async function pluginContentBlog( }, }; }), - metadata: aliasedSource(tagsMetadataPath), + tag: aliasedSource(tagPropPath), listMetadata: aliasedSource(listMetadataPath), }, }); @@ -364,25 +381,8 @@ export default async function pluginContentBlog( ); } - await Promise.all(Object.values(blogTags).map(createTagRoutes)); - - // Only create /tags page if there are tags. - if (Object.keys(blogTags).length > 0) { - const tagsListPath = await createData( - `${docuHash(`${blogTagsListPath}-tags`)}.json`, - JSON.stringify(tagsModule, null, 2), - ); - - addRoute({ - path: blogTagsListPath, - component: blogTagsListComponent, - exact: true, - modules: { - sidebar: aliasedSource(sidebarProp), - tags: aliasedSource(tagsListPath), - }, - }); - } + await createTagsListPage(); + await Promise.all(Object.values(blogTags).map(createTagPostsListPage)); }, translateContent({content, translationFiles}) { diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts index 1733bff9bc16..939ae794289f 100644 --- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts +++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts @@ -7,8 +7,9 @@ declare module '@docusaurus/plugin-content-blog' { import type {MDXOptions} from '@docusaurus/mdx-loader'; - import type {FrontMatterTag, Tag} from '@docusaurus/utils'; + import type {FrontMatterTag} from '@docusaurus/utils'; import type {Overwrite} from 'utility-types'; + import type {Tag} from '@docusaurus/types'; export type Assets = { /** @@ -406,17 +407,6 @@ declare module '@docusaurus/plugin-content-blog' { } >; - export type TagModule = { - /** Permalink of the tag's own page. */ - permalink: string; - /** Name of the tag. */ - name: string; - /** Number of posts with this tag. */ - count: number; - /** The tags list page. */ - allTagsPath: string; - }; - export type BlogSidebar = { title: string; items: {title: string; permalink: string}[]; @@ -511,28 +501,30 @@ declare module '@theme/BlogListPage' { } declare module '@theme/BlogTagsListPage' { - import type {BlogSidebar, TagModule} from '@docusaurus/plugin-content-blog'; + import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; + import type {TagsListItem} from '@docusaurus/types'; export interface Props { /** Blog sidebar. */ readonly sidebar: BlogSidebar; - /** A map from tag names to the full tag module. */ - readonly tags: Readonly<{[tagName: string]: TagModule}>; + /** All tags declared in this blog. */ + readonly tags: TagsListItem[]; } export default function BlogTagsListPage(props: Props): JSX.Element; } declare module '@theme/BlogTagsPostsPage' { - import type {BlogSidebar, TagModule} from '@docusaurus/plugin-content-blog'; + import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; import type {Content} from '@theme/BlogPostPage'; import type {Metadata} from '@theme/BlogListPage'; + import type {TagModule} from '@docusaurus/types'; export interface Props { /** Blog sidebar. */ readonly sidebar: BlogSidebar; /** Metadata of this tag. */ - readonly metadata: TagModule; + readonly tag: TagModule; /** Looks exactly the same as the posts list page */ readonly listMetadata: Metadata; /** diff --git a/packages/docusaurus-plugin-content-blog/src/types.ts b/packages/docusaurus-plugin-content-blog/src/types.ts index 220ddbf1d579..84f76fa92753 100644 --- a/packages/docusaurus-plugin-content-blog/src/types.ts +++ b/packages/docusaurus-plugin-content-blog/src/types.ts @@ -6,6 +6,7 @@ */ import type {BrokenMarkdownLink, ContentPaths} from '@docusaurus/utils'; +import type {Tag} from '@docusaurus/types'; import type {BlogPostMetadata} from '@docusaurus/plugin-content-blog'; import type {Metadata as BlogPaginatedMetadata} from '@theme/BlogListPage'; @@ -16,22 +17,16 @@ export type BlogContent = { blogPosts: BlogPost[]; blogListPaginated: BlogPaginated[]; blogTags: BlogTags; - blogTagsListPath: string | null; + blogTagsListPath: string; }; export type BlogTags = { - // TODO, the key is the tag slug/permalink - // This is due to legacy frontmatter: tags: - // [{label: "xyz", permalink: "/1"}, {label: "xyz", permalink: "/2"}] - // Soon we should forbid declaring permalink through frontmatter - [tagKey: string]: BlogTag; + [permalink: string]: BlogTag; }; -export type BlogTag = { - name: string; +export type BlogTag = Tag & { /** Blog post permalinks. */ items: string[]; - permalink: string; pages: BlogPaginated[]; }; diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/i18n/zh-Hans/docusaurus-plugin-content-docs/current/ipsum.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/i18n/zh-Hans/docusaurus-plugin-content-docs/current/ipsum.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/i18n/zh-Hans/docusaurus-plugin-content-docs/current/lorem.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/i18n/zh-Hans/docusaurus-plugin-content-docs/current/lorem.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/i18n/fr/docusaurus-plugin-content-docs-community/current/hello.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/i18n/fr/docusaurus-plugin-content-docs-community/current/hello.md new file mode 100644 index 000000000000..e69de29bb2d1 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 55f087ee4e7d..2af71ead7b26 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 @@ -714,9 +714,11 @@ exports[`simple website content: data 1`] = ` } }", "tag-docs-tags-tag-1-b3f.json": "{ - \\"name\\": \\"tag 1\\", + \\"label\\": \\"tag 1\\", \\"permalink\\": \\"/docs/tags/tag-1\\", - \\"docs\\": [ + \\"allTagsPath\\": \\"/docs/tags\\", + \\"count\\": 2, + \\"items\\": [ { \\"id\\": \\"foo/baz\\", \\"title\\": \\"baz\\", @@ -729,48 +731,49 @@ exports[`simple website content: data 1`] = ` \\"description\\": \\"Hi, Endilie here :)\\", \\"permalink\\": \\"/docs/\\" } - ], - \\"allTagsPath\\": \\"/docs/tags\\" + ] }", "tag-docs-tags-tag-2-custom-permalink-825.json": "{ - \\"name\\": \\"tag 2\\", + \\"label\\": \\"tag 2\\", \\"permalink\\": \\"/docs/tags/tag2-custom-permalink\\", - \\"docs\\": [ + \\"allTagsPath\\": \\"/docs/tags\\", + \\"count\\": 1, + \\"items\\": [ { \\"id\\": \\"foo/baz\\", \\"title\\": \\"baz\\", \\"description\\": \\"Images\\", \\"permalink\\": \\"/docs/foo/bazSlug.html\\" } - ], - \\"allTagsPath\\": \\"/docs/tags\\" + ] }", "tag-docs-tags-tag-3-ab5.json": "{ - \\"name\\": \\"tag 3\\", + \\"label\\": \\"tag 3\\", \\"permalink\\": \\"/docs/tags/tag-3\\", - \\"docs\\": [ + \\"allTagsPath\\": \\"/docs/tags\\", + \\"count\\": 1, + \\"items\\": [ { \\"id\\": \\"hello\\", \\"title\\": \\"Hello, World !\\", \\"description\\": \\"Hi, Endilie here :)\\", \\"permalink\\": \\"/docs/\\" } - ], - \\"allTagsPath\\": \\"/docs/tags\\" + ] }", "tags-list-current-prop-15a.json": "[ { - \\"name\\": \\"tag 1\\", + \\"label\\": \\"tag 1\\", \\"permalink\\": \\"/docs/tags/tag-1\\", \\"count\\": 2 }, { - \\"name\\": \\"tag 2\\", + \\"label\\": \\"tag 2\\", \\"permalink\\": \\"/docs/tags/tag2-custom-permalink\\", \\"count\\": 1 }, { - \\"name\\": \\"tag 3\\", + \\"label\\": \\"tag 3\\", \\"permalink\\": \\"/docs/tags/tag-3\\", \\"count\\": 1 } @@ -3172,57 +3175,60 @@ exports[`versioned website content: data 1`] = ` } }", "tag-docs-next-tags-bar-tag-1-a8f.json": "{ - \\"name\\": \\"barTag 1\\", + \\"label\\": \\"barTag 1\\", \\"permalink\\": \\"/docs/next/tags/bar-tag-1\\", - \\"docs\\": [ + \\"allTagsPath\\": \\"/docs/next/tags\\", + \\"count\\": 1, + \\"items\\": [ { \\"id\\": \\"foo/bar\\", \\"title\\": \\"bar\\", \\"description\\": \\"This is next version of bar.\\", \\"permalink\\": \\"/docs/next/foo/barSlug\\" } - ], - \\"allTagsPath\\": \\"/docs/next/tags\\" + ] }", "tag-docs-next-tags-bar-tag-2-216.json": "{ - \\"name\\": \\"barTag-2\\", + \\"label\\": \\"barTag-2\\", \\"permalink\\": \\"/docs/next/tags/bar-tag-2\\", - \\"docs\\": [ + \\"allTagsPath\\": \\"/docs/next/tags\\", + \\"count\\": 1, + \\"items\\": [ { \\"id\\": \\"foo/bar\\", \\"title\\": \\"bar\\", \\"description\\": \\"This is next version of bar.\\", \\"permalink\\": \\"/docs/next/foo/barSlug\\" } - ], - \\"allTagsPath\\": \\"/docs/next/tags\\" + ] }", "tag-docs-next-tags-bar-tag-3-permalink-94a.json": "{ - \\"name\\": \\"barTag 3\\", + \\"label\\": \\"barTag 3\\", \\"permalink\\": \\"/docs/next/tags/barTag-3-permalink\\", - \\"docs\\": [ + \\"allTagsPath\\": \\"/docs/next/tags\\", + \\"count\\": 1, + \\"items\\": [ { \\"id\\": \\"foo/bar\\", \\"title\\": \\"bar\\", \\"description\\": \\"This is next version of bar.\\", \\"permalink\\": \\"/docs/next/foo/barSlug\\" } - ], - \\"allTagsPath\\": \\"/docs/next/tags\\" + ] }", "tags-list-current-prop-15a.json": "[ { - \\"name\\": \\"barTag 1\\", + \\"label\\": \\"barTag 1\\", \\"permalink\\": \\"/docs/next/tags/bar-tag-1\\", \\"count\\": 1 }, { - \\"name\\": \\"barTag-2\\", + \\"label\\": \\"barTag-2\\", \\"permalink\\": \\"/docs/next/tags/bar-tag-2\\", \\"count\\": 1 }, { - \\"name\\": \\"barTag 3\\", + \\"label\\": \\"barTag 3\\", \\"permalink\\": \\"/docs/next/tags/barTag-3-permalink\\", \\"count\\": 1 } diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts index 22aeed790cb9..27d56ecee103 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/cli.test.ts @@ -8,10 +8,7 @@ import {jest} from '@jest/globals'; import path from 'path'; import {cliDocsVersionCommand} from '../cli'; -import type { - PathOptions, - SidebarOptions, -} from '@docusaurus/plugin-content-docs'; +import type {PluginOptions} from '@docusaurus/plugin-content-docs'; import fs from 'fs-extra'; import { getVersionedDocsDirPath, @@ -26,7 +23,8 @@ describe('docsVersion', () => { const simpleSiteDir = path.join(fixtureDir, 'simple-site'); const versionedSiteDir = path.join(fixtureDir, 'versioned-site'); - const DEFAULT_OPTIONS: PathOptions & SidebarOptions = { + const DEFAULT_OPTIONS: PluginOptions = { + id: 'default', path: 'docs', sidebarPath: '', sidebarCollapsed: true, @@ -35,32 +33,19 @@ describe('docsVersion', () => { it('no version tag provided', async () => { await expect(() => - cliDocsVersionCommand( - null, - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand(null, DEFAULT_OPTIONS, {siteDir: simpleSiteDir}), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: no version tag specified! Pass the version you wish to create as an argument, for example: 1.0.0."`, ); await expect(() => - cliDocsVersionCommand( - undefined, - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand(undefined, DEFAULT_OPTIONS, { + siteDir: simpleSiteDir, + }), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: no version tag specified! Pass the version you wish to create as an argument, for example: 1.0.0."`, ); await expect(() => - cliDocsVersionCommand( - '', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('', DEFAULT_OPTIONS, {siteDir: simpleSiteDir}), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: no version tag specified! Pass the version you wish to create as an argument, for example: 1.0.0."`, ); @@ -68,22 +53,16 @@ describe('docsVersion', () => { it('version tag should not have slash', async () => { await expect(() => - cliDocsVersionCommand( - 'foo/bar', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('foo/bar', DEFAULT_OPTIONS, { + siteDir: simpleSiteDir, + }), ).rejects.toThrowError( '[docs]: invalid version tag specified! Do not include slash (/) or backslash (\\). Try something like: 1.0.0.', ); await expect(() => - cliDocsVersionCommand( - 'foo\\bar', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('foo\\bar', DEFAULT_OPTIONS, { + siteDir: simpleSiteDir, + }), ).rejects.toThrowError( '[docs]: invalid version tag specified! Do not include slash (/) or backslash (\\). Try something like: 1.0.0.', ); @@ -91,12 +70,9 @@ describe('docsVersion', () => { it('version tag should not be too long', async () => { await expect(() => - cliDocsVersionCommand( - 'a'.repeat(255), - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('a'.repeat(255), DEFAULT_OPTIONS, { + siteDir: simpleSiteDir, + }), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: invalid version tag specified! Length cannot exceed 32 characters. Try something like: 1.0.0."`, ); @@ -104,22 +80,12 @@ describe('docsVersion', () => { it('version tag should not be a dot or two dots', async () => { await expect(() => - cliDocsVersionCommand( - '..', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('..', DEFAULT_OPTIONS, {siteDir: simpleSiteDir}), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: invalid version tag specified! Do not name your version \\".\\" or \\"..\\". Try something like: 1.0.0."`, ); await expect(() => - cliDocsVersionCommand( - '.', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('.', DEFAULT_OPTIONS, {siteDir: simpleSiteDir}), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: invalid version tag specified! Do not name your version \\".\\" or \\"..\\". Try something like: 1.0.0."`, ); @@ -127,32 +93,23 @@ describe('docsVersion', () => { it('version tag should be a valid pathname', async () => { await expect(() => - cliDocsVersionCommand( - '', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('', DEFAULT_OPTIONS, { + siteDir: simpleSiteDir, + }), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0."`, ); await expect(() => - cliDocsVersionCommand( - 'foo\x00bar', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('foo\x00bar', DEFAULT_OPTIONS, { + siteDir: simpleSiteDir, + }), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0."`, ); await expect(() => - cliDocsVersionCommand( - 'foo:bar', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('foo:bar', DEFAULT_OPTIONS, { + siteDir: simpleSiteDir, + }), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0."`, ); @@ -160,12 +117,9 @@ describe('docsVersion', () => { it('version tag already exist', async () => { await expect(() => - cliDocsVersionCommand( - '1.0.0', - versionedSiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('1.0.0', DEFAULT_OPTIONS, { + siteDir: versionedSiteDir, + }), ).rejects.toThrowErrorMatchingInlineSnapshot( `"[docs]: this version already exists! Use a version tag that does not already exist."`, ); @@ -174,14 +128,12 @@ describe('docsVersion', () => { it('no docs file to version', async () => { const emptySiteDir = path.join(fixtureDir, 'empty-site'); await expect(() => - cliDocsVersionCommand( - '1.0.0', - emptySiteDir, - DEFAULT_PLUGIN_ID, - DEFAULT_OPTIONS, - ), + cliDocsVersionCommand('1.0.0', DEFAULT_OPTIONS, { + siteDir: emptySiteDir, + i18n: {locales: ['en', 'zh-Hans'], defaultLocale: 'en'}, + }), ).rejects.toThrowErrorMatchingInlineSnapshot( - `"[docs]: no docs found in /packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/empty-site/docs."`, + `"[docs]: no docs found in \\"/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/empty-site/docs\\"."`, ); }); @@ -205,12 +157,10 @@ describe('docsVersion', () => { ...DEFAULT_OPTIONS, sidebarPath: path.join(simpleSiteDir, 'sidebars.json'), }; - await cliDocsVersionCommand( - '1.0.0', - simpleSiteDir, - DEFAULT_PLUGIN_ID, - options, - ); + await cliDocsVersionCommand('1.0.0', options, { + siteDir: simpleSiteDir, + i18n: {locales: ['en', 'zh-Hans'], defaultLocale: 'en'}, + }); expect(copyMock).toHaveBeenCalledWith( path.join(simpleSiteDir, options.path), path.join( @@ -218,6 +168,16 @@ describe('docsVersion', () => { 'version-1.0.0', ), ); + expect(copyMock).toHaveBeenCalledWith( + path.join( + simpleSiteDir, + 'i18n/zh-Hans/docusaurus-plugin-content-docs/current', + ), + path.join( + simpleSiteDir, + 'i18n/zh-Hans/docusaurus-plugin-content-docs/version-1.0.0', + ), + ); expect(versionedSidebar).toMatchSnapshot(); expect(versionedSidebarPath).toEqual( path.join( @@ -256,16 +216,15 @@ describe('docsVersion', () => { versions = JSON.parse(content as string); }); const consoleMock = jest.spyOn(console, 'log').mockImplementation(() => {}); + const warnMock = jest.spyOn(console, 'warn').mockImplementation(() => {}); const options = { ...DEFAULT_OPTIONS, sidebarPath: path.join(versionedSiteDir, 'sidebars.json'), }; - await cliDocsVersionCommand( - '2.0.0', - versionedSiteDir, - DEFAULT_PLUGIN_ID, - options, - ); + await cliDocsVersionCommand('2.0.0', options, { + siteDir: versionedSiteDir, + i18n: {locales: ['en', 'zh-Hans'], defaultLocale: 'en'}, + }); expect(copyMock).toHaveBeenCalledWith( path.join(versionedSiteDir, options.path), path.join( @@ -289,7 +248,11 @@ describe('docsVersion', () => { /.*\[SUCCESS\].*\[docs\].*: version .*2\.0\.0.* created!.*/, ), ); + expect(warnMock.mock.calls[0][0]).toMatchInlineSnapshot( + `"[WARNING] [docs]: no docs found in \\"/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/versioned-site/i18n/zh-Hans/docusaurus-plugin-content-docs/current\\". Skipping."`, + ); + warnMock.mockRestore(); copyMock.mockRestore(); writeMock.mockRestore(); consoleMock.mockRestore(); @@ -315,10 +278,14 @@ describe('docsVersion', () => { const consoleMock = jest.spyOn(console, 'log').mockImplementation(() => {}); const options = { ...DEFAULT_OPTIONS, + id: pluginId, path: 'community', sidebarPath: path.join(versionedSiteDir, 'community_sidebars.json'), }; - await cliDocsVersionCommand('2.0.0', versionedSiteDir, pluginId, options); + await cliDocsVersionCommand('2.0.0', options, { + siteDir: versionedSiteDir, + i18n: {locales: ['en', 'fr'], defaultLocale: 'en'}, + }); expect(copyMock).toHaveBeenCalledWith( path.join(versionedSiteDir, options.path), path.join( @@ -326,6 +293,16 @@ describe('docsVersion', () => { 'version-2.0.0', ), ); + expect(copyMock).toHaveBeenCalledWith( + path.join( + versionedSiteDir, + 'i18n/fr/docusaurus-plugin-content-docs-community/current', + ), + path.join( + versionedSiteDir, + 'i18n/fr/docusaurus-plugin-content-docs-community/version-2.0.0', + ), + ); expect(versionedSidebar).toMatchSnapshot(); expect(versionedSidebarPath).toEqual( path.join( diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts index 926bcfb8f96f..1ad236a88638 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/index.test.ts @@ -16,7 +16,7 @@ import pluginContentDocs from '../index'; import {loadContext} from '@docusaurus/core/src/server/index'; import {applyConfigureWebpack} from '@docusaurus/core/src/webpack/utils'; import type {RouteConfig} from '@docusaurus/types'; -import {posixPath, DEFAULT_PLUGIN_ID} from '@docusaurus/utils'; +import {posixPath} from '@docusaurus/utils'; import {sortConfig} from '@docusaurus/core/src/server/plugins/routeConfig'; import * as cliDocs from '../cli'; @@ -230,23 +230,21 @@ describe('simple website', () => { const siteDir = path.join(__dirname, '__fixtures__', 'simple-site'); const context = await loadContext({siteDir}); const sidebarPath = path.join(siteDir, 'sidebars.json'); - const plugin = await pluginContentDocs( - context, - validateOptions({ - validate: normalizePluginOptions, - options: { - path: 'docs', - sidebarPath, - }, - }), - ); + const options = validateOptions({ + validate: normalizePluginOptions, + options: { + path: 'docs', + sidebarPath, + }, + }); + const plugin = await pluginContentDocs(context, options); const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); - return {siteDir, context, sidebarPath, plugin, pluginContentDir}; + return {siteDir, context, sidebarPath, plugin, options, pluginContentDir}; } it('extendCli - docsVersion', async () => { - const {siteDir, sidebarPath, plugin} = await loadSite(); + const {plugin, options, context} = await loadSite(); const mock = jest .spyOn(cliDocs, 'cliDocsVersionCommand') .mockImplementation(async () => {}); @@ -256,12 +254,7 @@ describe('simple website', () => { plugin.extendCli!(cli); cli.parse(['node', 'test', 'docs:version', '1.0.0']); expect(mock).toHaveBeenCalledTimes(1); - expect(mock).toHaveBeenCalledWith('1.0.0', siteDir, DEFAULT_PLUGIN_ID, { - path: 'docs', - sidebarPath, - sidebarCollapsed: true, - sidebarCollapsible: true, - }); + expect(mock).toHaveBeenCalledWith('1.0.0', options, context); mock.mockRestore(); }); @@ -344,29 +337,28 @@ describe('versioned website', () => { const context = await loadContext({siteDir}); const sidebarPath = path.join(siteDir, 'sidebars.json'); const routeBasePath = 'docs'; - const plugin = await pluginContentDocs( - context, - validateOptions({ - validate: normalizePluginOptions, - options: { - routeBasePath, - sidebarPath, - }, - }), - ); + const options = validateOptions({ + validate: normalizePluginOptions, + options: { + routeBasePath, + sidebarPath, + }, + }); + const plugin = await pluginContentDocs(context, options); const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); return { siteDir, context, routeBasePath, sidebarPath, + options, plugin, pluginContentDir, }; } it('extendCli - docsVersion', async () => { - const {siteDir, routeBasePath, sidebarPath, plugin} = await loadSite(); + const {plugin, context, options} = await loadSite(); const mock = jest .spyOn(cliDocs, 'cliDocsVersionCommand') .mockImplementation(async () => {}); @@ -376,12 +368,7 @@ describe('versioned website', () => { plugin.extendCli!(cli); cli.parse(['node', 'test', 'docs:version', '2.0.0']); expect(mock).toHaveBeenCalledTimes(1); - expect(mock).toHaveBeenCalledWith('2.0.0', siteDir, DEFAULT_PLUGIN_ID, { - path: routeBasePath, - sidebarPath, - sidebarCollapsed: true, - sidebarCollapsible: true, - }); + expect(mock).toHaveBeenCalledWith('2.0.0', options, context); mock.mockRestore(); }); @@ -474,18 +461,16 @@ describe('versioned website (community)', () => { const sidebarPath = path.join(siteDir, 'community_sidebars.json'); const routeBasePath = 'community'; const pluginId = 'community'; - const plugin = await pluginContentDocs( - context, - validateOptions({ - validate: normalizePluginOptions, - options: { - id: 'community', - path: 'community', - routeBasePath, - sidebarPath, - }, - }), - ); + const options = validateOptions({ + validate: normalizePluginOptions, + options: { + id: 'community', + path: 'community', + routeBasePath, + sidebarPath, + }, + }); + const plugin = await pluginContentDocs(context, options); const pluginContentDir = path.join(context.generatedFilesDir, plugin.name); return { siteDir, @@ -493,14 +478,14 @@ describe('versioned website (community)', () => { routeBasePath, sidebarPath, pluginId, + options, plugin, pluginContentDir, }; } it('extendCli - docsVersion', async () => { - const {siteDir, routeBasePath, sidebarPath, pluginId, plugin} = - await loadSite(); + const {pluginId, plugin, options, context} = await loadSite(); const mock = jest .spyOn(cliDocs, 'cliDocsVersionCommand') .mockImplementation(async () => {}); @@ -510,12 +495,7 @@ describe('versioned website (community)', () => { plugin.extendCli!(cli); cli.parse(['node', 'test', `docs:version:${pluginId}`, '2.0.0']); expect(mock).toHaveBeenCalledTimes(1); - expect(mock).toHaveBeenCalledWith('2.0.0', siteDir, pluginId, { - path: routeBasePath, - sidebarPath, - sidebarCollapsed: true, - sidebarCollapsible: true, - }); + expect(mock).toHaveBeenCalledWith('2.0.0', options, context); mock.mockRestore(); }); diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/props.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/props.test.ts index 86bd7b88fc27..db2648c5e5d7 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/props.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/props.test.ts @@ -54,9 +54,10 @@ describe('toTagDocListProp', () => { expect(result).toEqual({ allTagsPath, - name: tag.label, + count: 2, + label: tag.label, permalink: tag.permalink, - docs: [doc3, doc1], // docs sorted by title, ignore "id5" absence + items: [doc3, doc1], // docs sorted by title, ignore "id5" absence }); }); }); diff --git a/packages/docusaurus-plugin-content-docs/src/cli.ts b/packages/docusaurus-plugin-content-docs/src/cli.ts index 710a5c3d7c67..e405d11560dc 100644 --- a/packages/docusaurus-plugin-content-docs/src/cli.ts +++ b/packages/docusaurus-plugin-content-docs/src/cli.ts @@ -9,16 +9,16 @@ import { getVersionsFilePath, getVersionedDocsDirPath, getVersionedSidebarsDirPath, + getDocsDirPathLocalized, } from './versions'; import fs from 'fs-extra'; import path from 'path'; -import type { - PathOptions, - SidebarOptions, -} from '@docusaurus/plugin-content-docs'; +import type {PluginOptions} from '@docusaurus/plugin-content-docs'; import {loadSidebarsFileUnsafe, resolveSidebarPathOption} from './sidebars'; +import {CURRENT_VERSION_NAME} from './constants'; import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils'; import logger from '@docusaurus/logger'; +import type {LoadContext} from '@docusaurus/types'; async function createVersionedSidebarFile({ siteDir, @@ -58,9 +58,8 @@ async function createVersionedSidebarFile({ // Tests depend on non-default export for mocking. export async function cliDocsVersionCommand( version: string | null | undefined, - siteDir: string, - pluginId: string, - options: PathOptions & SidebarOptions, + {id: pluginId, path: docsPath, sidebarPath}: PluginOptions, + {siteDir, i18n}: LoadContext, ): Promise { // It wouldn't be very user-friendly to show a [default] log prefix, // so we use [docs] instead of [default] @@ -114,22 +113,53 @@ export async function cliDocsVersionCommand( ); } - const {path: docsPath, sidebarPath} = options; - - // Copy docs files. - const docsDir = path.resolve(siteDir, docsPath); - - if ( - (await fs.pathExists(docsDir)) && - (await fs.readdir(docsDir)).length > 0 - ) { - const versionedDir = getVersionedDocsDirPath(siteDir, pluginId); - const newVersionDir = path.join(versionedDir, `version-${version}`); - await fs.copy(docsDir, newVersionDir); - } else { - throw new Error(`${pluginIdLogPrefix}: no docs found in ${docsDir}.`); + if (i18n.locales.length > 1) { + logger.info`Versioned docs will be created for the following locales: name=${i18n.locales}`; } + await Promise.all( + i18n.locales.map(async (locale) => { + // Copy docs files. + const docsDir = + locale === i18n.defaultLocale + ? path.resolve(siteDir, docsPath) + : getDocsDirPathLocalized({ + siteDir, + locale, + pluginId, + versionName: CURRENT_VERSION_NAME, + }); + + if ( + !(await fs.pathExists(docsDir)) || + (await fs.readdir(docsDir)).length === 0 + ) { + if (locale === i18n.defaultLocale) { + throw new Error( + logger.interpolate`${pluginIdLogPrefix}: no docs found in path=${docsDir}.`, + ); + } else { + logger.warn`${pluginIdLogPrefix}: no docs found in path=${docsDir}. Skipping.`; + return; + } + } + + const newVersionDir = + locale === i18n.defaultLocale + ? path.join( + getVersionedDocsDirPath(siteDir, pluginId), + `version-${version}`, + ) + : getDocsDirPathLocalized({ + siteDir, + locale, + pluginId, + versionName: version, + }); + await fs.copy(docsDir, newVersionDir); + }), + ); + await createVersionedSidebarFile({ siteDir, pluginId, diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts index a0dc21768960..7fbc9595b4b4 100644 --- a/packages/docusaurus-plugin-content-docs/src/index.ts +++ b/packages/docusaurus-plugin-content-docs/src/index.ts @@ -67,7 +67,7 @@ export default async function pluginContentDocs( const versionsMetadata = await readVersionsMetadata({context, options}); - const pluginId = options.id ?? DEFAULT_PLUGIN_ID; + const pluginId = options.id; const pluginDataDirRoot = path.join( generatedFilesDir, @@ -97,12 +97,7 @@ export default async function pluginContentDocs( .arguments('') .description(commandDescription) .action((version) => { - cliDocsVersionCommand(version, siteDir, pluginId, { - path: options.path, - sidebarPath: options.sidebarPath, - sidebarCollapsed: options.sidebarCollapsed, - sidebarCollapsible: options.sidebarCollapsible, - }); + cliDocsVersionCommand(version, options, context); }); }, @@ -228,13 +223,13 @@ export default async function pluginContentDocs( const tagsProp: PropTagsListPage['tags'] = Object.values( versionTags, ).map((tagValue) => ({ - name: tagValue.label, + label: tagValue.label, permalink: tagValue.permalink, count: tagValue.docIds.length, })); // Only create /tags page if there are tags. - if (Object.keys(tagsProp).length > 0) { + if (tagsProp.length > 0) { const tagsPropPath = await createData( `${docuHash(`tags-list-${version.versionName}-prop`)}.json`, JSON.stringify(tagsProp, null, 2), 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 271bfa4923db..77d4b7b894c0 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 @@ -7,7 +7,8 @@ declare module '@docusaurus/plugin-content-docs' { import type {MDXOptions} from '@docusaurus/mdx-loader'; - import type {ContentPaths, Tag, FrontMatterTag} from '@docusaurus/utils'; + import type {ContentPaths, FrontMatterTag} from '@docusaurus/utils'; + import type {TagsListItem, TagModule, Tag} from '@docusaurus/types'; import type {Required} from 'utility-types'; export type Assets = { @@ -483,25 +484,14 @@ declare module '@docusaurus/plugin-content-docs' { export type PropSidebar = import('./sidebars/types').PropSidebar; export type PropSidebars = import('./sidebars/types').PropSidebars; - export type PropTagDocListDoc = { - id: string; - title: string; - description: string; - permalink: string; - }; - export type PropTagDocList = { - allTagsPath: string; - name: string; // normalized name/label of the tag - permalink: string; // pathname of the tag - docs: PropTagDocListDoc[]; - }; + export type PropTagDocListDoc = Pick< + DocMetadata, + 'id' | 'title' | 'description' | 'permalink' + >; + export type PropTagDocList = TagModule & {items: PropTagDocListDoc[]}; export type PropTagsListPage = { - tags: { - name: string; - permalink: string; - count: number; - }[]; + tags: TagsListItem[]; }; } diff --git a/packages/docusaurus-plugin-content-docs/src/props.ts b/packages/docusaurus-plugin-content-docs/src/props.ts index d25888db63a8..43d3028d455c 100644 --- a/packages/docusaurus-plugin-content-docs/src/props.ts +++ b/packages/docusaurus-plugin-content-docs/src/props.ts @@ -137,7 +137,7 @@ export function toTagDocListProp({ }: { allTagsPath: string; tag: VersionTag; - docs: Pick[]; + docs: DocMetadata[]; }): PropTagDocList { function toDocListProp(): PropTagDocListDoc[] { const list = _.compact( @@ -154,9 +154,10 @@ export function toTagDocListProp({ } return { - name: tag.label, + label: tag.label, permalink: tag.permalink, - docs: toDocListProp(), allTagsPath, + count: tag.docIds.length, + items: toDocListProp(), }; } diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index 909a200da7e4..bb9f8e8e15d1 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -8,13 +8,14 @@ /// import type {Sidebars} from './sidebars/types'; -import type {BrokenMarkdownLink, Tag} from '@docusaurus/utils'; +import type {BrokenMarkdownLink} from '@docusaurus/utils'; import type { VersionMetadata, LastUpdateData, DocMetadata, CategoryGeneratedIndexMetadata, } from '@docusaurus/plugin-content-docs'; +import type {Tag} from '@docusaurus/types'; export type DocFile = { contentPath: string; // /!\ may be localized @@ -33,7 +34,7 @@ export type VersionTag = Tag & { docIds: string[]; }; export type VersionTags = { - [key: string]: VersionTag; + [permalink: string]: VersionTag; }; export type LoadedVersion = VersionMetadata & { diff --git a/packages/docusaurus-plugin-content-docs/src/versions.ts b/packages/docusaurus-plugin-content-docs/src/versions.ts index 7b06aa0e089c..a7f216f53223 100644 --- a/packages/docusaurus-plugin-content-docs/src/versions.ts +++ b/packages/docusaurus-plugin-content-docs/src/versions.ts @@ -133,7 +133,7 @@ export async function readVersionNames( return versions; } -function getDocsDirPathLocalized({ +export function getDocsDirPathLocalized({ siteDir, locale, pluginId, @@ -143,7 +143,7 @@ function getDocsDirPathLocalized({ locale: string; pluginId: string; versionName: string; -}) { +}): string { return getPluginI18nPath({ siteDir, locale, diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index 002e452e665c..4f95a2f19a84 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -47,13 +47,33 @@ declare module '@theme/BlogListPaginator' { export default function BlogListPaginator(props: Props): JSX.Element; } -declare module '@theme/BlogSidebar' { +declare module '@theme/BlogSidebar/Desktop' { + import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; + + export interface Props { + readonly sidebar: BlogSidebar; + } + + export default function BlogSidebarDesktop(props: Props): JSX.Element; +} + +declare module '@theme/BlogSidebar/Mobile' { import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; export interface Props { readonly sidebar: BlogSidebar; } + export default function BlogSidebarMobile(props: Props): JSX.Element; +} + +declare module '@theme/BlogSidebar' { + import type {BlogSidebar} from '@docusaurus/plugin-content-blog'; + + export interface Props { + readonly sidebar?: BlogSidebar; + } + export default function BlogSidebar(props: Props): JSX.Element; } @@ -1012,7 +1032,7 @@ declare module '@theme/IconExternalLink' { } declare module '@theme/TagsListByLetter' { - import type {TagsListItem} from '@docusaurus/theme-common'; + import type {TagsListItem} from '@docusaurus/types'; export interface Props { readonly tags: readonly TagsListItem[]; @@ -1021,7 +1041,7 @@ declare module '@theme/TagsListByLetter' { } declare module '@theme/TagsListInline' { - import type {Tag} from '@docusaurus/utils'; + import type {Tag} from '@docusaurus/types'; export interface Props { readonly tags: readonly Tag[]; @@ -1030,7 +1050,7 @@ declare module '@theme/TagsListInline' { } declare module '@theme/Tag' { - import type {TagsListItem} from '@docusaurus/theme-common'; + import type {TagsListItem} from '@docusaurus/types'; import type {Optional} from 'utility-types'; export interface Props extends Optional {} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx index 009eef93276d..30d68822c650 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogLayout/index.tsx @@ -20,11 +20,7 @@ export default function BlogLayout(props: Props): JSX.Element {
- {hasSidebar && ( - - )} +
+ + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogSidebar/styles.module.css b/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/styles.module.css similarity index 95% rename from packages/docusaurus-theme-classic/src/theme/BlogSidebar/styles.module.css rename to packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/styles.module.css index d3e7700af508..3ca0d4b8d5e7 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogSidebar/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/styles.module.css @@ -40,11 +40,7 @@ } @media (max-width: 996px) { - .sidebarDesktop { - display: none; - } - .sidebar { - top: 0; + display: none; } } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Mobile/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Mobile/index.tsx new file mode 100644 index 000000000000..a07967840419 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Mobile/index.tsx @@ -0,0 +1,38 @@ +/** + * 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 Link from '@docusaurus/Link'; +import {NavbarSecondaryMenuFiller} from '@docusaurus/theme-common'; +import type {Props} from '@theme/BlogSidebar/Mobile'; + +function BlogSidebarMobileSecondaryMenu({sidebar}: Props): JSX.Element { + return ( +
    + {sidebar.items.map((item) => ( +
  • + + {item.title} + +
  • + ))} +
+ ); +} + +export default function BlogSidebarMobile(props: Props): JSX.Element { + return ( + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/BlogSidebar/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogSidebar/index.tsx index ad478860fe42..742f4bd42781 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogSidebar/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogSidebar/index.tsx @@ -6,61 +6,19 @@ */ import React from 'react'; -import clsx from 'clsx'; -import Link from '@docusaurus/Link'; -import {translate} from '@docusaurus/Translate'; -import { - NavbarSecondaryMenuFiller, - useWindowSize, -} from '@docusaurus/theme-common'; +import BlogSidebarDesktop from '@theme/BlogSidebar/Desktop'; +import BlogSidebarMobile from '@theme/BlogSidebar/Mobile'; +import {useWindowSize} from '@docusaurus/theme-common'; import type {Props} from '@theme/BlogSidebar'; -import styles from './styles.module.css'; -function BlogSidebarContent({ - sidebar, - className, -}: Props & {className?: string}): JSX.Element { - return ( - - ); -} - -export default function BlogSidebar(props: Props): JSX.Element | null { +export default function BlogSidebar({sidebar}: Props): JSX.Element | null { const windowSize = useWindowSize(); - if (props.sidebar.items.length === 0) { + if (!sidebar?.items.length) { return null; } // Mobile sidebar doesn't need to be server-rendered if (windowSize === 'mobile') { - return ( - - ); + return ; } - return ; + return ; } diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx index 1124e06aaa37..2f53b6ee41fa 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsListPage/index.tsx @@ -19,8 +19,7 @@ import { import SearchMetadata from '../SearchMetadata'; import clsx from 'clsx'; -export default function BlogTagsListPage(props: Props): JSX.Element { - const {tags, sidebar} = props; +export default function BlogTagsListPage({tags, sidebar}: Props): JSX.Element { const title = translateTagsPageTitle(); return (

{title}

- +
); diff --git a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx index fa3bf26a2437..fddda0795d75 100644 --- a/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/BlogTagsPostsPage/index.tsx @@ -40,9 +40,12 @@ function useBlogPostsPlural() { ); } -export default function BlogTagsPostsPage(props: Props): JSX.Element { - const {metadata, items, sidebar, listMetadata} = props; - const {allTagsPath, name: tagName, count} = metadata; +export default function BlogTagsPostsPage({ + tag, + items, + sidebar, + listMetadata, +}: Props): JSX.Element { const blogPostsPlural = useBlogPostsPlural(); const title = translate( { @@ -50,7 +53,7 @@ export default function BlogTagsPostsPage(props: Props): JSX.Element { description: 'The title of the page for a blog tag', message: '{nPosts} tagged with "{tagName}"', }, - {nPosts: blogPostsPlural(count), tagName}, + {nPosts: blogPostsPlural(tag.count), tagName: tag.label}, ); return ( @@ -65,7 +68,7 @@ export default function BlogTagsPostsPage(props: Props): JSX.Element {

{title}

- + diff --git a/packages/docusaurus-theme-classic/src/theme/DocTagDocListPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocTagDocListPage/index.tsx index 512eabc4a892..22cf3ff7ac9b 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 { ThemeClassNames, usePluralForm, } from '@docusaurus/theme-common'; -import type {PropTagDocListDoc} from '@docusaurus/plugin-content-docs'; import Translate, {translate} from '@docusaurus/Translate'; import type {Props} from '@theme/DocTagDocListPage'; import SearchMetadata from '@theme/SearchMetadata'; @@ -39,7 +38,7 @@ function useNDocsTaggedPlural() { ); } -function DocItem({doc}: {doc: PropTagDocListDoc}): JSX.Element { +function DocItem({doc}: {doc: Props['tag']['items'][number]}): JSX.Element { return (
@@ -58,7 +57,7 @@ export default function DocTagDocListPage({tag}: Props): JSX.Element { description: 'The title of the page for a docs tag', message: '{nDocsTagged} with "{tagName}"', }, - {nDocsTagged: nDocsTaggedPlural(tag.docs.length), tagName: tag.name}, + {nDocsTagged: nDocsTaggedPlural(tag.count), tagName: tag.label}, ); return ( @@ -84,7 +83,7 @@ export default function DocTagDocListPage({tag}: Props): JSX.Element {
- {tag.docs.map((doc) => ( + {tag.items.map((doc) => ( ))}
diff --git a/packages/docusaurus-theme-classic/src/theme/Tag/index.tsx b/packages/docusaurus-theme-classic/src/theme/Tag/index.tsx index ffe3ea717df3..3b04395d3954 100644 --- a/packages/docusaurus-theme-classic/src/theme/Tag/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Tag/index.tsx @@ -12,9 +12,7 @@ import type {Props} from '@theme/Tag'; import styles from './styles.module.css'; -export default function Tag(props: Props): JSX.Element { - const {permalink, name, count} = props; - +export default function Tag({permalink, label, count}: Props): JSX.Element { return ( - {name} + {label} {count && {count}} ); diff --git a/packages/docusaurus-theme-classic/src/theme/TagsListInline/index.tsx b/packages/docusaurus-theme-classic/src/theme/TagsListInline/index.tsx index abf7bb497f78..b0b6fdaff5eb 100644 --- a/packages/docusaurus-theme-classic/src/theme/TagsListInline/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/TagsListInline/index.tsx @@ -26,7 +26,7 @@ export default function TagsListInline({tags}: Props): JSX.Element {
    {tags.map(({label, permalink: tagPermalink}) => (
  • - +
  • ))}
diff --git a/packages/docusaurus-theme-common/src/index.ts b/packages/docusaurus-theme-common/src/index.ts index a12e8e78f0b2..a094e736b59d 100644 --- a/packages/docusaurus-theme-common/src/index.ts +++ b/packages/docusaurus-theme-common/src/index.ts @@ -87,7 +87,6 @@ export { translateTagsPageTitle, listTagsByLetters, type TagLetterEntry, - type TagsListItem, } from './utils/tagsUtils'; export {useHistoryPopHandler} from './utils/historyUtils'; diff --git a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts index 2de931f7e047..4e1f614d7702 100644 --- a/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts +++ b/packages/docusaurus-theme-common/src/utils/__tests__/tagUtils.test.ts @@ -15,32 +15,32 @@ describe('listTagsByLetters', () => { it('creates letters list', () => { const tag1: Tag = { - name: 'tag1', + label: 'tag1', permalink: '/tag1', count: 1, }; const tag2: Tag = { - name: 'Tag2', + label: 'Tag2', permalink: '/tag2', count: 11, }; const tagZxy: Tag = { - name: 'zxy', + label: 'zxy', permalink: '/zxy', count: 987, }; const tagAbc: Tag = { - name: 'Abc', + label: 'Abc', permalink: '/abc', count: 123, }; const tagDef: Tag = { - name: 'def', + label: 'def', permalink: '/def', count: 1, }; const tagAaa: Tag = { - name: 'aaa', + label: 'aaa', permalink: '/aaa', count: 10, }; diff --git a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts index 99dfdc9d379f..c29afab1cf65 100644 --- a/packages/docusaurus-theme-common/src/utils/tagsUtils.ts +++ b/packages/docusaurus-theme-common/src/utils/tagsUtils.ts @@ -6,6 +6,7 @@ */ import {translate} from '@docusaurus/Translate'; +import type {TagsListItem} from '@docusaurus/types'; export const translateTagsPageTitle = (): string => translate({ @@ -14,13 +15,7 @@ export const translateTagsPageTitle = (): string => description: 'The title of the tag list page', }); -export type TagsListItem = Readonly<{ - name: string; - permalink: string; - count: number; -}>; - -export type TagLetterEntry = Readonly<{letter: string; tags: TagsListItem[]}>; +export type TagLetterEntry = {letter: string; tags: TagsListItem[]}; function getTagLetter(tag: string): string { return tag[0]!.toUpperCase(); @@ -35,7 +30,7 @@ export function listTagsByLetters( ): TagLetterEntry[] { const groups: {[initial: string]: TagsListItem[]} = {}; Object.values(tags).forEach((tag) => { - const initial = getTagLetter(tag.name); + const initial = getTagLetter(tag.label); groups[initial] ??= []; groups[initial]!.push(tag); }); @@ -47,7 +42,7 @@ export function listTagsByLetters( .map(([letter, letterTags]) => { // Sort tags inside a letter const sortedTags = letterTags.sort((tag1, tag2) => - tag1.name.localeCompare(tag2.name), + tag1.label.localeCompare(tag2.label), ); return {letter, tags: sortedTags}; }) diff --git a/packages/docusaurus-types/src/index.d.ts b/packages/docusaurus-types/src/index.d.ts index 7d24b70a47e7..e20bd74ee6a5 100644 --- a/packages/docusaurus-types/src/index.d.ts +++ b/packages/docusaurus-types/src/index.d.ts @@ -27,9 +27,15 @@ export type PluginConfig = | string | [string, PluginOptions] | [PluginModule, PluginOptions] - | PluginModule; + | PluginModule + | false + | null; -export type PresetConfig = string | [string, {[key: string]: unknown}]; +export type PresetConfig = + | string + | [string, {[key: string]: unknown}] + | false + | null; export type ThemeConfig = { [key: string]: unknown; @@ -487,6 +493,12 @@ export type RouteConfig = { * `createData`) */ modules?: RouteModules; + /** + * The route context will wrap the `component`. Use `useRouteContext` to + * retrieve what's declared here. Note that all custom route context declared + * here will be namespaced under {@link RouteContext.data}. + */ + context?: RouteModules; /** Nested routes config. */ routes?: RouteConfig[]; /** React router config option: `exact` routes would not match subroutes. */ @@ -588,3 +600,22 @@ export type ClientModule = { }) => void; onRouteUpdateDelayed?: (args: {location: Location}) => void; }; + +/** What the user configures. */ +export type Tag = { + label: string; + /** Permalink to this tag's page, without the `/tags/` base path. */ + permalink: string; +}; + +/** What the tags list page should know about each tag. */ +export type TagsListItem = Tag & { + /** Number of posts/docs with this tag. */ + count: number; +}; + +/** What the tag's own page should know about the tag. */ +export type TagModule = TagsListItem & { + /** The tags list page's permalink. */ + allTagsPath: string; +}; diff --git a/packages/docusaurus-utils-validation/src/validationSchemas.ts b/packages/docusaurus-utils-validation/src/validationSchemas.ts index 4e6134a7b2a1..385efd4ff226 100644 --- a/packages/docusaurus-utils-validation/src/validationSchemas.ts +++ b/packages/docusaurus-utils-validation/src/validationSchemas.ts @@ -7,7 +7,7 @@ import Joi from './Joi'; import {isValidPathname, DEFAULT_PLUGIN_ID} from '@docusaurus/utils'; -import type {Tag} from '@docusaurus/utils'; +import type {Tag} from '@docusaurus/types'; import {JoiFrontMatter} from './JoiFrontMatter'; export const PluginIdSchema = Joi.string() diff --git a/packages/docusaurus-utils/src/index.ts b/packages/docusaurus-utils/src/index.ts index 7cd871a631e8..575f67e62920 100644 --- a/packages/docusaurus-utils/src/index.ts +++ b/packages/docusaurus-utils/src/index.ts @@ -56,7 +56,6 @@ export { buildSshUrl, } from './urlUtils'; export { - type Tag, type FrontMatterTag, normalizeFrontMatterTags, groupTaggedItems, diff --git a/packages/docusaurus-utils/src/tags.ts b/packages/docusaurus-utils/src/tags.ts index fa5eb4eebd1e..e3eb2933e6f0 100644 --- a/packages/docusaurus-utils/src/tags.ts +++ b/packages/docusaurus-utils/src/tags.ts @@ -7,12 +7,7 @@ import _ from 'lodash'; import {normalizeUrl} from './urlUtils'; - -export type Tag = { - label: string; - /** Permalink to this tag's page, without the `/tags/` base path. */ - permalink: string; -}; +import type {Tag} from '@docusaurus/types'; export type FrontMatterTag = string | Tag; 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 80de4e006fca..9979705a3132 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/routes.test.ts.snap @@ -66,19 +66,26 @@ exports[`loadRoutes loads nested route config 1`] = ` "docsMetadata---docs-routef-34-881": "docs-b5f.json", "metadata---docs-foo-baz-2-cf-fa7": "docs-foo-baz-dd9.json", "metadata---docs-hello-956-741": "docs-hello-da2.json", + "plugin---docs-hello-665-3ca": "pluginRouteContextModule-100.json", }, "routesChunkNames": { - "/docs/hello-44b": { + "/docs/hello-fcc": { "__comp": "__comp---theme-doc-item-178-a40", + "__context": { + "plugin": "plugin---docs-hello-665-3ca", + }, "content": "content---docs-helloaff-811", "metadata": "metadata---docs-hello-956-741", }, - "/docs:route-52d": { + "/docs:route-502": { "__comp": "__comp---theme-doc-page-1-be-9be", "docsMetadata": "docsMetadata---docs-routef-34-881", }, - "docs/foo/baz-070": { + "docs/foo/baz-eb2": { "__comp": "__comp---theme-doc-item-178-a40", + "__context": { + "plugin": "plugin---docs-hello-665-3ca", + }, "content": "content---docs-foo-baz-8-ce-61e", "metadata": "metadata---docs-foo-baz-2-cf-fa7", }, @@ -89,17 +96,17 @@ import ComponentCreator from '@docusaurus/ComponentCreator'; export default [ { path: '/docs:route', - component: ComponentCreator('/docs:route', '52d'), + component: ComponentCreator('/docs:route', '502'), routes: [ { path: '/docs/hello', - component: ComponentCreator('/docs/hello', '44b'), + component: ComponentCreator('/docs/hello', 'fcc'), exact: true, sidebar: \\"main\\" }, { path: 'docs/foo/baz', - component: ComponentCreator('docs/foo/baz', '070'), + component: ComponentCreator('docs/foo/baz', 'eb2'), sidebar: \\"secondary\\", \\"key:a\\": \\"containing colon\\", \\"key'b\\": \\"containing quote\\", diff --git a/packages/docusaurus/src/server/__tests__/configValidation.test.ts b/packages/docusaurus/src/server/__tests__/configValidation.test.ts index b6d606b41701..1f8838efa7fc 100644 --- a/packages/docusaurus/src/server/__tests__/configValidation.test.ts +++ b/packages/docusaurus/src/server/__tests__/configValidation.test.ts @@ -173,6 +173,7 @@ describe('normalizeConfig', () => { 'should accept [function, object] for plugin', [[() => {}, {it: 'should work'}]], ], + ['should accept false/null for plugin', [false, null, 'classic']], ])(`%s for the input of: %p`, (_message, plugins) => { expect(() => { normalizeConfig({ @@ -211,6 +212,7 @@ describe('normalizeConfig', () => { 'should accept [function, object] for theme', [[function theme() {}, {it: 'should work'}]], ], + ['should accept false/null for themes', [false, null, 'classic']], ])(`%s for the input of: %p`, (_message, themes) => { expect(() => { normalizeConfig({ @@ -254,6 +256,14 @@ describe('normalizeConfig', () => { `); }); + it('accepts presets as false / null', () => { + expect(() => { + normalizeConfig({ + presets: [false, null, 'classic'], + }); + }).not.toThrow(); + }); + it("throws error if scripts doesn't have src", () => { expect(() => { normalizeConfig({ diff --git a/packages/docusaurus/src/server/__tests__/routes.test.ts b/packages/docusaurus/src/server/__tests__/routes.test.ts index 1e8062a69632..b18f0ccca679 100644 --- a/packages/docusaurus/src/server/__tests__/routes.test.ts +++ b/packages/docusaurus/src/server/__tests__/routes.test.ts @@ -117,6 +117,9 @@ describe('loadRoutes', () => { content: 'docs/hello.md', metadata: 'docs-hello-da2.json', }, + context: { + plugin: 'pluginRouteContextModule-100.json', + }, sidebar: 'main', }, { @@ -126,6 +129,9 @@ describe('loadRoutes', () => { content: 'docs/foo/baz.md', metadata: 'docs-foo-baz-dd9.json', }, + context: { + plugin: 'pluginRouteContextModule-100.json', + }, sidebar: 'secondary', 'key:a': 'containing colon', "key'b": 'containing quote', diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index a25659ae892e..74f3836b0e9f 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -71,7 +71,7 @@ function createPluginSchema(theme: boolean) { Joi.array() .ordered(Joi.string().required(), Joi.object().required()) .length(2), - Joi.bool().equal(false), // In case of conditional adding of plugins. + Joi.any().valid(false, null), ) // @ts-expect-error: bad lib def, doesn't recognize an array of reports .error((errors) => { @@ -119,6 +119,7 @@ const PresetSchema = Joi.alternatives() Joi.array() .items(Joi.string().required(), Joi.object().required()) .length(2), + Joi.any().valid(false, null), ) .messages({ 'alternatives.types': `{#label} does not look like a valid preset config. A preset config entry should be one of: diff --git a/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/presets/preset-mixed.js b/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/presets/preset-mixed.js index fdd55264f67d..bbe10eaa44fd 100644 --- a/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/presets/preset-mixed.js +++ b/packages/docusaurus/src/server/plugins/__tests__/__fixtures__/presets/preset-mixed.js @@ -7,7 +7,7 @@ module.exports = function preset(context, opts = {}) { return { - themes: [['@docusaurus/theme-classic', opts.test]], - plugins: [['@docusaurus/plugin-test', opts.test]], + themes: [['@docusaurus/theme-classic', opts.test], null], + plugins: [['@docusaurus/plugin-test', opts.test], false], }; }; diff --git a/packages/docusaurus/src/server/plugins/__tests__/__snapshots__/presets.test.ts.snap b/packages/docusaurus/src/server/plugins/__tests__/__snapshots__/presets.test.ts.snap index 84f8e1be4bfe..d5da620ec554 100644 --- a/packages/docusaurus/src/server/plugins/__tests__/__snapshots__/presets.test.ts.snap +++ b/packages/docusaurus/src/server/plugins/__tests__/__snapshots__/presets.test.ts.snap @@ -107,6 +107,7 @@ exports[`loadPresets mixed form with themes 1`] = ` "@docusaurus/plugin-test", undefined, ], + false, ], "themes": [ [ @@ -121,6 +122,7 @@ exports[`loadPresets mixed form with themes 1`] = ` "@docusaurus/theme-classic", undefined, ], + null, ], } `; diff --git a/packages/docusaurus/src/server/plugins/configs.ts b/packages/docusaurus/src/server/plugins/configs.ts index 2662ddc69b99..5c4b7711c0e6 100644 --- a/packages/docusaurus/src/server/plugins/configs.ts +++ b/packages/docusaurus/src/server/plugins/configs.ts @@ -17,7 +17,7 @@ import type { } from '@docusaurus/types'; async function normalizePluginConfig( - pluginConfig: PluginConfig, + pluginConfig: Exclude, configPath: string, pluginRequire: NodeRequire, ): Promise { @@ -120,7 +120,7 @@ export async function loadPluginConfigs( // Site config should be the highest priority. ...standalonePlugins, ...standaloneThemes, - ]; + ].filter((x: T | null | false): x is T => Boolean(x)); return Promise.all( pluginConfigs.map((pluginConfig) => normalizePluginConfig( diff --git a/packages/docusaurus/src/server/plugins/index.ts b/packages/docusaurus/src/server/plugins/index.ts index d63b95589bc4..c87c99dc9ef0 100644 --- a/packages/docusaurus/src/server/plugins/index.ts +++ b/packages/docusaurus/src/server/plugins/index.ts @@ -104,22 +104,19 @@ export async function loadPlugins(context: LoadContext): Promise<{ plugin.name, pluginId, ); - // TODO this would be better to do all that in the codegen phase - // TODO handle context for nested routes - const pluginRouteContext: PluginRouteContext = { - plugin: {name: plugin.name, id: pluginId}, - data: undefined, // TODO allow plugins to provide context data - }; const pluginRouteContextModulePath = path.join( dataDir, `${docuHash('pluginRouteContextModule')}.json`, ); + const pluginRouteContext: PluginRouteContext['plugin'] = { + name: plugin.name, + id: pluginId, + }; await generate( '/', pluginRouteContextModulePath, JSON.stringify(pluginRouteContext, null, 2), ); - const actions: PluginContentLoadedActions = { addRoute(initialRouteConfig) { // Trailing slash behavior is handled generically for all plugins @@ -129,9 +126,9 @@ export async function loadPlugins(context: LoadContext): Promise<{ ); pluginsRouteConfigs.push({ ...finalRouteConfig, - modules: { - ...finalRouteConfig.modules, - __context: pluginRouteContextModulePath, + context: { + ...(finalRouteConfig.context && {data: finalRouteConfig.context}), + plugin: pluginRouteContextModulePath, }, }); }, diff --git a/packages/docusaurus/src/server/plugins/presets.ts b/packages/docusaurus/src/server/plugins/presets.ts index 877150cc6703..27788b747d80 100644 --- a/packages/docusaurus/src/server/plugins/presets.ts +++ b/packages/docusaurus/src/server/plugins/presets.ts @@ -33,6 +33,9 @@ export async function loadPresets( presets.forEach((presetItem) => { let presetModuleImport: string; let presetOptions = {}; + if (!presetItem) { + return; + } if (typeof presetItem === 'string') { presetModuleImport = presetItem; } else { @@ -53,10 +56,10 @@ export async function loadPresets( ); if (preset.plugins) { - plugins.push(...preset.plugins.filter(Boolean)); + plugins.push(...preset.plugins); } if (preset.themes) { - themes.push(...preset.themes.filter(Boolean)); + themes.push(...preset.themes); } }); diff --git a/packages/docusaurus/src/server/routes.ts b/packages/docusaurus/src/server/routes.ts index 6350e2166069..733bf0bc2f51 100644 --- a/packages/docusaurus/src/server/routes.ts +++ b/packages/docusaurus/src/server/routes.ts @@ -250,6 +250,7 @@ function genRouteCode(routeConfig: RouteConfig, res: LoadedRoutes): string { path: routePath, component, modules = {}, + context, routes: subroutes, priority, exact, @@ -271,6 +272,9 @@ ${JSON.stringify(routeConfig)}`, res.routesChunkNames[`${routePath}-${routeHash}`] = { // Avoid clash with a prop called "component" ...genChunkNames({__comp: component}, 'component', component, res), + ...(context + ? genChunkNames({__context: context}, 'context', routePath, res) + : {}), ...genChunkNames(modules, 'module', routePath, res), };