mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-18 19:46:57 +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
|
// 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`] = `
|
exports[`blogFeed atom shows feed item for each post 1`] = `
|
||||||
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
||||||
|
@ -37,7 +49,20 @@ exports[`blogFeed atom shows feed item for each post 1`] = `
|
||||||
</feed>"
|
</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`] = `
|
exports[`blogFeed rss shows feed item for each post 1`] = `
|
||||||
"<?xml version=\\"1.0\\" encoding=\\"utf-8\\"?>
|
"<?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;
|
const {include, routeBasePath, truncateMarker} = options;
|
||||||
|
|
||||||
if (!fs.existsSync(blogDir)) {
|
if (!fs.existsSync(blogDir)) {
|
||||||
return null;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const {baseUrl = ''} = siteConfig;
|
const {baseUrl = ''} = siteConfig;
|
||||||
|
@ -156,3 +156,48 @@ export async function generateBlogPosts(
|
||||||
|
|
||||||
return blogPosts;
|
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,
|
TagsModule,
|
||||||
BlogPaginated,
|
BlogPaginated,
|
||||||
FeedType,
|
FeedType,
|
||||||
|
BlogPost,
|
||||||
} from './types';
|
} from './types';
|
||||||
import {
|
import {
|
||||||
LoadContext,
|
LoadContext,
|
||||||
|
@ -75,6 +76,7 @@ export default function pluginContentBlog(
|
||||||
generatedFilesDir,
|
generatedFilesDir,
|
||||||
'docusaurus-plugin-content-blog',
|
'docusaurus-plugin-content-blog',
|
||||||
);
|
);
|
||||||
|
let blogPosts: BlogPost[] = [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'docusaurus-plugin-content-blog',
|
name: 'docusaurus-plugin-content-blog',
|
||||||
|
@ -89,8 +91,8 @@ export default function pluginContentBlog(
|
||||||
async loadContent() {
|
async loadContent() {
|
||||||
const {postsPerPage, routeBasePath} = options;
|
const {postsPerPage, routeBasePath} = options;
|
||||||
|
|
||||||
const blogPosts = await generateBlogPosts(contentPath, context, options);
|
blogPosts = await generateBlogPosts(contentPath, context, options);
|
||||||
if (!blogPosts) {
|
if (!blogPosts.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,7 +393,10 @@ export default function pluginContentBlog(
|
||||||
{
|
{
|
||||||
loader: path.resolve(__dirname, './markdownLoader.js'),
|
loader: path.resolve(__dirname, './markdownLoader.js'),
|
||||||
options: {
|
options: {
|
||||||
|
siteDir,
|
||||||
|
contentPath,
|
||||||
truncateMarker,
|
truncateMarker,
|
||||||
|
blogPosts,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
].filter(Boolean) as Loader[],
|
].filter(Boolean) as Loader[],
|
||||||
|
|
|
@ -5,21 +5,20 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const {parseQuery, getOptions} = require('loader-utils');
|
|
||||||
import {loader} from 'webpack';
|
import {loader} from 'webpack';
|
||||||
import {truncate} from './blogUtils';
|
import {truncate, linkify} from './blogUtils';
|
||||||
|
const {parseQuery, getOptions} = require('loader-utils');
|
||||||
|
|
||||||
export = function(fileString: string) {
|
export = function(fileString: string) {
|
||||||
const callback = this.async();
|
const callback = this.async();
|
||||||
|
const {truncateMarker, siteDir, contentPath, blogPosts} = getOptions(this);
|
||||||
const {truncateMarker}: {truncateMarker: RegExp} = getOptions(this);
|
// Linkify posts
|
||||||
|
let finalContent = linkify(fileString, siteDir, contentPath, blogPosts);
|
||||||
let finalContent = fileString;
|
|
||||||
|
|
||||||
// Truncate content if requested (e.g: file.md?truncated=true).
|
// Truncate content if requested (e.g: file.md?truncated=true).
|
||||||
const {truncated} = this.resourceQuery && parseQuery(this.resourceQuery);
|
const {truncated} = this.resourceQuery && parseQuery(this.resourceQuery);
|
||||||
if (truncated) {
|
if (truncated) {
|
||||||
finalContent = truncate(fileString, truncateMarker);
|
finalContent = truncate(finalContent, truncateMarker);
|
||||||
}
|
}
|
||||||
|
|
||||||
return callback && callback(null, finalContent);
|
return callback && callback(null, finalContent);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue