diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/feed.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/feed.test.ts.snap
index b15b033406..ae568922d1 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/feed.test.ts.snap
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/feed.test.ts.snap
@@ -1,5 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`atom filters to the first two entries 1`] = `
+[
+ "
+
+ https://docusaurus.io/myBaseUrl/blog
+ Hello Blog
+ 2021-03-06T00:00:00.000Z
+ https://github.com/jpmonette/feed
+
+ Hello Blog
+ https://docusaurus.io/myBaseUrl/image/favicon.ico
+ Copyright
+
+
+ https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post
+
+ 2021-03-06T00:00:00.000Z
+
+ Test MDX with require calls


]]>
+
+
+
+ https://docusaurus.io/myBaseUrl/blog/mdx-blog-post
+
+ 2021-03-05T00:00:00.000Z
+
+ HTML Heading 1HTML Heading 2
HTML Paragraph
Import DOM
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Normal Text Italics Text Bold Text
link
]]>
+
+",
+]
+`;
+
exports[`atom has feed item for each post 1`] = `
[
"
@@ -84,6 +116,37 @@ exports[`atom has feed item for each post 1`] = `
]
`;
+exports[`json filters to the first two entries 1`] = `
+[
+ "{
+ "version": "https://jsonfeed.org/version/1",
+ "title": "Hello Blog",
+ "home_page_url": "https://docusaurus.io/myBaseUrl/blog",
+ "description": "Hello Blog",
+ "items": [
+ {
+ "id": "https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post",
+ "content_html": "Test MDX with require calls


",
+ "url": "https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post",
+ "title": "MDX Blog Sample with require calls",
+ "summary": "Test MDX with require calls",
+ "date_modified": "2021-03-06T00:00:00.000Z",
+ "tags": []
+ },
+ {
+ "id": "https://docusaurus.io/myBaseUrl/blog/mdx-blog-post",
+ "content_html": "HTML Heading 1
HTML Heading 2
HTML Paragraph
Import DOM
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Normal Text Italics Text Bold Text
link
",
+ "url": "https://docusaurus.io/myBaseUrl/blog/mdx-blog-post",
+ "title": "Full Blog Sample",
+ "summary": "HTML Heading 1",
+ "date_modified": "2021-03-05T00:00:00.000Z",
+ "tags": []
+ }
+ ]
+}",
+]
+`;
+
exports[`json has feed item for each post 1`] = `
[
"{
@@ -171,6 +234,40 @@ exports[`json has feed item for each post 1`] = `
]
`;
+exports[`rss filters to the first two entries 1`] = `
+[
+ "
+
+
+ Hello Blog
+ https://docusaurus.io/myBaseUrl/blog
+ Hello Blog
+ Sat, 06 Mar 2021 00:00:00 GMT
+ https://validator.w3.org/feed/docs/rss2.html
+ https://github.com/jpmonette/feed
+ en
+ Copyright
+ -
+
+ https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post
+ https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post
+ Sat, 06 Mar 2021 00:00:00 GMT
+
+ Test MDX with require calls


]]>
+
+ -
+
+ https://docusaurus.io/myBaseUrl/blog/mdx-blog-post
+ https://docusaurus.io/myBaseUrl/blog/mdx-blog-post
+ Fri, 05 Mar 2021 00:00:00 GMT
+
+ HTML Heading 1
HTML Heading 2
HTML Paragraph
Import DOM
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Normal Text Italics Text Bold Text
link
]]>
+
+
+",
+]
+`;
+
exports[`rss has feed item for each post 1`] = `
[
"
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
index 83d0af7715..6592c09d7a 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
@@ -143,4 +143,56 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
).toMatchSnapshot();
fsMock.mockClear();
});
+
+ it('filters to the first two entries', async () => {
+ const siteDir = path.join(__dirname, '__fixtures__', 'website');
+ const outDir = path.join(siteDir, 'build-snap');
+ const siteConfig = {
+ title: 'Hello',
+ baseUrl: '/myBaseUrl/',
+ url: 'https://docusaurus.io',
+ favicon: 'image/favicon.ico',
+ };
+
+ // Build is quite difficult to mock, so we built the blog beforehand and
+ // copied the output to the fixture...
+ await testGenerateFeeds(
+ {
+ siteDir,
+ siteConfig,
+ i18n: DefaultI18N,
+ outDir,
+ } as LoadContext,
+ {
+ path: 'blog',
+ routeBasePath: 'blog',
+ tagsBasePath: 'tags',
+ authorsMapPath: 'authors.yml',
+ include: DEFAULT_OPTIONS.include,
+ exclude: DEFAULT_OPTIONS.exclude,
+ feedOptions: {
+ type: [feedType],
+ copyright: 'Copyright',
+ createFeedItems: async (params) => {
+ const {blogPosts, defaultCreateFeedItems, ...rest} = params;
+ const blogPostsFiltered = blogPosts.filter(
+ (item, index) => index < 2,
+ );
+ return defaultCreateFeedItems({
+ blogPosts: blogPostsFiltered,
+ ...rest,
+ });
+ },
+ },
+ readingTime: ({content, defaultReadingTime}) =>
+ defaultReadingTime({content}),
+ truncateMarker: //,
+ } as PluginOptions,
+ );
+
+ expect(
+ fsMock.mock.calls.map((call) => call[1] as string),
+ ).toMatchSnapshot();
+ fsMock.mockClear();
+ });
});
diff --git a/packages/docusaurus-plugin-content-blog/src/feed.ts b/packages/docusaurus-plugin-content-blog/src/feed.ts
index ea73e7a6a5..dc9a7b85f8 100644
--- a/packages/docusaurus-plugin-content-blog/src/feed.ts
+++ b/packages/docusaurus-plugin-content-blog/src/feed.ts
@@ -8,7 +8,7 @@
import path from 'path';
import fs from 'fs-extra';
import logger from '@docusaurus/logger';
-import {Feed, type Author as FeedAuthor, type Item as FeedItem} from 'feed';
+import {Feed, type Author as FeedAuthor} from 'feed';
import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils';
import {blogPostContainerID} from '@docusaurus/utils-common';
import {load as cheerioLoad} from 'cheerio';
@@ -18,6 +18,7 @@ import type {
PluginOptions,
Author,
BlogPost,
+ BlogFeedItem,
} from '@docusaurus/plugin-content-blog';
async function generateBlogFeed({
@@ -54,11 +55,37 @@ async function generateBlogFeed({
copyright: feedOptions.copyright,
});
+ const createFeedItems =
+ options.feedOptions.createFeedItems ?? defaultCreateFeedItems;
+
+ const feedItems = await createFeedItems({
+ blogPosts,
+ siteConfig,
+ outDir,
+ defaultCreateFeedItems,
+ });
+
+ feedItems.forEach(feed.addItem);
+
+ return feed;
+}
+
+async function defaultCreateFeedItems({
+ blogPosts,
+ siteConfig,
+ outDir,
+}: {
+ blogPosts: BlogPost[];
+ siteConfig: DocusaurusConfig;
+ outDir: string;
+}): Promise {
+ const {url: siteUrl} = siteConfig;
+
function toFeedAuthor(author: Author): FeedAuthor {
return {name: author.name, link: author.url, email: author.email};
}
- await Promise.all(
+ return Promise.all(
blogPosts.map(async (post) => {
const {
metadata: {
@@ -79,7 +106,7 @@ async function generateBlogFeed({
const $ = cheerioLoad(content);
const link = normalizeUrl([siteUrl, permalink]);
- const feedItem: FeedItem = {
+ const feedItem: BlogFeedItem = {
title: metadataTitle,
id: link,
link,
@@ -99,9 +126,7 @@ async function generateBlogFeed({
return feedItem;
}),
- ).then((items) => items.forEach(feed.addItem));
-
- return feed;
+ );
}
async function createBlogFeedFile({
diff --git a/packages/docusaurus-plugin-content-blog/src/options.ts b/packages/docusaurus-plugin-content-blog/src/options.ts
index 804169f733..0ee9e50f8f 100644
--- a/packages/docusaurus-plugin-content-blog/src/options.ts
+++ b/packages/docusaurus-plugin-content-blog/src/options.ts
@@ -122,6 +122,7 @@ const PluginOptionSchema = Joi.object({
.default(DEFAULT_OPTIONS.feedOptions.copyright),
}),
language: Joi.string(),
+ createFeedItems: Joi.function(),
}).default(DEFAULT_OPTIONS.feedOptions),
authorsMapPath: Joi.string().default(DEFAULT_OPTIONS.authorsMapPath),
readingTime: Joi.function().default(() => DEFAULT_OPTIONS.readingTime),
diff --git a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts
index 96c288d335..bcce2e7c70 100644
--- a/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts
+++ b/packages/docusaurus-plugin-content-blog/src/plugin-content-blog.d.ts
@@ -9,12 +9,19 @@ declare module '@docusaurus/plugin-content-blog' {
import type {LoadedMDXContent} from '@docusaurus/mdx-loader';
import type {MDXOptions} from '@docusaurus/mdx-loader';
import type {FrontMatterTag, Tag} from '@docusaurus/utils';
- import type {Plugin, LoadContext} from '@docusaurus/types';
+ import type {DocusaurusConfig, Plugin, LoadContext} from '@docusaurus/types';
+ import type {Item as FeedItem} from 'feed';
import type {Overwrite} from 'utility-types';
export type Assets = {
/**
- * If `metadata.image` is a collocated image path, this entry will be the
+ * If `metadata.yarn workspace website typecheck
+4
+yarn workspace v1.22.19yarn workspace website typecheck
+4
+yarn workspace v1.22.19yarn workspace website typecheck
+4
+yarn workspace v1.22.19image` is a collocated image path, this entry will be the
* bundler-generated image path. Otherwise, it's empty, and the image URL
* should be accessed through `frontMatter.image`.
*/
@@ -263,6 +270,24 @@ declare module '@docusaurus/plugin-content-blog' {
copyright: string;
/** Language of the feed. */
language?: string;
+ /** Allow control over the construction of BlogFeedItems */
+ createFeedItems?: CreateFeedItemsFn;
+ };
+
+ type DefaultCreateFeedItemsParams = {
+ blogPosts: BlogPost[];
+ siteConfig: DocusaurusConfig;
+ outDir: string;
+ };
+
+ type CreateFeedItemsFn = (
+ params: CreateFeedItemsParams,
+ ) => Promise;
+
+ type CreateFeedItemsParams = DefaultCreateFeedItemsParams & {
+ defaultCreateFeedItems: (
+ params: DefaultCreateFeedItemsParams,
+ ) => Promise;
};
/**
@@ -451,6 +476,8 @@ declare module '@docusaurus/plugin-content-blog' {
content: string;
};
+ export type BlogFeedItem = FeedItem;
+
export type BlogPaginatedMetadata = {
/** Title of the entire blog. */
readonly blogTitle: string;
diff --git a/website/docs/api/plugins/plugin-content-blog.md b/website/docs/api/plugins/plugin-content-blog.md
index d31d00c00b..4e1ab5fcd0 100644
--- a/website/docs/api/plugins/plugin-content-blog.md
+++ b/website/docs/api/plugins/plugin-content-blog.md
@@ -67,6 +67,7 @@ Accepted fields:
| `authorsMapPath` | `string` | `'authors.yml'` | Path to the authors map file, relative to the blog content directory. |
| `feedOptions` | _See below_ | `{type: ['rss', 'atom']}` | Blog feed. |
| `feedOptions.type` | FeedType \| FeedType[] \| 'all' \| null
| **Required** | Type of feed to be generated. Use `null` to disable generation. |
+| `feedOptions.createFeedItems` | CreateFeedItemsFn \| undefined
| `undefined` | An optional function which can be used to transform and / or filter the items in the feed. |
| `feedOptions.title` | `string` | `siteConfig.title` | Title of the feed. |
| `feedOptions.description` | `string` | \`${siteConfig.title} Blog\`
| Description of the feed. |
| `feedOptions.copyright` | `string` | `undefined` | Copyright message. |
@@ -117,6 +118,17 @@ type ReadingTimeFn = (params: {
type FeedType = 'rss' | 'atom' | 'json';
```
+#### `CreateFeedItemsFn` {#CreateFeedItemsFn}
+
+```ts
+type CreateFeedItemsFn = (params: {
+ blogPosts: BlogPost[];
+ siteConfig: DocusaurusConfig;
+ outDir: string;
+ defaultCreateFeedItemsFn: CreateFeedItemsFn;
+}) => Promise;
+```
+
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
@@ -168,6 +180,14 @@ const config = {
description: '',
copyright: '',
language: undefined,
+ createFeedItems: async (params) => {
+ const {blogPosts, defaultCreateFeedItems, ...rest} = params;
+ return defaultCreateFeedItems({
+ // keep only the 10 most recent blog posts in the feed
+ blogPosts: blogPosts.filter((item, index) => index < 10),
+ ...rest,
+ });
+ },
},
};
```
diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx
index 780a07dbca..fc5fa93506 100644
--- a/website/docs/blog.mdx
+++ b/website/docs/blog.mdx
@@ -511,6 +511,17 @@ type BlogOptions = {
description?: string;
copyright: string;
language?: string; // possible values: http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
+ /** Allow control over the construction of BlogFeedItems */
+ createFeedItems?: (params: {
+ blogPosts: BlogPost[];
+ siteConfig: DocusaurusConfig;
+ outDir: string;
+ defaultCreateFeedItems: (params: {
+ blogPosts: BlogPost[];
+ siteConfig: DocusaurusConfig;
+ outDir: string;
+ }) => Promise;
+ }) => Promise;
};
};
```
@@ -529,6 +540,14 @@ module.exports = {
feedOptions: {
type: 'all',
copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`,
+ createFeedItems: async (params) => {
+ const {blogPosts, defaultCreateFeedItems, ...rest} = params;
+ return defaultCreateFeedItems({
+ // keep only the 10 most recent blog posts in the feed
+ blogPosts: blogPosts.filter((item, index) => index < 10),
+ ...rest,
+ });
+ },
},
// highlight-end
},