feat(blog): add blog pageBasePath plugin option (#9838)

Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
Liviu Ionescu 2024-02-13 14:27:23 +02:00 committed by GitHub
parent cc7f43580c
commit 70ba9d2d01
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 113 additions and 21 deletions

View file

@ -23,7 +23,32 @@ title: This post links to another one!
[Linked post](/blog/2018/12/14/Happy-First-Birthday-Slash)" [Linked post](/blog/2018/12/14/Happy-First-Birthday-Slash)"
`; `;
exports[`paginateBlogPosts generates right pages 1`] = ` exports[`paginateBlogPosts generates a single page 1`] = `
[
{
"items": [
"post1",
"post2",
"post3",
"post4",
"post5",
],
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": undefined,
"page": 1,
"permalink": "/",
"postsPerPage": 10,
"previousPage": undefined,
"totalCount": 5,
"totalPages": 1,
},
},
]
`;
exports[`paginateBlogPosts generates pages 1`] = `
[ [
{ {
"items": [ "items": [
@ -78,7 +103,7 @@ exports[`paginateBlogPosts generates right pages 1`] = `
] ]
`; `;
exports[`paginateBlogPosts generates right pages 2`] = ` exports[`paginateBlogPosts generates pages at blog root 1`] = `
[ [
{ {
"items": [ "items": [
@ -133,26 +158,56 @@ exports[`paginateBlogPosts generates right pages 2`] = `
] ]
`; `;
exports[`paginateBlogPosts generates right pages 3`] = ` exports[`paginateBlogPosts generates pages with custom pageBasePath 1`] = `
[ [
{ {
"items": [ "items": [
"post1", "post1",
"post2", "post2",
],
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": "/blog/customPageBasePath/2",
"page": 1,
"permalink": "/blog",
"postsPerPage": 2,
"previousPage": undefined,
"totalCount": 5,
"totalPages": 3,
},
},
{
"items": [
"post3", "post3",
"post4", "post4",
],
"metadata": {
"blogDescription": "Blog Description",
"blogTitle": "Blog Title",
"nextPage": "/blog/customPageBasePath/3",
"page": 2,
"permalink": "/blog/customPageBasePath/2",
"postsPerPage": 2,
"previousPage": "/blog",
"totalCount": 5,
"totalPages": 3,
},
},
{
"items": [
"post5", "post5",
], ],
"metadata": { "metadata": {
"blogDescription": "Blog Description", "blogDescription": "Blog Description",
"blogTitle": "Blog Title", "blogTitle": "Blog Title",
"nextPage": undefined, "nextPage": undefined,
"page": 1, "page": 3,
"permalink": "/", "permalink": "/blog/customPageBasePath/3",
"postsPerPage": 10, "postsPerPage": 2,
"previousPage": undefined, "previousPage": "/blog/customPageBasePath/2",
"totalCount": 5, "totalCount": 5,
"totalPages": 1, "totalPages": 3,
}, },
}, },
] ]

View file

@ -38,7 +38,6 @@ describe('truncate', () => {
}); });
describe('paginateBlogPosts', () => { describe('paginateBlogPosts', () => {
it('generates right pages', () => {
const blogPosts = [ const blogPosts = [
{id: 'post1', metadata: {}, content: 'Foo 1'}, {id: 'post1', metadata: {}, content: 'Foo 1'},
{id: 'post2', metadata: {}, content: 'Foo 2'}, {id: 'post2', metadata: {}, content: 'Foo 2'},
@ -46,6 +45,8 @@ describe('paginateBlogPosts', () => {
{id: 'post4', metadata: {}, content: 'Foo 4'}, {id: 'post4', metadata: {}, content: 'Foo 4'},
{id: 'post5', metadata: {}, content: 'Foo 5'}, {id: 'post5', metadata: {}, content: 'Foo 5'},
] as BlogPost[]; ] as BlogPost[];
it('generates pages', () => {
expect( expect(
paginateBlogPosts({ paginateBlogPosts({
blogPosts, blogPosts,
@ -53,8 +54,12 @@ describe('paginateBlogPosts', () => {
blogTitle: 'Blog Title', blogTitle: 'Blog Title',
blogDescription: 'Blog Description', blogDescription: 'Blog Description',
postsPerPageOption: 2, postsPerPageOption: 2,
pageBasePath: 'page',
}), }),
).toMatchSnapshot(); ).toMatchSnapshot();
});
it('generates pages at blog root', () => {
expect( expect(
paginateBlogPosts({ paginateBlogPosts({
blogPosts, blogPosts,
@ -62,8 +67,12 @@ describe('paginateBlogPosts', () => {
blogTitle: 'Blog Title', blogTitle: 'Blog Title',
blogDescription: 'Blog Description', blogDescription: 'Blog Description',
postsPerPageOption: 2, postsPerPageOption: 2,
pageBasePath: 'page',
}), }),
).toMatchSnapshot(); ).toMatchSnapshot();
});
it('generates a single page', () => {
expect( expect(
paginateBlogPosts({ paginateBlogPosts({
blogPosts, blogPosts,
@ -71,6 +80,20 @@ describe('paginateBlogPosts', () => {
blogTitle: 'Blog Title', blogTitle: 'Blog Title',
blogDescription: 'Blog Description', blogDescription: 'Blog Description',
postsPerPageOption: 10, postsPerPageOption: 10,
pageBasePath: 'page',
}),
).toMatchSnapshot();
});
it('generates pages with custom pageBasePath', () => {
expect(
paginateBlogPosts({
blogPosts,
basePageUrl: '/blog',
blogTitle: 'Blog Title',
blogDescription: 'Blog Description',
postsPerPageOption: 2,
pageBasePath: 'customPageBasePath',
}), }),
).toMatchSnapshot(); ).toMatchSnapshot();
}); });

View file

@ -57,12 +57,14 @@ export function paginateBlogPosts({
blogTitle, blogTitle,
blogDescription, blogDescription,
postsPerPageOption, postsPerPageOption,
pageBasePath,
}: { }: {
blogPosts: BlogPost[]; blogPosts: BlogPost[];
basePageUrl: string; basePageUrl: string;
blogTitle: string; blogTitle: string;
blogDescription: string; blogDescription: string;
postsPerPageOption: number | 'ALL'; postsPerPageOption: number | 'ALL';
pageBasePath: string;
}): BlogPaginated[] { }): BlogPaginated[] {
const totalCount = blogPosts.length; const totalCount = blogPosts.length;
const postsPerPage = const postsPerPage =
@ -73,7 +75,7 @@ export function paginateBlogPosts({
function permalink(page: number) { function permalink(page: number) {
return page > 0 return page > 0
? normalizeUrl([basePageUrl, `page/${page + 1}`]) ? normalizeUrl([basePageUrl, pageBasePath, `${page + 1}`])
: basePageUrl; : basePageUrl;
} }
@ -111,6 +113,7 @@ export function getBlogTags({
blogTitle: string; blogTitle: string;
blogDescription: string; blogDescription: string;
postsPerPageOption: number | 'ALL'; postsPerPageOption: number | 'ALL';
pageBasePath: string;
}): BlogTags { }): BlogTags {
const groups = groupTaggedItems( const groups = groupTaggedItems(
blogPosts, blogPosts,

View file

@ -107,6 +107,7 @@ export default async function pluginContentBlog(
blogDescription, blogDescription,
blogTitle, blogTitle,
blogSidebarTitle, blogSidebarTitle,
pageBasePath,
} = options; } = options;
const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]); const baseBlogUrl = normalizeUrl([baseUrl, routeBasePath]);
@ -121,11 +122,10 @@ export default async function pluginContentBlog(
blogListPaginated: [], blogListPaginated: [],
blogTags: {}, blogTags: {},
blogTagsListPath, blogTagsListPath,
blogTagsPaginated: [],
}; };
} }
// Colocate next and prev metadata. // Collocate next and prev metadata.
listedBlogPosts.forEach((blogPost, index) => { listedBlogPosts.forEach((blogPost, index) => {
const prevItem = index > 0 ? listedBlogPosts[index - 1] : null; const prevItem = index > 0 ? listedBlogPosts[index - 1] : null;
if (prevItem) { if (prevItem) {
@ -153,6 +153,7 @@ export default async function pluginContentBlog(
blogDescription, blogDescription,
postsPerPageOption, postsPerPageOption,
basePageUrl: baseBlogUrl, basePageUrl: baseBlogUrl,
pageBasePath,
}); });
const blogTags: BlogTags = getBlogTags({ const blogTags: BlogTags = getBlogTags({
@ -160,6 +161,7 @@ export default async function pluginContentBlog(
postsPerPageOption, postsPerPageOption,
blogDescription, blogDescription,
blogTitle, blogTitle,
pageBasePath,
}); });
return { return {

View file

@ -45,6 +45,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
routeBasePath: 'blog', routeBasePath: 'blog',
tagsBasePath: 'tags', tagsBasePath: 'tags',
archiveBasePath: 'archive', archiveBasePath: 'archive',
pageBasePath: 'page',
path: 'blog', path: 'blog',
editLocalizedFiles: false, editLocalizedFiles: false,
authorsMapPath: 'authors.yml', authorsMapPath: 'authors.yml',
@ -59,6 +60,7 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
.allow(null), .allow(null),
routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath), routeBasePath: RouteBasePathSchema.default(DEFAULT_OPTIONS.routeBasePath),
tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath), tagsBasePath: Joi.string().default(DEFAULT_OPTIONS.tagsBasePath),
pageBasePath: Joi.string().default(DEFAULT_OPTIONS.pageBasePath),
include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include), include: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.include),
exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude), exclude: Joi.array().items(Joi.string()).default(DEFAULT_OPTIONS.exclude),
postsPerPage: Joi.alternatives() postsPerPage: Joi.alternatives()

View file

@ -351,9 +351,14 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
routeBasePath: string; routeBasePath: string;
/** /**
* URL route for the tags section of your blog. Will be appended to * URL route for the tags section of your blog. Will be appended to
* `routeBasePath`. **DO NOT** include a trailing slash. * `routeBasePath`.
*/ */
tagsBasePath: string; tagsBasePath: string;
/**
* URL route for the pages section of your blog. Will be appended to
* `routeBasePath`.
*/
pageBasePath: string;
/** /**
* URL route for the archive section of your blog. Will be appended to * URL route for the archive section of your blog. Will be appended to
* `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to * `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to

View file

@ -53,6 +53,7 @@ The component used for Markdown pages is `@theme/MDXPage`. React pages are direc
The blog creates the following routes: The blog creates the following routes:
- **Posts list pages**: `/`, `/page/2`, `/page/3`... - **Posts list pages**: `/`, `/page/2`, `/page/3`...
- The route is customizable through the `pageBasePath` option.
- The component is `@theme/BlogListPage`. - The component is `@theme/BlogListPage`.
- **Post pages**: `/2021/11/21/algolia-docsearch-migration`, `/2021/05/12/announcing-docusaurus-two-beta`... - **Post pages**: `/2021/11/21/algolia-docsearch-migration`, `/2021/05/12/announcing-docusaurus-two-beta`...
- Generated from each Markdown post. - Generated from each Markdown post.

View file

@ -47,7 +47,8 @@ Accepted fields:
| `blogSidebarCount` | <code>number \| 'ALL'</code> | `5` | Number of blog post elements to show in the blog sidebar. `'ALL'` to show all blog posts; `0` to disable. | | `blogSidebarCount` | <code>number \| 'ALL'</code> | `5` | Number of blog post elements to show in the blog sidebar. `'ALL'` to show all blog posts; `0` to disable. |
| `blogSidebarTitle` | `string` | `'Recent posts'` | Title of the blog sidebar. | | `blogSidebarTitle` | `string` | `'Recent posts'` | Title of the blog sidebar. |
| `routeBasePath` | `string` | `'blog'` | URL route for the blog section of your site. **DO NOT** include a trailing slash. Use `/` to put the blog at root path. | | `routeBasePath` | `string` | `'blog'` | URL route for the blog section of your site. **DO NOT** include a trailing slash. Use `/` to put the blog at root path. |
| `tagsBasePath` | `string` | `'tags'` | URL route for the tags section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. | | `tagsBasePath` | `string` | `'tags'` | URL route for the tags section of your blog. Will be appended to `routeBasePath`. |
| `pageBasePath` | `string` | `'page'` | URL route for the pages section of your blog. Will be appended to `routeBasePath`. |
| `archiveBasePath` | <code>string \| null</code> | `'archive'` | URL route for the archive section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to disable generation of archive. | | `archiveBasePath` | <code>string \| null</code> | `'archive'` | URL route for the archive section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to disable generation of archive. |
| `include` | `string[]` | `['**/*.{md,mdx}']` | Array of glob patterns matching Markdown files to be built, relative to the content path. | | `include` | `string[]` | `['**/*.{md,mdx}']` | Array of glob patterns matching Markdown files to be built, relative to the content path. |
| `exclude` | `string[]` | _See example configuration_ | Array of glob patterns matching Markdown files to be excluded. Serves as refinement based on the `include` option. | | `exclude` | `string[]` | _See example configuration_ | Array of glob patterns matching Markdown files to be excluded. Serves as refinement based on the `include` option. |