Skip to content

Commit

Permalink
feat(blog): add LastUpdateAuthor & LastUpdateTime (#9912)
Browse files Browse the repository at this point in the history
Co-authored-by: OzakIOne <OzakIOne@users.noreply.github.com>
Co-authored-by: sebastien <lorber.sebastien@gmail.com>
  • Loading branch information
3 people committed Mar 15, 2024
1 parent 7938803 commit c745021
Show file tree
Hide file tree
Showing 40 changed files with 834 additions and 360 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Expand Up @@ -91,7 +91,7 @@ module.exports = {
'no-constant-binary-expression': ERROR,
'no-continue': OFF,
'no-control-regex': WARNING,
'no-else-return': [WARNING, {allowElseIf: true}],
'no-else-return': OFF,
'no-empty': [WARNING, {allowEmptyCatch: true}],
'no-lonely-if': WARNING,
'no-nested-ternary': WARNING,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
143 changes: 141 additions & 2 deletions packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
);
Expand All @@ -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();
});
});
9 changes: 9 additions & 0 deletions packages/docusaurus-plugin-content-blog/src/blogUtils.ts
Expand Up @@ -26,6 +26,7 @@ import {
getContentPathList,
isUnlisted,
isDraft,
readLastUpdateData,
} from '@docusaurus/utils';
import {validateBlogPostFrontMatter} from './frontMatter';
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
Expand Down Expand Up @@ -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});

Expand Down Expand Up @@ -337,6 +344,8 @@ async function processBlogSourceFile(
authors,
frontMatter,
unlisted,
lastUpdatedAt: lastUpdate.lastUpdatedAt,
lastUpdatedBy: lastUpdate.lastUpdatedBy,
},
content,
};
Expand Down
9 changes: 5 additions & 4 deletions packages/docusaurus-plugin-content-blog/src/frontMatter.ts
Expand Up @@ -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';

Expand Down Expand Up @@ -69,6 +69,7 @@ const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
hide_table_of_contents: Joi.boolean(),

...FrontMatterTOCHeadingLevels,
last_update: FrontMatterLastUpdateSchema,
})
.messages({
'deprecate.error':
Expand Down
6 changes: 6 additions & 0 deletions packages/docusaurus-plugin-content-blog/src/options.ts
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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),
Expand Down
Expand Up @@ -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';
Expand Down Expand Up @@ -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 & {
Expand All @@ -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;
/**
Expand Down Expand Up @@ -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...).
*/
Expand Down
Expand Up @@ -444,19 +444,19 @@ describe('validateDocFrontMatter last_update', () => {
invalidFrontMatters: [
[
{last_update: null},
'does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).',
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
],
[
{last_update: {}},
'does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).',
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
],
[
{last_update: ''},
'does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).',
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
],
[
{last_update: {invalid: 'key'}},
'does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).',
'"last_update" does not look like a valid last update object. Please use an author key with a string or a date with a string or Date',
],
[
{last_update: {author: 'test author', date: 'I am not a date :('}},
Expand Down

0 comments on commit c745021

Please sign in to comment.