feat(pages): add LastUpdateAuthor & LastUpdateTime & editUrl (#10032)

Co-authored-by: OzakIOne <OzakIOne@users.noreply.github.com>
Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
ozaki 2024-04-16 11:23:00 +02:00 committed by GitHub
parent e4ecffe418
commit d1590e37ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 254 additions and 8 deletions

View file

@ -14,9 +14,12 @@ exports[`docusaurus-plugin-content-pages loads simple pages 1`] = `
},
{
"description": "Markdown index page",
"editUrl": undefined,
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
},
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"permalink": "/hello/",
"source": "@site/src/pages/hello/index.md",
"title": "Index",
@ -25,11 +28,14 @@ exports[`docusaurus-plugin-content-pages loads simple pages 1`] = `
},
{
"description": "my MDX page",
"editUrl": undefined,
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
"description": "my MDX page",
"title": "MDX page",
},
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"permalink": "/hello/mdxPage",
"source": "@site/src/pages/hello/mdxPage.mdx",
"title": "MDX page",
@ -43,9 +49,12 @@ exports[`docusaurus-plugin-content-pages loads simple pages 1`] = `
},
{
"description": "translated Markdown page",
"editUrl": undefined,
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
},
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"permalink": "/hello/translatedMd",
"source": "@site/src/pages/hello/translatedMd.md",
"title": undefined,
@ -74,9 +83,12 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
},
{
"description": "Markdown index page",
"editUrl": undefined,
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
},
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"permalink": "/fr/hello/",
"source": "@site/src/pages/hello/index.md",
"title": "Index",
@ -85,11 +97,14 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
},
{
"description": "my MDX page",
"editUrl": undefined,
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
"description": "my MDX page",
"title": "MDX page",
},
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"permalink": "/fr/hello/mdxPage",
"source": "@site/src/pages/hello/mdxPage.mdx",
"title": "MDX page",
@ -103,9 +118,12 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
},
{
"description": "translated Markdown page (fr)",
"editUrl": undefined,
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
},
"lastUpdatedAt": undefined,
"lastUpdatedBy": undefined,
"permalink": "/fr/hello/translatedMd",
"source": "@site/i18n/fr/docusaurus-plugin-content-pages/hello/translatedMd.md",
"title": undefined,
@ -119,3 +137,72 @@ exports[`docusaurus-plugin-content-pages loads simple pages with french translat
},
]
`;
exports[`docusaurus-plugin-content-pages loads simple pages with last update 1`] = `
[
{
"permalink": "/",
"source": "@site/src/pages/index.js",
"type": "jsx",
},
{
"permalink": "/typescript",
"source": "@site/src/pages/typescript.tsx",
"type": "jsx",
},
{
"description": "Markdown index page",
"editUrl": "url placeholder",
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
},
"lastUpdatedAt": 1539502055000,
"lastUpdatedBy": "Author",
"permalink": "/hello/",
"source": "@site/src/pages/hello/index.md",
"title": "Index",
"type": "mdx",
"unlisted": false,
},
{
"description": "my MDX page",
"editUrl": "url placeholder",
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
"description": "my MDX page",
"title": "MDX page",
},
"lastUpdatedAt": 1539502055000,
"lastUpdatedBy": "Author",
"permalink": "/hello/mdxPage",
"source": "@site/src/pages/hello/mdxPage.mdx",
"title": "MDX page",
"type": "mdx",
"unlisted": false,
},
{
"permalink": "/hello/translatedJs",
"source": "@site/src/pages/hello/translatedJs.js",
"type": "jsx",
},
{
"description": "translated Markdown page",
"editUrl": "url placeholder",
"frontMatter": {
"custom_frontMatter": "added by parseFrontMatter",
},
"lastUpdatedAt": 1539502055000,
"lastUpdatedBy": "Author",
"permalink": "/hello/translatedMd",
"source": "@site/src/pages/hello/translatedMd.md",
"title": undefined,
"type": "mdx",
"unlisted": false,
},
{
"permalink": "/hello/world",
"source": "@site/src/pages/hello/world.js",
"type": "jsx",
},
]
`;

View file

@ -46,4 +46,24 @@ describe('docusaurus-plugin-content-pages', () => {
expect(pagesMetadata).toMatchSnapshot();
});
it('loads simple pages with last update', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');
const context = await loadContext({siteDir});
const plugin = pluginContentPages(
context,
validateOptions({
validate: normalizePluginOptions,
options: {
path: 'src/pages',
editUrl: () => 'url placeholder',
showLastUpdateAuthor: true,
showLastUpdateTime: true,
},
}),
);
const pagesMetadata = await plugin.loadContent!();
expect(pagesMetadata).toMatchSnapshot();
});
});

View file

@ -11,6 +11,7 @@ import {
FrontMatterTOCHeadingLevels,
ContentVisibilitySchema,
URISchema,
FrontMatterLastUpdateSchema,
} from '@docusaurus/utils-validation';
import type {PageFrontMatter} from '@docusaurus/plugin-content-pages';
@ -24,6 +25,7 @@ const PageFrontMatterSchema = Joi.object<PageFrontMatter>({
wrapperClassName: Joi.string(),
hide_table_of_contents: Joi.boolean(),
...FrontMatterTOCHeadingLevels,
last_update: FrontMatterLastUpdateSchema,
}).concat(ContentVisibilitySchema);
export function validatePageFrontMatter(frontMatter: {

View file

@ -23,6 +23,9 @@ import {
parseMarkdownFile,
isUnlisted,
isDraft,
readLastUpdateData,
getEditUrl,
posixPath,
} from '@docusaurus/utils';
import {validatePageFrontMatter} from './frontMatter';
import type {LoadContext, Plugin, RouteMetadata} from '@docusaurus/types';
@ -45,7 +48,8 @@ export default function pluginContentPages(
context: LoadContext,
options: PluginOptions,
): Plugin<LoadedContent | null> {
const {siteConfig, siteDir, generatedFilesDir, localizationDir} = context;
const {siteConfig, siteDir, generatedFilesDir, localizationDir, i18n} =
context;
const contentPaths: PagesContentPaths = {
contentPath: path.resolve(siteDir, options.path),
@ -73,7 +77,7 @@ export default function pluginContentPages(
},
async loadContent() {
const {include} = options;
const {include, editUrl} = options;
if (!(await fs.pathExists(contentPaths.contentPath))) {
return null;
@ -120,6 +124,50 @@ export default function pluginContentPages(
});
const frontMatter = validatePageFrontMatter(unsafeFrontMatter);
const pagesDirPath = await getFolderContainingFile(
getContentPathList(contentPaths),
relativeSource,
);
const pagesSourceAbsolute = path.join(pagesDirPath, relativeSource);
function getPagesEditUrl() {
const pagesPathRelative = path.relative(
pagesDirPath,
path.resolve(pagesSourceAbsolute),
);
if (typeof editUrl === 'function') {
return editUrl({
pagesDirPath: posixPath(path.relative(siteDir, pagesDirPath)),
pagesPath: posixPath(pagesPathRelative),
permalink,
locale: i18n.currentLocale,
});
} else if (typeof editUrl === 'string') {
const isLocalized =
pagesDirPath === contentPaths.contentPathLocalized;
const fileContentPath =
isLocalized && options.editLocalizedFiles
? contentPaths.contentPathLocalized
: contentPaths.contentPath;
const contentPathEditUrl = normalizeUrl([
editUrl,
posixPath(path.relative(siteDir, fileContentPath)),
]);
return getEditUrl(pagesPathRelative, contentPathEditUrl);
}
return undefined;
}
const lastUpdatedData = await readLastUpdateData(
source,
options,
frontMatter.last_update,
);
if (isDraft({frontMatter})) {
return undefined;
}
@ -132,6 +180,9 @@ export default function pluginContentPages(
title: frontMatter.title ?? contentTitle,
description: frontMatter.description ?? excerpt,
frontMatter,
lastUpdatedBy: lastUpdatedData.lastUpdatedBy,
lastUpdatedAt: lastUpdatedData.lastUpdatedAt,
editUrl: getPagesEditUrl(),
unlisted,
};
}
@ -160,12 +211,12 @@ export default function pluginContentPages(
const {addRoute, createData} = actions;
function createPageRouteMetadata(metadata: Metadata): RouteMetadata {
const lastUpdatedAt =
metadata.type === 'mdx' ? metadata.lastUpdatedAt : undefined;
return {
sourceFilePath: aliasedSitePathToRelativePath(metadata.source),
// TODO add support for last updated date in the page plugin
// at least for Markdown files
// lastUpdatedAt: metadata.lastUpdatedAt,
lastUpdatedAt: undefined,
lastUpdatedAt,
};
}

View file

@ -11,6 +11,7 @@ import {
RehypePluginsSchema,
AdmonitionsSchema,
RouteBasePathSchema,
URISchema,
} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
import type {OptionValidationContext} from '@docusaurus/types';
@ -27,6 +28,9 @@ export const DEFAULT_OPTIONS: PluginOptions = {
beforeDefaultRehypePlugins: [],
beforeDefaultRemarkPlugins: [],
admonitions: true,
showLastUpdateTime: false,
showLastUpdateAuthor: false,
editLocalizedFiles: false,
};
const PluginOptionSchema = Joi.object<PluginOptions>({
@ -44,6 +48,12 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
DEFAULT_OPTIONS.beforeDefaultRemarkPlugins,
),
admonitions: AdmonitionsSchema.default(DEFAULT_OPTIONS.admonitions),
showLastUpdateTime: Joi.bool().default(DEFAULT_OPTIONS.showLastUpdateTime),
showLastUpdateAuthor: Joi.bool().default(
DEFAULT_OPTIONS.showLastUpdateAuthor,
),
editUrl: Joi.alternatives().try(URISchema, Joi.function()),
editLocalizedFiles: Joi.boolean().default(DEFAULT_OPTIONS.editLocalizedFiles),
});
export function validateOptions({

View file

@ -8,6 +8,7 @@
declare module '@docusaurus/plugin-content-pages' {
import type {MDXOptions} from '@docusaurus/mdx-loader';
import type {LoadContext, Plugin} from '@docusaurus/types';
import type {FrontMatterLastUpdate, LastUpdateData} from '@docusaurus/utils';
export type Assets = {
image?: string;
@ -20,6 +21,10 @@ declare module '@docusaurus/plugin-content-pages' {
include: string[];
exclude: string[];
mdxPageComponent: string;
showLastUpdateTime: boolean;
showLastUpdateAuthor: boolean;
editUrl?: string | EditUrlFunction;
editLocalizedFiles?: boolean;
};
export type Options = Partial<PluginOptions>;
@ -35,6 +40,7 @@ declare module '@docusaurus/plugin-content-pages' {
readonly toc_max_heading_level?: number;
readonly draft?: boolean;
readonly unlisted?: boolean;
readonly last_update?: FrontMatterLastUpdate;
};
export type JSXPageMetadata = {
@ -43,16 +49,31 @@ declare module '@docusaurus/plugin-content-pages' {
source: string;
};
export type MDXPageMetadata = {
export type MDXPageMetadata = LastUpdateData & {
type: 'mdx';
permalink: string;
source: string;
frontMatter: PageFrontMatter & {[key: string]: unknown};
editUrl?: string;
title?: string;
description?: string;
unlisted: boolean;
};
export type EditUrlFunction = (editUrlParams: {
/**
* The root content directory containing this post file, relative to the
* site path. Usually the same as `options.path` but can be localized
*/
pagesDirPath: string;
/** Path to this pages file, relative to `pagesDirPath`. */
pagesPath: string;
/** @see {@link PagesPostMetadata.permalink} */
permalink: string;
/** Locale name. */
locale: string;
}) => string | undefined;
export type Metadata = JSXPageMetadata | MDXPageMetadata;
export type LoadedContent = Metadata[];