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 ae568922d131..3cfe3b572485 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 @@ -32,6 +32,38 @@ exports[`atom filters to the first two entries 1`] = ` ] `; +exports[`atom filters to the first two entries using limit 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 + + <![CDATA[MDX Blog Sample with require calls]]> + https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post + + 2021-03-06T00:00:00.000Z + + Test MDX with require calls

]]>
+
+ + <![CDATA[Full Blog Sample]]> + https://docusaurus.io/myBaseUrl/blog/mdx-blog-post + + 2021-03-05T00:00:00.000Z + + HTML Heading 1

HTML Heading 2

HTML Paragraph

Import DOM

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
  • list1
  • list2
  • list3
  • list1
  • list2
  • list3

Normal Text Italics Text Bold Text

linkimage

]]>
+
+
", +] +`; + exports[`atom has feed item for each post 1`] = ` [ " @@ -147,6 +179,37 @@ exports[`json filters to the first two entries 1`] = ` ] `; +exports[`json filters to the first two entries using limit 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\\"image\\"

", + "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`] = ` [ "{ @@ -268,6 +331,40 @@ exports[`rss filters to the first two entries 1`] = ` ] `; +exports[`rss filters to the first two entries using limit 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 + + <![CDATA[MDX Blog Sample with require calls]]> + 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

]]>
+
+ + <![CDATA[Full Blog Sample]]> + 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
  • list1
  • list2
  • list3
  • list1
  • list2
  • list3

Normal Text Italics Text Bold Text

linkimage

]]>
+
+
+
", +] +`; + 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 6592c09d7aa5..f928296bf9df 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts @@ -195,4 +195,47 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => { ).toMatchSnapshot(); fsMock.mockClear(); }); + + it('filters to the first two entries using limit', 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', + limit: 2, + }, + 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/__tests__/options.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/options.test.ts index bb0f53c5f338..8133d10df1fb 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/options.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/options.test.ts @@ -46,7 +46,7 @@ describe('validateOptions', () => { }; expect(testValidate(userOptions)).toEqual({ ...userOptions, - feedOptions: {type: ['rss'], title: 'myTitle', copyright: ''}, + feedOptions: {type: ['rss'], title: 'myTitle', copyright: '', limit: 20}, }); }); @@ -58,7 +58,6 @@ describe('validateOptions', () => { beforeDefaultRehypePlugins: [markdownPluginsFunctionStub], remarkPlugins: [[markdownPluginsFunctionStub, {option1: '42'}]], rehypePlugins: [ - // @ts-expect-error: it seems to work in practice markdownPluginsObjectStub, [markdownPluginsFunctionStub, {option1: '42'}], ], @@ -95,7 +94,7 @@ describe('validateOptions', () => { }), ).toEqual({ ...defaultOptions, - feedOptions: {type: ['rss', 'atom', 'json'], copyright: ''}, + feedOptions: {type: ['rss', 'atom', 'json'], copyright: '', limit: 20}, }); }); @@ -106,7 +105,7 @@ describe('validateOptions', () => { }), ).toEqual({ ...defaultOptions, - feedOptions: {type: null}, + feedOptions: {type: null, limit: 20}, }); }); @@ -125,7 +124,12 @@ describe('validateOptions', () => { }), ).toEqual({ ...defaultOptions, - feedOptions: {type: ['rss', 'atom'], title: 'title', copyright: ''}, + feedOptions: { + type: ['rss', 'atom'], + title: 'title', + copyright: '', + limit: 20, + }, }); }); diff --git a/packages/docusaurus-plugin-content-blog/src/feed.ts b/packages/docusaurus-plugin-content-blog/src/feed.ts index dc9a7b85f84c..3cfa0f929716 100644 --- a/packages/docusaurus-plugin-content-blog/src/feed.ts +++ b/packages/docusaurus-plugin-content-blog/src/feed.ts @@ -42,7 +42,12 @@ async function generateBlogFeed({ const {url: siteUrl, baseUrl, title, favicon} = siteConfig; const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]); - const updated = blogPosts[0]?.metadata.date; + const blogPostsForFeed = + feedOptions.limit === false || feedOptions.limit === null + ? blogPosts + : blogPosts.slice(0, feedOptions.limit); + + const updated = blogPostsForFeed[0]?.metadata.date; const feed = new Feed({ id: blogBaseUrl, @@ -59,7 +64,7 @@ async function generateBlogFeed({ options.feedOptions.createFeedItems ?? defaultCreateFeedItems; const feedItems = await createFeedItems({ - blogPosts, + blogPosts: blogPostsForFeed, siteConfig, outDir, defaultCreateFeedItems, diff --git a/packages/docusaurus-plugin-content-blog/src/options.ts b/packages/docusaurus-plugin-content-blog/src/options.ts index 5d136d43993e..5b9889399a10 100644 --- a/packages/docusaurus-plugin-content-blog/src/options.ts +++ b/packages/docusaurus-plugin-content-blog/src/options.ts @@ -22,7 +22,7 @@ import type { import type {OptionValidationContext} from '@docusaurus/types'; export const DEFAULT_OPTIONS: PluginOptions = { - feedOptions: {type: ['rss', 'atom'], copyright: ''}, + feedOptions: {type: ['rss', 'atom'], copyright: '', limit: 20}, beforeDefaultRehypePlugins: [], beforeDefaultRemarkPlugins: [], admonitions: true, @@ -123,6 +123,9 @@ const PluginOptionSchema = Joi.object({ }), language: Joi.string(), createFeedItems: Joi.function(), + limit: Joi.alternatives() + .try(Joi.number(), Joi.valid(null), Joi.valid(false)) + .default(DEFAULT_OPTIONS.feedOptions.limit), }).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 bcce2e7c70ec..b1915f75196f 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 @@ -272,6 +272,8 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the language?: string; /** Allow control over the construction of BlogFeedItems */ createFeedItems?: CreateFeedItemsFn; + /** Limits the feed to the specified number of posts, false|null for all */ + limit?: number | false | null; }; type DefaultCreateFeedItemsParams = { diff --git a/website/docs/api/plugins/plugin-content-blog.mdx b/website/docs/api/plugins/plugin-content-blog.mdx index 52212f8b56c1..b640dcae105e 100644 --- a/website/docs/api/plugins/plugin-content-blog.mdx +++ b/website/docs/api/plugins/plugin-content-blog.mdx @@ -68,6 +68,7 @@ Accepted fields: | `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.limit` | `number \| null \| false` | `20` | Limits the feed to the specified number of posts, `false` or `null` for all entries. Defaults to `20`. | | `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. | diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index 742692f55570..3bd2e65875bb 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -520,6 +520,7 @@ type BlogOptions = { description?: string; copyright: string; language?: string; // possible values: http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes + limit?: number | false | null; // defaults to 20 /** Allow control over the construction of BlogFeedItems */ createFeedItems?: (params: { blogPosts: BlogPost[];