diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/mdx-require-blog-post.mdx b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/mdx-require-blog-post.mdx
new file mode 100644
index 0000000000..5ff703b4cb
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog/mdx-require-blog-post.mdx
@@ -0,0 +1,14 @@
+---
+title: MDX Blog Sample with require calls
+date: 2021-03-06
+---
+
+Test MDX with require calls
+
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+
+
+
+
+
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/static/img/docusaurus.png b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/static/img/docusaurus.png
new file mode 100644
index 0000000000..f458149e3c
Binary files /dev/null and b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/static/img/docusaurus.png differ
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/feed.test.ts.snap
similarity index 90%
rename from packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap
rename to packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/feed.test.ts.snap
index 8914514ceb..8e600fd89a 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/feed.test.ts.snap
@@ -1,18 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`blogFeed atom should not show feed without posts 1`] = `null`;
-
exports[`blogFeed atom shows feed item for each post 1`] = `
"
https://docusaurus.io/myBaseUrl/blog
Hello Blog
- 2021-03-05T00:00:00.000Z
+ 2021-03-06T00:00:00.000Z
https://github.com/jpmonette/feed
Hello Blog
https://docusaurus.io/myBaseUrl/image/favicon.ico
Copyright
+
+
+ MDX Blog Sample with require calls
+
+ 2021-03-06T00:00:00.000Z
+
+
Full Blog Sample
@@ -81,8 +86,6 @@ exports[`blogFeed atom shows feed item for each post 1`] = `
"
`;
-exports[`blogFeed rss should not show feed without posts 1`] = `null`;
-
exports[`blogFeed rss shows feed item for each post 1`] = `
"
@@ -90,10 +93,17 @@ exports[`blogFeed rss shows feed item for each post 1`] = `
Hello Blog
https://docusaurus.io/myBaseUrl/blog
Hello Blog
- Fri, 05 Mar 2021 00:00:00 GMT
+ Sat, 06 Mar 2021 00:00:00 GMT
https://validator.w3.org/feed/docs/rss2.html
https://github.com/jpmonette/feed
Copyright
+ -
+
+ https://docusaurus.io/myBaseUrl/blog/mdx-require-blog-post
+ MDX Blog Sample with require calls
+ Sat, 06 Mar 2021 00:00:00 GMT
+
+
-
https://docusaurus.io/myBaseUrl/blog/mdx-blog-post
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/generateBlogFeed.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
similarity index 82%
rename from packages/docusaurus-plugin-content-blog/src/__tests__/generateBlogFeed.test.ts
rename to packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
index c9cc678e7b..d2261547a7 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/generateBlogFeed.test.ts
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/feed.test.ts
@@ -6,10 +6,12 @@
*/
import path from 'path';
-import {generateBlogFeed} from '../blogUtils';
+import {generateBlogFeed} from '../feed';
import {LoadContext, I18n} from '@docusaurus/types';
import {PluginOptions, BlogContentPaths} from '../types';
import {DEFAULT_OPTIONS} from '../pluginOptionSchema';
+import {generateBlogPosts} from '../blogUtils';
+import {Feed} from 'feed';
const DefaultI18N: I18n = {
currentLocale: 'en',
@@ -30,6 +32,23 @@ function getBlogContentPaths(siteDir: string): BlogContentPaths {
};
}
+async function testGenerateFeeds(
+ context: LoadContext,
+ options: PluginOptions,
+): Promise {
+ const blogPosts = await generateBlogPosts(
+ getBlogContentPaths(context.siteDir),
+ context,
+ options,
+ );
+
+ return generateBlogFeed({
+ blogPosts,
+ options,
+ siteConfig: context.siteConfig,
+ });
+}
+
describe('blogFeed', () => {
(['atom', 'rss'] as const).forEach((feedType) => {
describe(`${feedType}`, () => {
@@ -42,8 +61,7 @@ describe('blogFeed', () => {
favicon: 'image/favicon.ico',
};
- const feed = await generateBlogFeed(
- getBlogContentPaths(siteDir),
+ const feed = await testGenerateFeeds(
{
siteDir,
siteConfig,
@@ -61,9 +79,8 @@ describe('blogFeed', () => {
},
} as PluginOptions,
);
- const feedContent =
- feed && (feedType === 'rss' ? feed.rss2() : feed.atom1());
- expect(feedContent).toMatchSnapshot();
+
+ expect(feed).toEqual(null);
});
test('shows feed item for each post', async () => {
@@ -76,8 +93,7 @@ describe('blogFeed', () => {
favicon: 'image/favicon.ico',
};
- const feed = await generateBlogFeed(
- getBlogContentPaths(siteDir),
+ const feed = await testGenerateFeeds(
{
siteDir,
siteConfig,
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts
index 238b4f04fe..0aa725b0bf 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/index.test.ts
@@ -246,26 +246,29 @@ describe('loadBlog', () => {
test('simple website blog dates localized', async () => {
const siteDir = path.join(__dirname, '__fixtures__', 'website');
const blogPostsFrench = await getBlogPosts(siteDir, {}, getI18n('fr'));
- expect(blogPostsFrench).toHaveLength(7);
+ expect(blogPostsFrench).toHaveLength(8);
expect(blogPostsFrench[0].metadata.formattedDate).toMatchInlineSnapshot(
- `"5 mars 2021"`,
+ `"6 mars 2021"`,
);
expect(blogPostsFrench[1].metadata.formattedDate).toMatchInlineSnapshot(
- `"16 août 2020"`,
+ `"5 mars 2021"`,
);
expect(blogPostsFrench[2].metadata.formattedDate).toMatchInlineSnapshot(
- `"15 août 2020"`,
+ `"16 août 2020"`,
);
expect(blogPostsFrench[3].metadata.formattedDate).toMatchInlineSnapshot(
- `"27 février 2020"`,
+ `"15 août 2020"`,
);
expect(blogPostsFrench[4].metadata.formattedDate).toMatchInlineSnapshot(
- `"2 janvier 2019"`,
+ `"27 février 2020"`,
);
expect(blogPostsFrench[5].metadata.formattedDate).toMatchInlineSnapshot(
- `"1 janvier 2019"`,
+ `"2 janvier 2019"`,
);
expect(blogPostsFrench[6].metadata.formattedDate).toMatchInlineSnapshot(
+ `"1 janvier 2019"`,
+ );
+ expect(blogPostsFrench[7].metadata.formattedDate).toMatchInlineSnapshot(
`"14 décembre 2018"`,
);
});
@@ -295,7 +298,8 @@ describe('loadBlog', () => {
expect(blogPost.metadata.editUrl).toEqual(hardcodedEditUrl);
});
- expect(editUrlFunction).toHaveBeenCalledTimes(7);
+ expect(editUrlFunction).toHaveBeenCalledTimes(8);
+
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
blogPath: 'date-matter.md',
@@ -314,6 +318,12 @@ describe('loadBlog', () => {
permalink: '/blog/mdx-blog-post',
locale: 'en',
});
+ expect(editUrlFunction).toHaveBeenCalledWith({
+ blogDirPath: 'blog',
+ blogPath: 'mdx-require-blog-post.mdx',
+ permalink: '/blog/mdx-require-blog-post',
+ locale: 'en',
+ });
expect(editUrlFunction).toHaveBeenCalledWith({
blogDirPath: 'blog',
blogPath: 'complex-slug.md',
diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
index 9465103030..011eb4fa6a 100644
--- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
+++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
@@ -9,7 +9,6 @@ import fs from 'fs-extra';
import chalk from 'chalk';
import path from 'path';
import readingTime from 'reading-time';
-import {Feed, Author as FeedAuthor} from 'feed';
import {compact, keyBy, mapValues} from 'lodash';
import {
PluginOptions,
@@ -17,7 +16,6 @@ import {
BlogContentPaths,
BlogMarkdownLoaderOptions,
BlogTags,
- Author,
} from './types';
import {
parseMarkdownFile,
@@ -26,7 +24,6 @@ import {
getEditUrl,
getFolderContainingFile,
posixPath,
- mdxToHtml,
replaceMarkdownLinks,
Globby,
normalizeFrontMatterTags,
@@ -104,66 +101,6 @@ function formatBlogPostDate(locale: string, date: Date): string {
}
}
-export async function generateBlogFeed(
- contentPaths: BlogContentPaths,
- context: LoadContext,
- options: PluginOptions,
-): Promise {
- if (!options.feedOptions) {
- throw new Error(
- 'Invalid options: "feedOptions" is not expected to be null.',
- );
- }
- const {siteConfig} = context;
- const blogPosts = await generateBlogPosts(contentPaths, context, options);
- if (!blogPosts.length) {
- return null;
- }
-
- const {feedOptions, routeBasePath} = options;
- const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
- const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
-
- const updated =
- (blogPosts[0] && blogPosts[0].metadata.date) ||
- new Date('2015-10-25T16:29:00.000-07:00');
-
- const feed = new Feed({
- id: blogBaseUrl,
- title: feedOptions.title || `${title} Blog`,
- updated,
- language: feedOptions.language,
- link: blogBaseUrl,
- description: feedOptions.description || `${siteConfig.title} Blog`,
- favicon: favicon ? normalizeUrl([siteUrl, baseUrl, favicon]) : undefined,
- copyright: feedOptions.copyright,
- });
-
- function toFeedAuthor(author: Author): FeedAuthor {
- // TODO ask author emails?
- // RSS feed requires email to render authors
- return {name: author.name, link: author.url};
- }
-
- blogPosts.forEach((post) => {
- const {
- id,
- metadata: {title: metadataTitle, permalink, date, description, authors},
- } = post;
- feed.addItem({
- title: metadataTitle,
- id,
- link: normalizeUrl([siteUrl, permalink]),
- date,
- description,
- content: mdxToHtml(post.content),
- author: authors.map(toFeedAuthor),
- });
- });
-
- return feed;
-}
-
async function parseBlogPostMarkdownFile(blogSourceAbsolute: string) {
const result = await parseMarkdownFile(blogSourceAbsolute, {
removeContentTitle: true,
diff --git a/packages/docusaurus-plugin-content-blog/src/feed.ts b/packages/docusaurus-plugin-content-blog/src/feed.ts
new file mode 100644
index 0000000000..7d0ce97add
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/feed.ts
@@ -0,0 +1,129 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import {Feed, Author as FeedAuthor} from 'feed';
+import {PluginOptions, Author, BlogPost, FeedType} from './types';
+import {normalizeUrl, mdxToHtml} from '@docusaurus/utils';
+import {DocusaurusConfig} from '@docusaurus/types';
+import path from 'path';
+import fs from 'fs-extra';
+
+// TODO this is temporary until we handle mdxToHtml better
+// It's hard to convert reliably JSX/require calls to an html feed content
+// See https://github.com/facebook/docusaurus/issues/5664
+function mdxToFeedContent(mdxContent: string): string | undefined {
+ try {
+ return mdxToHtml(mdxContent);
+ } catch (e) {
+ // TODO will we need a plugin option to configure how to handle such an error
+ // Swallow the error on purpose for now, until we understand better the problem space
+ return undefined;
+ }
+}
+
+export async function generateBlogFeed({
+ blogPosts,
+ options,
+ siteConfig,
+}: {
+ blogPosts: BlogPost[];
+ options: PluginOptions;
+ siteConfig: DocusaurusConfig;
+}): Promise {
+ if (!blogPosts.length) {
+ return null;
+ }
+
+ const {feedOptions, routeBasePath} = options;
+ const {url: siteUrl, baseUrl, title, favicon} = siteConfig;
+ const blogBaseUrl = normalizeUrl([siteUrl, baseUrl, routeBasePath]);
+
+ const updated =
+ (blogPosts[0] && blogPosts[0].metadata.date) ||
+ new Date('2015-10-25T16:29:00.000-07:00'); // weird legacy magic date
+
+ const feed = new Feed({
+ id: blogBaseUrl,
+ title: feedOptions.title || `${title} Blog`,
+ updated,
+ language: feedOptions.language,
+ link: blogBaseUrl,
+ description: feedOptions.description || `${siteConfig.title} Blog`,
+ favicon: favicon ? normalizeUrl([siteUrl, baseUrl, favicon]) : undefined,
+ copyright: feedOptions.copyright,
+ });
+
+ function toFeedAuthor(author: Author): FeedAuthor {
+ // TODO ask author emails?
+ // RSS feed requires email to render authors
+ return {name: author.name, link: author.url};
+ }
+
+ blogPosts.forEach((post) => {
+ const {
+ id,
+ metadata: {title: metadataTitle, permalink, date, description, authors},
+ } = post;
+ feed.addItem({
+ title: metadataTitle,
+ id,
+ link: normalizeUrl([siteUrl, permalink]),
+ date,
+ description,
+ content: mdxToFeedContent(post.content),
+ author: authors.map(toFeedAuthor),
+ });
+ });
+
+ return feed;
+}
+
+async function createBlogFeedFile({
+ feed,
+ feedType,
+ filePath,
+}: {
+ feed: Feed;
+ feedType: FeedType;
+ filePath: string;
+}) {
+ const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
+ try {
+ await fs.outputFile(filePath, feedContent);
+ } catch (err) {
+ throw new Error(`Generating ${feedType} feed failed: ${err}.`);
+ }
+}
+
+export async function createBlogFeedFiles({
+ blogPosts,
+ options,
+ siteConfig,
+ outDir,
+}: {
+ blogPosts: BlogPost[];
+ options: PluginOptions;
+ siteConfig: DocusaurusConfig;
+ outDir: string;
+}): Promise {
+ const feed = await generateBlogFeed({blogPosts, options, siteConfig});
+
+ const feedTypes = options.feedOptions.type;
+ if (!feed || !feedTypes) {
+ return;
+ }
+
+ await Promise.all(
+ feedTypes.map(async function (feedType) {
+ await createBlogFeedFile({
+ feed,
+ feedType,
+ filePath: path.join(outDir, options.routeBasePath, `${feedType}.xml`),
+ });
+ }),
+ );
+}
diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts
index 3cb75b946a..b7f2cf7ebd 100644
--- a/packages/docusaurus-plugin-content-blog/src/index.ts
+++ b/packages/docusaurus-plugin-content-blog/src/index.ts
@@ -5,7 +5,6 @@
* LICENSE file in the root directory of this source tree.
*/
-import fs from 'fs-extra';
import path from 'path';
import admonitions from 'remark-admonitions';
import {
@@ -49,13 +48,13 @@ import {
} from '@docusaurus/types';
import {Configuration} from 'webpack';
import {
- generateBlogFeed,
generateBlogPosts,
getContentPathList,
getSourceToPermalink,
getBlogTags,
} from './blogUtils';
import {BlogPostFrontMatter} from './blogFrontMatter';
+import {createBlogFeedFiles} from './feed';
export default function pluginContentBlog(
context: LoadContext,
@@ -69,10 +68,11 @@ export default function pluginContentBlog(
const {
siteDir,
- siteConfig: {onBrokenMarkdownLinks, baseUrl},
+ siteConfig,
generatedFilesDir,
i18n: {currentLocale},
} = context;
+ const {onBrokenMarkdownLinks, baseUrl} = siteConfig;
const contentPaths: BlogContentPaths = {
contentPath: path.resolve(siteDir, options.path),
@@ -519,29 +519,18 @@ export default function pluginContentBlog(
return;
}
- const feed = await generateBlogFeed(contentPaths, context, options);
-
- if (!feed) {
+ // TODO: we shouldn't need to re-read the posts here!
+ // postBuild should receive loadedContent
+ const blogPosts = await generateBlogPosts(contentPaths, context, options);
+ if (blogPosts.length) {
return;
}
-
- const feedTypes = options.feedOptions.type;
-
- await Promise.all(
- feedTypes.map(async (feedType) => {
- const feedPath = path.join(
- outDir,
- options.routeBasePath,
- `${feedType}.xml`,
- );
- const feedContent = feedType === 'rss' ? feed.rss2() : feed.atom1();
- try {
- await fs.outputFile(feedPath, feedContent);
- } catch (err) {
- throw new Error(`Generating ${feedType} feed failed: ${err}.`);
- }
- }),
- );
+ await createBlogFeedFiles({
+ blogPosts,
+ options,
+ outDir,
+ siteConfig,
+ });
},
injectHtmlTags({content}) {
diff --git a/website/_dogfooding/_blog tests/2021-10-08-blog-post-mdx-require-feed-tests.mdx b/website/_dogfooding/_blog tests/2021-10-08-blog-post-mdx-require-feed-tests.mdx
new file mode 100644
index 0000000000..29c0cd60fa
--- /dev/null
+++ b/website/_dogfooding/_blog tests/2021-10-08-blog-post-mdx-require-feed-tests.mdx
@@ -0,0 +1,20 @@
+---
+title: Blog post MDX require Feed tests
+authors:
+ - slorber
+tags: [blog, docusaurus, long-long, long-long-long, long-long-long-long]
+---
+
+Some MDX tests, mostly to test how the RSS feed render those
+
+
+
+Test MDX with require calls
+
+import useBaseUrl from '@docusaurus/useBaseUrl';
+
+
+
+
+
+