Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 20 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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,
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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,
Expand All @@ -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",
},
]
`;
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
2 changes: 2 additions & 0 deletions packages/docusaurus-plugin-content-pages/src/frontMatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
FrontMatterTOCHeadingLevels,
ContentVisibilitySchema,
URISchema,
FrontMatterLastUpdateSchema,
} from '@docusaurus/utils-validation';
import type {PageFrontMatter} from '@docusaurus/plugin-content-pages';

Expand All @@ -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: {
Expand Down
63 changes: 57 additions & 6 deletions packages/docusaurus-plugin-content-pages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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),
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Comment on lines +134 to +163
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if we could find a way to deduplicate/extract that complexity to utils? 🤔


const lastUpdatedData = await readLastUpdateData(
source,
options,
frontMatter.last_update,
);

if (isDraft({frontMatter})) {
return undefined;
}
Expand All @@ -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,
};
}
Expand Down Expand Up @@ -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,
};
}

Expand Down
10 changes: 10 additions & 0 deletions packages/docusaurus-plugin-content-pages/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
RehypePluginsSchema,
AdmonitionsSchema,
RouteBasePathSchema,
URISchema,
} from '@docusaurus/utils-validation';
import {GlobExcludeDefault} from '@docusaurus/utils';
import type {OptionValidationContext} from '@docusaurus/types';
Expand All @@ -27,6 +28,9 @@ export const DEFAULT_OPTIONS: PluginOptions = {
beforeDefaultRehypePlugins: [],
beforeDefaultRemarkPlugins: [],
admonitions: true,
showLastUpdateTime: false,
showLastUpdateAuthor: false,
editLocalizedFiles: false,
};

const PluginOptionSchema = Joi.object<PluginOptions>({
Expand All @@ -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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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>;
Expand All @@ -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 = {
Expand All @@ -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[];
Expand Down