Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

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

Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,24 @@
exports[`simple site > custom pagination - development 1`] = `
{
"pagination": [
{
"id": "customCreated",
"next": {
"permalink": "/docs/customLastUpdate",
"title": "Custom Last Update",
},
"prev": undefined,
},
{
"id": "customLastUpdate",
"next": {
"permalink": "/docs/doc with space",
"title": "Hoo hoo, if this path tricks you...",
},
"prev": undefined,
"prev": {
"permalink": "/docs/customCreated",
"title": "Custom Created",
},
},
{
"id": "doc with space",
Expand Down Expand Up @@ -236,6 +247,10 @@ exports[`simple site > custom pagination - development 1`] = `
],
"sidebars": {
"defaultSidebar": [
{
"id": "customCreated",
"type": "doc",
},
{
"id": "customLastUpdate",
"type": "doc",
Expand Down Expand Up @@ -359,13 +374,24 @@ exports[`simple site > custom pagination - development 1`] = `
exports[`simple site > custom pagination - production 1`] = `
{
"pagination": [
{
"id": "customCreated",
"next": {
"permalink": "/docs/customLastUpdate",
"title": "Custom Last Update",
},
"prev": undefined,
},
{
"id": "customLastUpdate",
"next": {
"permalink": "/docs/doc with space",
"title": "Hoo hoo, if this path tricks you...",
},
"prev": undefined,
"prev": {
"permalink": "/docs/customCreated",
"title": "Custom Created",
},
},
{
"id": "doc with space",
Expand Down Expand Up @@ -571,6 +597,10 @@ exports[`simple site > custom pagination - production 1`] = `
],
"sidebars": {
"defaultSidebar": [
{
"id": "customCreated",
"type": "doc",
},
{
"id": "customLastUpdate",
"type": "doc",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ describe('simple site', () => {
'doc-draft.md',
'doc-unlisted.md',
'customLastUpdate.md',
'customCreated.md',
'lastUpdateAuthorOnly.md',
'lastUpdateDateOnly.md',
'foo/bar.md',
Expand Down Expand Up @@ -710,6 +711,62 @@ describe('simple site', () => {
});
});

it('docs with created front matter', async () => {
const {defaultTestUtils} = await loadSite();

await defaultTestUtils.testMeta('customCreated.md', {
version: 'current',
id: 'customCreated',
sourceDirName: '.',
permalink: '/docs/customCreated',
slug: '/customCreated',
title: 'Custom Created',
description: 'Custom created date',
frontMatter: {
title: 'Custom Created',
// YAML parses bare date values (e.g. 2024-01-15) into Date objects
created: new Date('2024-01-15'),
},
createdAt: new Date('2024-01-15').getTime(),
sidebarPosition: undefined,
tags: [],
unlisted: false,
});
});

it('docs without created front matter have undefined createdAt', async () => {
const {defaultTestUtils} = await loadSite();

// lorem.md has no 'created' field, so createdAt should be undefined
const metadata = await defaultTestUtils.processDocFile('lorem.md');
expect(metadata.createdAt).toBeUndefined();
});

it('docs with created front matter using processDocFile directly', async () => {
const {defaultTestUtils} = await loadSite();

// Use a fake doc file to test various created date formats
const isoDateDoc = createFakeDocFile({
source: 'isoDate.md',
frontMatter: {created: '2023-06-15T10:30:00'},
});
const isoMeta = await defaultTestUtils.processDocFile(isoDateDoc);
expect(isoMeta.createdAt).toBe(new Date('2023-06-15T10:30:00').getTime());

const plainDateDoc = createFakeDocFile({
source: 'plainDate.md',
frontMatter: {created: '1/1/2000'},
});
const plainMeta = await defaultTestUtils.processDocFile(plainDateDoc);
expect(plainMeta.createdAt).toBe(new Date('1/1/2000').getTime());

const noCreatedDoc = createFakeDocFile({
source: 'noCreated.md',
});
const noCreatedMeta = await defaultTestUtils.processDocFile(noCreatedDoc);
expect(noCreatedMeta.createdAt).toBeUndefined();
});

it('docs with last_update front matter disabled', async () => {
const {siteDir, context, options, currentVersion, createTestUtilsPartial} =
await loadSite({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,3 +492,35 @@ describe('validateDocFrontMatter last_update', () => {
],
});
});

describe('validateDocFrontMatter created', () => {
testField({
prefix: 'created',
validFrontMatters: [
{created: undefined},
{created: '1/1/2000'},
{created: new Date('1/1/2000')},
{created: '2024-01-15'},
{created: '1995-12-17T03:24:00'},
{created: 'December 17, 1995 03:24:00'},
],
invalidFrontMatters: [
[
{created: 'I am not a date :('},
'does not look like a valid created date',
],
[
{created: '2011-10-45'},
'does not look like a valid created date',
],
[
{created: '2011-0-10'},
'does not look like a valid created date',
],
[
{created: ''},
'does not look like a valid created date',
],
],
});
});
12 changes: 12 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ async function doProcessDocMetadata({
// but allow to disable this behavior with front matter
parse_number_prefixes: parseNumberPrefixes = true,
last_update: lastUpdateFrontMatter,
created: createdFrontMatter,
} = frontMatter;

const lastUpdate = await readLastUpdateData(
Expand Down Expand Up @@ -225,6 +226,16 @@ async function doProcessDocMetadata({
// NodeJS optimization.
// Adding properties to object after instantiation will cause hidden
// class transitions.

// Parse the created front matter value into a millisecond timestamp
const createdAt: number | null | undefined =
createdFrontMatter !== undefined
? (() => {
const parsed = new Date(createdFrontMatter).getTime();
return Number.isNaN(parsed) ? null : parsed;
})()
: undefined;

return {
id,
title,
Expand All @@ -240,6 +251,7 @@ async function doProcessDocMetadata({
version: versionMetadata.versionName,
lastUpdatedBy: lastUpdate.lastUpdatedBy,
lastUpdatedAt: lastUpdate.lastUpdatedAt,
createdAt,
sidebarPosition,
frontMatter,
};
Expand Down
2 changes: 2 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/frontMatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
validateFrontMatter,
ContentVisibilitySchema,
FrontMatterLastUpdateSchema,
FrontMatterCreatedSchema,
} from '@docusaurus/utils-validation';
import type {DocFrontMatter} from '@docusaurus/plugin-content-docs';

Expand Down Expand Up @@ -46,6 +47,7 @@ export const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
pagination_prev: Joi.string().allow(null),
...FrontMatterTOCHeadingLevels,
last_update: FrontMatterLastUpdateSchema,
created: FrontMatterCreatedSchema,
})
.unknown()
.concat(ContentVisibilitySchema);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,12 @@ declare module '@docusaurus/plugin-content-docs' {
unlisted?: boolean;
/** Allows overriding the last updated author and/or date. */
last_update?: FrontMatterLastUpdate;
/**
* The creation date of the document. Can be any
* [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse).
* @see https://github.com/facebook/docusaurus/issues/5691
*/
created?: Date | string;
};

export type DocMetadataBase = LastUpdateData & {
Expand Down Expand Up @@ -460,6 +466,13 @@ declare module '@docusaurus/plugin-content-docs' {
editUrl?: string | null;
/** Tags, normalized. */
tags: TagMetadata[];
/**
* The creation timestamp of the document in milliseconds, sourced from
* the `created` front matter field.
* `undefined`: no `created` front matter provided
* `null`: value could not be parsed
*/
createdAt?: number | null;
/** Front matter, as-is. */
frontMatter: DocFrontMatter & {[key: string]: unknown};
};
Expand Down
2 changes: 2 additions & 0 deletions packages/docusaurus-utils-validation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,7 @@ export {
ContentVisibilitySchema,
FrontMatterLastUpdateErrorMessage,
FrontMatterLastUpdateSchema,
FrontMatterCreatedErrorMessage,
FrontMatterCreatedSchema,
} from './validationSchemas';
export {getTagsFilePathsToWatch, getTagsFile} from './tagsFile';
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,10 @@ export const FrontMatterLastUpdateSchema = Joi.object({
'object.missing': FrontMatterLastUpdateErrorMessage,
'object.base': FrontMatterLastUpdateErrorMessage,
});

export const FrontMatterCreatedErrorMessage =
'{{#label}} does not look like a valid created date. Please use a string or Date value.';

export const FrontMatterCreatedSchema = Joi.date().raw().messages({
'date.base': FrontMatterCreatedErrorMessage,
});
2 changes: 2 additions & 0 deletions website/docs/api/plugins/plugin-content-docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ Accepted fields:
| `draft` | `boolean` | `false` | Draft documents will only be available during development. |
| `unlisted` | `boolean` | `false` | Unlisted documents will be available in both development and production. They will be "hidden" in production, not indexed, excluded from sitemaps, and can only be accessed by users having a direct link. |
| `last_update` | `FrontMatterLastUpdate` | `undefined` | Allows overriding the last update author/date. Date can be any [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). |
| `created` | `Date \| string` | `undefined` | The creation date of the document. Can be any [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). Exposed as `createdAt` (milliseconds timestamp) in the doc metadata. |

```mdx-code-block
</APITable>
Expand Down Expand Up @@ -331,6 +332,7 @@ keywords:
tags: [docusaurus]
image: https://i.imgur.com/mErPwqL.png
slug: /myDoc
created: 2024-01-15
last_update:
date: 1/1/2000
author: custom author name
Expand Down