mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-28 14:08:21 +02:00
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:
parent
e4ecffe418
commit
d1590e37ac
12 changed files with 254 additions and 8 deletions
|
@ -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",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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[];
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue