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);