mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-18 11:36:53 +02:00
fix(v2): linkify blog posts (#2326)
* fix(v2): linkify blog posts * Fix tests
This commit is contained in:
parent
84eeb5120c
commit
36163773ec
8 changed files with 161 additions and 12 deletions
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Happy 1st Birthday Slash!
|
||||
---
|
||||
|
||||
pattern name
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: This post links to another one!
|
||||
---
|
||||
|
||||
[Linked post](2018-12-14-Happy-First-Birthday-Slash.md)
|
|
@ -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`] = `
|
||||
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
||||
<feed xmlns=\\"http://www.w3.org/2005/Atom\\">
|
||||
<id>https://docusaurus.io/blog</id>
|
||||
<title>Hello Blog</title>
|
||||
<updated>2015-10-25T23:29:00.000Z</updated>
|
||||
<generator>https://github.com/jpmonette/feed</generator>
|
||||
<link rel=\\"alternate\\" href=\\"https://docusaurus.io/blog\\"/>
|
||||
<subtitle>Hello Blog</subtitle>
|
||||
<icon>https://docusaurus.io/image/favicon.ico</icon>
|
||||
<rights>Copyright</rights>
|
||||
</feed>"
|
||||
`;
|
||||
|
||||
exports[`blogFeed atom shows feed item for each post 1`] = `
|
||||
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
||||
|
@ -37,7 +49,20 @@ exports[`blogFeed atom shows feed item for each post 1`] = `
|
|||
</feed>"
|
||||
`;
|
||||
|
||||
exports[`blogFeed rss can show feed without posts 1`] = `null`;
|
||||
exports[`blogFeed rss can show feed without posts 1`] = `
|
||||
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
||||
<rss version=\\"2.0\\">
|
||||
<channel>
|
||||
<title>Hello Blog</title>
|
||||
<link>https://docusaurus.io/blog</link>
|
||||
<description>Hello Blog</description>
|
||||
<lastBuildDate>Sun, 25 Oct 2015 23:29:00 GMT</lastBuildDate>
|
||||
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
|
||||
<generator>https://github.com/jpmonette/feed</generator>
|
||||
<copyright>Copyright</copyright>
|
||||
</channel>
|
||||
</rss>"
|
||||
`;
|
||||
|
||||
exports[`blogFeed rss shows feed item for each post 1`] = `
|
||||
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
||||
|
|
|
@ -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)"
|
||||
`;
|
|
@ -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);
|
||||
});
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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[],
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue