diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website-blog-with-tags/blog/another-with-tags-unlisted.md b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website-blog-with-tags/blog/another-with-tags-unlisted.md
new file mode 100644
index 000000000000..36e186ceca2e
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website-blog-with-tags/blog/another-with-tags-unlisted.md
@@ -0,0 +1,9 @@
+---
+slug: /another/blog-with-tags-unlisted
+title: Another Blog With Tag - unlisted
+date: 2020-08-19
+tags: [unlisted]
+unlisted: true
+---
+
+with tag
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/unlisted.md b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/unlisted.md
new file mode 100644
index 000000000000..0c5ca5d55022
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/unlisted.md
@@ -0,0 +1,6 @@
+---
+date: 2020-02-27
+unlisted: true
+---
+
+this post is unlisted
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/build-snap/blog/unlisted/index.html b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/build-snap/blog/unlisted/index.html
new file mode 100644
index 000000000000..7167942e8fff
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/build-snap/blog/unlisted/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+Unlisted | My Site
+
+
+
+
+
+
+
+
+
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 c19351278b5f..b884dddfc605 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
@@ -30,6 +30,7 @@ exports[`blog plugin works on blog tags without pagination 1`] = `
},
],
"permalink": "/blog/tags/tag-1",
+ "unlisted": false,
},
"/blog/tags/tag-2": {
"items": [
@@ -57,6 +58,33 @@ exports[`blog plugin works on blog tags without pagination 1`] = `
},
],
"permalink": "/blog/tags/tag-2",
+ "unlisted": false,
+ },
+ "/blog/tags/unlisted": {
+ "items": [
+ "/another/blog-with-tags-unlisted",
+ ],
+ "label": "unlisted",
+ "pages": [
+ {
+ "items": [
+ "/another/blog-with-tags-unlisted",
+ ],
+ "metadata": {
+ "blogDescription": "Blog",
+ "blogTitle": "Blog",
+ "nextPage": undefined,
+ "page": 1,
+ "permalink": "/blog/tags/unlisted",
+ "postsPerPage": 1,
+ "previousPage": undefined,
+ "totalCount": 1,
+ "totalPages": 1,
+ },
+ },
+ ],
+ "permalink": "/blog/tags/unlisted",
+ "unlisted": false,
},
}
`;
@@ -106,6 +134,7 @@ exports[`blog plugin works with blog tags 1`] = `
},
],
"permalink": "/blog/tags/tag-1",
+ "unlisted": false,
},
"/blog/tags/tag-2": {
"items": [
@@ -133,6 +162,33 @@ exports[`blog plugin works with blog tags 1`] = `
},
],
"permalink": "/blog/tags/tag-2",
+ "unlisted": false,
+ },
+ "/blog/tags/unlisted": {
+ "items": [
+ "/another/blog-with-tags-unlisted",
+ ],
+ "label": "unlisted",
+ "pages": [
+ {
+ "items": [
+ "/another/blog-with-tags-unlisted",
+ ],
+ "metadata": {
+ "blogDescription": "Blog",
+ "blogTitle": "Blog",
+ "nextPage": undefined,
+ "page": 1,
+ "permalink": "/blog/tags/unlisted",
+ "postsPerPage": 2,
+ "previousPage": undefined,
+ "totalCount": 1,
+ "totalPages": 1,
+ },
+ },
+ ],
+ "permalink": "/blog/tags/unlisted",
+ "unlisted": false,
},
}
`;
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
index e56e35d17620..83d0af77157c 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
@@ -54,7 +54,7 @@ async function testGenerateFeeds(
);
await createBlogFeedFiles({
- blogPosts: blogPosts.filter((post) => !post.metadata.frontMatter.draft),
+ blogPosts,
options,
siteConfig: context.siteConfig,
outDir: context.outDir,
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/frontMatter.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/frontMatter.test.ts
index 7d769afe3f69..3e3580d92c5c 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/frontMatter.test.ts
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/frontMatter.test.ts
@@ -12,7 +12,7 @@ import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
// TODO this abstraction reduce verbosity but it makes it harder to debug
// It would be preferable to just expose helper methods
function testField(params: {
- fieldName: keyof BlogPostFrontMatter;
+ prefix: string;
validFrontMatters: BlogPostFrontMatter[];
convertibleFrontMatter?: [
ConvertibleFrontMatter: {[key: string]: unknown},
@@ -23,7 +23,7 @@ function testField(params: {
ErrorMessage: string,
][];
}) {
- describe(`"${params.fieldName}" field`, () => {
+ describe(`"${params.prefix}" field`, () => {
it('accept valid values', () => {
params.validFrontMatters.forEach((frontMatter) => {
expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter);
@@ -44,15 +44,12 @@ function testField(params: {
params.invalidFrontMatters?.forEach(([frontMatter, message]) => {
try {
validateBlogPostFrontMatter(frontMatter);
- // eslint-disable-next-line jest/no-jasmine-globals
- fail(
- new Error(
- `Blog front matter is expected to be rejected, but was accepted successfully:\n ${JSON.stringify(
- frontMatter,
- null,
- 2,
- )}`,
- ),
+ throw new Error(
+ `Blog front matter is expected to be rejected, but was accepted successfully:\n ${JSON.stringify(
+ frontMatter,
+ null,
+ 2,
+ )}`,
);
} catch (err) {
// eslint-disable-next-line jest/no-conditional-expect
@@ -79,7 +76,7 @@ describe('validateBlogPostFrontMatter', () => {
describe('validateBlogPostFrontMatter description', () => {
testField({
- fieldName: 'description',
+ prefix: 'description',
validFrontMatters: [
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
{description: ''},
@@ -90,7 +87,7 @@ describe('validateBlogPostFrontMatter description', () => {
describe('validateBlogPostFrontMatter title', () => {
testField({
- fieldName: 'title',
+ prefix: 'title',
validFrontMatters: [
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
{title: ''},
@@ -101,7 +98,7 @@ describe('validateBlogPostFrontMatter title', () => {
describe('validateBlogPostFrontMatter id', () => {
testField({
- fieldName: 'id',
+ prefix: 'id',
validFrontMatters: [{id: '123'}, {id: 'id'}],
invalidFrontMatters: [[{id: ''}, 'not allowed to be empty']],
});
@@ -132,7 +129,7 @@ describe('validateBlogPostFrontMatter handles legacy/new author front matter', (
describe('validateBlogPostFrontMatter author', () => {
testField({
- fieldName: 'author',
+ prefix: 'author',
validFrontMatters: [{author: '123'}, {author: 'author'}],
invalidFrontMatters: [[{author: ''}, 'not allowed to be empty']],
});
@@ -140,7 +137,7 @@ describe('validateBlogPostFrontMatter author', () => {
describe('validateBlogPostFrontMatter author_title', () => {
testField({
- fieldName: 'author_title',
+ prefix: 'author_title',
validFrontMatters: [
{author: '123', author_title: '123'},
{author: '123', author_title: 'author_title'},
@@ -149,7 +146,7 @@ describe('validateBlogPostFrontMatter author_title', () => {
});
testField({
- fieldName: 'authorTitle',
+ prefix: 'authorTitle',
validFrontMatters: [{authorTitle: '123'}, {authorTitle: 'authorTitle'}],
invalidFrontMatters: [[{authorTitle: ''}, 'not allowed to be empty']],
});
@@ -157,7 +154,7 @@ describe('validateBlogPostFrontMatter author_title', () => {
describe('validateBlogPostFrontMatter author_url', () => {
testField({
- fieldName: 'author_url',
+ prefix: 'author_url',
validFrontMatters: [
{author_url: 'https://docusaurus.io'},
{author_url: '../../relative'},
@@ -172,7 +169,7 @@ describe('validateBlogPostFrontMatter author_url', () => {
});
testField({
- fieldName: 'authorURL',
+ prefix: 'authorURL',
validFrontMatters: [
{authorURL: 'https://docusaurus.io'},
{authorURL: '../../relative'},
@@ -190,7 +187,7 @@ describe('validateBlogPostFrontMatter author_url', () => {
describe('validateBlogPostFrontMatter author_image_url', () => {
testField({
- fieldName: 'author_image_url',
+ prefix: 'author_image_url',
validFrontMatters: [
{author_image_url: 'https://docusaurus.io/asset/image.png'},
{author_image_url: '../../relative'},
@@ -205,7 +202,7 @@ describe('validateBlogPostFrontMatter author_image_url', () => {
});
testField({
- fieldName: 'authorImageURL',
+ prefix: 'authorImageURL',
validFrontMatters: [
{authorImageURL: 'https://docusaurus.io/asset/image.png'},
{authorImageURL: '../../relative'},
@@ -222,7 +219,7 @@ describe('validateBlogPostFrontMatter author_image_url', () => {
describe('validateBlogPostFrontMatter authors', () => {
testField({
- fieldName: 'author',
+ prefix: 'author',
validFrontMatters: [
{authors: []},
{authors: 'authorKey'},
@@ -270,7 +267,7 @@ describe('validateBlogPostFrontMatter authors', () => {
describe('validateBlogPostFrontMatter slug', () => {
testField({
- fieldName: 'slug',
+ prefix: 'slug',
validFrontMatters: [
{slug: 'blog/'},
{slug: '/blog'},
@@ -287,7 +284,7 @@ describe('validateBlogPostFrontMatter slug', () => {
describe('validateBlogPostFrontMatter image', () => {
testField({
- fieldName: 'image',
+ prefix: 'image',
validFrontMatters: [
{image: 'https://docusaurus.io/image.png'},
{image: 'blog/'},
@@ -307,7 +304,7 @@ describe('validateBlogPostFrontMatter image', () => {
describe('validateBlogPostFrontMatter tags', () => {
testField({
- fieldName: 'tags',
+ prefix: 'tags',
validFrontMatters: [
{tags: []},
{tags: ['hello']},
@@ -335,7 +332,7 @@ describe('validateBlogPostFrontMatter tags', () => {
describe('validateBlogPostFrontMatter keywords', () => {
testField({
- fieldName: 'keywords',
+ prefix: 'keywords',
validFrontMatters: [
{keywords: ['hello']},
{keywords: ['hello', 'world']},
@@ -352,7 +349,7 @@ describe('validateBlogPostFrontMatter keywords', () => {
describe('validateBlogPostFrontMatter draft', () => {
testField({
- fieldName: 'draft',
+ prefix: 'draft',
validFrontMatters: [{draft: true}, {draft: false}],
convertibleFrontMatter: [
[{draft: 'true'}, {draft: true}],
@@ -365,9 +362,43 @@ describe('validateBlogPostFrontMatter draft', () => {
});
});
+describe('validateBlogPostFrontMatter unlisted', () => {
+ testField({
+ prefix: 'unlisted',
+ validFrontMatters: [{unlisted: true}, {unlisted: false}],
+ convertibleFrontMatter: [
+ [{unlisted: 'true'}, {unlisted: true}],
+ [{unlisted: 'false'}, {unlisted: false}],
+ ],
+ invalidFrontMatters: [
+ [{unlisted: 'yes'}, 'must be a boolean'],
+ [{unlisted: 'no'}, 'must be a boolean'],
+ ],
+ });
+});
+
+describe('validateDocFrontMatter draft XOR unlisted', () => {
+ testField({
+ prefix: 'draft XOR unlisted',
+ validFrontMatters: [
+ {draft: false},
+ {unlisted: false},
+ {draft: false, unlisted: false},
+ {draft: true, unlisted: false},
+ {draft: false, unlisted: true},
+ ],
+ invalidFrontMatters: [
+ [
+ {draft: true, unlisted: true},
+ "Can't be draft and unlisted at the same time.",
+ ],
+ ],
+ });
+});
+
describe('validateBlogPostFrontMatter hide_table_of_contents', () => {
testField({
- fieldName: 'hide_table_of_contents',
+ prefix: 'hide_table_of_contents',
validFrontMatters: [
{hide_table_of_contents: true},
{hide_table_of_contents: false},
@@ -385,7 +416,7 @@ describe('validateBlogPostFrontMatter hide_table_of_contents', () => {
describe('validateBlogPostFrontMatter date', () => {
testField({
- fieldName: 'date',
+ prefix: 'date',
validFrontMatters: [
{date: new Date('2020-01-01')},
{date: '2020-01-01'},
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts
index 64998082ff53..ee7c68680cb5 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts
@@ -172,6 +172,7 @@ describe('blog plugin', () => {
title: 'Happy 1st Birthday Slash! (translated)',
},
hasTruncateMarker: false,
+ unlisted: false,
});
expect(
@@ -215,6 +216,7 @@ describe('blog plugin', () => {
title: 'date-matter',
},
hasTruncateMarker: false,
+ unlisted: false,
});
expect({
@@ -252,6 +254,7 @@ describe('blog plugin', () => {
},
],
hasTruncateMarker: false,
+ unlisted: false,
});
expect({
@@ -289,6 +292,7 @@ describe('blog plugin', () => {
},
tags: [],
hasTruncateMarker: false,
+ unlisted: false,
});
expect({
@@ -314,13 +318,14 @@ describe('blog plugin', () => {
title: 'date-matter',
},
hasTruncateMarker: false,
+ unlisted: false,
});
});
it('builds simple website blog with localized dates', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');
const blogPostsFrench = await getBlogPosts(siteDir, {}, getI18n('fr'));
- expect(blogPostsFrench).toHaveLength(8);
+ expect(blogPostsFrench).toHaveLength(9);
expect(blogPostsFrench[0]!.metadata.formattedDate).toMatchInlineSnapshot(
`"6 mars 2021"`,
);
@@ -337,13 +342,13 @@ describe('blog plugin', () => {
`"27 février 2020"`,
);
expect(blogPostsFrench[5]!.metadata.formattedDate).toMatchInlineSnapshot(
- `"2 janvier 2019"`,
+ `"27 février 2020"`,
);
expect(blogPostsFrench[6]!.metadata.formattedDate).toMatchInlineSnapshot(
- `"1 janvier 2019"`,
+ `"2 janvier 2019"`,
);
expect(blogPostsFrench[7]!.metadata.formattedDate).toMatchInlineSnapshot(
- `"14 décembre 2018"`,
+ `"1 janvier 2019"`,
);
});
@@ -372,7 +377,7 @@ describe('blog plugin', () => {
expect(blogPost.metadata.editUrl).toEqual(hardcodedEditUrl);
});
- expect(editUrlFunction).toHaveBeenCalledTimes(8);
+ expect(editUrlFunction).toHaveBeenCalledTimes(9);
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
@@ -471,6 +476,7 @@ describe('blog plugin', () => {
prevItem: undefined,
nextItem: undefined,
hasTruncateMarker: false,
+ unlisted: false,
});
});
@@ -495,7 +501,7 @@ describe('blog plugin', () => {
postsPerPage: 2,
});
- expect(Object.keys(blogTags)).toHaveLength(2);
+ expect(Object.keys(blogTags)).toHaveLength(3);
expect(blogTags).toMatchSnapshot();
});
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/props.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/props.test.ts
new file mode 100644
index 000000000000..3446a3601468
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/props.test.ts
@@ -0,0 +1,70 @@
+/**
+ * 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 {toTagsProp} from '../props';
+
+describe('toTagsProp', () => {
+ type Tags = Parameters[0]['blogTags'];
+ type Tag = Tags[number];
+
+ const tag1: Tag = {
+ label: 'Tag 1',
+ permalink: '/tag1',
+ items: ['item1', 'item2'],
+ pages: [],
+ unlisted: false,
+ };
+
+ const tag2: Tag = {
+ label: 'Tag 2',
+ permalink: '/tag2',
+ items: ['item3'],
+ pages: [],
+ unlisted: false,
+ };
+
+ function testTags(...tags: Tag[]) {
+ const blogTags: Tags = {};
+ tags.forEach((tag) => {
+ blogTags[tag.permalink] = tag;
+ });
+ return toTagsProp({blogTags});
+ }
+
+ it('works', () => {
+ expect(testTags(tag1, tag2)).toEqual([
+ {
+ count: tag1.items.length,
+ label: tag1.label,
+ permalink: tag1.permalink,
+ },
+ {
+ count: tag2.items.length,
+ label: tag2.label,
+ permalink: tag2.permalink,
+ },
+ ]);
+ });
+
+ it('filters unlisted tags', () => {
+ expect(testTags(tag1, {...tag2, unlisted: true})).toEqual([
+ {
+ count: tag1.items.length,
+ label: tag1.label,
+ permalink: tag1.permalink,
+ },
+ ]);
+
+ expect(testTags({...tag1, unlisted: true}, tag2)).toEqual([
+ {
+ count: tag2.items.length,
+ label: tag2.label,
+ permalink: tag2.permalink,
+ },
+ ]);
+ });
+});
diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
index 17e0dbb0bffc..36028495381c 100644
--- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
+++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
@@ -21,8 +21,11 @@ import {
Globby,
normalizeFrontMatterTags,
groupTaggedItems,
+ getTagVisibility,
getFileCommitDate,
getContentPathList,
+ isUnlisted,
+ isDraft,
} from '@docusaurus/utils';
import {validateBlogPostFrontMatter} from './frontMatter';
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
@@ -96,6 +99,10 @@ export function paginateBlogPosts({
return pages;
}
+export function shouldBeListed(blogPost: BlogPost): boolean {
+ return !blogPost.metadata.unlisted;
+}
+
export function getBlogTags({
blogPosts,
...params
@@ -109,17 +116,23 @@ export function getBlogTags({
blogPosts,
(blogPost) => blogPost.metadata.tags,
);
-
- return _.mapValues(groups, ({tag, items: tagBlogPosts}) => ({
- label: tag.label,
- items: tagBlogPosts.map((item) => item.id),
- permalink: tag.permalink,
- pages: paginateBlogPosts({
- blogPosts: tagBlogPosts,
- basePageUrl: tag.permalink,
- ...params,
- }),
- }));
+ return _.mapValues(groups, ({tag, items: tagBlogPosts}) => {
+ const tagVisibility = getTagVisibility({
+ items: tagBlogPosts,
+ isUnlisted: (item) => item.metadata.unlisted,
+ });
+ return {
+ label: tag.label,
+ items: tagVisibility.listedItems.map((item) => item.id),
+ permalink: tag.permalink,
+ pages: paginateBlogPosts({
+ blogPosts: tagVisibility.listedItems,
+ basePageUrl: tag.permalink,
+ ...params,
+ }),
+ unlisted: tagVisibility.unlisted,
+ };
+ });
}
const DATE_FILENAME_REGEX =
@@ -219,7 +232,10 @@ async function processBlogSourceFile(
const aliasedSource = aliasedSitePath(blogSourceAbsolute, siteDir);
- if (frontMatter.draft && process.env.NODE_ENV === 'production') {
+ const draft = isDraft({frontMatter});
+ const unlisted = isUnlisted({frontMatter});
+
+ if (draft) {
return undefined;
}
@@ -326,6 +342,7 @@ async function processBlogSourceFile(
hasTruncateMarker: truncateMarker.test(content),
authors,
frontMatter,
+ unlisted,
},
content,
};
@@ -352,23 +369,25 @@ export async function generateBlogPosts(
authorsMapPath: options.authorsMapPath,
});
+ async function doProcessBlogSourceFile(blogSourceFile: string) {
+ try {
+ return await processBlogSourceFile(
+ blogSourceFile,
+ contentPaths,
+ context,
+ options,
+ authorsMap,
+ );
+ } catch (err) {
+ throw new Error(
+ `Processing of blog source file path=${blogSourceFile} failed.`,
+ {cause: err as Error},
+ );
+ }
+ }
+
const blogPosts = (
- await Promise.all(
- blogSourceFiles.map(async (blogSourceFile: string) => {
- try {
- return await processBlogSourceFile(
- blogSourceFile,
- contentPaths,
- context,
- options,
- authorsMap,
- );
- } catch (err) {
- logger.error`Processing of blog source file path=${blogSourceFile} failed.`;
- throw err;
- }
- }),
- )
+ await Promise.all(blogSourceFiles.map(doProcessBlogSourceFile))
).filter(Boolean) as BlogPost[];
blogPosts.sort(
diff --git a/packages/docusaurus-plugin-content-blog/src/feed.ts b/packages/docusaurus-plugin-content-blog/src/feed.ts
index 222fcb84b2b5..81d7d45fe99f 100644
--- a/packages/docusaurus-plugin-content-blog/src/feed.ts
+++ b/packages/docusaurus-plugin-content-blog/src/feed.ts
@@ -133,8 +133,15 @@ async function createBlogFeedFile({
}
}
+function shouldBeInFeed(blogPost: BlogPost): boolean {
+ const excluded =
+ blogPost.metadata.frontMatter.draft ||
+ blogPost.metadata.frontMatter.unlisted;
+ return !excluded;
+}
+
export async function createBlogFeedFiles({
- blogPosts,
+ blogPosts: allBlogPosts,
options,
siteConfig,
outDir,
@@ -146,6 +153,8 @@ export async function createBlogFeedFiles({
outDir: string;
locale: string;
}): Promise {
+ const blogPosts = allBlogPosts.filter(shouldBeInFeed);
+
const feed = await generateBlogFeed({
blogPosts,
options,
diff --git a/packages/docusaurus-plugin-content-blog/src/frontMatter.ts b/packages/docusaurus-plugin-content-blog/src/frontMatter.ts
index 417bf1df00fb..73b4d37d25ee 100644
--- a/packages/docusaurus-plugin-content-blog/src/frontMatter.ts
+++ b/packages/docusaurus-plugin-content-blog/src/frontMatter.ts
@@ -11,6 +11,7 @@ import {
validateFrontMatter,
FrontMatterTagsSchema,
FrontMatterTOCHeadingLevels,
+ ContentVisibilitySchema,
} from '@docusaurus/utils-validation';
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
@@ -32,7 +33,6 @@ const BlogFrontMatterSchema = Joi.object({
title: Joi.string().allow(''),
description: Joi.string().allow(''),
tags: FrontMatterTagsSchema,
- draft: Joi.boolean(),
date: Joi.date().raw(),
// New multi-authors front matter:
@@ -69,10 +69,12 @@ const BlogFrontMatterSchema = Joi.object({
hide_table_of_contents: Joi.boolean(),
...FrontMatterTOCHeadingLevels,
-}).messages({
- 'deprecate.error':
- '{#label} blog frontMatter field is deprecated. Please use {#alternative} instead.',
-});
+})
+ .messages({
+ 'deprecate.error':
+ '{#label} blog frontMatter field is deprecated. Please use {#alternative} instead.',
+ })
+ .concat(ContentVisibilitySchema);
export function validateBlogPostFrontMatter(frontMatter: {
[key: string]: unknown;
diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts
index 51bdf7516c32..a86b835e0556 100644
--- a/packages/docusaurus-plugin-content-blog/src/index.ts
+++ b/packages/docusaurus-plugin-content-blog/src/index.ts
@@ -18,19 +18,19 @@ import {
getContentPathList,
getDataFilePath,
DEFAULT_PLUGIN_ID,
- type TagsListItem,
- type TagModule,
} from '@docusaurus/utils';
import {
generateBlogPosts,
getSourceToPermalink,
getBlogTags,
paginateBlogPosts,
+ shouldBeListed,
} from './blogUtils';
import footnoteIDFixer from './remark/footnoteIDFixer';
import {translateContent, getTranslationFiles} from './translations';
import {createBlogFeedFiles} from './feed';
+import {toTagProp, toTagsProp} from './props';
import type {BlogContentPaths, BlogMarkdownLoaderOptions} from './types';
import type {LoadContext, Plugin, HtmlTags} from '@docusaurus/types';
import type {
@@ -112,6 +112,7 @@ export default async function pluginContentBlog(
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
const blogTagsListPath = normalizeUrl([baseBlogUrl, tagsBasePath]);
const blogPosts = await generateBlogPosts(contentPaths, context, options);
+ const listedBlogPosts = blogPosts.filter(shouldBeListed);
if (!blogPosts.length) {
return {
@@ -125,8 +126,8 @@ export default async function pluginContentBlog(
}
// Colocate next and prev metadata.
- blogPosts.forEach((blogPost, index) => {
- const prevItem = index > 0 ? blogPosts[index - 1] : null;
+ listedBlogPosts.forEach((blogPost, index) => {
+ const prevItem = index > 0 ? listedBlogPosts[index - 1] : null;
if (prevItem) {
blogPost.metadata.prevItem = {
title: prevItem.metadata.title,
@@ -135,7 +136,9 @@ export default async function pluginContentBlog(
}
const nextItem =
- index < blogPosts.length - 1 ? blogPosts[index + 1] : null;
+ index < listedBlogPosts.length - 1
+ ? listedBlogPosts[index + 1]
+ : null;
if (nextItem) {
blogPost.metadata.nextItem = {
title: nextItem.metadata.title,
@@ -145,7 +148,7 @@ export default async function pluginContentBlog(
});
const blogListPaginated: BlogPaginated[] = paginateBlogPosts({
- blogPosts,
+ blogPosts: listedBlogPosts,
blogTitle,
blogDescription,
postsPerPageOption,
@@ -242,6 +245,7 @@ export default async function pluginContentBlog(
items: sidebarBlogPosts.map((blogPost) => ({
title: blogPost.metadata.title,
permalink: blogPost.metadata.permalink,
+ unlisted: blogPost.metadata.unlisted,
})),
},
null,
@@ -303,17 +307,10 @@ export default async function pluginContentBlog(
}
async function createTagsListPage() {
- const tagsProp: TagsListItem[] = Object.values(blogTags).map((tag) => ({
- label: tag.label,
- permalink: tag.permalink,
- count: tag.items.length,
- }));
-
const tagsPropPath = await createData(
`${docuHash(`${blogTagsListPath}-tags`)}.json`,
- JSON.stringify(tagsProp, null, 2),
+ JSON.stringify(toTagsProp({blogTags}), null, 2),
);
-
addRoute({
path: blogTagsListPath,
component: blogTagsListComponent,
@@ -329,15 +326,9 @@ export default async function pluginContentBlog(
await Promise.all(
tag.pages.map(async (blogPaginated) => {
const {metadata, items} = blogPaginated;
- 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(tagProp, null, 2),
+ JSON.stringify(toTagProp({tag, blogTagsListPath}), null, 2),
);
const listMetadataPath = await createData(
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 63d31d5f58c7..96c288d335f4 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
@@ -90,6 +90,10 @@ declare module '@docusaurus/plugin-content-blog' {
* Marks the post as draft and excludes it from the production build.
*/
draft?: boolean;
+ /**
+ * Marks the post as unlisted and visibly hides it unless directly accessed.
+ */
+ unlisted?: boolean;
/**
* Will override the default publish date inferred from git/filename. Yaml
* only converts standard yyyy-MM-dd format to dates, so this may stay as a
@@ -222,6 +226,10 @@ declare module '@docusaurus/plugin-content-blog' {
readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown};
/** Tags, normalized. */
readonly tags: Tag[];
+ /**
+ * Marks the post as unlisted and visibly hides it unless directly accessed.
+ */
+ readonly unlisted: boolean;
};
/**
* @returns The edit URL that's directly plugged into metadata.
@@ -407,9 +415,15 @@ declare module '@docusaurus/plugin-content-blog' {
}
>;
+ export type BlogSidebarItem = {
+ title: string;
+ permalink: string;
+ unlisted: boolean;
+ };
+
export type BlogSidebar = {
title: string;
- items: {title: string; permalink: string}[];
+ items: BlogSidebarItem[];
};
export type BlogContent = {
@@ -428,6 +442,7 @@ declare module '@docusaurus/plugin-content-blog' {
/** Blog post permalinks. */
items: string[];
pages: BlogPaginated[];
+ unlisted: boolean;
};
export type BlogPost = {
diff --git a/packages/docusaurus-plugin-content-blog/src/props.ts b/packages/docusaurus-plugin-content-blog/src/props.ts
new file mode 100644
index 000000000000..8946bc15d580
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/props.ts
@@ -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 type {TagsListItem, TagModule} from '@docusaurus/utils';
+import type {BlogTag, BlogTags} from '@docusaurus/plugin-content-blog';
+
+export function toTagsProp({blogTags}: {blogTags: BlogTags}): TagsListItem[] {
+ return Object.values(blogTags)
+ .filter((tag) => !tag.unlisted)
+ .map((tag) => ({
+ label: tag.label,
+ permalink: tag.permalink,
+ count: tag.items.length,
+ }));
+}
+
+export function toTagProp({
+ blogTagsListPath,
+ tag,
+}: {
+ blogTagsListPath: string;
+ tag: BlogTag;
+}): TagModule {
+ return {
+ label: tag.label,
+ permalink: tag.permalink,
+ allTagsPath: blogTagsListPath,
+ count: tag.items.length,
+ unlisted: tag.unlisted,
+ };
+}
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/doc-unlisted.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/doc-unlisted.md
new file mode 100644
index 000000000000..54090b5a48b0
--- /dev/null
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/doc-unlisted.md
@@ -0,0 +1,5 @@
+---
+unlisted: true
+---
+
+This is an unlisted document
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/ipsum.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/ipsum.md
index 706687dbc53b..6bac3bd5a49a 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/ipsum.md
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/ipsum.md
@@ -1,5 +1,6 @@
---
custom_edit_url: null
+pagination_next: doc-unlisted
---
Lorem ipsum.
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/unlisted-category/index.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/unlisted-category/index.md
new file mode 100644
index 000000000000..440e184ebcca
--- /dev/null
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/unlisted-category/index.md
@@ -0,0 +1,6 @@
+---
+id: unlisted-category-index
+unlisted: true
+---
+
+This is an unlisted category index
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/unlisted-category/unlisted-category-doc.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/unlisted-category/unlisted-category-doc.md
new file mode 100644
index 000000000000..9f91c9baaf5f
--- /dev/null
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/unlisted-category/unlisted-category-doc.md
@@ -0,0 +1,6 @@
+---
+id: unlisted-category-doc
+unlisted: true
+---
+
+This is an unlisted category doc
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/sidebars.json b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/sidebars.json
index 7f74d2280b67..19ff4b802bf7 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/sidebars.json
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/sidebars.json
@@ -4,7 +4,16 @@
{
"type": "category",
"label": "foo",
- "items": ["foo/bar", "foo/baz"]
+ "items": ["foo/bar", "doc-unlisted", "foo/baz"]
+ },
+ {
+ "type": "category",
+ "label": "Unlisted category",
+ "link": {
+ "type": "doc",
+ "id": "unlisted-category/unlisted-category-index"
+ },
+ "items": ["unlisted-category/unlisted-category-doc"]
},
{
"type": "category",
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/cli.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/cli.test.ts.snap
index edff61da5b8b..1aa0e1d12474 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/cli.test.ts.snap
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/cli.test.ts.snap
@@ -10,11 +10,23 @@ exports[`docsVersion first time versioning 1`] = `
{
"items": [
"foo/bar",
+ "doc-unlisted",
"foo/baz",
],
"label": "foo",
"type": "category",
},
+ {
+ "items": [
+ "unlisted-category/unlisted-category-doc",
+ ],
+ "label": "Unlisted category",
+ "link": {
+ "id": "unlisted-category/unlisted-category-index",
+ "type": "doc",
+ },
+ "type": "category",
+ },
{
"items": [
"rootAbsoluteSlug",
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/docs.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/docs.test.ts.snap
index 69401a06a04c..a9c3c1d46336 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/docs.test.ts.snap
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/docs.test.ts.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`simple site custom pagination 1`] = `
+exports[`simple site custom pagination - development 1`] = `
{
"pagination": [
{
@@ -25,14 +25,25 @@ exports[`simple site custom pagination 1`] = `
{
"id": "doc-draft",
"next": {
- "permalink": "/docs/foo/bar",
- "title": "Bar",
+ "permalink": "/docs/doc-unlisted",
+ "title": "doc-unlisted",
},
"prev": {
"permalink": "/docs/doc with space",
"title": "Hoo hoo, if this path tricks you...",
},
},
+ {
+ "id": "doc-unlisted",
+ "next": {
+ "permalink": "/docs/foo/bar",
+ "title": "Bar",
+ },
+ "prev": {
+ "permalink": "/docs/doc-draft",
+ "title": "doc-draft",
+ },
+ },
{
"id": "foo/bar",
"next": undefined,
@@ -74,9 +85,356 @@ exports[`simple site custom pagination 1`] = `
{
"id": "ipsum",
"next": {
+ "permalink": "/docs/doc-unlisted",
+ "title": "doc-unlisted",
+ },
+ "prev": {
+ "permalink": "/docs/",
+ "title": "Hello sidebar_label",
+ },
+ },
+ {
+ "id": "lastUpdateAuthorOnly",
+ "next": {
+ "permalink": "/docs/lastUpdateDateOnly",
+ "title": "Last Update Date Only",
+ },
+ "prev": {
+ "permalink": "/docs/ipsum",
+ "title": "ipsum",
+ },
+ },
+ {
+ "id": "lastUpdateDateOnly",
+ "next": {
+ "permalink": "/docs/lorem",
+ "title": "lorem",
+ },
+ "prev": {
"permalink": "/docs/lastUpdateAuthorOnly",
"title": "Last Update Author Only",
},
+ },
+ {
+ "id": "lorem",
+ "next": {
+ "permalink": "/docs/rootAbsoluteSlug",
+ "title": "rootAbsoluteSlug",
+ },
+ "prev": {
+ "permalink": "/docs/lastUpdateDateOnly",
+ "title": "Last Update Date Only",
+ },
+ },
+ {
+ "id": "rootAbsoluteSlug",
+ "next": {
+ "permalink": "/docs/headingAsTitle",
+ "title": "My heading as title",
+ },
+ "prev": {
+ "permalink": "/docs/foo/bazSlug.html",
+ "title": "baz pagination_label",
+ },
+ },
+ {
+ "id": "rootRelativeSlug",
+ "next": {
+ "permalink": "/docs/headingAsTitle",
+ "title": "My heading as title",
+ },
+ "prev": {
+ "permalink": "/docs/foo/bazSlug.html",
+ "title": "baz pagination_label",
+ },
+ },
+ {
+ "id": "rootResolvedSlug",
+ "next": {
+ "permalink": "/docs/headingAsTitle",
+ "title": "My heading as title",
+ },
+ "prev": {
+ "permalink": "/docs/foo/bazSlug.html",
+ "title": "baz pagination_label",
+ },
+ },
+ {
+ "id": "rootTryToEscapeSlug",
+ "next": {
+ "permalink": "/docs/headingAsTitle",
+ "title": "My heading as title",
+ },
+ "prev": {
+ "permalink": "/docs/foo/bazSlug.html",
+ "title": "baz pagination_label",
+ },
+ },
+ {
+ "id": "slugs/absoluteSlug",
+ "next": {
+ "permalink": "/docs/slugs/relativeSlug",
+ "title": "relativeSlug",
+ },
+ "prev": {
+ "permalink": "/docs/rootTryToEscapeSlug",
+ "title": "rootTryToEscapeSlug",
+ },
+ },
+ {
+ "id": "slugs/relativeSlug",
+ "next": {
+ "permalink": "/docs/slugs/hey/resolvedSlug",
+ "title": "resolvedSlug",
+ },
+ "prev": {
+ "permalink": "/docs/absoluteSlug",
+ "title": "absoluteSlug",
+ },
+ },
+ {
+ "id": "slugs/resolvedSlug",
+ "next": {
+ "permalink": "/docs/tryToEscapeSlug",
+ "title": "tryToEscapeSlug",
+ },
+ "prev": {
+ "permalink": "/docs/slugs/relativeSlug",
+ "title": "relativeSlug",
+ },
+ },
+ {
+ "id": "slugs/tryToEscapeSlug",
+ "next": {
+ "permalink": "/docs/unlisted-category/",
+ "title": "unlisted-category-index",
+ },
+ "prev": {
+ "permalink": "/docs/slugs/hey/resolvedSlug",
+ "title": "resolvedSlug",
+ },
+ },
+ {
+ "id": "unlisted-category/unlisted-category-doc",
+ "next": undefined,
+ "prev": {
+ "permalink": "/docs/unlisted-category/",
+ "title": "unlisted-category-index",
+ },
+ },
+ {
+ "id": "unlisted-category/unlisted-category-index",
+ "next": {
+ "permalink": "/docs/unlisted-category/unlisted-category-doc",
+ "title": "unlisted-category-doc",
+ },
+ "prev": {
+ "permalink": "/docs/tryToEscapeSlug",
+ "title": "tryToEscapeSlug",
+ },
+ },
+ ],
+ "sidebars": {
+ "defaultSidebar": [
+ {
+ "id": "customLastUpdate",
+ "type": "doc",
+ },
+ {
+ "id": "doc with space",
+ "type": "doc",
+ },
+ {
+ "id": "doc-draft",
+ "type": "doc",
+ },
+ {
+ "id": "doc-unlisted",
+ "type": "doc",
+ },
+ {
+ "collapsed": false,
+ "collapsible": true,
+ "items": [
+ {
+ "id": "foo/bar",
+ "type": "doc",
+ },
+ {
+ "id": "foo/baz",
+ "type": "doc",
+ },
+ ],
+ "label": "foo",
+ "link": undefined,
+ "type": "category",
+ },
+ {
+ "id": "headingAsTitle",
+ "type": "doc",
+ },
+ {
+ "id": "hello",
+ "label": "Hello sidebar_label",
+ "type": "doc",
+ },
+ {
+ "id": "ipsum",
+ "type": "doc",
+ },
+ {
+ "id": "lastUpdateAuthorOnly",
+ "type": "doc",
+ },
+ {
+ "id": "lastUpdateDateOnly",
+ "type": "doc",
+ },
+ {
+ "id": "lorem",
+ "type": "doc",
+ },
+ {
+ "id": "rootAbsoluteSlug",
+ "type": "doc",
+ },
+ {
+ "id": "rootRelativeSlug",
+ "type": "doc",
+ },
+ {
+ "id": "rootResolvedSlug",
+ "type": "doc",
+ },
+ {
+ "id": "rootTryToEscapeSlug",
+ "type": "doc",
+ },
+ {
+ "collapsed": false,
+ "collapsible": true,
+ "items": [
+ {
+ "id": "slugs/absoluteSlug",
+ "type": "doc",
+ },
+ {
+ "id": "slugs/relativeSlug",
+ "type": "doc",
+ },
+ {
+ "id": "slugs/resolvedSlug",
+ "type": "doc",
+ },
+ {
+ "id": "slugs/tryToEscapeSlug",
+ "type": "doc",
+ },
+ ],
+ "label": "slugs",
+ "link": undefined,
+ "type": "category",
+ },
+ {
+ "collapsed": false,
+ "collapsible": true,
+ "items": [
+ {
+ "id": "unlisted-category/unlisted-category-doc",
+ "type": "doc",
+ },
+ ],
+ "label": "unlisted-category-index",
+ "link": {
+ "id": "unlisted-category/unlisted-category-index",
+ "type": "doc",
+ },
+ "type": "category",
+ },
+ ],
+ },
+}
+`;
+
+exports[`simple site custom pagination - production 1`] = `
+{
+ "pagination": [
+ {
+ "id": "customLastUpdate",
+ "next": {
+ "permalink": "/docs/doc with space",
+ "title": "Hoo hoo, if this path tricks you...",
+ },
+ "prev": undefined,
+ },
+ {
+ "id": "doc with space",
+ "next": {
+ "permalink": "/docs/doc-draft",
+ "title": "doc-draft",
+ },
+ "prev": {
+ "permalink": "/docs/customLastUpdate",
+ "title": "Custom Last Update",
+ },
+ },
+ {
+ "id": "doc-draft",
+ "next": {
+ "permalink": "/docs/foo/bar",
+ "title": "Bar",
+ },
+ "prev": {
+ "permalink": "/docs/doc with space",
+ "title": "Hoo hoo, if this path tricks you...",
+ },
+ },
+ {
+ "id": "doc-unlisted",
+ "next": undefined,
+ "prev": undefined,
+ },
+ {
+ "id": "foo/bar",
+ "next": undefined,
+ "prev": undefined,
+ },
+ {
+ "id": "foo/baz",
+ "next": {
+ "permalink": "/docs/headingAsTitle",
+ "title": "My heading as title",
+ },
+ "prev": {
+ "permalink": "/docs/foo/bar",
+ "title": "Bar",
+ },
+ },
+ {
+ "id": "headingAsTitle",
+ "next": {
+ "permalink": "/docs/",
+ "title": "Hello sidebar_label",
+ },
+ "prev": {
+ "permalink": "/docs/foo/bazSlug.html",
+ "title": "baz pagination_label",
+ },
+ },
+ {
+ "id": "hello",
+ "next": {
+ "permalink": "/docs/ipsum",
+ "title": "ipsum",
+ },
+ "prev": {
+ "permalink": "/docs/headingAsTitle",
+ "title": "My heading as title",
+ },
+ },
+ {
+ "id": "ipsum",
+ "next": undefined,
"prev": {
"permalink": "/docs/",
"title": "Hello sidebar_label",
@@ -200,6 +558,16 @@ exports[`simple site custom pagination 1`] = `
"title": "resolvedSlug",
},
},
+ {
+ "id": "unlisted-category/unlisted-category-doc",
+ "next": undefined,
+ "prev": undefined,
+ },
+ {
+ "id": "unlisted-category/unlisted-category-index",
+ "next": undefined,
+ "prev": undefined,
+ },
],
"sidebars": {
"defaultSidebar": [
@@ -215,6 +583,10 @@ exports[`simple site custom pagination 1`] = `
"id": "doc-draft",
"type": "doc",
},
+ {
+ "id": "doc-unlisted",
+ "type": "doc",
+ },
{
"collapsed": false,
"collapsible": true,
@@ -298,6 +670,22 @@ exports[`simple site custom pagination 1`] = `
"link": undefined,
"type": "category",
},
+ {
+ "collapsed": false,
+ "collapsible": true,
+ "items": [
+ {
+ "id": "unlisted-category/unlisted-category-doc",
+ "type": "doc",
+ },
+ ],
+ "label": "unlisted-category-index",
+ "link": {
+ "id": "unlisted-category/unlisted-category-index",
+ "type": "doc",
+ },
+ "type": "category",
+ },
],
},
}
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/globalData.test.ts.snap b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/globalData.test.ts.snap
index 0bd0862b0f8d..b8bcdf05873b 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/globalData.test.ts.snap
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__snapshots__/globalData.test.ts.snap
@@ -13,6 +13,12 @@ exports[`toGlobalDataVersion generates the right docs, sidebars, and metadata 1`
"path": "/current/doc",
"sidebar": "tutorial",
},
+ {
+ "id": "docNoSidebarUnlisted",
+ "path": "/current/docNoSidebarUnlisted",
+ "sidebar": undefined,
+ "unlisted": true,
+ },
{
"id": "/current/generated",
"path": "/current/generated",
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 244c608c1c0b..0e1e7a59263f 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
@@ -25,6 +25,7 @@ Available document ids are:
- customLastUpdate
- doc with space
- doc-draft
+- doc-unlisted
- foo/bar
- foo/baz
- headingAsTitle
@@ -40,7 +41,9 @@ Available document ids are:
- slugs/absoluteSlug
- slugs/relativeSlug
- slugs/resolvedSlug
-- slugs/tryToEscapeSlug"
+- slugs/tryToEscapeSlug
+- unlisted-category/unlisted-category-doc
+- unlisted-category/unlisted-category-index"
`;
exports[`simple website content 1`] = `
@@ -67,13 +70,13 @@ exports[`simple website content 1`] = `
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"next": {
- "permalink": "/docs/category/slugs",
- "title": "Slugs",
+ "permalink": "/docs/unlisted-category/",
+ "title": "unlisted-category-index",
},
"permalink": "/docs/foo/bazSlug.html",
"previous": {
- "permalink": "/docs/foo/bar",
- "title": "Bar",
+ "permalink": "/docs/doc-unlisted",
+ "title": "doc-unlisted",
},
"sidebar": "docs",
"sidebarPosition": undefined,
@@ -91,6 +94,7 @@ exports[`simple website content 1`] = `
},
],
"title": "baz",
+ "unlisted": false,
"unversionedId": "foo/baz",
"version": "current",
}
@@ -137,6 +141,7 @@ exports[`simple website content 2`] = `
},
],
"title": "Hello, World !",
+ "unlisted": false,
"unversionedId": "hello",
"version": "current",
}
@@ -168,6 +173,7 @@ exports[`simple website content 3`] = `
"sourceDirName": "foo",
"tags": [],
"title": "Bar",
+ "unlisted": false,
"unversionedId": "foo/bar",
"version": "current",
}
@@ -188,6 +194,10 @@ exports[`simple website content 4`] = `
"id": "foo/bar",
"type": "doc",
},
+ {
+ "id": "doc-unlisted",
+ "type": "doc",
+ },
{
"id": "foo/baz",
"type": "doc",
@@ -197,6 +207,22 @@ exports[`simple website content 4`] = `
"link": undefined,
"type": "category",
},
+ {
+ "collapsed": true,
+ "collapsible": true,
+ "items": [
+ {
+ "id": "unlisted-category/unlisted-category-doc",
+ "type": "doc",
+ },
+ ],
+ "label": "Unlisted category",
+ "link": {
+ "id": "unlisted-category/unlisted-category-index",
+ "type": "doc",
+ },
+ "type": "category",
+ },
{
"collapsed": true,
"collapsible": true,
@@ -285,6 +311,11 @@ exports[`simple website content 5`] = `
"path": "/docs/doc-draft",
"sidebar": undefined,
},
+ {
+ "id": "doc-unlisted",
+ "path": "/docs/doc-unlisted",
+ "sidebar": "docs",
+ },
{
"id": "foo/bar",
"path": "/docs/foo/bar",
@@ -365,6 +396,16 @@ exports[`simple website content 5`] = `
"path": "/docs/tryToEscapeSlug",
"sidebar": undefined,
},
+ {
+ "id": "unlisted-category/unlisted-category-doc",
+ "path": "/docs/unlisted-category/unlisted-category-doc",
+ "sidebar": "docs",
+ },
+ {
+ "id": "unlisted-category/unlisted-category-index",
+ "path": "/docs/unlisted-category/",
+ "sidebar": "docs",
+ },
{
"id": "/category/slugs",
"path": "/docs/category/slugs",
@@ -400,8 +441,8 @@ exports[`simple website content: data 1`] = `
"permalink": "/docs/category/slugs",
"navigation": {
"previous": {
- "title": "baz pagination_label",
- "permalink": "/docs/foo/bazSlug.html"
+ "title": "unlisted-category-doc",
+ "permalink": "/docs/unlisted-category/unlisted-category-doc"
},
"next": {
"title": "rootAbsoluteSlug",
@@ -419,6 +460,7 @@ exports[`simple website content: data 1`] = `
"slug": "/customLastUpdate",
"permalink": "/docs/customLastUpdate",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -439,11 +481,38 @@ exports[`simple website content: data 1`] = `
"slug": "/doc-draft",
"permalink": "/docs/doc-draft",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
"draft": true
}
+}",
+ "site-docs-doc-unlisted-md-80b.json": "{
+ "unversionedId": "doc-unlisted",
+ "id": "doc-unlisted",
+ "title": "doc-unlisted",
+ "description": "This is an unlisted document",
+ "source": "@site/docs/doc-unlisted.md",
+ "sourceDirName": ".",
+ "slug": "/doc-unlisted",
+ "permalink": "/docs/doc-unlisted",
+ "draft": false,
+ "unlisted": false,
+ "tags": [],
+ "version": "current",
+ "frontMatter": {
+ "unlisted": true
+ },
+ "sidebar": "docs",
+ "previous": {
+ "title": "Bar",
+ "permalink": "/docs/foo/bar"
+ },
+ "next": {
+ "title": "baz pagination_label",
+ "permalink": "/docs/foo/bazSlug.html"
+ }
}",
"site-docs-doc-with-space-md-e90.json": "{
"unversionedId": "doc with space",
@@ -455,6 +524,7 @@ exports[`simple website content: data 1`] = `
"slug": "/doc with space",
"permalink": "/docs/doc with space",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {}
@@ -469,6 +539,7 @@ exports[`simple website content: data 1`] = `
"slug": "/foo/bar",
"permalink": "/docs/foo/bar",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -490,6 +561,7 @@ exports[`simple website content: data 1`] = `
"slug": "/foo/bazSlug.html",
"permalink": "/docs/foo/bazSlug.html",
"draft": false,
+ "unlisted": false,
"tags": [
{
"label": "tag 1",
@@ -517,12 +589,12 @@ exports[`simple website content: data 1`] = `
},
"sidebar": "docs",
"previous": {
- "title": "Bar",
- "permalink": "/docs/foo/bar"
+ "title": "doc-unlisted",
+ "permalink": "/docs/doc-unlisted"
},
"next": {
- "title": "Slugs",
- "permalink": "/docs/category/slugs"
+ "title": "unlisted-category-index",
+ "permalink": "/docs/unlisted-category/"
}
}",
"site-docs-heading-as-title-md-c6d.json": "{
@@ -535,6 +607,7 @@ exports[`simple website content: data 1`] = `
"slug": "/headingAsTitle",
"permalink": "/docs/headingAsTitle",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {},
@@ -558,6 +631,7 @@ exports[`simple website content: data 1`] = `
"slug": "/",
"permalink": "/docs/",
"draft": false,
+ "unlisted": false,
"tags": [
{
"label": "tag-1",
@@ -595,11 +669,17 @@ exports[`simple website content: data 1`] = `
"slug": "/ipsum",
"permalink": "/docs/ipsum",
"draft": false,
+ "unlisted": false,
"editUrl": null,
"tags": [],
"version": "current",
"frontMatter": {
- "custom_edit_url": null
+ "custom_edit_url": null,
+ "pagination_next": "doc-unlisted"
+ },
+ "next": {
+ "title": "doc-unlisted",
+ "permalink": "/docs/doc-unlisted"
}
}",
"site-docs-last-update-author-only-md-352.json": "{
@@ -612,6 +692,7 @@ exports[`simple website content: data 1`] = `
"slug": "/lastUpdateAuthorOnly",
"permalink": "/docs/lastUpdateAuthorOnly",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -631,6 +712,7 @@ exports[`simple website content: data 1`] = `
"slug": "/lastUpdateDateOnly",
"permalink": "/docs/lastUpdateDateOnly",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -650,6 +732,7 @@ exports[`simple website content: data 1`] = `
"slug": "/lorem",
"permalink": "/docs/lorem",
"draft": false,
+ "unlisted": false,
"editUrl": "https://github.com/customUrl/docs/lorem.md",
"tags": [],
"version": "current",
@@ -668,6 +751,7 @@ exports[`simple website content: data 1`] = `
"slug": "/rootAbsoluteSlug",
"permalink": "/docs/rootAbsoluteSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -695,6 +779,7 @@ exports[`simple website content: data 1`] = `
"slug": "/rootRelativeSlug",
"permalink": "/docs/rootRelativeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -722,6 +807,7 @@ exports[`simple website content: data 1`] = `
"slug": "/hey/rootResolvedSlug",
"permalink": "/docs/hey/rootResolvedSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -749,6 +835,7 @@ exports[`simple website content: data 1`] = `
"slug": "/rootTryToEscapeSlug",
"permalink": "/docs/rootTryToEscapeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -776,6 +863,7 @@ exports[`simple website content: data 1`] = `
"slug": "/absoluteSlug",
"permalink": "/docs/absoluteSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -792,6 +880,7 @@ exports[`simple website content: data 1`] = `
"slug": "/slugs/relativeSlug",
"permalink": "/docs/slugs/relativeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -808,6 +897,7 @@ exports[`simple website content: data 1`] = `
"slug": "/slugs/hey/resolvedSlug",
"permalink": "/docs/slugs/hey/resolvedSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -824,11 +914,66 @@ exports[`simple website content: data 1`] = `
"slug": "/tryToEscapeSlug",
"permalink": "/docs/tryToEscapeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
"slug": "../../../../../../../../tryToEscapeSlug"
}
+}",
+ "site-docs-unlisted-category-index-md-efa.json": "{
+ "unversionedId": "unlisted-category/unlisted-category-index",
+ "id": "unlisted-category/unlisted-category-index",
+ "title": "unlisted-category-index",
+ "description": "This is an unlisted category index",
+ "source": "@site/docs/unlisted-category/index.md",
+ "sourceDirName": "unlisted-category",
+ "slug": "/unlisted-category/",
+ "permalink": "/docs/unlisted-category/",
+ "draft": false,
+ "unlisted": false,
+ "tags": [],
+ "version": "current",
+ "frontMatter": {
+ "id": "unlisted-category-index",
+ "unlisted": true
+ },
+ "sidebar": "docs",
+ "previous": {
+ "title": "baz pagination_label",
+ "permalink": "/docs/foo/bazSlug.html"
+ },
+ "next": {
+ "title": "unlisted-category-doc",
+ "permalink": "/docs/unlisted-category/unlisted-category-doc"
+ }
+}",
+ "site-docs-unlisted-category-unlisted-category-doc-md-bd6.json": "{
+ "unversionedId": "unlisted-category/unlisted-category-doc",
+ "id": "unlisted-category/unlisted-category-doc",
+ "title": "unlisted-category-doc",
+ "description": "This is an unlisted category doc",
+ "source": "@site/docs/unlisted-category/unlisted-category-doc.md",
+ "sourceDirName": "unlisted-category",
+ "slug": "/unlisted-category/unlisted-category-doc",
+ "permalink": "/docs/unlisted-category/unlisted-category-doc",
+ "draft": false,
+ "unlisted": false,
+ "tags": [],
+ "version": "current",
+ "frontMatter": {
+ "id": "unlisted-category-doc",
+ "unlisted": true
+ },
+ "sidebar": "docs",
+ "previous": {
+ "title": "unlisted-category-index",
+ "permalink": "/docs/unlisted-category/"
+ },
+ "next": {
+ "title": "Slugs",
+ "permalink": "/docs/category/slugs"
+ }
}",
"tag-docs-tags-tag-1-b3f.json": "{
"label": "tag 1",
@@ -848,7 +993,8 @@ exports[`simple website content: data 1`] = `
"description": "Hi, Endilie here :)",
"permalink": "/docs/"
}
- ]
+ ],
+ "unlisted": false
}",
"tag-docs-tags-tag-2-custom-permalink-825.json": "{
"label": "tag 2",
@@ -862,7 +1008,8 @@ exports[`simple website content: data 1`] = `
"description": "Images",
"permalink": "/docs/foo/bazSlug.html"
}
- ]
+ ],
+ "unlisted": false
}",
"tag-docs-tags-tag-3-ab5.json": "{
"label": "tag 3",
@@ -876,7 +1023,8 @@ exports[`simple website content: data 1`] = `
"description": "Hi, Endilie here :)",
"permalink": "/docs/"
}
- ]
+ ],
+ "unlisted": false
}",
"tags-list-current-prop-15a.json": "[
{
@@ -918,18 +1066,43 @@ exports[`simple website content: data 1`] = `
"type": "link",
"label": "Bar",
"href": "/docs/foo/bar",
- "docId": "foo/bar"
+ "docId": "foo/bar",
+ "unlisted": false
+ },
+ {
+ "type": "link",
+ "label": "doc-unlisted",
+ "href": "/docs/doc-unlisted",
+ "docId": "doc-unlisted",
+ "unlisted": false
},
{
"type": "link",
"label": "baz",
"href": "/docs/foo/bazSlug.html",
- "docId": "foo/baz"
+ "docId": "foo/baz",
+ "unlisted": false
}
],
"collapsed": true,
"collapsible": true
},
+ {
+ "type": "category",
+ "label": "Unlisted category",
+ "items": [
+ {
+ "type": "link",
+ "label": "unlisted-category-doc",
+ "href": "/docs/unlisted-category/unlisted-category-doc",
+ "docId": "unlisted-category/unlisted-category-doc",
+ "unlisted": false
+ }
+ ],
+ "collapsed": true,
+ "collapsible": true,
+ "href": "/docs/unlisted-category/"
+ },
{
"type": "category",
"label": "Slugs",
@@ -938,25 +1111,29 @@ exports[`simple website content: data 1`] = `
"type": "link",
"label": "rootAbsoluteSlug",
"href": "/docs/rootAbsoluteSlug",
- "docId": "rootAbsoluteSlug"
+ "docId": "rootAbsoluteSlug",
+ "unlisted": false
},
{
"type": "link",
"label": "rootRelativeSlug",
"href": "/docs/rootRelativeSlug",
- "docId": "rootRelativeSlug"
+ "docId": "rootRelativeSlug",
+ "unlisted": false
},
{
"type": "link",
"label": "rootResolvedSlug",
"href": "/docs/hey/rootResolvedSlug",
- "docId": "rootResolvedSlug"
+ "docId": "rootResolvedSlug",
+ "unlisted": false
},
{
"type": "link",
"label": "rootTryToEscapeSlug",
"href": "/docs/rootTryToEscapeSlug",
- "docId": "rootTryToEscapeSlug"
+ "docId": "rootTryToEscapeSlug",
+ "unlisted": false
}
],
"collapsed": true,
@@ -967,7 +1144,8 @@ exports[`simple website content: data 1`] = `
"type": "link",
"label": "My heading as title",
"href": "/docs/headingAsTitle",
- "docId": "headingAsTitle"
+ "docId": "headingAsTitle",
+ "unlisted": false
},
{
"type": "link",
@@ -978,7 +1156,8 @@ exports[`simple website content: data 1`] = `
"type": "link",
"label": "Hello sidebar_label",
"href": "/docs/",
- "docId": "hello"
+ "docId": "hello",
+ "unlisted": false
}
],
"collapsed": true,
@@ -992,7 +1171,8 @@ exports[`simple website content: data 1`] = `
"type": "link",
"label": "Hello sidebar_label",
"href": "/docs/",
- "docId": "hello"
+ "docId": "hello",
+ "unlisted": false
}
],
"collapsed": true,
@@ -1016,6 +1196,12 @@ exports[`simple website content: data 1`] = `
"title": "doc-draft",
"description": "This is a draft document"
},
+ "doc-unlisted": {
+ "id": "doc-unlisted",
+ "title": "doc-unlisted",
+ "description": "This is an unlisted document",
+ "sidebar": "docs"
+ },
"foo/bar": {
"id": "foo/bar",
"title": "Bar",
@@ -1103,6 +1289,18 @@ exports[`simple website content: data 1`] = `
"id": "slugs/tryToEscapeSlug",
"title": "tryToEscapeSlug",
"description": "Lorem"
+ },
+ "unlisted-category/unlisted-category-doc": {
+ "id": "unlisted-category/unlisted-category-doc",
+ "title": "unlisted-category-doc",
+ "description": "This is an unlisted category doc",
+ "sidebar": "docs"
+ },
+ "unlisted-category/unlisted-category-index": {
+ "id": "unlisted-category/unlisted-category-index",
+ "title": "unlisted-category-index",
+ "description": "This is an unlisted category index",
+ "sidebar": "docs"
}
}
}",
@@ -1133,6 +1331,11 @@ exports[`simple website content: global data 1`] = `
"path": "/docs/doc-draft",
"sidebar": undefined,
},
+ {
+ "id": "doc-unlisted",
+ "path": "/docs/doc-unlisted",
+ "sidebar": "docs",
+ },
{
"id": "foo/bar",
"path": "/docs/foo/bar",
@@ -1213,6 +1416,16 @@ exports[`simple website content: global data 1`] = `
"path": "/docs/tryToEscapeSlug",
"sidebar": undefined,
},
+ {
+ "id": "unlisted-category/unlisted-category-doc",
+ "path": "/docs/unlisted-category/unlisted-category-doc",
+ "sidebar": "docs",
+ },
+ {
+ "id": "unlisted-category/unlisted-category-index",
+ "path": "/docs/unlisted-category/",
+ "sidebar": "docs",
+ },
{
"id": "/category/slugs",
"path": "/docs/category/slugs",
@@ -1343,6 +1556,15 @@ exports[`simple website content: route config 1`] = `
},
"path": "/docs/doc-draft",
},
+ {
+ "component": "@theme/DocItem",
+ "exact": true,
+ "modules": {
+ "content": "@site/docs/doc-unlisted.md",
+ },
+ "path": "/docs/doc-unlisted",
+ "sidebar": "docs",
+ },
{
"component": "@theme/DocItem",
"exact": true,
@@ -1462,6 +1684,24 @@ exports[`simple website content: route config 1`] = `
},
"path": "/docs/tryToEscapeSlug",
},
+ {
+ "component": "@theme/DocItem",
+ "exact": true,
+ "modules": {
+ "content": "@site/docs/unlisted-category/index.md",
+ },
+ "path": "/docs/unlisted-category/",
+ "sidebar": "docs",
+ },
+ {
+ "component": "@theme/DocItem",
+ "exact": true,
+ "modules": {
+ "content": "@site/docs/unlisted-category/unlisted-category-doc.md",
+ },
+ "path": "/docs/unlisted-category/unlisted-category-doc",
+ "sidebar": "docs",
+ },
],
},
],
@@ -1787,6 +2027,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": ".",
"tags": [],
"title": "Getting Started",
+ "unlisted": false,
"unversionedId": "getting-started",
"version": "current",
}
@@ -1818,6 +2059,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": ".",
"tags": [],
"title": "Installation",
+ "unlisted": false,
"unversionedId": "installation",
"version": "current",
}
@@ -1852,6 +2094,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "Guides",
"tags": [],
"title": "Guide 1",
+ "unlisted": false,
"unversionedId": "Guides/guide1",
"version": "current",
}
@@ -1885,6 +2128,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "Guides",
"tags": [],
"title": "Guide 2",
+ "unlisted": false,
"unversionedId": "Guides/guide2",
"version": "current",
}
@@ -1919,6 +2163,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "Guides",
"tags": [],
"title": "Guide 2.5",
+ "unlisted": false,
"unversionedId": "Guides/guide2.5",
"version": "current",
}
@@ -1953,6 +2198,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "Guides",
"tags": [],
"title": "Guide 3",
+ "unlisted": false,
"unversionedId": "Guides/guide3",
"version": "current",
}
@@ -1986,6 +2232,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "Guides",
"tags": [],
"title": "Guide 4",
+ "unlisted": false,
"unversionedId": "Guides/guide4",
"version": "current",
}
@@ -2019,6 +2266,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "Guides",
"tags": [],
"title": "Guide 5",
+ "unlisted": false,
"unversionedId": "Guides/guide5",
"version": "current",
}
@@ -2050,6 +2298,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "3-API",
"tags": [],
"title": "API Overview",
+ "unlisted": false,
"unversionedId": "API/api-overview",
"version": "current",
}
@@ -2081,6 +2330,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "3-API/01_Core APIs",
"tags": [],
"title": "Client API",
+ "unlisted": false,
"unversionedId": "API/Core APIs/Client API",
"version": "current",
}
@@ -2112,6 +2362,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "3-API/01_Core APIs",
"tags": [],
"title": "Server API",
+ "unlisted": false,
"unversionedId": "API/Core APIs/Server API",
"version": "current",
}
@@ -2143,6 +2394,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "3-API/02_Extension APIs",
"tags": [],
"title": "Plugin API",
+ "unlisted": false,
"unversionedId": "API/Extension APIs/Plugin API",
"version": "current",
}
@@ -2174,6 +2426,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "3-API/02_Extension APIs",
"tags": [],
"title": "Theme API",
+ "unlisted": false,
"unversionedId": "API/Extension APIs/Theme API",
"version": "current",
}
@@ -2202,6 +2455,7 @@ exports[`site with full autogenerated sidebar docs in fully generated sidebar ha
"sourceDirName": "3-API",
"tags": [],
"title": "API End",
+ "unlisted": false,
"unversionedId": "API/api-end",
"version": "current",
}
@@ -2382,6 +2636,7 @@ exports[`site with partial autogenerated sidebars docs in partially generated si
"sourceDirName": "3-API",
"tags": [],
"title": "API End",
+ "unlisted": false,
"unversionedId": "API/api-end",
"version": "current",
}
@@ -2413,6 +2668,7 @@ exports[`site with partial autogenerated sidebars docs in partially generated si
"sourceDirName": "3-API",
"tags": [],
"title": "API Overview",
+ "unlisted": false,
"unversionedId": "API/api-overview",
"version": "current",
}
@@ -2444,6 +2700,7 @@ exports[`site with partial autogenerated sidebars docs in partially generated si
"sourceDirName": "3-API/02_Extension APIs",
"tags": [],
"title": "Plugin API",
+ "unlisted": false,
"unversionedId": "API/Extension APIs/Plugin API",
"version": "current",
}
@@ -2472,6 +2729,7 @@ exports[`site with partial autogenerated sidebars docs in partially generated si
"sourceDirName": "3-API/02_Extension APIs",
"tags": [],
"title": "Theme API",
+ "unlisted": false,
"unversionedId": "API/Extension APIs/Theme API",
"version": "current",
}
@@ -2531,6 +2789,7 @@ exports[`versioned website (community) content 1`] = `
"sourceDirName": ".",
"tags": [],
"title": "Team title translated",
+ "unlisted": false,
"unversionedId": "team",
"version": "current",
}
@@ -2556,6 +2815,7 @@ exports[`versioned website (community) content 2`] = `
"sourceDirName": ".",
"tags": [],
"title": "team",
+ "unlisted": false,
"unversionedId": "team",
"version": "1.0.0",
}
@@ -2595,6 +2855,7 @@ exports[`versioned website (community) content: data 1`] = `
"slug": "/team",
"permalink": "/community/team",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "1.0.0",
"frontMatter": {},
@@ -2610,6 +2871,7 @@ exports[`versioned website (community) content: data 1`] = `
"slug": "/team",
"permalink": "/community/next/team",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -2632,7 +2894,8 @@ exports[`versioned website (community) content: data 1`] = `
"type": "link",
"label": "team",
"href": "/community/team",
- "docId": "team"
+ "docId": "team",
+ "unlisted": false
}
]
},
@@ -2660,7 +2923,8 @@ exports[`versioned website (community) content: data 1`] = `
"type": "link",
"label": "Team title translated",
"href": "/community/next/team",
- "docId": "team"
+ "docId": "team",
+ "unlisted": false
}
]
},
@@ -2860,6 +3124,7 @@ exports[`versioned website content 1`] = `
},
],
"title": "bar",
+ "unlisted": false,
"unversionedId": "foo/bar",
"version": "current",
}
@@ -2888,6 +3153,7 @@ exports[`versioned website content 2`] = `
"sourceDirName": "foo",
"tags": [],
"title": "bar",
+ "unlisted": false,
"unversionedId": "foo/bar",
"version": "1.0.1",
}
@@ -2918,6 +3184,7 @@ exports[`versioned website content 3`] = `
"sourceDirName": ".",
"tags": [],
"title": "hello",
+ "unlisted": false,
"unversionedId": "hello",
"version": "current",
}
@@ -2948,6 +3215,7 @@ exports[`versioned website content 4`] = `
"sourceDirName": ".",
"tags": [],
"title": "hello",
+ "unlisted": false,
"unversionedId": "hello",
"version": "1.0.1",
}
@@ -2979,6 +3247,7 @@ exports[`versioned website content 5`] = `
"sourceDirName": "foo",
"tags": [],
"title": "baz",
+ "unlisted": false,
"unversionedId": "foo/baz",
"version": "1.0.0",
}
@@ -3099,6 +3368,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/foo/barSlug",
"permalink": "/docs/next/foo/barSlug",
"draft": false,
+ "unlisted": false,
"tags": [
{
"label": "barTag 1",
@@ -3141,6 +3411,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/",
"permalink": "/docs/next/",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -3162,6 +3433,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/absoluteSlug",
"permalink": "/docs/next/absoluteSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -3178,6 +3450,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/slugs/relativeSlug",
"permalink": "/docs/next/slugs/relativeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -3194,6 +3467,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/slugs/hey/resolvedSlug",
"permalink": "/docs/next/slugs/hey/resolvedSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -3210,6 +3484,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/tryToEscapeSlug",
"permalink": "/docs/next/tryToEscapeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "current",
"frontMatter": {
@@ -3226,6 +3501,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/",
"permalink": "/docs/1.0.0/",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "1.0.0",
"frontMatter": {
@@ -3247,6 +3523,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/foo/barSlug",
"permalink": "/docs/1.0.0/foo/barSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "1.0.0",
"frontMatter": {
@@ -3268,6 +3545,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/foo/baz",
"permalink": "/docs/1.0.0/foo/baz",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "1.0.0",
"frontMatter": {},
@@ -3291,6 +3569,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/foo/bar",
"permalink": "/docs/foo/bar",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "1.0.1",
"frontMatter": {},
@@ -3310,6 +3589,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/",
"permalink": "/docs/",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "1.0.1",
"frontMatter": {
@@ -3331,6 +3611,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/rootAbsoluteSlug",
"permalink": "/docs/withSlugs/rootAbsoluteSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "withSlugs",
"frontMatter": {
@@ -3348,6 +3629,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/rootRelativeSlug",
"permalink": "/docs/withSlugs/rootRelativeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "withSlugs",
"frontMatter": {
@@ -3364,6 +3646,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/hey/rootResolvedSlug",
"permalink": "/docs/withSlugs/hey/rootResolvedSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "withSlugs",
"frontMatter": {
@@ -3380,6 +3663,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/rootTryToEscapeSlug",
"permalink": "/docs/withSlugs/rootTryToEscapeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "withSlugs",
"frontMatter": {
@@ -3396,6 +3680,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/absoluteSlug",
"permalink": "/docs/withSlugs/absoluteSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "withSlugs",
"frontMatter": {
@@ -3412,6 +3697,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/slugs/relativeSlug",
"permalink": "/docs/withSlugs/slugs/relativeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "withSlugs",
"frontMatter": {
@@ -3428,6 +3714,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/slugs/hey/resolvedSlug",
"permalink": "/docs/withSlugs/slugs/hey/resolvedSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "withSlugs",
"frontMatter": {
@@ -3444,6 +3731,7 @@ exports[`versioned website content: data 1`] = `
"slug": "/tryToEscapeSlug",
"permalink": "/docs/withSlugs/tryToEscapeSlug",
"draft": false,
+ "unlisted": false,
"tags": [],
"version": "withSlugs",
"frontMatter": {
@@ -3462,7 +3750,8 @@ exports[`versioned website content: data 1`] = `
"description": "This is next version of bar.",
"permalink": "/docs/next/foo/barSlug"
}
- ]
+ ],
+ "unlisted": false
}",
"tag-docs-next-tags-bar-tag-2-216.json": "{
"label": "barTag-2",
@@ -3476,7 +3765,8 @@ exports[`versioned website content: data 1`] = `
"description": "This is next version of bar.",
"permalink": "/docs/next/foo/barSlug"
}
- ]
+ ],
+ "unlisted": false
}",
"tag-docs-next-tags-bar-tag-3-permalink-94a.json": "{
"label": "barTag 3",
@@ -3490,7 +3780,8 @@ exports[`versioned website content: data 1`] = `
"description": "This is next version of bar.",
"permalink": "/docs/next/foo/barSlug"
}
- ]
+ ],
+ "unlisted": false
}",
"tags-list-current-prop-15a.json": "[
{
@@ -3528,13 +3819,15 @@ exports[`versioned website content: data 1`] = `
"type": "link",
"label": "bar",
"href": "/docs/1.0.0/foo/barSlug",
- "docId": "foo/bar"
+ "docId": "foo/bar",
+ "unlisted": false
},
{
"type": "link",
"label": "baz",
"href": "/docs/1.0.0/foo/baz",
- "docId": "foo/baz"
+ "docId": "foo/baz",
+ "unlisted": false
}
],
"collapsed": true,
@@ -3548,7 +3841,8 @@ exports[`versioned website content: data 1`] = `
"type": "link",
"label": "hello",
"href": "/docs/1.0.0/",
- "docId": "hello"
+ "docId": "hello",
+ "unlisted": false
}
],
"collapsed": true,
@@ -3596,7 +3890,8 @@ exports[`versioned website content: data 1`] = `
"type": "link",
"label": "bar",
"href": "/docs/foo/bar",
- "docId": "foo/bar"
+ "docId": "foo/bar",
+ "unlisted": false
}
],
"collapsed": true,
@@ -3610,7 +3905,8 @@ exports[`versioned website content: data 1`] = `
"type": "link",
"label": "hello",
"href": "/docs/",
- "docId": "hello"
+ "docId": "hello",
+ "unlisted": false
}
],
"collapsed": true,
@@ -3652,7 +3948,8 @@ exports[`versioned website content: data 1`] = `
"type": "link",
"label": "bar",
"href": "/docs/next/foo/barSlug",
- "docId": "foo/bar"
+ "docId": "foo/bar",
+ "unlisted": false
}
],
"collapsed": true,
@@ -3666,7 +3963,8 @@ exports[`versioned website content: data 1`] = `
"type": "link",
"label": "hello",
"href": "/docs/next/",
- "docId": "hello"
+ "docId": "hello",
+ "unlisted": false
}
],
"collapsed": true,
@@ -3728,7 +4026,8 @@ exports[`versioned website content: data 1`] = `
"type": "link",
"label": "rootAbsoluteSlug",
"href": "/docs/withSlugs/rootAbsoluteSlug",
- "docId": "rootAbsoluteSlug"
+ "docId": "rootAbsoluteSlug",
+ "unlisted": false
}
],
"collapsed": true,
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts
index f0971546e594..831b655a2721 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts
@@ -153,7 +153,7 @@ function createTestUtils({
versionMetadata,
context,
options,
- env: 'production',
+ env,
}),
),
);
@@ -173,11 +173,11 @@ function createTestUtils({
const sidebarsUtils = createSidebarsUtils(sidebars);
return {
- pagination: addDocNavigation(
- rawDocs,
+ pagination: addDocNavigation({
+ docs: rawDocs,
sidebarsUtils,
- versionMetadata.sidebarFilePath as string,
- ).map((doc) => ({prev: doc.previous, next: doc.next, id: doc.id})),
+ sidebarFilePath: versionMetadata.sidebarFilePath as string,
+ }).map((doc) => ({prev: doc.previous, next: doc.next, id: doc.id})),
sidebars,
};
}
@@ -247,6 +247,7 @@ describe('simple site', () => {
'headingAsTitle.md',
'doc with space.md',
'doc-draft.md',
+ 'doc-unlisted.md',
'customLastUpdate.md',
'lastUpdateAuthorOnly.md',
'lastUpdateDateOnly.md',
@@ -256,6 +257,8 @@ describe('simple site', () => {
'slugs/relativeSlug.md',
'slugs/resolvedSlug.md',
'slugs/tryToEscapeSlug.md',
+ 'unlisted-category/index.md',
+ 'unlisted-category/unlisted-category-doc.md',
].sort(),
);
});
@@ -279,6 +282,7 @@ describe('simple site', () => {
pagination_prev: null,
},
tags: [],
+ unlisted: false,
});
await defaultTestUtils.testMeta(path.join('hello.md'), {
version: 'current',
@@ -306,6 +310,7 @@ describe('simple site', () => {
permalink: '/docs/tags/tag-3',
},
],
+ unlisted: false,
});
});
@@ -356,6 +361,7 @@ describe('simple site', () => {
permalink: '/docs/tags/tag2-custom-permalink',
},
],
+ unlisted: false,
});
});
@@ -377,6 +383,7 @@ describe('simple site', () => {
unrelated_front_matter: "won't be part of metadata",
},
tags: [],
+ unlisted: false,
});
});
@@ -430,6 +437,7 @@ describe('simple site', () => {
permalink: '/docs/tags/tag2-custom-permalink',
},
],
+ unlisted: false,
});
expect(editUrlFunction).toHaveBeenCalledTimes(1);
@@ -476,6 +484,7 @@ describe('simple site', () => {
formattedLastUpdatedAt: 'Oct 14, 2018',
lastUpdatedBy: 'Author',
tags: [],
+ unlisted: false,
});
});
@@ -501,6 +510,44 @@ describe('simple site', () => {
});
});
+ it('docs with unlisted frontmatter', async () => {
+ const {createTestUtilsPartial} = await loadSite();
+
+ const baseMeta = {
+ version: 'current',
+ id: 'doc-unlisted',
+ unversionedId: 'doc-unlisted',
+ sourceDirName: '.',
+ permalink: '/docs/doc-unlisted',
+ slug: '/doc-unlisted',
+ title: 'doc-unlisted',
+ description: 'This is an unlisted document',
+ frontMatter: {
+ unlisted: true,
+ },
+ sidebarPosition: undefined,
+ tags: [],
+ };
+
+ const testUtilsProd = createTestUtilsPartial({
+ env: 'production',
+ });
+
+ await testUtilsProd.testMeta('doc-unlisted.md', {
+ ...baseMeta,
+ unlisted: true,
+ });
+
+ const testUtilsDev = createTestUtilsPartial({
+ env: 'development',
+ });
+
+ await testUtilsDev.testMeta('doc-unlisted.md', {
+ ...baseMeta,
+ unlisted: false,
+ });
+ });
+
it('docs with last_update front matter', async () => {
const {siteDir, context, options, currentVersion, createTestUtilsPartial} =
await loadSite({
@@ -538,6 +585,7 @@ describe('simple site', () => {
lastUpdatedBy: 'Custom Author',
sidebarPosition: undefined,
tags: [],
+ unlisted: false,
});
});
@@ -577,6 +625,7 @@ describe('simple site', () => {
lastUpdatedBy: 'Custom Author',
sidebarPosition: undefined,
tags: [],
+ unlisted: false,
});
});
@@ -616,6 +665,7 @@ describe('simple site', () => {
lastUpdatedBy: 'Author',
sidebarPosition: undefined,
tags: [],
+ unlisted: false,
});
});
@@ -656,6 +706,7 @@ describe('simple site', () => {
lastUpdatedBy: undefined,
sidebarPosition: undefined,
tags: [],
+ unlisted: false,
});
});
@@ -718,12 +769,20 @@ describe('simple site', () => {
);
});
- it('custom pagination', async () => {
- const {defaultTestUtils, options, versionsMetadata} = await loadSite();
+ it('custom pagination - production', async () => {
+ const {createTestUtilsPartial, options, versionsMetadata} =
+ await loadSite();
+ const testUtils = createTestUtilsPartial({env: 'production'});
const docs = await readVersionDocs(versionsMetadata[0]!, options);
- await expect(
- defaultTestUtils.generateNavigation(docs),
- ).resolves.toMatchSnapshot();
+ await expect(testUtils.generateNavigation(docs)).resolves.toMatchSnapshot();
+ });
+
+ it('custom pagination - development', async () => {
+ const {createTestUtilsPartial, options, versionsMetadata} =
+ await loadSite();
+ const testUtils = createTestUtilsPartial({env: 'development'});
+ const docs = await readVersionDocs(versionsMetadata[0]!, options);
+ await expect(testUtils.generateNavigation(docs)).resolves.toMatchSnapshot();
});
it('bad pagination', async () => {
@@ -847,6 +906,7 @@ describe('versioned site', () => {
permalink: '/docs/next/tags/barTag-3-permalink',
},
],
+ unlisted: false,
});
await currentVersionTestUtils.testMeta(path.join('hello.md'), {
id: 'hello',
@@ -861,6 +921,7 @@ describe('versioned site', () => {
slug: '/',
},
tags: [],
+ unlisted: false,
});
});
@@ -878,6 +939,7 @@ describe('versioned site', () => {
frontMatter: {slug: 'barSlug'},
version: '1.0.0',
tags: [],
+ unlisted: false,
});
await version100TestUtils.testMeta(path.join('hello.md'), {
id: 'version-1.0.0/hello',
@@ -894,6 +956,7 @@ describe('versioned site', () => {
source:
'@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
tags: [],
+ unlisted: false,
});
await version101TestUtils.testMeta(path.join('foo', 'bar.md'), {
id: 'version-1.0.1/foo/bar',
@@ -906,6 +969,7 @@ describe('versioned site', () => {
version: '1.0.1',
frontMatter: {},
tags: [],
+ unlisted: false,
});
await version101TestUtils.testMeta(path.join('hello.md'), {
id: 'version-1.0.1/hello',
@@ -920,6 +984,7 @@ describe('versioned site', () => {
slug: '/',
},
tags: [],
+ unlisted: false,
});
});
@@ -1016,6 +1081,7 @@ describe('versioned site', () => {
'@site/i18n/en/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
editUrl: hardcodedEditUrl,
tags: [],
+ unlisted: false,
});
expect(editUrlFunction).toHaveBeenCalledTimes(1);
@@ -1059,6 +1125,7 @@ describe('versioned site', () => {
editUrl:
'https://github.com/facebook/docusaurus/edit/main/website/versioned_docs/version-1.0.0/hello.md',
tags: [],
+ unlisted: false,
});
});
@@ -1094,6 +1161,7 @@ describe('versioned site', () => {
editUrl:
'https://github.com/facebook/docusaurus/edit/main/website/docs/hello.md',
tags: [],
+ unlisted: false,
});
});
@@ -1130,6 +1198,7 @@ describe('versioned site', () => {
editUrl:
'https://github.com/facebook/docusaurus/edit/main/website/i18n/fr/docusaurus-plugin-content-docs/version-1.0.0/hello.md',
tags: [],
+ unlisted: false,
});
});
@@ -1167,6 +1236,7 @@ describe('versioned site', () => {
editUrl:
'https://github.com/facebook/docusaurus/edit/main/website/i18n/fr/docusaurus-plugin-content-docs/current/hello.md',
tags: [],
+ unlisted: false,
});
});
});
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts
index 55da1682c725..7aeada8201b8 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts
@@ -44,15 +44,12 @@ function testField(params: {
params.invalidFrontMatters?.forEach(([frontMatter, message]) => {
try {
validateDocFrontMatter(frontMatter);
- // eslint-disable-next-line jest/no-jasmine-globals
- fail(
- new Error(
- `Doc front matter is expected to be rejected, but was accepted successfully:\n ${JSON.stringify(
- frontMatter,
- null,
- 2,
- )}`,
- ),
+ throw new Error(
+ `Doc front matter is expected to be rejected, but was accepted successfully:\n ${JSON.stringify(
+ frontMatter,
+ null,
+ 2,
+ )}`,
);
} catch (err) {
// eslint-disable-next-line jest/no-conditional-expect
@@ -397,6 +394,41 @@ describe('validateDocFrontMatter draft', () => {
});
});
+describe('validateDocFrontMatter unlisted', () => {
+ testField({
+ prefix: 'unlisted',
+ validFrontMatters: [{unlisted: true}, {unlisted: false}],
+ convertibleFrontMatter: [
+ [{unlisted: 'true'}, {unlisted: true}],
+ [{unlisted: 'false'}, {unlisted: false}],
+ ],
+ invalidFrontMatters: [
+ [{unlisted: 'yes'}, 'must be a boolean'],
+ [{unlisted: 'no'}, 'must be a boolean'],
+ [{unlisted: ''}, 'must be a boolean'],
+ ],
+ });
+});
+
+describe('validateDocFrontMatter draft XOR unlisted', () => {
+ testField({
+ prefix: 'draft XOR unlisted',
+ validFrontMatters: [
+ {draft: false},
+ {unlisted: false},
+ {draft: false, unlisted: false},
+ {draft: true, unlisted: false},
+ {draft: false, unlisted: true},
+ ],
+ invalidFrontMatters: [
+ [
+ {draft: true, unlisted: true},
+ "Can't be draft and unlisted at the same time.",
+ ],
+ ],
+ });
+});
+
describe('validateDocFrontMatter last_update', () => {
testField({
prefix: 'last_update',
diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/globalData.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/globalData.test.ts
index 35663e48b7aa..062c02404003 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/globalData.test.ts
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/globalData.test.ts
@@ -19,12 +19,21 @@ describe('toGlobalDataVersion', () => {
permalink: '/current/main',
sidebar: 'tutorial',
frontMatter: {},
+ unlisted: false,
},
{
unversionedId: 'doc',
permalink: '/current/doc',
sidebar: 'tutorial',
frontMatter: {},
+ unlisted: undefined,
+ },
+ {
+ unversionedId: 'docNoSidebarUnlisted',
+ permalink: '/current/docNoSidebarUnlisted',
+ sidebar: undefined,
+ frontMatter: {},
+ unlisted: true,
},
] as DocMetadata[];
const sidebars: Sidebars = {
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 7103e3908d59..6a2f0f70d63f 100644
--- a/packages/docusaurus-plugin-content-docs/src/__tests__/props.test.ts
+++ b/packages/docusaurus-plugin-content-docs/src/__tests__/props.test.ts
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
-import {toTagDocListProp} from '../props';
+import {toSidebarDocItemLinkProp, toTagDocListProp} from '../props';
describe('toTagDocListProp', () => {
type Params = Parameters[0];
@@ -61,3 +61,74 @@ describe('toTagDocListProp', () => {
});
});
});
+
+describe('toSidebarDocItemLinkProp', () => {
+ type Params = Parameters[0];
+ type Result = ReturnType;
+ type DocSidebarItem = Params['item'];
+ type Doc = Params['doc'];
+
+ const id = 'some-doc-id';
+ const unversionedId = 'some-unversioned-doc-id';
+
+ const item: DocSidebarItem = {
+ type: 'doc',
+ id,
+ label: 'doc sidebar item label',
+ };
+
+ const doc: Doc = {
+ id,
+ unversionedId,
+ title: 'doc title',
+ permalink: '/docPermalink',
+ frontMatter: {},
+ unlisted: false,
+ };
+
+ it('works', () => {
+ const result = toSidebarDocItemLinkProp({
+ item,
+ doc,
+ });
+
+ expect(result).toEqual({
+ type: 'link',
+ docId: unversionedId,
+ unlisted: false,
+ label: item.label,
+ autoAddBaseUrl: undefined,
+ className: undefined,
+ href: doc.permalink,
+ customProps: undefined,
+ } as Result);
+ });
+
+ it('uses unlisted from metadata and ignores frontMatter', () => {
+ expect(
+ toSidebarDocItemLinkProp({
+ item,
+ doc: {
+ ...doc,
+ unlisted: true,
+ frontMatter: {
+ unlisted: false,
+ },
+ },
+ }).unlisted,
+ ).toBe(true);
+
+ expect(
+ toSidebarDocItemLinkProp({
+ item,
+ doc: {
+ ...doc,
+ unlisted: false,
+ frontMatter: {
+ unlisted: true,
+ },
+ },
+ }).unlisted,
+ ).toBe(false);
+ });
+});
diff --git a/packages/docusaurus-plugin-content-docs/src/client/index.ts b/packages/docusaurus-plugin-content-docs/src/client/index.ts
index bf475ee00e9d..335729b898fd 100644
--- a/packages/docusaurus-plugin-content-docs/src/client/index.ts
+++ b/packages/docusaurus-plugin-content-docs/src/client/index.ts
@@ -37,7 +37,8 @@ export type GlobalDoc = {
*/
id: string;
path: string;
- sidebar: string | undefined;
+ sidebar?: string;
+ unlisted?: boolean;
};
export type GlobalVersion = {
diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts
index 40fe33eb52da..da620f2876c4 100644
--- a/packages/docusaurus-plugin-content-docs/src/docs.ts
+++ b/packages/docusaurus-plugin-content-docs/src/docs.ts
@@ -18,6 +18,8 @@ import {
posixPath,
Globby,
normalizeFrontMatterTags,
+ isUnlisted,
+ isDraft,
} from '@docusaurus/utils';
import {getFileLastUpdate} from './lastUpdate';
@@ -35,7 +37,6 @@ import type {
PropNavigationLink,
LastUpdateData,
VersionMetadata,
- DocFrontMatter,
LoadedVersion,
FileChange,
} from '@docusaurus/plugin-content-docs';
@@ -125,17 +126,6 @@ export async function readVersionDocs(
export type DocEnv = 'production' | 'development';
-/** Docs with draft front matter are only considered draft in production. */
-function isDraftForEnvironment({
- env,
- frontMatter,
-}: {
- frontMatter: DocFrontMatter;
- env: DocEnv;
-}): boolean {
- return (env === 'production' && frontMatter.draft) ?? false;
-}
-
async function doProcessDocMetadata({
docFile,
versionMetadata,
@@ -268,7 +258,8 @@ async function doProcessDocMetadata({
return undefined;
}
- const draft = isDraftForEnvironment({env, frontMatter});
+ const draft = isDraft({env, frontMatter});
+ const unlisted = isUnlisted({env, frontMatter});
const formatDate = (locale: string, date: Date, calendar: string): string => {
try {
@@ -299,6 +290,7 @@ async function doProcessDocMetadata({
slug: docSlug,
permalink,
draft,
+ unlisted,
editUrl: customEditURL !== undefined ? customEditURL : getDocEditUrl(),
tags: normalizeFrontMatterTags(versionMetadata.tagsPath, frontMatter.tags),
version: versionMetadata.versionName,
@@ -333,25 +325,32 @@ export async function processDocMetadata(args: {
}
}
-export function addDocNavigation(
- docsBase: DocMetadataBase[],
- sidebarsUtils: SidebarsUtils,
- sidebarFilePath: string,
-): LoadedVersion['docs'] {
- const docsById = createDocsByIdIndex(docsBase);
+function getUnlistedIds(docs: DocMetadataBase[]): Set {
+ return new Set(docs.filter((doc) => doc.unlisted).map((doc) => doc.id));
+}
- sidebarsUtils.checkSidebarsDocIds(
- docsBase.flatMap(getDocIds),
- sidebarFilePath,
- );
+export function addDocNavigation({
+ docs,
+ sidebarsUtils,
+ sidebarFilePath,
+}: {
+ docs: DocMetadataBase[];
+ sidebarsUtils: SidebarsUtils;
+ sidebarFilePath: string;
+}): LoadedVersion['docs'] {
+ const docsById = createDocsByIdIndex(docs);
+ const unlistedIds = getUnlistedIds(docs);
+
+ sidebarsUtils.checkSidebarsDocIds(docs.flatMap(getDocIds), sidebarFilePath);
// Add sidebar/next/previous to the docs
function addNavData(doc: DocMetadataBase): DocMetadata {
- const navigation = sidebarsUtils.getDocNavigation(
- doc.unversionedId,
- doc.id,
- doc.frontMatter.displayed_sidebar,
- );
+ const navigation = sidebarsUtils.getDocNavigation({
+ unversionedId: doc.unversionedId,
+ versionedId: doc.id,
+ displayedSidebar: doc.frontMatter.displayed_sidebar,
+ unlistedIds,
+ });
const toNavigationLinkByDocId = (
docId: string | null | undefined,
@@ -367,6 +366,10 @@ export function addDocNavigation(
`Error when loading ${doc.id} in ${doc.sourceDirName}: the pagination_${type} front matter points to a non-existent ID ${docId}.`,
);
}
+ // Gracefully handle explicitly providing an unlisted doc ID in production
+ if (navDoc.unlisted) {
+ return undefined;
+ }
return toDocNavigationLink(navDoc);
};
@@ -382,7 +385,7 @@ export function addDocNavigation(
return {...doc, sidebar: navigation.sidebarName, previous, next};
}
- const docsWithNavigation = docsBase.map(addNavData);
+ const docsWithNavigation = docs.map(addNavData);
// Sort to ensure consistent output for tests
docsWithNavigation.sort((a, b) => a.id.localeCompare(b.id));
return docsWithNavigation;
diff --git a/packages/docusaurus-plugin-content-docs/src/frontMatter.ts b/packages/docusaurus-plugin-content-docs/src/frontMatter.ts
index 9d210a831b25..1cffac35f32c 100644
--- a/packages/docusaurus-plugin-content-docs/src/frontMatter.ts
+++ b/packages/docusaurus-plugin-content-docs/src/frontMatter.ts
@@ -11,6 +11,7 @@ import {
FrontMatterTagsSchema,
FrontMatterTOCHeadingLevels,
validateFrontMatter,
+ ContentVisibilitySchema,
} from '@docusaurus/utils-validation';
import type {DocFrontMatter} from '@docusaurus/plugin-content-docs';
@@ -43,7 +44,6 @@ const DocFrontMatterSchema = Joi.object({
parse_number_prefixes: Joi.boolean(),
pagination_next: Joi.string().allow(null),
pagination_prev: Joi.string().allow(null),
- draft: Joi.boolean(),
...FrontMatterTOCHeadingLevels,
last_update: Joi.object({
author: Joi.string(),
@@ -54,7 +54,9 @@ const DocFrontMatterSchema = Joi.object({
'object.missing': FrontMatterLastUpdateErrorMessage,
'object.base': FrontMatterLastUpdateErrorMessage,
}),
-}).unknown();
+})
+ .unknown()
+ .concat(ContentVisibilitySchema);
export function validateDocFrontMatter(frontMatter: {
[key: string]: unknown;
diff --git a/packages/docusaurus-plugin-content-docs/src/globalData.ts b/packages/docusaurus-plugin-content-docs/src/globalData.ts
index 594e14c0f200..5679e2f68e52 100644
--- a/packages/docusaurus-plugin-content-docs/src/globalData.ts
+++ b/packages/docusaurus-plugin-content-docs/src/globalData.ts
@@ -23,6 +23,11 @@ function toGlobalDataDoc(doc: DocMetadata): GlobalDoc {
return {
id: doc.unversionedId,
path: doc.permalink,
+
+ // optimize global data size: do not add unlisted: false/undefined
+ ...(doc.unlisted && {unlisted: doc.unlisted}),
+
+ // TODO optimize size? remove attribute when no sidebar (breaking change?)
sidebar: doc.sidebar,
};
}
diff --git a/packages/docusaurus-plugin-content-docs/src/index.ts b/packages/docusaurus-plugin-content-docs/src/index.ts
index 74eac5d38afe..cb34f5249a82 100644
--- a/packages/docusaurus-plugin-content-docs/src/index.ts
+++ b/packages/docusaurus-plugin-content-docs/src/index.ts
@@ -75,6 +75,9 @@ export default async function pluginContentDocs(
const aliasedSource = (source: string) =>
`~docs/${posixPath(path.relative(pluginDataDirRoot, source))}`;
+ // TODO env should be injected into all plugins
+ const env = process.env.NODE_ENV as DocEnv;
+
return {
name: 'docusaurus-plugin-content-docs',
@@ -143,7 +146,7 @@ export default async function pluginContentDocs(
versionMetadata,
context,
options,
- env: process.env.NODE_ENV as DocEnv,
+ env,
});
}
return Promise.all(docFiles.map(processVersionDoc));
@@ -156,6 +159,9 @@ export default async function pluginContentDocs(
versionMetadata,
);
+ // TODO we only ever need draftIds in further code, not full draft items
+ // To simplify and prevent mistakes, avoid exposing draft
+ // replace draft=>draftIds in content loaded
const [drafts, docs] = _.partition(docsBase, (doc) => doc.draft);
const sidebars = await loadSidebars(versionMetadata.sidebarFilePath, {
@@ -175,11 +181,11 @@ export default async function pluginContentDocs(
return {
...versionMetadata,
- docs: addDocNavigation(
+ docs: addDocNavigation({
docs,
sidebarsUtils,
- versionMetadata.sidebarFilePath as string,
- ),
+ sidebarFilePath: versionMetadata.sidebarFilePath as string,
+ }),
drafts,
sidebars,
};
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 3ae1e0fb7acc..9462c9c92a8a 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
@@ -398,6 +398,8 @@ declare module '@docusaurus/plugin-content-docs' {
pagination_prev?: string | null;
/** Should this doc be excluded from production builds? */
draft?: boolean;
+ /** Should this doc be accessible but hidden in production builds? */
+ unlisted?: boolean;
/** Allows overriding the last updated author and/or date. */
last_update?: FileChange;
};
@@ -448,6 +450,11 @@ declare module '@docusaurus/plugin-content-docs' {
* Draft docs will be excluded for production environment.
*/
draft: boolean;
+ /**
+ * Unlisted docs are accessible when directly visible, but will be hidden
+ * from the sidebar and pagination in production.
+ */
+ unlisted: boolean;
/**
* Position in an autogenerated sidebar slice, acquired through front matter
* or number prefix.
diff --git a/packages/docusaurus-plugin-content-docs/src/props.ts b/packages/docusaurus-plugin-content-docs/src/props.ts
index c9f58096ac50..c53329564a49 100644
--- a/packages/docusaurus-plugin-content-docs/src/props.ts
+++ b/packages/docusaurus-plugin-content-docs/src/props.ts
@@ -28,6 +28,37 @@ import type {
LoadedVersion,
} from '@docusaurus/plugin-content-docs';
+export function toSidebarDocItemLinkProp({
+ item,
+ doc,
+}: {
+ item: SidebarItemDoc;
+ doc: Pick<
+ DocMetadata,
+ 'id' | 'title' | 'permalink' | 'unlisted' | 'frontMatter' | 'unversionedId'
+ >;
+}): PropSidebarItemLink {
+ const {
+ title,
+ permalink,
+ frontMatter: {
+ sidebar_label: sidebarLabel,
+ sidebar_custom_props: customProps,
+ },
+ unlisted,
+ unversionedId,
+ } = doc;
+ return {
+ type: 'link',
+ label: sidebarLabel ?? item.label ?? title,
+ href: permalink,
+ className: item.className,
+ customProps: item.customProps ?? customProps,
+ docId: unversionedId,
+ unlisted,
+ };
+}
+
export function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars {
const docsById = createDocsByIdIndex(loadedVersion.docs);
@@ -44,21 +75,8 @@ Available document ids are:
}
const convertDocLink = (item: SidebarItemDoc): PropSidebarItemLink => {
- const docMetadata = getDocById(item.id);
- const {
- title,
- permalink,
- frontMatter: {sidebar_label: sidebarLabel},
- } = docMetadata;
- return {
- type: 'link',
- label: sidebarLabel ?? item.label ?? title,
- href: permalink,
- className: item.className,
- customProps:
- item.customProps ?? docMetadata.frontMatter.sidebar_custom_props,
- docId: docMetadata.unversionedId,
- };
+ const doc = getDocById(item.id);
+ return toSidebarDocItemLinkProp({item, doc});
};
function getCategoryLinkHref(
@@ -74,6 +92,15 @@ Available document ids are:
}
}
+ function getCategoryLinkUnlisted(
+ link: SidebarItemCategoryLink | undefined,
+ ): boolean {
+ if (link?.type === 'doc') {
+ return getDocById(link.id).unlisted;
+ }
+ return false;
+ }
+
function getCategoryLinkCustomProps(
link: SidebarItemCategoryLink | undefined,
) {
@@ -88,12 +115,14 @@ Available document ids are:
function convertCategory(item: SidebarItemCategory): PropSidebarItemCategory {
const {link, ...rest} = item;
const href = getCategoryLinkHref(link);
+ const linkUnlisted = getCategoryLinkUnlisted(link);
const customProps = item.customProps ?? getCategoryLinkCustomProps(link);
return {
...rest,
items: item.items.map(normalizeItem),
...(href && {href}),
+ ...(linkUnlisted && {linkUnlisted}),
...(customProps && {customProps}),
};
}
@@ -180,15 +209,18 @@ export function toTagDocListProp({
allTagsPath,
count: tag.docIds.length,
items: toDocListProp(),
+ unlisted: tag.unlisted,
};
}
export function toTagsListTagsProp(
versionTags: VersionTags,
): PropTagsListPage['tags'] {
- return Object.values(versionTags).map((tagValue) => ({
- label: tagValue.label,
- permalink: tagValue.permalink,
- count: tagValue.docIds.length,
- }));
+ return Object.values(versionTags)
+ .filter((tagValue) => !tagValue.unlisted)
+ .map((tagValue) => ({
+ label: tagValue.label,
+ permalink: tagValue.permalink,
+ count: tagValue.docIds.length,
+ }));
}
diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/utils.test.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/utils.test.ts
index 60c30b96a889..bc6c50b74c86 100644
--- a/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/utils.test.ts
+++ b/packages/docusaurus-plugin-content-docs/src/sidebars/__tests__/utils.test.ts
@@ -153,7 +153,14 @@ describe('createSidebarsUtils', () => {
});
it('getDocNavigation', () => {
- expect(getDocNavigation('doc1', 'doc1', undefined)).toEqual({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc1',
+ versionedId: 'doc1',
+ displayedSidebar: undefined,
+ unlistedIds: new Set(),
+ }),
+ ).toEqual({
sidebarName: 'sidebar1',
previous: undefined,
next: {
@@ -161,7 +168,14 @@ describe('createSidebarsUtils', () => {
id: 'doc2',
},
});
- expect(getDocNavigation('doc2', 'doc2', undefined)).toEqual({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc2',
+ versionedId: 'doc2',
+ displayedSidebar: undefined,
+ unlistedIds: new Set(),
+ }),
+ ).toEqual({
sidebarName: 'sidebar1',
previous: {
type: 'doc',
@@ -170,7 +184,14 @@ describe('createSidebarsUtils', () => {
next: undefined,
});
- expect(getDocNavigation('doc3', 'doc3', undefined)).toEqual({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc3',
+ versionedId: 'doc3',
+ displayedSidebar: undefined,
+ unlistedIds: new Set(),
+ }),
+ ).toEqual({
sidebarName: 'sidebar2',
previous: undefined,
next: {
@@ -178,7 +199,14 @@ describe('createSidebarsUtils', () => {
id: 'doc4',
},
});
- expect(getDocNavigation('doc4', 'doc4', undefined)).toEqual({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc4',
+ versionedId: 'doc4',
+ displayedSidebar: undefined,
+ unlistedIds: new Set(),
+ }),
+ ).toEqual({
sidebarName: 'sidebar2',
previous: {
type: 'doc',
@@ -188,7 +216,14 @@ describe('createSidebarsUtils', () => {
next: undefined,
});
- expect(getDocNavigation('doc5', 'doc5', undefined)).toMatchObject({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc5',
+ versionedId: 'doc5',
+ displayedSidebar: undefined,
+ unlistedIds: new Set(),
+ }),
+ ).toMatchObject({
sidebarName: 'sidebar3',
previous: undefined,
next: {
@@ -196,7 +231,14 @@ describe('createSidebarsUtils', () => {
label: 'S3 SubCategory',
},
});
- expect(getDocNavigation('doc6', 'doc6', undefined)).toMatchObject({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc6',
+ versionedId: 'doc6',
+ displayedSidebar: undefined,
+ unlistedIds: new Set(),
+ }),
+ ).toMatchObject({
sidebarName: 'sidebar3',
previous: {
type: 'category',
@@ -207,7 +249,14 @@ describe('createSidebarsUtils', () => {
id: 'doc7',
},
});
- expect(getDocNavigation('doc7', 'doc7', undefined)).toEqual({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc7',
+ versionedId: 'doc7',
+ displayedSidebar: undefined,
+ unlistedIds: new Set(),
+ }),
+ ).toEqual({
sidebarName: 'sidebar3',
previous: {
type: 'doc',
@@ -215,17 +264,36 @@ describe('createSidebarsUtils', () => {
},
next: undefined,
});
- expect(getDocNavigation('doc3', 'doc3', null)).toEqual({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc3',
+ versionedId: 'doc3',
+ displayedSidebar: null,
+ unlistedIds: new Set(),
+ }),
+ ).toEqual({
sidebarName: undefined,
previous: undefined,
next: undefined,
});
expect(() =>
- getDocNavigation('doc3', 'doc3', 'foo'),
+ getDocNavigation({
+ unversionedId: 'doc3',
+ versionedId: 'doc3',
+ displayedSidebar: 'foo',
+ unlistedIds: new Set(),
+ }),
).toThrowErrorMatchingInlineSnapshot(
`"Doc with ID doc3 wants to display sidebar foo but a sidebar with this name doesn't exist"`,
);
- expect(getDocNavigation('doc3', 'doc3', 'sidebar1')).toEqual({
+ expect(
+ getDocNavigation({
+ unversionedId: 'doc3',
+ versionedId: 'doc3',
+ displayedSidebar: 'sidebar1',
+ unlistedIds: new Set(),
+ }),
+ ).toEqual({
sidebarName: 'sidebar1',
previous: undefined,
next: undefined,
diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts
index 555004846ea4..d6d7f07a7587 100644
--- a/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts
+++ b/packages/docusaurus-plugin-content-docs/src/sidebars/types.ts
@@ -183,11 +183,18 @@ export type PropSidebarItemCategory = Expand<
SidebarItemCategoryBase & {
items: PropSidebarItem[];
href?: string;
+
+ // Weird name => it would have been more convenient to have link.unlisted
+ // Note it is the category link that is unlisted, not the category itself
+ // We want to prevent users from clicking on an unlisted category link
+ // We can't use "href: undefined" otherwise sidebar item is not highlighted
+ linkUnlisted?: boolean;
}
>;
export type PropSidebarItemLink = SidebarItemLink & {
docId?: string;
+ unlisted?: boolean;
};
export type PropSidebarItemHtml = SidebarItemHtml;
diff --git a/packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts b/packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts
index 1ac140cd627b..59d10f4a1430 100644
--- a/packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts
+++ b/packages/docusaurus-plugin-content-docs/src/sidebars/utils.ts
@@ -135,11 +135,12 @@ export type SidebarsUtils = {
sidebars: Sidebars;
getFirstDocIdOfFirstSidebar: () => string | undefined;
getSidebarNameByDocId: (docId: string) => string | undefined;
- getDocNavigation: (
- unversionedId: string,
- versionedId: string,
- displayedSidebar: string | null | undefined,
- ) => SidebarNavigation;
+ getDocNavigation: (params: {
+ unversionedId: string;
+ versionedId: string;
+ displayedSidebar: string | null | undefined;
+ unlistedIds: Set;
+ }) => SidebarNavigation;
getCategoryGeneratedIndexList: () => SidebarItemCategoryWithGeneratedIndex[];
getCategoryGeneratedIndexNavigation: (
categoryGeneratedIndexPermalink: string,
@@ -192,11 +193,17 @@ export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
};
}
- function getDocNavigation(
- unversionedId: string,
- versionedId: string,
- displayedSidebar: string | null | undefined,
- ): SidebarNavigation {
+ function getDocNavigation({
+ unversionedId,
+ versionedId,
+ displayedSidebar,
+ unlistedIds,
+ }: {
+ unversionedId: string;
+ versionedId: string;
+ displayedSidebar: string | null | undefined;
+ unlistedIds: Set;
+ }): SidebarNavigation {
// TODO legacy id retro-compatibility!
let docId = unversionedId;
let sidebarName =
@@ -211,12 +218,28 @@ export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
if (!sidebarName) {
return emptySidebarNavigation();
}
- const navigationItems = sidebarNameToNavigationItems[sidebarName];
+ let navigationItems = sidebarNameToNavigationItems[sidebarName];
if (!navigationItems) {
throw new Error(
`Doc with ID ${docId} wants to display sidebar ${sidebarName} but a sidebar with this name doesn't exist`,
);
}
+
+ // Filter unlisted items from navigation
+ navigationItems = navigationItems.filter((item) => {
+ if (item.type === 'doc' && unlistedIds.has(item.id)) {
+ return false;
+ }
+ if (
+ item.type === 'category' &&
+ item.link.type === 'doc' &&
+ unlistedIds.has(item.link.id)
+ ) {
+ return false;
+ }
+ return true;
+ });
+
const currentItemIndex = navigationItems.findIndex((item) => {
if (item.type === 'doc') {
return item.id === docId;
diff --git a/packages/docusaurus-plugin-content-docs/src/tags.ts b/packages/docusaurus-plugin-content-docs/src/tags.ts
index dfa0e3284587..1fd784c213fa 100644
--- a/packages/docusaurus-plugin-content-docs/src/tags.ts
+++ b/packages/docusaurus-plugin-content-docs/src/tags.ts
@@ -6,15 +6,22 @@
*/
import _ from 'lodash';
-import {groupTaggedItems} from '@docusaurus/utils';
+import {getTagVisibility, groupTaggedItems} from '@docusaurus/utils';
import type {VersionTags} from './types';
import type {DocMetadata} from '@docusaurus/plugin-content-docs';
export function getVersionTags(docs: DocMetadata[]): VersionTags {
const groups = groupTaggedItems(docs, (doc) => doc.tags);
- return _.mapValues(groups, (group) => ({
- label: group.tag.label,
- docIds: group.items.map((item) => item.id),
- permalink: group.tag.permalink,
- }));
+ return _.mapValues(groups, ({tag, items: tagDocs}) => {
+ const tagVisibility = getTagVisibility({
+ items: tagDocs,
+ isUnlisted: (item) => item.unlisted,
+ });
+ return {
+ label: tag.label,
+ docIds: tagVisibility.listedItems.map((item) => item.id),
+ permalink: tag.permalink,
+ unlisted: tagVisibility.unlisted,
+ };
+ });
}
diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts
index 4c4568517fbb..d78521c71684 100644
--- a/packages/docusaurus-plugin-content-docs/src/types.ts
+++ b/packages/docusaurus-plugin-content-docs/src/types.ts
@@ -27,6 +27,7 @@ export type SourceToPermalink = {
export type VersionTag = Tag & {
/** All doc ids having this tag. */
docIds: string[];
+ unlisted: boolean;
};
export type VersionTags = {
[permalink: string]: VersionTag;
diff --git a/packages/docusaurus-plugin-content-pages/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-plugin-content-pages/src/__tests__/__snapshots__/index.test.ts.snap
index c4c22e849260..56028498771e 100644
--- a/packages/docusaurus-plugin-content-pages/src/__tests__/__snapshots__/index.test.ts.snap
+++ b/packages/docusaurus-plugin-content-pages/src/__tests__/__snapshots__/index.test.ts.snap
@@ -19,6 +19,7 @@ exports[`docusaurus-plugin-content-pages loads simple pages 1`] = `
"source": "@site/src/pages/hello/index.md",
"title": "Index",
"type": "mdx",
+ "unlisted": false,
},
{
"description": "my MDX page",
@@ -30,6 +31,7 @@ exports[`docusaurus-plugin-content-pages loads simple pages 1`] = `
"source": "@site/src/pages/hello/mdxPage.mdx",
"title": "MDX page",
"type": "mdx",
+ "unlisted": false,
},
{
"permalink": "/hello/translatedJs",
@@ -43,6 +45,7 @@ exports[`docusaurus-plugin-content-pages loads simple pages 1`] = `
"source": "@site/src/pages/hello/translatedMd.md",
"title": undefined,
"type": "mdx",
+ "unlisted": false,
},
{
"permalink": "/hello/world",
@@ -71,6 +74,7 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
"source": "@site/src/pages/hello/index.md",
"title": "Index",
"type": "mdx",
+ "unlisted": false,
},
{
"description": "my MDX page",
@@ -82,6 +86,7 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
"source": "@site/src/pages/hello/mdxPage.mdx",
"title": "MDX page",
"type": "mdx",
+ "unlisted": false,
},
{
"permalink": "/fr/hello/translatedJs",
@@ -95,6 +100,7 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
"source": "@site/i18n/fr/docusaurus-plugin-content-pages/hello/translatedMd.md",
"title": undefined,
"type": "mdx",
+ "unlisted": false,
},
{
"permalink": "/fr/hello/world",
diff --git a/packages/docusaurus-plugin-content-pages/src/frontMatter.ts b/packages/docusaurus-plugin-content-pages/src/frontMatter.ts
index 8c9247da710a..87562de53e34 100644
--- a/packages/docusaurus-plugin-content-pages/src/frontMatter.ts
+++ b/packages/docusaurus-plugin-content-pages/src/frontMatter.ts
@@ -9,6 +9,7 @@ import {
Joi,
validateFrontMatter,
FrontMatterTOCHeadingLevels,
+ ContentVisibilitySchema,
} from '@docusaurus/utils-validation';
import type {FrontMatter} from '@docusaurus/plugin-content-pages';
@@ -18,7 +19,7 @@ const PageFrontMatterSchema = Joi.object({
wrapperClassName: Joi.string(),
hide_table_of_contents: Joi.boolean(),
...FrontMatterTOCHeadingLevels,
-});
+}).concat(ContentVisibilitySchema);
export function validatePageFrontMatter(frontMatter: {
[key: string]: unknown;
diff --git a/packages/docusaurus-plugin-content-pages/src/index.ts b/packages/docusaurus-plugin-content-pages/src/index.ts
index c3c51b883283..ab1b3c70ccf3 100644
--- a/packages/docusaurus-plugin-content-pages/src/index.ts
+++ b/packages/docusaurus-plugin-content-pages/src/index.ts
@@ -20,6 +20,8 @@ import {
normalizeUrl,
DEFAULT_PLUGIN_ID,
parseMarkdownString,
+ isUnlisted,
+ isDraft,
} from '@docusaurus/utils';
import {validatePageFrontMatter} from './frontMatter';
@@ -82,7 +84,9 @@ export default function pluginContentPages(
ignore: options.exclude,
});
- async function toMetadata(relativeSource: string): Promise {
+ async function processPageSourceFile(
+ relativeSource: string,
+ ): Promise {
// Lookup in localized folder in priority
const contentPath = await getFolderContainingFile(
getContentPathList(contentPaths),
@@ -110,6 +114,12 @@ export default function pluginContentPages(
excerpt,
} = parseMarkdownString(content);
const frontMatter = validatePageFrontMatter(unsafeFrontMatter);
+
+ if (isDraft({frontMatter})) {
+ return undefined;
+ }
+ const unlisted = isUnlisted({frontMatter});
+
return {
type: 'mdx',
permalink,
@@ -117,10 +127,24 @@ export default function pluginContentPages(
title: frontMatter.title ?? contentTitle,
description: frontMatter.description ?? excerpt,
frontMatter,
+ unlisted,
};
}
- return Promise.all(pagesFiles.map(toMetadata));
+ async function doProcessPageSourceFile(relativeSource: string) {
+ try {
+ return await processPageSourceFile(relativeSource);
+ } catch (err) {
+ throw new Error(
+ `Processing of page source file path=${relativeSource} failed.`,
+ {cause: err as Error},
+ );
+ }
+ }
+
+ return (
+ await Promise.all(pagesFiles.map(doProcessPageSourceFile))
+ ).filter(Boolean) as Metadata[];
},
async contentLoaded({content, actions}) {
diff --git a/packages/docusaurus-plugin-content-pages/src/plugin-content-pages.d.ts b/packages/docusaurus-plugin-content-pages/src/plugin-content-pages.d.ts
index f60623c7de65..09279828392a 100644
--- a/packages/docusaurus-plugin-content-pages/src/plugin-content-pages.d.ts
+++ b/packages/docusaurus-plugin-content-pages/src/plugin-content-pages.d.ts
@@ -27,6 +27,8 @@ declare module '@docusaurus/plugin-content-pages' {
readonly hide_table_of_contents?: string;
readonly toc_min_heading_level?: number;
readonly toc_max_heading_level?: number;
+ readonly draft?: boolean;
+ readonly unlisted?: boolean;
};
export type JSXPageMetadata = {
@@ -42,6 +44,7 @@ declare module '@docusaurus/plugin-content-pages' {
frontMatter: FrontMatter & {[key: string]: unknown};
title?: string;
description?: string;
+ unlisted: boolean;
};
export type Metadata = JSXPageMetadata | MDXPageMetadata;
diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts
index 2e89d14c8ddd..be4cf3fa94e2 100644
--- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts
+++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts
@@ -1494,6 +1494,14 @@ declare module '@theme/Tag' {
export default function Tag(props: Props): JSX.Element;
}
+declare module '@theme/Unlisted' {
+ export interface Props {
+ className?: string;
+ }
+
+ export default function Unlisted(props: Props): JSX.Element;
+}
+
declare module '@theme/prism-include-languages' {
import type * as PrismNamespace from 'prismjs';
diff --git a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx
index 2dec817ef470..f640f603347b 100644
--- a/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx
+++ b/packages/docusaurus-theme-classic/src/theme/BlogPostPage/index.tsx
@@ -15,6 +15,7 @@ import BlogPostPaginator from '@theme/BlogPostPaginator';
import BlogPostPageMetadata from '@theme/BlogPostPage/Metadata';
import TOC from '@theme/TOC';
import type {Props} from '@theme/BlogPostPage';
+import Unlisted from '@theme/Unlisted';
import type {BlogSidebar} from '@docusaurus/plugin-content-blog';
function BlogPostPageContent({
@@ -25,7 +26,7 @@ function BlogPostPageContent({
children: ReactNode;
}): JSX.Element {
const {metadata, toc} = useBlogPost();
- const {nextItem, prevItem, frontMatter} = metadata;
+ const {nextItem, prevItem, frontMatter, unlisted} = metadata;
const {
hide_table_of_contents: hideTableOfContents,
toc_min_heading_level: tocMinHeadingLevel,
@@ -43,6 +44,7 @@ function BlogPostPageContent({
/>
) : undefined
}>
+ {unlisted && }
{children}
{(nextItem || prevItem) && (
diff --git a/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/index.tsx b/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/index.tsx
index f010d2144836..8488a49e3116 100644
--- a/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/index.tsx
+++ b/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/index.tsx
@@ -9,11 +9,13 @@ import React from 'react';
import clsx from 'clsx';
import Link from '@docusaurus/Link';
import {translate} from '@docusaurus/Translate';
+import {useVisibleBlogSidebarItems} from '@docusaurus/theme-common/internal';
import type {Props} from '@theme/BlogSidebar/Desktop';
import styles from './styles.module.css';
export default function BlogSidebarDesktop({sidebar}: Props): JSX.Element {
+ const items = useVisibleBlogSidebarItems(sidebar.items);
return (