fix(v2): fix too strict markdown frontmatter validation (#4654)

* start work

* use orta.vscode-jest

* node 14

* add some better  infra to validate markdown frontmatter

* better docs frontmatter validation

* fix Yaml / Joi validation issues

* fix Yaml / Joi validation issues

Co-authored-by: slorber <lorber.sebastien@gmail.com>
This commit is contained in:
John Reilly 2021-04-21 15:19:55 +01:00 committed by GitHub
parent c04e613ffe
commit e11597aba9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 238 additions and 21 deletions

View file

@ -0,0 +1,67 @@
/**
* 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 {
BlogPostFrontMatter,
validateBlogPostFrontMatter,
} from '../blogFrontMatter';
describe('validateBlogPostFrontMatter', () => {
test('accept empty object', () => {
const frontMatter = {};
expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter);
});
test('accept valid values', () => {
const frontMatter: BlogPostFrontMatter = {
id: 'blog',
title: 'title',
description: 'description',
date: 'date',
slug: 'slug',
draft: true,
tags: ['hello', {label: 'tagLabel', permalink: '/tagPermalink'}],
};
expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter);
});
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
test('accept empty title', () => {
const frontMatter: BlogPostFrontMatter = {title: ''};
expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter);
});
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
test('accept empty description', () => {
const frontMatter: BlogPostFrontMatter = {description: ''};
expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter);
});
// See https://github.com/facebook/docusaurus/issues/4642
test('convert tags as numbers', () => {
const frontMatter: BlogPostFrontMatter = {
tags: [
// @ts-expect-error: number for test
42,
{
// @ts-expect-error: number for test
label: 84,
permalink: '/tagPermalink',
},
],
};
expect(validateBlogPostFrontMatter(frontMatter)).toEqual({
tags: [
'42',
{
label: '84',
permalink: '/tagPermalink',
},
],
});
});
});

View file

@ -5,11 +5,14 @@
* LICENSE file in the root directory of this source tree.
*/
import {Joi} from '@docusaurus/utils-validation';
import {
JoiFrontMatter as Joi, // Custom instance for frontmatter
validateFrontMatter,
} from '@docusaurus/utils-validation';
import {Tag} from './types';
// TODO complete this frontmatter + add unit tests
type BlogPostFrontMatter = {
export type BlogPostFrontMatter = {
id?: string;
title?: string;
description?: string;
@ -19,6 +22,10 @@ type BlogPostFrontMatter = {
date?: string;
};
// NOTE: we don't add any default value on purpose here
// We don't want default values to magically appear in doc metadatas and props
// While the user did not provide those values explicitly
// We use default values in code instead
const BlogTagSchema = Joi.alternatives().try(
Joi.string().required(),
Joi.object<Tag>({
@ -29,15 +36,16 @@ const BlogTagSchema = Joi.alternatives().try(
const BlogFrontMatterSchema = Joi.object<BlogPostFrontMatter>({
id: Joi.string(),
title: Joi.string(),
description: Joi.string(),
title: Joi.string().allow(''),
description: Joi.string().allow(''),
tags: Joi.array().items(BlogTagSchema),
slug: Joi.string(),
draft: Joi.boolean(),
date: Joi.string().allow(''), // TODO validate the date better!
}).unknown();
export function assertBlogPostFrontMatter(
export function validateBlogPostFrontMatter(
frontMatter: Record<string, unknown>,
): asserts frontMatter is BlogPostFrontMatter {
Joi.attempt(frontMatter, BlogFrontMatterSchema);
): BlogPostFrontMatter {
return validateFrontMatter(frontMatter, BlogFrontMatterSchema);
}

View file

@ -29,7 +29,7 @@ import {
replaceMarkdownLinks,
} from '@docusaurus/utils';
import {LoadContext} from '@docusaurus/types';
import {assertBlogPostFrontMatter} from './blogFrontMatter';
import {validateBlogPostFrontMatter} from './blogFrontMatter';
export function truncate(fileString: string, truncateMarker: RegExp): string {
return fileString.split(truncateMarker, 1).shift()!;
@ -142,12 +142,12 @@ export async function generateBlogPosts(
const source = path.join(blogDirPath, blogSourceFile);
const {
frontMatter,
frontMatter: unsafeFrontMatter,
content,
contentTitle,
excerpt,
} = await parseMarkdownFile(source);
assertBlogPostFrontMatter(frontMatter);
const frontMatter = validateBlogPostFrontMatter(unsafeFrontMatter);
const aliasedSource = aliasedSitePath(source, siteDir);