mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-18 03:26:57 +02:00
feat(v2): various markdown string parsing improvements/fixes (#4590)
* extract createExcerpt code in separate file + add bad test * almost working markdown parsing refactor * complete parseMarkdownString refactor * fix tests * fix blog test issue * fix docusaurus utils imports
This commit is contained in:
parent
b743edf5fb
commit
4efe6824b3
15 changed files with 895 additions and 563 deletions
|
@ -12,9 +12,26 @@ import path from 'path';
|
|||
import pluginContentBlog from '../index';
|
||||
import {DocusaurusConfig, LoadContext, I18n} from '@docusaurus/types';
|
||||
import {PluginOptionSchema} from '../pluginOptionSchema';
|
||||
import {PluginOptions, EditUrlFunction} from '../types';
|
||||
import {PluginOptions, EditUrlFunction, BlogPost} from '../types';
|
||||
import {Joi} from '@docusaurus/utils-validation';
|
||||
|
||||
function findByTitle(
|
||||
blogPosts: BlogPost[],
|
||||
title: string,
|
||||
): BlogPost | undefined {
|
||||
return blogPosts.find((v) => v.metadata.title === title);
|
||||
}
|
||||
function getByTitle(blogPosts: BlogPost[], title: string): BlogPost {
|
||||
const post = findByTitle(blogPosts, title);
|
||||
if (!post) {
|
||||
throw new Error(`can't find blog post with title ${title}.
|
||||
Available blog post titles are:\n- ${blogPosts
|
||||
.map((p) => p.metadata.title)
|
||||
.join('\n- ')}`);
|
||||
}
|
||||
return post;
|
||||
}
|
||||
|
||||
function getI18n(locale: string): I18n {
|
||||
return {
|
||||
currentLocale: locale,
|
||||
|
@ -77,7 +94,7 @@ describe('loadBlog', () => {
|
|||
const blogPosts = await getBlogPosts(siteDir);
|
||||
|
||||
expect({
|
||||
...blogPosts.find((v) => v.metadata.title === 'date-matter')!.metadata,
|
||||
...getByTitle(blogPosts, 'date-matter').metadata,
|
||||
...{prevItem: undefined},
|
||||
}).toEqual({
|
||||
editUrl: `${BaseEditUrl}/blog/date-matter.md`,
|
||||
|
@ -98,9 +115,7 @@ describe('loadBlog', () => {
|
|||
});
|
||||
|
||||
expect(
|
||||
blogPosts.find(
|
||||
(v) => v.metadata.title === 'Happy 1st Birthday Slash! (translated)',
|
||||
)!.metadata,
|
||||
getByTitle(blogPosts, 'Happy 1st Birthday Slash! (translated)').metadata,
|
||||
).toEqual({
|
||||
editUrl: `${BaseEditUrl}/blog/2018-12-14-Happy-First-Birthday-Slash.md`,
|
||||
permalink: '/blog/2018/12/14/Happy-First-Birthday-Slash',
|
||||
|
@ -124,7 +139,7 @@ describe('loadBlog', () => {
|
|||
});
|
||||
|
||||
expect({
|
||||
...blogPosts.find((v) => v.metadata.title === 'Complex Slug')!.metadata,
|
||||
...getByTitle(blogPosts, 'Complex Slug').metadata,
|
||||
...{prevItem: undefined},
|
||||
}).toEqual({
|
||||
editUrl: `${BaseEditUrl}/blog/complex-slug.md`,
|
||||
|
@ -145,7 +160,7 @@ describe('loadBlog', () => {
|
|||
});
|
||||
|
||||
expect({
|
||||
...blogPosts.find((v) => v.metadata.title === 'Simple Slug')!.metadata,
|
||||
...getByTitle(blogPosts, 'Simple Slug').metadata,
|
||||
...{prevItem: undefined},
|
||||
}).toEqual({
|
||||
editUrl: `${BaseEditUrl}/blog/simple-slug.md`,
|
||||
|
@ -166,7 +181,7 @@ describe('loadBlog', () => {
|
|||
});
|
||||
|
||||
expect({
|
||||
...blogPosts.find((v) => v.metadata.title === 'some heading')!.metadata,
|
||||
...getByTitle(blogPosts, 'some heading').metadata,
|
||||
prevItem: undefined,
|
||||
}).toEqual({
|
||||
editUrl: `${BaseEditUrl}/blog/heading-as-title.md`,
|
||||
|
@ -301,7 +316,7 @@ describe('loadBlog', () => {
|
|||
}).format(noDateSourceBirthTime);
|
||||
|
||||
expect({
|
||||
...blogPosts.find((v) => v.metadata.title === 'no date')!.metadata,
|
||||
...getByTitle(blogPosts, 'no date').metadata,
|
||||
...{prevItem: undefined},
|
||||
}).toEqual({
|
||||
editUrl: `${BaseEditUrl}/blog/no date.md`,
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* 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 {Joi} from '@docusaurus/utils-validation';
|
||||
import {Tag} from './types';
|
||||
|
||||
// TODO complete this frontmatter + add unit tests
|
||||
type BlogPostFrontMatter = {
|
||||
id?: string;
|
||||
title?: string;
|
||||
description?: string;
|
||||
tags?: (string | Tag)[];
|
||||
slug?: string;
|
||||
draft?: boolean;
|
||||
date?: string;
|
||||
};
|
||||
|
||||
const BlogTagSchema = Joi.alternatives().try(
|
||||
Joi.string().required(),
|
||||
Joi.object<Tag>({
|
||||
label: Joi.string().required(),
|
||||
permalink: Joi.string().required(),
|
||||
}),
|
||||
);
|
||||
|
||||
const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
|
||||
id: Joi.string(),
|
||||
title: Joi.string(),
|
||||
description: Joi.string(),
|
||||
tags: Joi.array().items(BlogTagSchema),
|
||||
slug: Joi.string(),
|
||||
draft: Joi.boolean(),
|
||||
}).unknown();
|
||||
|
||||
export function assertBlogPostFrontMatter(
|
||||
frontMatter: Record<string, unknown>,
|
||||
): asserts frontMatter is BlogPostFrontMatter {
|
||||
Joi.attempt(frontMatter, BlogFrontMatterSchema);
|
||||
}
|
|
@ -26,9 +26,10 @@ import {
|
|||
getEditUrl,
|
||||
getFolderContainingFile,
|
||||
posixPath,
|
||||
replaceMarkdownLinks,
|
||||
} from '@docusaurus/utils';
|
||||
import {LoadContext} from '@docusaurus/types';
|
||||
import {replaceMarkdownLinks} from '@docusaurus/utils/lib/markdownLinks';
|
||||
import {assertBlogPostFrontMatter} from './blogFrontMatter';
|
||||
|
||||
export function truncate(fileString: string, truncateMarker: RegExp): string {
|
||||
return fileString.split(truncateMarker, 1).shift()!;
|
||||
|
@ -140,12 +141,18 @@ export async function generateBlogPosts(
|
|||
|
||||
const source = path.join(blogDirPath, blogSourceFile);
|
||||
|
||||
const {
|
||||
frontMatter,
|
||||
content,
|
||||
contentTitle,
|
||||
excerpt,
|
||||
} = await parseMarkdownFile(source);
|
||||
assertBlogPostFrontMatter(frontMatter);
|
||||
|
||||
const aliasedSource = aliasedSitePath(source, siteDir);
|
||||
|
||||
const blogFileName = path.basename(blogSourceFile);
|
||||
|
||||
const {frontMatter, content, excerpt} = await parseMarkdownFile(source);
|
||||
|
||||
if (frontMatter.draft && process.env.NODE_ENV === 'production') {
|
||||
return;
|
||||
}
|
||||
|
@ -182,9 +189,11 @@ export async function generateBlogPosts(
|
|||
year: 'numeric',
|
||||
}).format(date);
|
||||
|
||||
const title = frontMatter.title ?? contentTitle ?? linkName;
|
||||
const description = frontMatter.description ?? excerpt ?? '';
|
||||
|
||||
const slug =
|
||||
frontMatter.slug || (match ? toUrl({date, link: linkName}) : linkName);
|
||||
frontMatter.title = frontMatter.title || linkName;
|
||||
|
||||
const permalink = normalizeUrl([baseUrl, routeBasePath, slug]);
|
||||
|
||||
|
@ -220,16 +229,16 @@ export async function generateBlogPosts(
|
|||
}
|
||||
|
||||
blogPosts.push({
|
||||
id: frontMatter.slug || frontMatter.title,
|
||||
id: frontMatter.slug ?? title,
|
||||
metadata: {
|
||||
permalink,
|
||||
editUrl: getBlogEditUrl(),
|
||||
source: aliasedSource,
|
||||
description: frontMatter.description || excerpt,
|
||||
title,
|
||||
description,
|
||||
date,
|
||||
formattedDate,
|
||||
tags: frontMatter.tags,
|
||||
title: frontMatter.title,
|
||||
tags: frontMatter.tags ?? [],
|
||||
readingTime: showReadingTime
|
||||
? readingTime(content).minutes
|
||||
: undefined,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue