mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-10 07:37:19 +02:00
feat(blog): add LastUpdateAuthor & LastUpdateTime (#9912)
Co-authored-by: OzakIOne <OzakIOne@users.noreply.github.com> Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
parent
7938803747
commit
c745021b01
40 changed files with 833 additions and 359 deletions
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Author
|
||||
slug: author
|
||||
author: ozaki
|
||||
last_update:
|
||||
author: seb
|
||||
---
|
||||
|
||||
author
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: Both
|
||||
slug: both
|
||||
date: 2020-01-01
|
||||
last_update:
|
||||
date: 2021-01-01
|
||||
author: seb
|
||||
author: ozaki
|
||||
---
|
||||
|
||||
last update date
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
title: Last update date
|
||||
slug: lastUpdateDate
|
||||
date: 2020-01-01
|
||||
last_update:
|
||||
date: 2021-01-01
|
||||
---
|
||||
|
||||
last update date
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Nothing
|
||||
slug: nothing
|
||||
---
|
||||
|
||||
nothing
|
|
@ -137,6 +137,8 @@ exports[`blog plugin process blog posts load content 2`] = `
|
|||
"title": "Another Simple Slug",
|
||||
},
|
||||
"hasTruncateMarker": false,
|
||||
"lastUpdatedAt": undefined,
|
||||
"lastUpdatedBy": undefined,
|
||||
"nextItem": {
|
||||
"permalink": "/blog/another/tags",
|
||||
"title": "Another With Tag",
|
||||
|
@ -172,6 +174,8 @@ exports[`blog plugin process blog posts load content 2`] = `
|
|||
"title": "Another With Tag",
|
||||
},
|
||||
"hasTruncateMarker": false,
|
||||
"lastUpdatedAt": undefined,
|
||||
"lastUpdatedBy": undefined,
|
||||
"nextItem": {
|
||||
"permalink": "/blog/another/tags2",
|
||||
"title": "Another With Tag",
|
||||
|
@ -215,6 +219,8 @@ exports[`blog plugin process blog posts load content 2`] = `
|
|||
"title": "Another With Tag",
|
||||
},
|
||||
"hasTruncateMarker": false,
|
||||
"lastUpdatedAt": undefined,
|
||||
"lastUpdatedBy": undefined,
|
||||
"permalink": "/blog/another/tags2",
|
||||
"prevItem": {
|
||||
"permalink": "/blog/another/tags",
|
||||
|
|
|
@ -8,7 +8,12 @@
|
|||
import {jest} from '@jest/globals';
|
||||
import path from 'path';
|
||||
import {normalizePluginOptions} from '@docusaurus/utils-validation';
|
||||
import {posixPath, getFileCommitDate} from '@docusaurus/utils';
|
||||
import {
|
||||
posixPath,
|
||||
getFileCommitDate,
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
} from '@docusaurus/utils';
|
||||
import pluginContentBlog from '../index';
|
||||
import {validateOptions} from '../options';
|
||||
import type {
|
||||
|
@ -510,7 +515,7 @@ describe('blog plugin', () => {
|
|||
{
|
||||
postsPerPage: 1,
|
||||
processBlogPosts: async ({blogPosts}) =>
|
||||
blogPosts.filter((blog) => blog.metadata.tags[0].label === 'tag1'),
|
||||
blogPosts.filter((blog) => blog.metadata.tags[0]?.label === 'tag1'),
|
||||
},
|
||||
DefaultI18N,
|
||||
);
|
||||
|
@ -526,3 +531,137 @@ describe('blog plugin', () => {
|
|||
expect(blogPosts).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('last update', () => {
|
||||
const siteDir = path.join(
|
||||
__dirname,
|
||||
'__fixtures__',
|
||||
'website-blog-with-last-update',
|
||||
);
|
||||
|
||||
const lastUpdateFor = (date: string) => new Date(date).getTime() / 1000;
|
||||
|
||||
it('author and time', async () => {
|
||||
const plugin = await getPlugin(
|
||||
siteDir,
|
||||
{
|
||||
showLastUpdateAuthor: true,
|
||||
showLastUpdateTime: true,
|
||||
},
|
||||
DefaultI18N,
|
||||
);
|
||||
const {blogPosts} = (await plugin.loadContent!())!;
|
||||
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBe('seb');
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
);
|
||||
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
);
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
);
|
||||
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBe('seb');
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBe(
|
||||
lastUpdateFor('2021-01-01'),
|
||||
);
|
||||
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
);
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(
|
||||
lastUpdateFor('2021-01-01'),
|
||||
);
|
||||
});
|
||||
|
||||
it('time only', async () => {
|
||||
const plugin = await getPlugin(
|
||||
siteDir,
|
||||
{
|
||||
showLastUpdateAuthor: false,
|
||||
showLastUpdateTime: true,
|
||||
},
|
||||
DefaultI18N,
|
||||
);
|
||||
const {blogPosts} = (await plugin.loadContent!())!;
|
||||
|
||||
expect(blogPosts[0]?.metadata.title).toBe('Author');
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
);
|
||||
|
||||
expect(blogPosts[1]?.metadata.title).toBe('Nothing');
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_DATE,
|
||||
);
|
||||
|
||||
expect(blogPosts[2]?.metadata.title).toBe('Both');
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBe(
|
||||
lastUpdateFor('2021-01-01'),
|
||||
);
|
||||
|
||||
expect(blogPosts[3]?.metadata.title).toBe('Last update date');
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBe(
|
||||
lastUpdateFor('2021-01-01'),
|
||||
);
|
||||
});
|
||||
|
||||
it('author only', async () => {
|
||||
const plugin = await getPlugin(
|
||||
siteDir,
|
||||
{
|
||||
showLastUpdateAuthor: true,
|
||||
showLastUpdateTime: false,
|
||||
},
|
||||
DefaultI18N,
|
||||
);
|
||||
const {blogPosts} = (await plugin.loadContent!())!;
|
||||
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBe('seb');
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
);
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBe('seb');
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBe(
|
||||
GIT_FALLBACK_LAST_UPDATE_AUTHOR,
|
||||
);
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
});
|
||||
|
||||
it('none', async () => {
|
||||
const plugin = await getPlugin(
|
||||
siteDir,
|
||||
{
|
||||
showLastUpdateAuthor: false,
|
||||
showLastUpdateTime: false,
|
||||
},
|
||||
DefaultI18N,
|
||||
);
|
||||
const {blogPosts} = (await plugin.loadContent!())!;
|
||||
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[0]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[1]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[2]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedBy).toBeUndefined();
|
||||
expect(blogPosts[3]?.metadata.lastUpdatedAt).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,6 +26,7 @@ import {
|
|||
getContentPathList,
|
||||
isUnlisted,
|
||||
isDraft,
|
||||
readLastUpdateData,
|
||||
} from '@docusaurus/utils';
|
||||
import {validateBlogPostFrontMatter} from './frontMatter';
|
||||
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
||||
|
@ -231,6 +232,12 @@ async function processBlogSourceFile(
|
|||
|
||||
const aliasedSource = aliasedSitePath(blogSourceAbsolute, siteDir);
|
||||
|
||||
const lastUpdate = await readLastUpdateData(
|
||||
blogSourceAbsolute,
|
||||
options,
|
||||
frontMatter.last_update,
|
||||
);
|
||||
|
||||
const draft = isDraft({frontMatter});
|
||||
const unlisted = isUnlisted({frontMatter});
|
||||
|
||||
|
@ -337,6 +344,8 @@ async function processBlogSourceFile(
|
|||
authors,
|
||||
frontMatter,
|
||||
unlisted,
|
||||
lastUpdatedAt: lastUpdate.lastUpdatedAt,
|
||||
lastUpdatedBy: lastUpdate.lastUpdatedBy,
|
||||
},
|
||||
content,
|
||||
};
|
||||
|
|
|
@ -4,14 +4,14 @@
|
|||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import {
|
||||
ContentVisibilitySchema,
|
||||
FrontMatterLastUpdateSchema,
|
||||
FrontMatterTOCHeadingLevels,
|
||||
FrontMatterTagsSchema,
|
||||
JoiFrontMatter as Joi, // Custom instance for front matter
|
||||
URISchema,
|
||||
validateFrontMatter,
|
||||
FrontMatterTagsSchema,
|
||||
FrontMatterTOCHeadingLevels,
|
||||
ContentVisibilitySchema,
|
||||
} from '@docusaurus/utils-validation';
|
||||
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
||||
|
||||
|
@ -69,6 +69,7 @@ const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
|
|||
hide_table_of_contents: Joi.boolean(),
|
||||
|
||||
...FrontMatterTOCHeadingLevels,
|
||||
last_update: FrontMatterLastUpdateSchema,
|
||||
})
|
||||
.messages({
|
||||
'deprecate.error':
|
||||
|
|
|
@ -51,6 +51,8 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
|||
authorsMapPath: 'authors.yml',
|
||||
readingTime: ({content, defaultReadingTime}) => defaultReadingTime({content}),
|
||||
sortPosts: 'descending',
|
||||
showLastUpdateTime: false,
|
||||
showLastUpdateAuthor: false,
|
||||
processBlogPosts: async () => undefined,
|
||||
};
|
||||
|
||||
|
@ -135,6 +137,10 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
|
|||
sortPosts: Joi.string()
|
||||
.valid('descending', 'ascending')
|
||||
.default(DEFAULT_OPTIONS.sortPosts),
|
||||
showLastUpdateTime: Joi.bool().default(DEFAULT_OPTIONS.showLastUpdateTime),
|
||||
showLastUpdateAuthor: Joi.bool().default(
|
||||
DEFAULT_OPTIONS.showLastUpdateAuthor,
|
||||
),
|
||||
processBlogPosts: Joi.function()
|
||||
.optional()
|
||||
.default(() => DEFAULT_OPTIONS.processBlogPosts),
|
||||
|
|
|
@ -10,7 +10,12 @@
|
|||
declare module '@docusaurus/plugin-content-blog' {
|
||||
import type {LoadedMDXContent} from '@docusaurus/mdx-loader';
|
||||
import type {MDXOptions} from '@docusaurus/mdx-loader';
|
||||
import type {FrontMatterTag, Tag} from '@docusaurus/utils';
|
||||
import type {
|
||||
FrontMatterTag,
|
||||
Tag,
|
||||
LastUpdateData,
|
||||
FrontMatterLastUpdate,
|
||||
} from '@docusaurus/utils';
|
||||
import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types';
|
||||
import type {Item as FeedItem} from 'feed';
|
||||
import type {Overwrite} from 'utility-types';
|
||||
|
@ -156,6 +161,8 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
|
|||
toc_min_heading_level?: number;
|
||||
/** Maximum TOC heading level. Must be between 2 and 6. */
|
||||
toc_max_heading_level?: number;
|
||||
/** Allows overriding the last updated author and/or date. */
|
||||
last_update?: FrontMatterLastUpdate;
|
||||
};
|
||||
|
||||
export type BlogPostFrontMatterAuthor = Author & {
|
||||
|
@ -180,7 +187,7 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
|
|||
| BlogPostFrontMatterAuthor
|
||||
| (string | BlogPostFrontMatterAuthor)[];
|
||||
|
||||
export type BlogPostMetadata = {
|
||||
export type BlogPostMetadata = LastUpdateData & {
|
||||
/** Path to the Markdown source, with `@site` alias. */
|
||||
readonly source: string;
|
||||
/**
|
||||
|
@ -426,6 +433,10 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
|
|||
readingTime: ReadingTimeFunctionOption;
|
||||
/** Governs the direction of blog post sorting. */
|
||||
sortPosts: 'ascending' | 'descending';
|
||||
/** Whether to display the last date the doc was updated. */
|
||||
showLastUpdateTime: boolean;
|
||||
/** Whether to display the author who last updated the doc. */
|
||||
showLastUpdateAuthor: boolean;
|
||||
/** An optional function which can be used to transform blog posts
|
||||
* (filter, modify, delete, etc...).
|
||||
*/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue