From 9a9248d71f2860d3168ba212cf7db981ae0356f9 Mon Sep 17 00:00:00 2001 From: David Pang Date: Sat, 21 May 2022 20:49:35 -0400 Subject: [PATCH 01/13] feat(content-docs): add last update front matter --- .../simple-site/docs/customLastUpdate.md | 8 + .../simple-site/docs/lastUpdateAuthorOnly.md | 7 + .../simple-site/docs/lastUpdateDateOnly.md | 7 + .../__tests__/__snapshots__/docs.test.ts.snap | 55 +++++- .../__snapshots__/index.test.ts.snap | 130 ++++++++++++++ .../src/__tests__/docs.test.ts | 161 ++++++++++++++++++ .../src/__tests__/frontMatter.test.ts | 33 ++++ .../src/docs.ts | 10 ++ .../src/frontMatter.ts | 4 + .../src/plugin-content-docs.d.ts | 7 + .../_docs tests/doc-with-last-update.md | 7 + website/_dogfooding/docs-tests-sidebars.js | 1 + website/_dogfooding/dogfooding.config.js | 1 + .../docs/api/plugins/plugin-content-docs.md | 8 + 14 files changed, 434 insertions(+), 5 deletions(-) create mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/customLastUpdate.md create mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/lastUpdateAuthorOnly.md create mode 100644 packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/lastUpdateDateOnly.md create mode 100644 website/_dogfooding/_docs tests/doc-with-last-update.md diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/customLastUpdate.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/customLastUpdate.md new file mode 100644 index 000000000000..b8e37d4f8a9e --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/customLastUpdate.md @@ -0,0 +1,8 @@ +--- +title: Custom Last Update +last_update: + author: Custom Author + date: 1/1/2000 +--- + +Custom last update \ No newline at end of file diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/lastUpdateAuthorOnly.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/lastUpdateAuthorOnly.md new file mode 100644 index 000000000000..4929ed91a9c5 --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/lastUpdateAuthorOnly.md @@ -0,0 +1,7 @@ +--- +title: Last Update Author Only +last_update: + author: Custom Author +--- + +Only custom author, so it will still use the date from Git \ No newline at end of file diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/lastUpdateDateOnly.md b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/lastUpdateDateOnly.md new file mode 100644 index 000000000000..f9bfeb9759d9 --- /dev/null +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/__fixtures__/simple-site/docs/lastUpdateDateOnly.md @@ -0,0 +1,7 @@ +--- +title: Last Update Date Only +last_update: + date: 1/1/2000 +--- + +Only custom date, so it will still use the author from Git \ No newline at end of file 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 f05c66e2aa21..69401a06a04c 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 @@ -3,13 +3,24 @@ exports[`simple site custom pagination 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": undefined, + "prev": { + "permalink": "/docs/customLastUpdate", + "title": "Custom Last Update", + }, }, { "id": "doc-draft", @@ -63,14 +74,36 @@ exports[`simple site custom pagination 1`] = ` { "id": "ipsum", "next": { - "permalink": "/docs/lorem", - "title": "lorem", + "permalink": "/docs/lastUpdateAuthorOnly", + "title": "Last Update Author Only", }, "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": { @@ -78,8 +111,8 @@ exports[`simple site custom pagination 1`] = ` "title": "rootAbsoluteSlug", }, "prev": { - "permalink": "/docs/ipsum", - "title": "ipsum", + "permalink": "/docs/lastUpdateDateOnly", + "title": "Last Update Date Only", }, }, { @@ -170,6 +203,10 @@ exports[`simple site custom pagination 1`] = ` ], "sidebars": { "defaultSidebar": [ + { + "id": "customLastUpdate", + "type": "doc", + }, { "id": "doc with space", "type": "doc", @@ -208,6 +245,14 @@ exports[`simple site custom pagination 1`] = ` "id": "ipsum", "type": "doc", }, + { + "id": "lastUpdateAuthorOnly", + "type": "doc", + }, + { + "id": "lastUpdateDateOnly", + "type": "doc", + }, { "id": "lorem", "type": "doc", 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 c8b0408c5ee1..c428a6c55e86 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 @@ -22,6 +22,7 @@ These sidebar document ids do not exist: - nonExistent Available document ids are: +- customLastUpdate - doc with space - doc-draft - foo/bar @@ -29,6 +30,8 @@ Available document ids are: - headingAsTitle - hello - ipsum +- lastUpdateAuthorOnly +- lastUpdateDateOnly - lorem - rootAbsoluteSlug - rootRelativeSlug @@ -267,6 +270,11 @@ exports[`simple website content 5`] = ` "versions": [ { "docs": [ + { + "id": "customLastUpdate", + "path": "/docs/customLastUpdate", + "sidebar": undefined, + }, { "id": "doc with space", "path": "/docs/doc with space", @@ -302,6 +310,16 @@ exports[`simple website content 5`] = ` "path": "/docs/ipsum", "sidebar": undefined, }, + { + "id": "lastUpdateAuthorOnly", + "path": "/docs/lastUpdateAuthorOnly", + "sidebar": undefined, + }, + { + "id": "lastUpdateDateOnly", + "path": "/docs/lastUpdateDateOnly", + "sidebar": undefined, + }, { "id": "lorem", "path": "/docs/lorem", @@ -390,6 +408,26 @@ exports[`simple website content: data 1`] = ` "permalink": "/docs/rootAbsoluteSlug" } } +}", + "site-docs-custom-last-update-md-b8d.json": "{ + "unversionedId": "customLastUpdate", + "id": "customLastUpdate", + "title": "Custom Last Update", + "description": "Custom last update", + "source": "@site/docs/customLastUpdate.md", + "sourceDirName": ".", + "slug": "/customLastUpdate", + "permalink": "/docs/customLastUpdate", + "draft": false, + "tags": [], + "version": "current", + "frontMatter": { + "title": "Custom Last Update", + "last_update": { + "author": "Custom Author", + "date": "1/1/2000" + } + } }", "site-docs-doc-draft-md-584.json": "{ "unversionedId": "doc-draft", @@ -563,6 +601,44 @@ exports[`simple website content: data 1`] = ` "frontMatter": { "custom_edit_url": null } +}", + "site-docs-last-update-author-only-md-352.json": "{ + "unversionedId": "lastUpdateAuthorOnly", + "id": "lastUpdateAuthorOnly", + "title": "Last Update Author Only", + "description": "Only custom author, so it will still use the date from Git", + "source": "@site/docs/lastUpdateAuthorOnly.md", + "sourceDirName": ".", + "slug": "/lastUpdateAuthorOnly", + "permalink": "/docs/lastUpdateAuthorOnly", + "draft": false, + "tags": [], + "version": "current", + "frontMatter": { + "title": "Last Update Author Only", + "last_update": { + "author": "Custom Author" + } + } +}", + "site-docs-last-update-date-only-md-987.json": "{ + "unversionedId": "lastUpdateDateOnly", + "id": "lastUpdateDateOnly", + "title": "Last Update Date Only", + "description": "Only custom date, so it will still use the author from Git", + "source": "@site/docs/lastUpdateDateOnly.md", + "sourceDirName": ".", + "slug": "/lastUpdateDateOnly", + "permalink": "/docs/lastUpdateDateOnly", + "draft": false, + "tags": [], + "version": "current", + "frontMatter": { + "title": "Last Update Date Only", + "last_update": { + "date": "1/1/2000" + } + } }", "site-docs-lorem-md-b27.json": "{ "unversionedId": "lorem", @@ -924,6 +1000,11 @@ exports[`simple website content: data 1`] = ` ] }, "docs": { + "customLastUpdate": { + "id": "customLastUpdate", + "title": "Custom Last Update", + "description": "Custom last update" + }, "doc with space": { "id": "doc with space", "title": "Hoo hoo, if this path tricks you...", @@ -963,6 +1044,16 @@ exports[`simple website content: data 1`] = ` "title": "ipsum", "description": "Lorem ipsum." }, + "lastUpdateAuthorOnly": { + "id": "lastUpdateAuthorOnly", + "title": "Last Update Author Only", + "description": "Only custom author, so it will still use the date from Git" + }, + "lastUpdateDateOnly": { + "id": "lastUpdateDateOnly", + "title": "Last Update Date Only", + "description": "Only custom date, so it will still use the author from Git" + }, "lorem": { "id": "lorem", "title": "lorem", @@ -1026,6 +1117,11 @@ exports[`simple website content: global data 1`] = ` "versions": [ { "docs": [ + { + "id": "customLastUpdate", + "path": "/docs/customLastUpdate", + "sidebar": undefined, + }, { "id": "doc with space", "path": "/docs/doc with space", @@ -1061,6 +1157,16 @@ exports[`simple website content: global data 1`] = ` "path": "/docs/ipsum", "sidebar": undefined, }, + { + "id": "lastUpdateAuthorOnly", + "path": "/docs/lastUpdateAuthorOnly", + "sidebar": undefined, + }, + { + "id": "lastUpdateDateOnly", + "path": "/docs/lastUpdateDateOnly", + "sidebar": undefined, + }, { "id": "lorem", "path": "/docs/lorem", @@ -1202,6 +1308,14 @@ exports[`simple website content: route config 1`] = ` "path": "/docs/category/slugs", "sidebar": "docs", }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/customLastUpdate.md", + }, + "path": "/docs/customLastUpdate", + }, { "component": "@theme/DocItem", "exact": true, @@ -1262,6 +1376,22 @@ exports[`simple website content: route config 1`] = ` }, "path": "/docs/ipsum", }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/lastUpdateAuthorOnly.md", + }, + "path": "/docs/lastUpdateAuthorOnly", + }, + { + "component": "@theme/DocItem", + "exact": true, + "modules": { + "content": "@site/docs/lastUpdateDateOnly.md", + }, + "path": "/docs/lastUpdateDateOnly", + }, { "component": "@theme/DocItem", "exact": 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 3e0f09010925..79aae3273e84 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts @@ -230,6 +230,9 @@ describe('simple site', () => { 'headingAsTitle.md', 'doc with space.md', 'doc-draft.md', + 'customLastUpdate.md', + 'lastUpdateAuthorOnly.md', + 'lastUpdateDateOnly.md', 'foo/bar.md', 'foo/baz.md', 'slugs/absoluteSlug.md', @@ -481,6 +484,164 @@ describe('simple site', () => { }); }); + it('docs with last_update front matter', async () => { + const {siteDir, context, options, currentVersion, createTestUtilsPartial} = + await loadSite({ + options: { + showLastUpdateAuthor: true, + showLastUpdateTime: true, + }, + }); + + const testUtilsLocal = createTestUtilsPartial({ + siteDir, + context, + options, + versionMetadata: currentVersion, + }); + + await testUtilsLocal.testMeta('customLastUpdate.md', { + version: 'current', + id: 'customLastUpdate', + unversionedId: 'customLastUpdate', + sourceDirName: '.', + permalink: '/docs/customLastUpdate', + slug: '/customLastUpdate', + title: 'Custom Last Update', + description: 'Custom last update', + frontMatter: { + last_update: { + author: 'Custom Author', + date: '1/1/2000', + }, + title: 'Custom Last Update', + }, + lastUpdatedAt: new Date('1/1/2000').getTime() / 1000, + formattedLastUpdatedAt: '1/1/2000', + lastUpdatedBy: 'Custom Author', + sidebarPosition: undefined, + tags: [], + }); + }); + + it('docs with only last_update author front matter', async () => { + const {siteDir, context, options, currentVersion, createTestUtilsPartial} = + await loadSite({ + options: { + showLastUpdateAuthor: true, + showLastUpdateTime: true, + }, + }); + + const testUtilsLocal = createTestUtilsPartial({ + siteDir, + context, + options, + versionMetadata: currentVersion, + }); + + await testUtilsLocal.testMeta('lastUpdateAuthorOnly.md', { + version: 'current', + id: 'lastUpdateAuthorOnly', + unversionedId: 'lastUpdateAuthorOnly', + sourceDirName: '.', + permalink: '/docs/lastUpdateAuthorOnly', + slug: '/lastUpdateAuthorOnly', + title: 'Last Update Author Only', + description: 'Only custom author, so it will still use the date from Git', + frontMatter: { + last_update: { + author: 'Custom Author', + }, + title: 'Last Update Author Only', + }, + lastUpdatedAt: 1539502055, + formattedLastUpdatedAt: '10/14/2018', + lastUpdatedBy: 'Custom Author', + sidebarPosition: undefined, + tags: [], + }); + }); + + it('docs with only last_update date front matter', async () => { + const {siteDir, context, options, currentVersion, createTestUtilsPartial} = + await loadSite({ + options: { + showLastUpdateAuthor: true, + showLastUpdateTime: true, + }, + }); + + const testUtilsLocal = createTestUtilsPartial({ + siteDir, + context, + options, + versionMetadata: currentVersion, + }); + + await testUtilsLocal.testMeta('lastUpdateDateOnly.md', { + version: 'current', + id: 'lastUpdateDateOnly', + unversionedId: 'lastUpdateDateOnly', + sourceDirName: '.', + permalink: '/docs/lastUpdateDateOnly', + slug: '/lastUpdateDateOnly', + title: 'Last Update Date Only', + description: 'Only custom date, so it will still use the author from Git', + frontMatter: { + last_update: { + date: '1/1/2000', + }, + title: 'Last Update Date Only', + }, + lastUpdatedAt: new Date('1/1/2000').getTime() / 1000, + formattedLastUpdatedAt: '1/1/2000', + lastUpdatedBy: 'Author', + sidebarPosition: undefined, + tags: [], + }); + }); + + it('docs with last_update front matter disabled', async () => { + const {siteDir, context, options, currentVersion, createTestUtilsPartial} = + await loadSite({ + options: { + showLastUpdateAuthor: false, + showLastUpdateTime: false, + }, + }); + + const testUtilsLocal = createTestUtilsPartial({ + siteDir, + context, + options, + versionMetadata: currentVersion, + }); + + await testUtilsLocal.testMeta('customLastUpdate.md', { + version: 'current', + id: 'customLastUpdate', + unversionedId: 'customLastUpdate', + sourceDirName: '.', + permalink: '/docs/customLastUpdate', + slug: '/customLastUpdate', + title: 'Custom Last Update', + description: 'Custom last update', + frontMatter: { + last_update: { + author: 'Custom Author', + date: '1/1/2000', + }, + title: 'Custom Last Update', + }, + lastUpdatedAt: undefined, + formattedLastUpdatedAt: undefined, + lastUpdatedBy: undefined, + sidebarPosition: undefined, + tags: [], + }); + }); + it('docs with slugs', async () => { const {defaultTestUtils} = await loadSite(); 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 6612927c2d0c..b3f16057dd92 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts @@ -394,3 +394,36 @@ describe('validateDocFrontMatter draft', () => { ], }); }); + +describe('validateDocFrontMatter last_update', () => { + testField({ + prefix: 'last_update', + validFrontMatters: [ + {last_update: undefined}, + {last_update: {author: 'test author', date: undefined}}, + {last_update: {author: undefined, date: '1/1/2000'}}, + {last_update: {author: undefined, date: new Date('1/1/2000')}}, + {last_update: {author: 'test author', date: '1/1/2000'}}, + {last_update: {author: 'test author', date: '1995-12-17T03:24:00'}}, + {last_update: {author: undefined, date: 'December 17, 1995 03:24:00'}}, + ], + invalidFrontMatters: [ + [ + {last_update: {author: 'test author', date: 'I am not a date :('}}, + 'must be a valid date', + ], + [ + {last_update: {author: 'test author', date: '2011-10-45'}}, + 'must be a valid date', + ], + [ + {last_update: {author: 'test author', date: '2011-0-10'}}, + 'must be a valid date', + ], + [ + {last_update: {author: 'test author', date: ''}}, + 'must be a valid date', + ], + ], + }); +}); diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index 2ea127f99fbb..7a57fa1d81a4 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -155,8 +155,18 @@ function doProcessDocMetadata({ // (01-MyFolder/01-MyDoc.md => MyFolder/MyDoc) // but allow to disable this behavior with front matter parse_number_prefixes: parseNumberPrefixes = true, + last_update: lastUpdateFrontMatter, } = frontMatter; + if (lastUpdateFrontMatter?.author && options.showLastUpdateAuthor) { + lastUpdate.lastUpdatedBy = lastUpdateFrontMatter.author; + } + if (lastUpdateFrontMatter?.date && options.showLastUpdateTime) { + lastUpdate.lastUpdatedAt = + new Date(lastUpdateFrontMatter.date).getTime() / 1000; + lastUpdate.formattedLastUpdatedAt = undefined; + } + // E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc const sourceFileNameWithoutExtension = path.basename( source, diff --git a/packages/docusaurus-plugin-content-docs/src/frontMatter.ts b/packages/docusaurus-plugin-content-docs/src/frontMatter.ts index de944a6b1716..ade1810455eb 100644 --- a/packages/docusaurus-plugin-content-docs/src/frontMatter.ts +++ b/packages/docusaurus-plugin-content-docs/src/frontMatter.ts @@ -42,6 +42,10 @@ const DocFrontMatterSchema = Joi.object({ pagination_prev: Joi.string().allow(null), draft: Joi.boolean(), ...FrontMatterTOCHeadingLevels, + last_update: Joi.object({ + author: Joi.string(), + date: Joi.date().raw(), + }), }).unknown(); export function validateDocFrontMatter(frontMatter: { 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 1d7784c63fc7..7c0fb61cefc5 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 @@ -23,6 +23,11 @@ declare module '@docusaurus/plugin-content-docs' { image?: string; }; + export type FileChange = { + author?: string; + date?: Date | string; + }; + /** * Custom callback for parsing number prefixes from file/folder names. */ @@ -356,6 +361,8 @@ declare module '@docusaurus/plugin-content-docs' { pagination_prev?: string | null; /** Should this doc be excluded from production builds? */ draft?: boolean; + /** Front matter override for last update to doc */ + last_update?: FileChange; }; export type LastUpdateData = { diff --git a/website/_dogfooding/_docs tests/doc-with-last-update.md b/website/_dogfooding/_docs tests/doc-with-last-update.md new file mode 100644 index 000000000000..34c1cb0c7cb6 --- /dev/null +++ b/website/_dogfooding/_docs tests/doc-with-last-update.md @@ -0,0 +1,7 @@ +--- +last_update: + author: custom author + date: 1/1/2000 +--- + +# Doc With Last Update Front Matter diff --git a/website/_dogfooding/docs-tests-sidebars.js b/website/_dogfooding/docs-tests-sidebars.js index 6b8b8f9c4365..0183dbb53925 100644 --- a/website/_dogfooding/docs-tests-sidebars.js +++ b/website/_dogfooding/docs-tests-sidebars.js @@ -17,6 +17,7 @@ const sidebars = { 'test-draft', 'doc-without-sidebar', 'doc-with-another-sidebar', + 'doc-with-last-update', { type: 'category', label: 'Tests', diff --git a/website/_dogfooding/dogfooding.config.js b/website/_dogfooding/dogfooding.config.js index 95c1d5161843..bafce78b219f 100644 --- a/website/_dogfooding/dogfooding.config.js +++ b/website/_dogfooding/dogfooding.config.js @@ -34,6 +34,7 @@ const dogfoodingPluginInstances = [ // eslint-disable-next-line no-restricted-properties path: fs.realpathSync('_dogfooding/docs-tests-symlink'), showLastUpdateTime: true, + showLastUpdateAuthor: true, sidebarItemsGenerator(args) { return args.defaultSidebarItemsGenerator({ ...args, diff --git a/website/docs/api/plugins/plugin-content-docs.md b/website/docs/api/plugins/plugin-content-docs.md index e5971ee2fa59..95d3cee68d03 100644 --- a/website/docs/api/plugins/plugin-content-docs.md +++ b/website/docs/api/plugins/plugin-content-docs.md @@ -263,6 +263,7 @@ Accepted fields: | `slug` | `string` | File path | Allows to customize the document url (`//`). Support multiple patterns: `slug: my-doc`, `slug: /my/path/myDoc`, `slug: /`. | | `tags` | `Tag[]` | `undefined` | A list of strings or objects of two string fields `label` and `permalink` to tag to your docs. | | `draft` | `boolean` | `false` | A boolean flag to indicate that a document is a work-in-progress. Draft documents will only be displayed during development. | +| `last_update` | `FileChange` | `undefined` | Allows overriding the last updated author and/or date. Date can be any [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). | @@ -270,6 +271,10 @@ Accepted fields: type Tag = string | {label: string; permalink: string}; ``` +```ts +type FileChange = {date: string; author: string}; +``` + Example: ```md @@ -288,6 +293,9 @@ keywords: - docusaurus image: https://i.imgur.com/mErPwqL.png slug: /myDoc +last_update: + date: 1/1/2000 + author: custom author name --- # Markdown Features From 1f4efc1927c5f9d618577dd089219022574d04f6 Mon Sep 17 00:00:00 2001 From: David Pang Date: Mon, 23 May 2022 08:35:03 -0400 Subject: [PATCH 02/13] refactor: update last_update JSDoc --- .../src/plugin-content-docs.d.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 7c0fb61cefc5..0311e4bef11f 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 @@ -361,7 +361,10 @@ declare module '@docusaurus/plugin-content-docs' { pagination_prev?: string | null; /** Should this doc be excluded from production builds? */ draft?: boolean; - /** Front matter override for last update to doc */ + /** Allows overriding the last updated author and/or date. Date can be any + * parsable date string. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse + */ last_update?: FileChange; }; From 60f3c3453e91eb4f07253a0f6b7c685a411dea4e Mon Sep 17 00:00:00 2001 From: David Pang Date: Mon, 23 May 2022 08:53:03 -0400 Subject: [PATCH 03/13] refactor: separate last_update JSDoc --- .../src/plugin-content-docs.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 0311e4bef11f..c96ce4314b1f 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 @@ -25,6 +25,9 @@ declare module '@docusaurus/plugin-content-docs' { export type FileChange = { author?: string; + /** Date can be any + * [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). + */ date?: Date | string; }; @@ -361,10 +364,7 @@ declare module '@docusaurus/plugin-content-docs' { pagination_prev?: string | null; /** Should this doc be excluded from production builds? */ draft?: boolean; - /** Allows overriding the last updated author and/or date. Date can be any - * parsable date string. - * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse - */ + /** Allows overriding the last updated author and/or date. */ last_update?: FileChange; }; From eac8588f507cff2f9ff5867a02a120d2aae77c19 Mon Sep 17 00:00:00 2001 From: David Pang Date: Mon, 23 May 2022 10:24:37 -0400 Subject: [PATCH 04/13] refactor: move readLastUpdateData to doProcessDocMetadata --- .../src/__tests__/docs.test.ts | 21 +++---- .../src/docs.ts | 58 +++++++++++-------- .../src/types.ts | 2 - 3 files changed, 46 insertions(+), 35 deletions(-) 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 79aae3273e84..88b74733f7ce 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts @@ -57,7 +57,6 @@ ${markdown} return { source, content, - lastUpdate: {}, contentPath: 'docs', filePath: source, }; @@ -79,7 +78,7 @@ function createTestUtils({ env = 'production', }: TestUtilsArg) { async function readDoc(docFileSource: string) { - return readDocFile(versionMetadata, docFileSource, options); + return readDocFile(versionMetadata, docFileSource); } async function processDocFile(docFileArg: DocFile | string) { const docFile: DocFile = @@ -137,14 +136,16 @@ function createTestUtils({ }[]; sidebars: Sidebars; }> { - const rawDocs = docFiles.map((docFile) => - processDocMetadata({ - docFile, - versionMetadata, - context, - options, - env: 'production', - }), + const rawDocs = await Promise.all( + docFiles.map(async (docFile) => + processDocMetadata({ + docFile, + versionMetadata, + context, + options, + env: 'production', + }), + ), ); const sidebars = await loadSidebars(versionMetadata.sidebarFilePath, { sidebarItemsGenerator: ({defaultSidebarItemsGenerator, ...args}) => diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index 7a57fa1d81a4..80a56bf40250 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -37,6 +37,7 @@ import type { VersionMetadata, DocFrontMatter, LoadedVersion, + FileChange, } from '@docusaurus/plugin-content-docs'; import type {LoadContext} from '@docusaurus/types'; import type {SidebarsUtils} from './sidebars/utils'; @@ -50,6 +51,7 @@ type LastUpdateOptions = Pick< async function readLastUpdateData( filePath: string, options: LastUpdateOptions, + lastUpdateFrontMatter: FileChange | undefined, ): Promise { const {showLastUpdateAuthor, showLastUpdateTime} = options; if (showLastUpdateAuthor || showLastUpdateTime) { @@ -64,9 +66,26 @@ async function readLastUpdateData( if (fileLastUpdateData) { const {author, timestamp} = fileLastUpdateData; + return { - lastUpdatedAt: showLastUpdateTime ? timestamp : undefined, - lastUpdatedBy: showLastUpdateAuthor ? author : undefined, + lastUpdatedAt: (() => { + if (showLastUpdateTime) { + if (lastUpdateFrontMatter?.date) { + return new Date(lastUpdateFrontMatter.date).getTime() / 1000; + } + return timestamp; + } + return undefined; + })(), + lastUpdatedBy: (() => { + if (showLastUpdateAuthor) { + if (lastUpdateFrontMatter?.author) { + return lastUpdateFrontMatter.author; + } + return author; + } + return undefined; + })(), }; } } @@ -80,7 +99,6 @@ export async function readDocFile( 'contentPath' | 'contentPathLocalized' >, source: string, - options: LastUpdateOptions, ): Promise { const contentPath = await getFolderContainingFile( getContentPathList(versionMetadata), @@ -89,11 +107,8 @@ export async function readDocFile( const filePath = path.join(contentPath, source); - const [content, lastUpdate] = await Promise.all([ - fs.readFile(filePath, 'utf-8'), - readLastUpdateData(filePath, options), - ]); - return {source, content, lastUpdate, contentPath, filePath}; + const content = await fs.readFile(filePath, 'utf-8'); + return {source, content, contentPath, filePath}; } export async function readVersionDocs( @@ -108,7 +123,7 @@ export async function readVersionDocs( ignore: options.exclude, }); return Promise.all( - sources.map((source) => readDocFile(versionMetadata, source, options)), + sources.map((source) => readDocFile(versionMetadata, source)), ); } @@ -125,7 +140,7 @@ function isDraftForEnvironment({ return (env === 'production' && frontMatter.draft) ?? false; } -function doProcessDocMetadata({ +async function doProcessDocMetadata({ docFile, versionMetadata, context, @@ -137,8 +152,8 @@ function doProcessDocMetadata({ context: LoadContext; options: MetadataOptions; env: DocEnv; -}): DocMetadataBase { - const {source, content, lastUpdate, contentPath, filePath} = docFile; +}): Promise { + const {source, content, contentPath, filePath} = docFile; const {siteDir, i18n} = context; const { @@ -158,14 +173,11 @@ function doProcessDocMetadata({ last_update: lastUpdateFrontMatter, } = frontMatter; - if (lastUpdateFrontMatter?.author && options.showLastUpdateAuthor) { - lastUpdate.lastUpdatedBy = lastUpdateFrontMatter.author; - } - if (lastUpdateFrontMatter?.date && options.showLastUpdateTime) { - lastUpdate.lastUpdatedAt = - new Date(lastUpdateFrontMatter.date).getTime() / 1000; - lastUpdate.formattedLastUpdatedAt = undefined; - } + const lastUpdate = await readLastUpdateData( + filePath, + options, + lastUpdateFrontMatter, + ); // E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc const sourceFileNameWithoutExtension = path.basename( @@ -291,15 +303,15 @@ function doProcessDocMetadata({ }; } -export function processDocMetadata(args: { +export async function processDocMetadata(args: { docFile: DocFile; versionMetadata: VersionMetadata; context: LoadContext; options: MetadataOptions; env: DocEnv; -}): DocMetadataBase { +}): Promise { try { - return doProcessDocMetadata(args); + return await doProcessDocMetadata(args); } catch (err) { logger.error`Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`; throw err; diff --git a/packages/docusaurus-plugin-content-docs/src/types.ts b/packages/docusaurus-plugin-content-docs/src/types.ts index cb4506b20d1a..4c4568517fbb 100644 --- a/packages/docusaurus-plugin-content-docs/src/types.ts +++ b/packages/docusaurus-plugin-content-docs/src/types.ts @@ -8,7 +8,6 @@ import type {BrokenMarkdownLink, Tag} from '@docusaurus/utils'; import type { VersionMetadata, - LastUpdateData, LoadedVersion, CategoryGeneratedIndexMetadata, } from '@docusaurus/plugin-content-docs'; @@ -19,7 +18,6 @@ export type DocFile = { filePath: string; // /!\ may be localized source: string; content: string; - lastUpdate: LastUpdateData; }; export type SourceToPermalink = { From aa2f8c57eb2123306412d87ab4a572287ce61769 Mon Sep 17 00:00:00 2001 From: David Pang Date: Tue, 24 May 2022 16:10:04 -0400 Subject: [PATCH 05/13] refactor: change readLastUpdateData to synchronous --- .../src/__tests__/docs.test.ts | 18 ++++++++---------- .../src/__tests__/lastUpdate.test.ts | 18 +++++++++--------- .../docusaurus-plugin-content-docs/src/docs.ts | 18 +++++++++--------- .../src/lastUpdate.ts | 4 ++-- 4 files changed, 28 insertions(+), 30 deletions(-) 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 88b74733f7ce..fb183ca50561 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts @@ -136,16 +136,14 @@ function createTestUtils({ }[]; sidebars: Sidebars; }> { - const rawDocs = await Promise.all( - docFiles.map(async (docFile) => - processDocMetadata({ - docFile, - versionMetadata, - context, - options, - env: 'production', - }), - ), + const rawDocs = docFiles.map((docFile) => + processDocMetadata({ + docFile, + versionMetadata, + context, + options, + env: 'production', + }), ); const sidebars = await loadSidebars(versionMetadata.sidebarFilePath, { sidebarItemsGenerator: ({defaultSidebarItemsGenerator, ...args}) => diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts index 47aff760539e..29b19485a0ac 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts @@ -19,7 +19,7 @@ describe('getFileLastUpdate', () => { '__fixtures__/simple-site/docs/hello.md', ); it('existing test file in repository with Git timestamp', async () => { - const lastUpdateData = await getFileLastUpdate(existingFilePath); + const lastUpdateData = getFileLastUpdate(existingFilePath); expect(lastUpdateData).not.toBeNull(); const {author, timestamp} = lastUpdateData; @@ -35,7 +35,7 @@ describe('getFileLastUpdate', () => { __dirname, '__fixtures__/simple-site/docs/doc with space.md', ); - const lastUpdateData = await getFileLastUpdate(filePathWithSpace); + const lastUpdateData = getFileLastUpdate(filePathWithSpace); expect(lastUpdateData).not.toBeNull(); const {author, timestamp} = lastUpdateData; @@ -56,13 +56,13 @@ describe('getFileLastUpdate', () => { '__fixtures__', nonExistingFileName, ); - await expect(getFileLastUpdate(nonExistingFilePath)).resolves.toBeNull(); + expect(getFileLastUpdate(nonExistingFilePath)).toBeNull(); expect(consoleMock).toHaveBeenCalledTimes(1); expect(consoleMock).toHaveBeenLastCalledWith( expect.stringMatching(/because the file does not exist./), ); - await expect(getFileLastUpdate(null)).resolves.toBeNull(); - await expect(getFileLastUpdate(undefined)).resolves.toBeNull(); + expect(getFileLastUpdate(null)).toBeNull(); + expect(getFileLastUpdate(undefined)).toBeNull(); consoleMock.mockRestore(); }); @@ -73,7 +73,7 @@ describe('getFileLastUpdate', () => { const {repoDir} = createTempRepo(); const tempFilePath = path.join(repoDir, 'file.md'); await fs.writeFile(tempFilePath, 'Lorem ipsum :)'); - await expect(getFileLastUpdate(tempFilePath)).resolves.toBeNull(); + expect(getFileLastUpdate(tempFilePath)).toBeNull(); expect(consoleMock).toHaveBeenCalledTimes(1); expect(consoleMock).toHaveBeenLastCalledWith( expect.stringMatching(/not tracked by git./), @@ -90,8 +90,8 @@ describe('getFileLastUpdate', () => { const tempFilePath2 = path.join(repoDir, 'file2.md'); await fs.writeFile(tempFilePath1, 'Lorem ipsum :)'); await fs.writeFile(tempFilePath2, 'Lorem ipsum :)'); - await expect(getFileLastUpdate(tempFilePath1)).resolves.toBeNull(); - await expect(getFileLastUpdate(tempFilePath2)).resolves.toBeNull(); + expect(getFileLastUpdate(tempFilePath1)).toBeNull(); + expect(getFileLastUpdate(tempFilePath2)).toBeNull(); expect(consoleMock).toHaveBeenCalledTimes(1); expect(consoleMock).toHaveBeenLastCalledWith( expect.stringMatching(/not tracked by git./), @@ -105,7 +105,7 @@ describe('getFileLastUpdate', () => { const consoleMock = jest .spyOn(console, 'warn') .mockImplementation(() => {}); - const lastUpdateData = await getFileLastUpdate(existingFilePath); + const lastUpdateData = getFileLastUpdate(existingFilePath); expect(lastUpdateData).toBeNull(); expect(consoleMock).toHaveBeenLastCalledWith( expect.stringMatching( diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index 80a56bf40250..4cd526355feb 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -48,17 +48,17 @@ type LastUpdateOptions = Pick< 'showLastUpdateAuthor' | 'showLastUpdateTime' >; -async function readLastUpdateData( +function readLastUpdateData( filePath: string, options: LastUpdateOptions, lastUpdateFrontMatter: FileChange | undefined, -): Promise { +): LastUpdateData { const {showLastUpdateAuthor, showLastUpdateTime} = options; if (showLastUpdateAuthor || showLastUpdateTime) { // Use fake data in dev for faster development. const fileLastUpdateData = process.env.NODE_ENV === 'production' - ? await getFileLastUpdate(filePath) + ? getFileLastUpdate(filePath) : { author: 'Author', timestamp: 1539502055, @@ -140,7 +140,7 @@ function isDraftForEnvironment({ return (env === 'production' && frontMatter.draft) ?? false; } -async function doProcessDocMetadata({ +function doProcessDocMetadata({ docFile, versionMetadata, context, @@ -152,7 +152,7 @@ async function doProcessDocMetadata({ context: LoadContext; options: MetadataOptions; env: DocEnv; -}): Promise { +}): DocMetadataBase { const {source, content, contentPath, filePath} = docFile; const {siteDir, i18n} = context; @@ -173,7 +173,7 @@ async function doProcessDocMetadata({ last_update: lastUpdateFrontMatter, } = frontMatter; - const lastUpdate = await readLastUpdateData( + const lastUpdate = readLastUpdateData( filePath, options, lastUpdateFrontMatter, @@ -303,15 +303,15 @@ async function doProcessDocMetadata({ }; } -export async function processDocMetadata(args: { +export function processDocMetadata(args: { docFile: DocFile; versionMetadata: VersionMetadata; context: LoadContext; options: MetadataOptions; env: DocEnv; -}): Promise { +}): DocMetadataBase { try { - return await doProcessDocMetadata(args); + return doProcessDocMetadata(args); } catch (err) { logger.error`Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`; throw err; diff --git a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts index a0eb4d775997..477bc1d5bc30 100644 --- a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts +++ b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts @@ -15,9 +15,9 @@ import { let showedGitRequirementError = false; let showedFileNotTrackedError = false; -export async function getFileLastUpdate( +export function getFileLastUpdate( filePath?: string, -): Promise<{timestamp: number; author: string} | null> { +): {timestamp: number; author: string} | null { if (!filePath) { return null; } From 8e9880fdf118cf0b8e4229c9650ef3eed31a55ca Mon Sep 17 00:00:00 2001 From: dpang314 Date: Wed, 25 May 2022 21:05:39 -0400 Subject: [PATCH 06/13] Update packages/docusaurus-plugin-content-docs/src/docs.ts Co-authored-by: Joshua Chen --- packages/docusaurus-plugin-content-docs/src/docs.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index 4cd526355feb..e6241524ef84 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -77,15 +77,7 @@ function readLastUpdateData( } return undefined; })(), - lastUpdatedBy: (() => { - if (showLastUpdateAuthor) { - if (lastUpdateFrontMatter?.author) { - return lastUpdateFrontMatter.author; - } - return author; - } - return undefined; - })(), + lastUpdatedBy: showLastUpdateAuthor ? lastUpdateFrontMatter?.author ?? author : undefined, }; } } From f6b0aa311f9d455322714543cc72872e0a90d213 Mon Sep 17 00:00:00 2001 From: David Pang Date: Wed, 25 May 2022 21:44:06 -0400 Subject: [PATCH 07/13] fix: front matter should work without Git history --- .../src/docs.ts | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index e6241524ef84..e0f9fa79f5af 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -64,22 +64,19 @@ function readLastUpdateData( timestamp: 1539502055, }; - if (fileLastUpdateData) { - const {author, timestamp} = fileLastUpdateData; - - return { - lastUpdatedAt: (() => { - if (showLastUpdateTime) { - if (lastUpdateFrontMatter?.date) { - return new Date(lastUpdateFrontMatter.date).getTime() / 1000; - } - return timestamp; - } - return undefined; - })(), - lastUpdatedBy: showLastUpdateAuthor ? lastUpdateFrontMatter?.author ?? author : undefined, - }; - } + const {author, timestamp} = fileLastUpdateData ?? {}; + const frontMatterTimestamp = lastUpdateFrontMatter?.date + ? new Date(lastUpdateFrontMatter.date).getTime() / 1000 + : undefined; + + return { + lastUpdatedBy: showLastUpdateAuthor + ? lastUpdateFrontMatter?.author ?? author + : undefined, + lastUpdatedAt: showLastUpdateTime + ? frontMatterTimestamp ?? timestamp + : undefined, + }; } return {}; @@ -165,6 +162,8 @@ function doProcessDocMetadata({ last_update: lastUpdateFrontMatter, } = frontMatter; + console.log(lastUpdateFrontMatter); + const lastUpdate = readLastUpdateData( filePath, options, From f14674506c2788756adb96792a4b6531bbb6aecd Mon Sep 17 00:00:00 2001 From: David Pang Date: Fri, 27 May 2022 11:09:51 -0400 Subject: [PATCH 08/13] refactor: revert readLastUpdateData to async --- .../src/__tests__/docs.test.ts | 18 +++++++++-------- .../src/__tests__/lastUpdate.test.ts | 18 ++++++++--------- .../src/docs.ts | 20 +++++++++---------- .../src/lastUpdate.ts | 4 ++-- 4 files changed, 30 insertions(+), 30 deletions(-) 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 fb183ca50561..88b74733f7ce 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts @@ -136,14 +136,16 @@ function createTestUtils({ }[]; sidebars: Sidebars; }> { - const rawDocs = docFiles.map((docFile) => - processDocMetadata({ - docFile, - versionMetadata, - context, - options, - env: 'production', - }), + const rawDocs = await Promise.all( + docFiles.map(async (docFile) => + processDocMetadata({ + docFile, + versionMetadata, + context, + options, + env: 'production', + }), + ), ); const sidebars = await loadSidebars(versionMetadata.sidebarFilePath, { sidebarItemsGenerator: ({defaultSidebarItemsGenerator, ...args}) => diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts index 29b19485a0ac..47aff760539e 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/lastUpdate.test.ts @@ -19,7 +19,7 @@ describe('getFileLastUpdate', () => { '__fixtures__/simple-site/docs/hello.md', ); it('existing test file in repository with Git timestamp', async () => { - const lastUpdateData = getFileLastUpdate(existingFilePath); + const lastUpdateData = await getFileLastUpdate(existingFilePath); expect(lastUpdateData).not.toBeNull(); const {author, timestamp} = lastUpdateData; @@ -35,7 +35,7 @@ describe('getFileLastUpdate', () => { __dirname, '__fixtures__/simple-site/docs/doc with space.md', ); - const lastUpdateData = getFileLastUpdate(filePathWithSpace); + const lastUpdateData = await getFileLastUpdate(filePathWithSpace); expect(lastUpdateData).not.toBeNull(); const {author, timestamp} = lastUpdateData; @@ -56,13 +56,13 @@ describe('getFileLastUpdate', () => { '__fixtures__', nonExistingFileName, ); - expect(getFileLastUpdate(nonExistingFilePath)).toBeNull(); + await expect(getFileLastUpdate(nonExistingFilePath)).resolves.toBeNull(); expect(consoleMock).toHaveBeenCalledTimes(1); expect(consoleMock).toHaveBeenLastCalledWith( expect.stringMatching(/because the file does not exist./), ); - expect(getFileLastUpdate(null)).toBeNull(); - expect(getFileLastUpdate(undefined)).toBeNull(); + await expect(getFileLastUpdate(null)).resolves.toBeNull(); + await expect(getFileLastUpdate(undefined)).resolves.toBeNull(); consoleMock.mockRestore(); }); @@ -73,7 +73,7 @@ describe('getFileLastUpdate', () => { const {repoDir} = createTempRepo(); const tempFilePath = path.join(repoDir, 'file.md'); await fs.writeFile(tempFilePath, 'Lorem ipsum :)'); - expect(getFileLastUpdate(tempFilePath)).toBeNull(); + await expect(getFileLastUpdate(tempFilePath)).resolves.toBeNull(); expect(consoleMock).toHaveBeenCalledTimes(1); expect(consoleMock).toHaveBeenLastCalledWith( expect.stringMatching(/not tracked by git./), @@ -90,8 +90,8 @@ describe('getFileLastUpdate', () => { const tempFilePath2 = path.join(repoDir, 'file2.md'); await fs.writeFile(tempFilePath1, 'Lorem ipsum :)'); await fs.writeFile(tempFilePath2, 'Lorem ipsum :)'); - expect(getFileLastUpdate(tempFilePath1)).toBeNull(); - expect(getFileLastUpdate(tempFilePath2)).toBeNull(); + await expect(getFileLastUpdate(tempFilePath1)).resolves.toBeNull(); + await expect(getFileLastUpdate(tempFilePath2)).resolves.toBeNull(); expect(consoleMock).toHaveBeenCalledTimes(1); expect(consoleMock).toHaveBeenLastCalledWith( expect.stringMatching(/not tracked by git./), @@ -105,7 +105,7 @@ describe('getFileLastUpdate', () => { const consoleMock = jest .spyOn(console, 'warn') .mockImplementation(() => {}); - const lastUpdateData = getFileLastUpdate(existingFilePath); + const lastUpdateData = await getFileLastUpdate(existingFilePath); expect(lastUpdateData).toBeNull(); expect(consoleMock).toHaveBeenLastCalledWith( expect.stringMatching( diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index e0f9fa79f5af..90d563dadff2 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -48,17 +48,17 @@ type LastUpdateOptions = Pick< 'showLastUpdateAuthor' | 'showLastUpdateTime' >; -function readLastUpdateData( +async function readLastUpdateData( filePath: string, options: LastUpdateOptions, lastUpdateFrontMatter: FileChange | undefined, -): LastUpdateData { +): Promise { const {showLastUpdateAuthor, showLastUpdateTime} = options; if (showLastUpdateAuthor || showLastUpdateTime) { // Use fake data in dev for faster development. const fileLastUpdateData = process.env.NODE_ENV === 'production' - ? getFileLastUpdate(filePath) + ? await getFileLastUpdate(filePath) : { author: 'Author', timestamp: 1539502055, @@ -129,7 +129,7 @@ function isDraftForEnvironment({ return (env === 'production' && frontMatter.draft) ?? false; } -function doProcessDocMetadata({ +async function doProcessDocMetadata({ docFile, versionMetadata, context, @@ -141,7 +141,7 @@ function doProcessDocMetadata({ context: LoadContext; options: MetadataOptions; env: DocEnv; -}): DocMetadataBase { +}): Promise { const {source, content, contentPath, filePath} = docFile; const {siteDir, i18n} = context; @@ -162,9 +162,7 @@ function doProcessDocMetadata({ last_update: lastUpdateFrontMatter, } = frontMatter; - console.log(lastUpdateFrontMatter); - - const lastUpdate = readLastUpdateData( + const lastUpdate = await readLastUpdateData( filePath, options, lastUpdateFrontMatter, @@ -294,15 +292,15 @@ function doProcessDocMetadata({ }; } -export function processDocMetadata(args: { +export async function processDocMetadata(args: { docFile: DocFile; versionMetadata: VersionMetadata; context: LoadContext; options: MetadataOptions; env: DocEnv; -}): DocMetadataBase { +}): Promise { try { - return doProcessDocMetadata(args); + return await doProcessDocMetadata(args); } catch (err) { logger.error`Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`; throw err; diff --git a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts index 477bc1d5bc30..a0eb4d775997 100644 --- a/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts +++ b/packages/docusaurus-plugin-content-docs/src/lastUpdate.ts @@ -15,9 +15,9 @@ import { let showedGitRequirementError = false; let showedFileNotTrackedError = false; -export function getFileLastUpdate( +export async function getFileLastUpdate( filePath?: string, -): {timestamp: number; author: string} | null { +): Promise<{timestamp: number; author: string} | null> { if (!filePath) { return null; } From 13f04f08e3519fe1a5baaa6abb528228f046f100 Mon Sep 17 00:00:00 2001 From: David Pang Date: Fri, 27 May 2022 11:55:22 -0400 Subject: [PATCH 09/13] refactor: revert unnecessary async --- packages/docusaurus-plugin-content-docs/src/docs.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index 90d563dadff2..0caab78debae 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -292,7 +292,7 @@ async function doProcessDocMetadata({ }; } -export async function processDocMetadata(args: { +export function processDocMetadata(args: { docFile: DocFile; versionMetadata: VersionMetadata; context: LoadContext; @@ -300,7 +300,7 @@ export async function processDocMetadata(args: { env: DocEnv; }): Promise { try { - return await doProcessDocMetadata(args); + return doProcessDocMetadata(args); } catch (err) { logger.error`Can't process doc metadata for doc at path path=${args.docFile.filePath} in version name=${args.versionMetadata.versionName}`; throw err; From 84433c8c82a1fb482e3cad02f2b1e6402a1e482c Mon Sep 17 00:00:00 2001 From: David Pang Date: Fri, 27 May 2022 13:36:51 -0400 Subject: [PATCH 10/13] refactor: avoid calling getFileLastUpdate unnecessarily --- .../docusaurus-plugin-content-docs/src/docs.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/docusaurus-plugin-content-docs/src/docs.ts b/packages/docusaurus-plugin-content-docs/src/docs.ts index 0caab78debae..580d90101740 100644 --- a/packages/docusaurus-plugin-content-docs/src/docs.ts +++ b/packages/docusaurus-plugin-content-docs/src/docs.ts @@ -55,6 +55,17 @@ async function readLastUpdateData( ): Promise { const {showLastUpdateAuthor, showLastUpdateTime} = options; if (showLastUpdateAuthor || showLastUpdateTime) { + const frontMatterTimestamp = lastUpdateFrontMatter?.date + ? new Date(lastUpdateFrontMatter.date).getTime() / 1000 + : undefined; + + if (lastUpdateFrontMatter?.author && lastUpdateFrontMatter.date) { + return { + lastUpdatedAt: frontMatterTimestamp, + lastUpdatedBy: lastUpdateFrontMatter.author, + }; + } + // Use fake data in dev for faster development. const fileLastUpdateData = process.env.NODE_ENV === 'production' @@ -63,11 +74,7 @@ async function readLastUpdateData( author: 'Author', timestamp: 1539502055, }; - const {author, timestamp} = fileLastUpdateData ?? {}; - const frontMatterTimestamp = lastUpdateFrontMatter?.date - ? new Date(lastUpdateFrontMatter.date).getTime() / 1000 - : undefined; return { lastUpdatedBy: showLastUpdateAuthor From f19e0cacfe0f1dbe77fb797bf3400bdb289b859c Mon Sep 17 00:00:00 2001 From: David Pang Date: Fri, 27 May 2022 18:32:16 -0400 Subject: [PATCH 11/13] test: reject empty and invalid objects --- .../src/__tests__/frontMatter.test.ts | 12 ++++++++++++ .../src/frontMatter.ts | 10 +++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) 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 b3f16057dd92..49876967f75a 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts @@ -408,6 +408,18 @@ describe('validateDocFrontMatter last_update', () => { {last_update: {author: undefined, date: 'December 17, 1995 03:24:00'}}, ], 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 front matter FileChange object. Please use a FileChange object (with an author and/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: {author: 'test author', date: 'I am not a date :('}}, 'must be a valid date', diff --git a/packages/docusaurus-plugin-content-docs/src/frontMatter.ts b/packages/docusaurus-plugin-content-docs/src/frontMatter.ts index ade1810455eb..569717f2142c 100644 --- a/packages/docusaurus-plugin-content-docs/src/frontMatter.ts +++ b/packages/docusaurus-plugin-content-docs/src/frontMatter.ts @@ -14,6 +14,9 @@ import { } from '@docusaurus/utils-validation'; import type {DocFrontMatter} from '@docusaurus/plugin-content-docs'; +const FrontMatterLastUpdateErrorMessage = + '{{#label}} does not look like a valid front matter FileChange object. Please use a FileChange object (with an author and/or date).'; + // NOTE: we don't add any default value on purpose here // We don't want default values to magically appear in doc metadata and props // While the user did not provide those values explicitly @@ -45,7 +48,12 @@ const DocFrontMatterSchema = Joi.object({ last_update: Joi.object({ author: Joi.string(), date: Joi.date().raw(), - }), + }) + .min(1) + .messages({ + 'object.min': FrontMatterLastUpdateErrorMessage, + 'object.base': FrontMatterLastUpdateErrorMessage, + }), }).unknown(); export function validateDocFrontMatter(frontMatter: { From bfcb4d35b5d787f4d0f84e5c0c7bc410d83b61a1 Mon Sep 17 00:00:00 2001 From: David Pang Date: Fri, 27 May 2022 19:14:42 -0400 Subject: [PATCH 12/13] refactor: improve check for valid last_update object --- .../src/__tests__/frontMatter.test.ts | 4 ++++ packages/docusaurus-plugin-content-docs/src/frontMatter.ts | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) 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 49876967f75a..a5f5e423c5d5 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/frontMatter.test.ts @@ -420,6 +420,10 @@ describe('validateDocFrontMatter last_update', () => { {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: {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: {author: 'test author', date: 'I am not a date :('}}, 'must be a valid date', diff --git a/packages/docusaurus-plugin-content-docs/src/frontMatter.ts b/packages/docusaurus-plugin-content-docs/src/frontMatter.ts index 569717f2142c..9d210a831b25 100644 --- a/packages/docusaurus-plugin-content-docs/src/frontMatter.ts +++ b/packages/docusaurus-plugin-content-docs/src/frontMatter.ts @@ -49,9 +49,9 @@ const DocFrontMatterSchema = Joi.object({ author: Joi.string(), date: Joi.date().raw(), }) - .min(1) + .or('author', 'date') .messages({ - 'object.min': FrontMatterLastUpdateErrorMessage, + 'object.missing': FrontMatterLastUpdateErrorMessage, 'object.base': FrontMatterLastUpdateErrorMessage, }), }).unknown(); From f43af83fb7c807ccc82908ebf3116b78e75a18b8 Mon Sep 17 00:00:00 2001 From: sebastienlorber Date: Wed, 1 Jun 2022 15:54:26 +0200 Subject: [PATCH 13/13] fix test typo --- .../docusaurus-plugin-content-docs/src/__tests__/docs.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 562b873dda0b..88b74733f7ce 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/docs.test.ts @@ -118,7 +118,7 @@ function createTestUtils({ async function testSlug(docFileSource: string, expectedPermalink: string) { const docFile = await readDoc(docFileSource); - const metadata = processDocMetadata({ + const metadata = await processDocMetadata({ docFile, versionMetadata, context,