Skip to content

Commit 59e0353

Browse files
author
Weza Mwiwa
committed
feat(content-docs,content-blog,theme-classic): add created_at front matter and creation metadata (AI-assisted)
Fixes #5691 Expose creation date and author from VCS git history, alongside existing last_updated feature, via new created_at front matter field. - Add readCreationData() with memoization, mirroring readLastUpdateData - Add FrontMatterCreationSchema for validation - Add showCreatedTime / showCreatedBy plugin options (default false) - Include createdAt/createdBy in doc and blog metadata - Render creation info in theme LastUpdated component - Include createdAt in route metadata for SSG cache
1 parent 4cbfcfa commit 59e0353

56 files changed

Lines changed: 491 additions & 114 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/docusaurus-plugin-content-blog/src/blogUtils.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
isUnlisted,
2424
isDraft,
2525
readLastUpdateData,
26+
readCreationData,
2627
normalizeTags,
2728
aliasedSitePathToRelativePath,
2829
} from '@docusaurus/utils';
@@ -260,6 +261,13 @@ async function processBlogSourceFile(
260261
vcs,
261262
);
262263

264+
const creation = await readCreationData(
265+
blogSourceAbsolute,
266+
options,
267+
frontMatter.created_at,
268+
vcs,
269+
);
270+
263271
const draft = isDraft({frontMatter});
264272
const unlisted = isUnlisted({frontMatter});
265273

@@ -379,6 +387,8 @@ async function processBlogSourceFile(
379387
unlisted,
380388
lastUpdatedAt: lastUpdate.lastUpdatedAt,
381389
lastUpdatedBy: lastUpdate.lastUpdatedBy,
390+
createdAt: creation.createdAt,
391+
createdBy: creation.createdBy,
382392
},
383393
content,
384394
};

packages/docusaurus-plugin-content-blog/src/frontMatter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import {
1010
ContentVisibilitySchema,
1111
FrontMatterLastUpdateSchema,
12+
FrontMatterCreationSchema,
1213
FrontMatterTOCHeadingLevels,
1314
FrontMatterTagsSchema,
1415
JoiFrontMatter as Joi, // Custom instance for front matter
@@ -76,6 +77,7 @@ const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
7677

7778
...FrontMatterTOCHeadingLevels,
7879
last_update: FrontMatterLastUpdateSchema,
80+
created_at: FrontMatterCreationSchema,
7981
})
8082
.messages({
8183
'deprecate.error':

packages/docusaurus-plugin-content-blog/src/options.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ export const DEFAULT_OPTIONS: PluginOptions = {
6969
sortPosts: 'descending',
7070
showLastUpdateTime: false,
7171
showLastUpdateAuthor: false,
72+
showCreatedTime: false,
73+
showCreatedBy: false,
7274
processBlogPosts: async () => undefined,
7375
tags: undefined,
7476
authorsBasePath: 'authors',
@@ -227,6 +229,8 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
227229
showLastUpdateAuthor: Joi.bool().default(
228230
DEFAULT_OPTIONS.showLastUpdateAuthor,
229231
),
232+
showCreatedTime: Joi.bool().default(DEFAULT_OPTIONS.showCreatedTime),
233+
showCreatedBy: Joi.bool().default(DEFAULT_OPTIONS.showCreatedBy),
230234
processBlogPosts: Joi.function()
231235
.optional()
232236
.default(() => DEFAULT_OPTIONS.processBlogPosts),

packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ declare module '@docusaurus/plugin-content-blog' {
1212
FrontMatterTag,
1313
TagMetadata,
1414
LastUpdateData,
15+
CreationData,
1516
FrontMatterLastUpdate,
17+
FrontMatterCreation,
1618
TagsPluginOptions,
1719
} from '@docusaurus/utils';
1820
import type {
@@ -236,6 +238,8 @@ declare module '@docusaurus/plugin-content-blog' {
236238
toc_max_heading_level?: number;
237239
/** Allows overriding the last updated author and/or date. */
238240
last_update?: FrontMatterLastUpdate;
241+
/** Allows overriding the creation author and/or date. */
242+
created_at?: FrontMatterCreation;
239243
};
240244

241245
export type BlogPostFrontMatterAuthor = AuthorAttributes & {
@@ -260,61 +264,62 @@ declare module '@docusaurus/plugin-content-blog' {
260264
| BlogPostFrontMatterAuthor
261265
| (string | BlogPostFrontMatterAuthor)[];
262266

263-
export type BlogPostMetadata = LastUpdateData & {
264-
/** Path to the Markdown source, with `@site` alias. */
265-
readonly source: string;
266-
/**
267-
* Used to generate the page h1 heading, tab title, and pagination title.
268-
*/
269-
readonly title: string;
270-
/**
271-
* The publish date of the post. On client side, this will be serialized
272-
* into a string.
273-
*/
274-
readonly date: Date;
275-
/** Full link including base URL. */
276-
readonly permalink: string;
277-
/**
278-
* Description used in the meta. Could be an empty string (empty content)
279-
*/
280-
readonly description: string;
281-
/**
282-
* Absolute URL to the editing page of the post. Undefined if the post
283-
* shouldn't be edited.
284-
*/
285-
readonly editUrl?: string;
286-
/**
287-
* Reading time in minutes calculated based on word count.
288-
*/
289-
readonly readingTime?: number;
290-
/**
291-
* Whether the truncate marker exists in the post's content.
292-
*/
293-
readonly hasTruncateMarker: boolean;
294-
/**
295-
* Used in pagination. Generated after the other metadata, so not readonly.
296-
* Content is just a subset of another post's metadata.
297-
*/
298-
nextItem?: {readonly title: string; readonly permalink: string};
299-
/**
300-
* Used in pagination. Generated after the other metadata, so not readonly.
301-
* Content is just a subset of another post's metadata.
302-
*/
303-
prevItem?: {readonly title: string; readonly permalink: string};
304-
/**
305-
* Author metadata, normalized. Should be used in joint with
306-
* `assets.authorsImageUrls` on client side.
307-
*/
308-
readonly authors: Author[];
309-
/** Front matter, as-is. */
310-
readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown};
311-
/** Tags, normalized. */
312-
readonly tags: TagMetadata[];
313-
/**
314-
* Marks the post as unlisted and visibly hides it unless directly accessed.
315-
*/
316-
readonly unlisted: boolean;
317-
};
267+
export type BlogPostMetadata = LastUpdateData &
268+
CreationData & {
269+
/** Path to the Markdown source, with `@site` alias. */
270+
readonly source: string;
271+
/**
272+
* Used to generate the page h1 heading, tab title, and pagination title.
273+
*/
274+
readonly title: string;
275+
/**
276+
* The publish date of the post. On client side, this will be serialized
277+
* into a string.
278+
*/
279+
readonly date: Date;
280+
/** Full link including base URL. */
281+
readonly permalink: string;
282+
/**
283+
* Description used in the meta. Could be an empty string (empty content)
284+
*/
285+
readonly description: string;
286+
/**
287+
* Absolute URL to the editing page of the post. Undefined if the post
288+
* shouldn't be edited.
289+
*/
290+
readonly editUrl?: string;
291+
/**
292+
* Reading time in minutes calculated based on word count.
293+
*/
294+
readonly readingTime?: number;
295+
/**
296+
* Whether the truncate marker exists in the post's content.
297+
*/
298+
readonly hasTruncateMarker: boolean;
299+
/**
300+
* Used in pagination. Generated after the other metadata, so not readonly.
301+
* Content is just a subset of another post's metadata.
302+
*/
303+
nextItem?: {readonly title: string; readonly permalink: string};
304+
/**
305+
* Used in pagination. Generated after the other metadata, so not readonly.
306+
* Content is just a subset of another post's metadata.
307+
*/
308+
prevItem?: {readonly title: string; readonly permalink: string};
309+
/**
310+
* Author metadata, normalized. Should be used in joint with
311+
* `assets.authorsImageUrls` on client side.
312+
*/
313+
readonly authors: Author[];
314+
/** Front matter, as-is. */
315+
readonly frontMatter: BlogPostFrontMatter & {[key: string]: unknown};
316+
/** Tags, normalized. */
317+
readonly tags: TagMetadata[];
318+
/**
319+
* Marks the post as unlisted and visibly hides it unless directly accessed.
320+
*/
321+
readonly unlisted: boolean;
322+
};
318323
/**
319324
* @returns The edit URL that's directly plugged into metadata.
320325
*/
@@ -524,6 +529,10 @@ declare module '@docusaurus/plugin-content-blog' {
524529
showLastUpdateTime: boolean;
525530
/** Whether to display the author who last updated the blog post. */
526531
showLastUpdateAuthor: boolean;
532+
/** Whether to display the creation date of the blog post. */
533+
showCreatedTime: boolean;
534+
/** Whether to display the author who created the blog post. */
535+
showCreatedBy: boolean;
527536
/** An optional function which can be used to transform blog posts
528537
* (filter, modify, delete, etc...).
529538
*/

packages/docusaurus-plugin-content-docs/src/docs.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
isUnlisted,
2121
isDraft,
2222
readLastUpdateData,
23+
readCreationData,
2324
normalizeTags,
2425
} from '@docusaurus/utils';
2526
import {validateDocFrontMatter} from './frontMatter';
@@ -63,7 +64,12 @@ export async function readVersionDocs(
6364
versionMetadata: VersionMetadata,
6465
options: Pick<
6566
PluginOptions,
66-
'include' | 'exclude' | 'showLastUpdateAuthor' | 'showLastUpdateTime'
67+
| 'include'
68+
| 'exclude'
69+
| 'showLastUpdateAuthor'
70+
| 'showLastUpdateTime'
71+
| 'showCreatedTime'
72+
| 'showCreatedBy'
6773
>,
6874
): Promise<DocFile[]> {
6975
const sources = await Globby(options.include, {
@@ -120,6 +126,7 @@ async function doProcessDocMetadata({
120126
// but allow to disable this behavior with front matter
121127
parse_number_prefixes: parseNumberPrefixes = true,
122128
last_update: lastUpdateFrontMatter,
129+
created_at: creationFrontMatter,
123130
} = frontMatter;
124131

125132
const lastUpdate = await readLastUpdateData(
@@ -129,6 +136,13 @@ async function doProcessDocMetadata({
129136
vcs,
130137
);
131138

139+
const creation = await readCreationData(
140+
filePath,
141+
options,
142+
creationFrontMatter,
143+
vcs,
144+
);
145+
132146
// E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc
133147
const sourceFileNameWithoutExtension = path.basename(
134148
source,
@@ -240,6 +254,8 @@ async function doProcessDocMetadata({
240254
version: versionMetadata.versionName,
241255
lastUpdatedBy: lastUpdate.lastUpdatedBy,
242256
lastUpdatedAt: lastUpdate.lastUpdatedAt,
257+
createdBy: creation.createdBy,
258+
createdAt: creation.createdAt,
243259
sidebarPosition,
244260
frontMatter,
245261
};

packages/docusaurus-plugin-content-docs/src/frontMatter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
validateFrontMatter,
1515
ContentVisibilitySchema,
1616
FrontMatterLastUpdateSchema,
17+
FrontMatterCreationSchema,
1718
} from '@docusaurus/utils-validation';
1819
import type {DocFrontMatter} from '@docusaurus/plugin-content-docs';
1920

@@ -46,6 +47,7 @@ export const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
4647
pagination_prev: Joi.string().allow(null),
4748
...FrontMatterTOCHeadingLevels,
4849
last_update: FrontMatterLastUpdateSchema,
50+
created_at: FrontMatterCreationSchema,
4951
})
5052
.unknown()
5153
.concat(ContentVisibilitySchema);

packages/docusaurus-plugin-content-docs/src/options.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id' | 'sidebarPath'> = {
4646
beforeDefaultRehypePlugins: [],
4747
showLastUpdateTime: false,
4848
showLastUpdateAuthor: false,
49+
showCreatedTime: false,
50+
showCreatedBy: false,
4951
admonitions: true,
5052
includeCurrentVersion: true,
5153
disableVersioning: false,
@@ -137,6 +139,8 @@ const OptionsSchema = Joi.object<PluginOptions>({
137139
showLastUpdateAuthor: Joi.bool().default(
138140
DEFAULT_OPTIONS.showLastUpdateAuthor,
139141
),
142+
showCreatedTime: Joi.bool().default(DEFAULT_OPTIONS.showCreatedTime),
143+
showCreatedBy: Joi.bool().default(DEFAULT_OPTIONS.showCreatedBy),
140144
includeCurrentVersion: Joi.bool().default(
141145
DEFAULT_OPTIONS.includeCurrentVersion,
142146
),

0 commit comments

Comments
 (0)