diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md
new file mode 100644
index 000000000000..c04bd7b802a0
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog-with-ref/2018-12-14-Happy-First-Birthday-Slash.md
@@ -0,0 +1,5 @@
+---
+title: Happy 1st Birthday Slash!
+---
+
+pattern name
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog-with-ref/post.md b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog-with-ref/post.md
new file mode 100644
index 000000000000..7779964b6023
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__fixtures__/website/blog-with-ref/post.md
@@ -0,0 +1,5 @@
+---
+title: This post links to another one!
+---
+
+[Linked post](2018-12-14-Happy-First-Birthday-Slash.md)
\ No newline at end of file
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap
index 5df2f18082a8..1e2d5ea081f0 100644
--- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/generateBlogFeed.test.ts.snap
@@ -1,6 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`blogFeed atom can show feed without posts 1`] = `null`;
+exports[`blogFeed atom can show feed without posts 1`] = `
+"
+
+ https://docusaurus.io/blog
+ Hello Blog
+ 2015-10-25T23:29:00.000Z
+ https://github.com/jpmonette/feed
+
+ Hello Blog
+ https://docusaurus.io/image/favicon.ico
+ Copyright
+"
+`;
exports[`blogFeed atom shows feed item for each post 1`] = `
"
@@ -37,7 +49,20 @@ exports[`blogFeed atom shows feed item for each post 1`] = `
"
`;
-exports[`blogFeed rss can show feed without posts 1`] = `null`;
+exports[`blogFeed rss can show feed without posts 1`] = `
+"
+
+
+ Hello Blog
+ https://docusaurus.io/blog
+ Hello Blog
+ Sun, 25 Oct 2015 23:29:00 GMT
+ http://blogs.law.harvard.edu/tech/rss
+ https://github.com/jpmonette/feed
+ Copyright
+
+"
+`;
exports[`blogFeed rss shows feed item for each post 1`] = `
"
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/linkify.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/linkify.test.ts.snap
new file mode 100644
index 000000000000..eb124f41c131
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/linkify.test.ts.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`transform to correct link 1`] = `
+"---
+title: This post links to another one!
+---
+
+[Linked post](/blog/2018/12/14/Happy-First-Birthday-Slash)"
+`;
diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/linkify.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/linkify.test.ts
new file mode 100644
index 000000000000..af6535f81d3e
--- /dev/null
+++ b/packages/docusaurus-plugin-content-blog/src/__tests__/linkify.test.ts
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2017-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import fs from 'fs-extra';
+import path from 'path';
+import {linkify} from '../blogUtils';
+import {BlogPost} from '../types';
+
+const sitePath = path.join(__dirname, '__fixtures__', 'website');
+const blogPath = path.join(sitePath, 'blog-with-ref');
+const pluginDir = 'blog-with-ref';
+const blogPosts: BlogPost[] = [
+ {
+ id: 'Happy 1st Birthday Slash!',
+ metadata: {
+ permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
+ source: path.join(
+ '@site',
+ pluginDir,
+ '2018-12-14-Happy-First-Birthday-Slash.md',
+ ),
+ title: 'Happy 1st Birthday Slash!',
+ description: `pattern name`,
+ date: new Date('2018-12-14'),
+ tags: [],
+ prevItem: {
+ permalink: '/blog/2019/01/01/date-matter',
+ title: 'date-matter',
+ },
+ truncated: false,
+ },
+ },
+];
+
+const transform = filepath => {
+ const content = fs.readFileSync(filepath, 'utf-8');
+ const transformedContent = linkify(content, sitePath, blogPath, blogPosts);
+ return [content, transformedContent];
+};
+
+test('transform to correct link', () => {
+ const post = path.join(blogPath, 'post.md');
+ const [content, transformedContent] = transform(post);
+ expect(transformedContent).toMatchSnapshot();
+ expect(transformedContent).toContain(
+ '](/blog/2018/12/14/Happy-First-Birthday-Slash',
+ );
+ expect(transformedContent).not.toContain(
+ '](2018-12-14-Happy-First-Birthday-Slash.md)',
+ );
+ expect(content).not.toEqual(transformedContent);
+});
diff --git a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
index 6a0be67e944c..22e14691782d 100644
--- a/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
+++ b/packages/docusaurus-plugin-content-blog/src/blogUtils.ts
@@ -88,7 +88,7 @@ export async function generateBlogPosts(
const {include, routeBasePath, truncateMarker} = options;
if (!fs.existsSync(blogDir)) {
- return null;
+ return [];
}
const {baseUrl = ''} = siteConfig;
@@ -156,3 +156,48 @@ export async function generateBlogPosts(
return blogPosts;
}
+
+export function linkify(
+ fileContent: string,
+ siteDir: string,
+ blogPath: string,
+ blogPosts: BlogPost[],
+) {
+ let fencedBlock = false;
+ const lines = fileContent.split('\n').map(line => {
+ if (line.trim().startsWith('```')) {
+ fencedBlock = !fencedBlock;
+ }
+
+ if (fencedBlock) return line;
+
+ let modifiedLine = line;
+ const mdRegex = /(?:(?:\]\()|(?:\]:\s?))(?!https)([^'")\]\s>]+\.mdx?)/g;
+ let mdMatch = mdRegex.exec(modifiedLine);
+
+ while (mdMatch !== null) {
+ const mdLink = mdMatch[1];
+ const aliasedPostSource = `@site/${path.relative(
+ siteDir,
+ path.resolve(blogPath, mdLink),
+ )}`;
+ let blogPostPermalink = null;
+
+ blogPosts.forEach(blogPost => {
+ if (blogPost.metadata.source === aliasedPostSource) {
+ blogPostPermalink = blogPost.metadata.permalink;
+ }
+ });
+
+ if (blogPostPermalink) {
+ modifiedLine = modifiedLine.replace(mdLink, blogPostPermalink);
+ }
+
+ mdMatch = mdRegex.exec(modifiedLine);
+ }
+
+ return modifiedLine;
+ });
+
+ return lines.join('\n');
+}
diff --git a/packages/docusaurus-plugin-content-blog/src/index.ts b/packages/docusaurus-plugin-content-blog/src/index.ts
index fa36f4314fff..0772a6a79b8a 100644
--- a/packages/docusaurus-plugin-content-blog/src/index.ts
+++ b/packages/docusaurus-plugin-content-blog/src/index.ts
@@ -18,6 +18,7 @@ import {
TagsModule,
BlogPaginated,
FeedType,
+ BlogPost,
} from './types';
import {
LoadContext,
@@ -75,6 +76,7 @@ export default function pluginContentBlog(
generatedFilesDir,
'docusaurus-plugin-content-blog',
);
+ let blogPosts: BlogPost[] = [];
return {
name: 'docusaurus-plugin-content-blog',
@@ -89,8 +91,8 @@ export default function pluginContentBlog(
async loadContent() {
const {postsPerPage, routeBasePath} = options;
- const blogPosts = await generateBlogPosts(contentPath, context, options);
- if (!blogPosts) {
+ blogPosts = await generateBlogPosts(contentPath, context, options);
+ if (!blogPosts.length) {
return null;
}
@@ -391,7 +393,10 @@ export default function pluginContentBlog(
{
loader: path.resolve(__dirname, './markdownLoader.js'),
options: {
+ siteDir,
+ contentPath,
truncateMarker,
+ blogPosts,
},
},
].filter(Boolean) as Loader[],
diff --git a/packages/docusaurus-plugin-content-blog/src/markdownLoader.ts b/packages/docusaurus-plugin-content-blog/src/markdownLoader.ts
index 2411291690a6..baf152147535 100644
--- a/packages/docusaurus-plugin-content-blog/src/markdownLoader.ts
+++ b/packages/docusaurus-plugin-content-blog/src/markdownLoader.ts
@@ -5,21 +5,20 @@
* LICENSE file in the root directory of this source tree.
*/
-const {parseQuery, getOptions} = require('loader-utils');
import {loader} from 'webpack';
-import {truncate} from './blogUtils';
+import {truncate, linkify} from './blogUtils';
+const {parseQuery, getOptions} = require('loader-utils');
export = function(fileString: string) {
const callback = this.async();
-
- const {truncateMarker}: {truncateMarker: RegExp} = getOptions(this);
-
- let finalContent = fileString;
+ const {truncateMarker, siteDir, contentPath, blogPosts} = getOptions(this);
+ // Linkify posts
+ let finalContent = linkify(fileString, siteDir, contentPath, blogPosts);
// Truncate content if requested (e.g: file.md?truncated=true).
const {truncated} = this.resourceQuery && parseQuery(this.resourceQuery);
if (truncated) {
- finalContent = truncate(fileString, truncateMarker);
+ finalContent = truncate(finalContent, truncateMarker);
}
return callback && callback(null, finalContent);