mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-30 10:48:05 +02:00
feat(blog): warn duplicate and inline authors (#10224)
Co-authored-by: OzakIOne <OzakIOne@users.noreply.github.com> Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
parent
1405b25fc7
commit
de59621fbb
16 changed files with 299 additions and 21 deletions
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import path from 'path';
|
import * as path from 'path';
|
||||||
import {
|
import {
|
||||||
type AuthorsMap,
|
type AuthorsMap,
|
||||||
getAuthorsMap,
|
getAuthorsMap,
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
/**
|
||||||
|
* 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 {jest} from '@jest/globals';
|
||||||
|
import {reportDuplicateAuthors, reportInlineAuthors} from '../authorsProblems';
|
||||||
|
import type {Author} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
|
const blogSourceRelative = 'doc.md';
|
||||||
|
|
||||||
|
describe('duplicate authors', () => {
|
||||||
|
function testReport({authors}: {authors: Author[]}) {
|
||||||
|
reportDuplicateAuthors({
|
||||||
|
authors,
|
||||||
|
blogSourceRelative,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('allows duplicated inline authors', () => {
|
||||||
|
const authors: Author[] = [
|
||||||
|
{
|
||||||
|
name: 'Sébastien Lorber',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Sébastien Lorber',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
testReport({
|
||||||
|
authors,
|
||||||
|
}),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects duplicated key authors', () => {
|
||||||
|
const authors: Author[] = [
|
||||||
|
{
|
||||||
|
key: 'slorber',
|
||||||
|
name: 'Sébastien Lorber 1',
|
||||||
|
title: 'some title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'slorber',
|
||||||
|
name: 'Sébastien Lorber 2',
|
||||||
|
imageURL: '/slorber.png',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
testReport({
|
||||||
|
authors,
|
||||||
|
}),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(`
|
||||||
|
"Duplicate blog post authors were found in blog post "doc.md" front matter:
|
||||||
|
- {"key":"slorber","name":"Sébastien Lorber 2","imageURL":"/slorber.png"}"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('inline authors', () => {
|
||||||
|
type Options = Parameters<typeof reportInlineAuthors>[0]['options'];
|
||||||
|
|
||||||
|
function testReport({
|
||||||
|
authors,
|
||||||
|
options = {},
|
||||||
|
}: {
|
||||||
|
authors: Author[];
|
||||||
|
options?: Partial<Options>;
|
||||||
|
}) {
|
||||||
|
const defaultOptions: Options = {
|
||||||
|
onInlineAuthors: 'throw',
|
||||||
|
authorsMapPath: 'authors.yml',
|
||||||
|
};
|
||||||
|
|
||||||
|
reportInlineAuthors({
|
||||||
|
authors,
|
||||||
|
options: {
|
||||||
|
...defaultOptions,
|
||||||
|
...options,
|
||||||
|
},
|
||||||
|
blogSourceRelative,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('allows predefined authors', () => {
|
||||||
|
const authors: Author[] = [
|
||||||
|
{
|
||||||
|
key: 'slorber',
|
||||||
|
name: 'Sébastien Lorber',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'ozaki',
|
||||||
|
name: 'Clément Couriol',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
testReport({
|
||||||
|
authors,
|
||||||
|
}),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects inline authors', () => {
|
||||||
|
const authors: Author[] = [
|
||||||
|
{
|
||||||
|
key: 'slorber',
|
||||||
|
name: 'Sébastien Lorber',
|
||||||
|
},
|
||||||
|
{name: 'Inline author 1'},
|
||||||
|
{
|
||||||
|
key: 'ozaki',
|
||||||
|
name: 'Clément Couriol',
|
||||||
|
},
|
||||||
|
{imageURL: '/inline-author2.png'},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
testReport({
|
||||||
|
authors,
|
||||||
|
}),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(`
|
||||||
|
"Some blog authors used in "doc.md" are not defined in "authors.yml":
|
||||||
|
- {"name":"Inline author 1"}
|
||||||
|
- {"imageURL":"/inline-author2.png"}
|
||||||
|
|
||||||
|
Note that we recommend to declare authors once in a "authors.yml" file and reference them by key in blog posts front matter to avoid author info duplication.
|
||||||
|
But if you want to allow inline blog authors, you can disable this message by setting onInlineAuthors: 'ignore' in your blog plugin options.
|
||||||
|
More info at https://docusaurus.io/docs/blog
|
||||||
|
"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('warn inline authors', () => {
|
||||||
|
const authors: Author[] = [
|
||||||
|
{
|
||||||
|
key: 'slorber',
|
||||||
|
name: 'Sébastien Lorber',
|
||||||
|
},
|
||||||
|
{name: 'Inline author 1'},
|
||||||
|
{
|
||||||
|
key: 'ozaki',
|
||||||
|
name: 'Clément Couriol',
|
||||||
|
},
|
||||||
|
{imageURL: '/inline-author2.png'},
|
||||||
|
];
|
||||||
|
|
||||||
|
const consoleMock = jest
|
||||||
|
.spyOn(console, 'warn')
|
||||||
|
.mockImplementation(() => {});
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
testReport({
|
||||||
|
authors,
|
||||||
|
options: {
|
||||||
|
onInlineAuthors: 'warn',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).not.toThrow();
|
||||||
|
expect(consoleMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(consoleMock.mock.calls[0]).toMatchInlineSnapshot(`
|
||||||
|
[
|
||||||
|
"[WARNING] Some blog authors used in "doc.md" are not defined in "authors.yml":
|
||||||
|
- {"name":"Inline author 1"}
|
||||||
|
- {"imageURL":"/inline-author2.png"}
|
||||||
|
|
||||||
|
Note that we recommend to declare authors once in a "authors.yml" file and reference them by key in blog posts front matter to avoid author info duplication.
|
||||||
|
But if you want to allow inline blog authors, you can disable this message by setting onInlineAuthors: 'ignore' in your blog plugin options.
|
||||||
|
More info at https://docusaurus.io/docs/blog
|
||||||
|
",
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
|
@ -101,6 +101,7 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
|
||||||
defaultReadingTime({content}),
|
defaultReadingTime({content}),
|
||||||
truncateMarker: /<!--\s*truncate\s*-->/,
|
truncateMarker: /<!--\s*truncate\s*-->/,
|
||||||
onInlineTags: 'ignore',
|
onInlineTags: 'ignore',
|
||||||
|
onInlineAuthors: 'ignore',
|
||||||
} as PluginOptions,
|
} as PluginOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -143,6 +144,7 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
|
||||||
defaultReadingTime({content}),
|
defaultReadingTime({content}),
|
||||||
truncateMarker: /<!--\s*truncate\s*-->/,
|
truncateMarker: /<!--\s*truncate\s*-->/,
|
||||||
onInlineTags: 'ignore',
|
onInlineTags: 'ignore',
|
||||||
|
onInlineAuthors: 'ignore',
|
||||||
} as PluginOptions,
|
} as PluginOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -197,6 +199,7 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
|
||||||
defaultReadingTime({content}),
|
defaultReadingTime({content}),
|
||||||
truncateMarker: /<!--\s*truncate\s*-->/,
|
truncateMarker: /<!--\s*truncate\s*-->/,
|
||||||
onInlineTags: 'ignore',
|
onInlineTags: 'ignore',
|
||||||
|
onInlineAuthors: 'ignore',
|
||||||
} as PluginOptions,
|
} as PluginOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -242,6 +245,7 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
|
||||||
defaultReadingTime({content}),
|
defaultReadingTime({content}),
|
||||||
truncateMarker: /<!--\s*truncate\s*-->/,
|
truncateMarker: /<!--\s*truncate\s*-->/,
|
||||||
onInlineTags: 'ignore',
|
onInlineTags: 'ignore',
|
||||||
|
onInlineAuthors: 'ignore',
|
||||||
} as PluginOptions,
|
} as PluginOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -287,6 +291,7 @@ describe.each(['atom', 'rss', 'json'])('%s', (feedType) => {
|
||||||
defaultReadingTime({content}),
|
defaultReadingTime({content}),
|
||||||
truncateMarker: /<!--\s*truncate\s*-->/,
|
truncateMarker: /<!--\s*truncate\s*-->/,
|
||||||
onInlineTags: 'ignore',
|
onInlineTags: 'ignore',
|
||||||
|
onInlineAuthors: 'ignore',
|
||||||
} as PluginOptions,
|
} as PluginOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* 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 _ from 'lodash';
|
||||||
|
import logger from '@docusaurus/logger';
|
||||||
|
import type {Author, PluginOptions} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
|
export function reportAuthorsProblems(params: {
|
||||||
|
authors: Author[];
|
||||||
|
blogSourceRelative: string;
|
||||||
|
options: Pick<PluginOptions, 'onInlineAuthors' | 'authorsMapPath'>;
|
||||||
|
}): void {
|
||||||
|
reportInlineAuthors(params);
|
||||||
|
reportDuplicateAuthors(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reportInlineAuthors({
|
||||||
|
authors,
|
||||||
|
blogSourceRelative,
|
||||||
|
options: {onInlineAuthors, authorsMapPath},
|
||||||
|
}: {
|
||||||
|
authors: Author[];
|
||||||
|
blogSourceRelative: string;
|
||||||
|
options: Pick<PluginOptions, 'onInlineAuthors' | 'authorsMapPath'>;
|
||||||
|
}): void {
|
||||||
|
if (onInlineAuthors === 'ignore') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const inlineAuthors = authors.filter((author) => !author.key);
|
||||||
|
if (inlineAuthors.length > 0) {
|
||||||
|
logger.report(onInlineAuthors)(
|
||||||
|
logger.interpolate`Some blog authors used in path=${blogSourceRelative} are not defined in path=${authorsMapPath}:
|
||||||
|
- ${inlineAuthors.map(authorToString).join('\n- ')}
|
||||||
|
|
||||||
|
Note that we recommend to declare authors once in a path=${authorsMapPath} file and reference them by key in blog posts front matter to avoid author info duplication.
|
||||||
|
But if you want to allow inline blog authors, you can disable this message by setting onInlineAuthors: 'ignore' in your blog plugin options.
|
||||||
|
More info at url=${'https://docusaurus.io/docs/blog'}
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reportDuplicateAuthors({
|
||||||
|
authors,
|
||||||
|
blogSourceRelative,
|
||||||
|
}: {
|
||||||
|
authors: Author[];
|
||||||
|
blogSourceRelative: string;
|
||||||
|
}): void {
|
||||||
|
const duplicateAuthors = _(authors)
|
||||||
|
// for now we only check for predefined authors duplicates
|
||||||
|
.filter((author) => !!author.key)
|
||||||
|
.groupBy((author) => author.key)
|
||||||
|
.pickBy((authorsByKey) => authorsByKey.length > 1)
|
||||||
|
// We only keep the "test" of all the duplicate groups
|
||||||
|
// The first author of a group is not really a duplicate...
|
||||||
|
.flatMap(([, ...rest]) => rest)
|
||||||
|
.value();
|
||||||
|
|
||||||
|
if (duplicateAuthors.length > 0) {
|
||||||
|
throw new Error(logger.interpolate`Duplicate blog post authors were found in blog post path=${blogSourceRelative} front matter:
|
||||||
|
- ${duplicateAuthors.map(authorToString).join('\n- ')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function authorToString(author: Author) {
|
||||||
|
return JSON.stringify(author);
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ import {
|
||||||
import {getTagsFile} from '@docusaurus/utils-validation';
|
import {getTagsFile} from '@docusaurus/utils-validation';
|
||||||
import {validateBlogPostFrontMatter} from './frontMatter';
|
import {validateBlogPostFrontMatter} from './frontMatter';
|
||||||
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
import {type AuthorsMap, getAuthorsMap, getBlogPostAuthors} from './authors';
|
||||||
|
import {reportAuthorsProblems} from './authorsProblems';
|
||||||
import type {TagsFile} from '@docusaurus/utils';
|
import type {TagsFile} from '@docusaurus/utils';
|
||||||
import type {LoadContext, ParseFrontMatter} from '@docusaurus/types';
|
import type {LoadContext, ParseFrontMatter} from '@docusaurus/types';
|
||||||
import type {
|
import type {
|
||||||
|
@ -317,7 +318,13 @@ async function processBlogSourceFile(
|
||||||
routeBasePath,
|
routeBasePath,
|
||||||
tagsRouteBasePath,
|
tagsRouteBasePath,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl});
|
const authors = getBlogPostAuthors({authorsMap, frontMatter, baseUrl});
|
||||||
|
reportAuthorsProblems({
|
||||||
|
authors,
|
||||||
|
blogSourceRelative,
|
||||||
|
options,
|
||||||
|
});
|
||||||
|
|
||||||
const tags = normalizeTags({
|
const tags = normalizeTags({
|
||||||
options,
|
options,
|
||||||
|
|
|
@ -58,6 +58,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
||||||
processBlogPosts: async () => undefined,
|
processBlogPosts: async () => undefined,
|
||||||
onInlineTags: 'warn',
|
onInlineTags: 'warn',
|
||||||
tags: undefined,
|
tags: undefined,
|
||||||
|
onInlineAuthors: 'warn',
|
||||||
};
|
};
|
||||||
|
|
||||||
const PluginOptionSchema = Joi.object<PluginOptions>({
|
const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||||
|
@ -156,6 +157,9 @@ const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||||
.disallow('')
|
.disallow('')
|
||||||
.allow(null, false)
|
.allow(null, false)
|
||||||
.default(() => DEFAULT_OPTIONS.tags),
|
.default(() => DEFAULT_OPTIONS.tags),
|
||||||
|
onInlineAuthors: Joi.string()
|
||||||
|
.equal('ignore', 'log', 'warn', 'throw')
|
||||||
|
.default(DEFAULT_OPTIONS.onInlineAuthors),
|
||||||
}).default(DEFAULT_OPTIONS);
|
}).default(DEFAULT_OPTIONS);
|
||||||
|
|
||||||
export function validateOptions({
|
export function validateOptions({
|
||||||
|
|
|
@ -44,6 +44,8 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Author = {
|
export type Author = {
|
||||||
|
key?: string; // TODO temporary, need refactor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If `name` doesn't exist, an `imageURL` is expected.
|
* If `name` doesn't exist, an `imageURL` is expected.
|
||||||
*/
|
*/
|
||||||
|
@ -442,6 +444,8 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
|
||||||
* (filter, modify, delete, etc...).
|
* (filter, modify, delete, etc...).
|
||||||
*/
|
*/
|
||||||
processBlogPosts: ProcessBlogPostsFn;
|
processBlogPosts: ProcessBlogPostsFn;
|
||||||
|
/** The behavior of Docusaurus when it finds inline authors. */
|
||||||
|
onInlineAuthors: 'ignore' | 'log' | 'warn' | 'throw';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,7 +28,7 @@ export type TagsPluginOptions = {
|
||||||
// TODO allow option tags later? | TagsFile;
|
// TODO allow option tags later? | TagsFile;
|
||||||
/** Path to the tags file. */
|
/** Path to the tags file. */
|
||||||
tags: string | false | null | undefined;
|
tags: string | false | null | undefined;
|
||||||
/** The behavior of Docusaurus when it found inline tags. */
|
/** The behavior of Docusaurus when it finds inline tags. */
|
||||||
onInlineTags: 'ignore' | 'log' | 'warn' | 'throw';
|
onInlineTags: 'ignore' | 'log' | 'warn' | 'throw';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
---
|
---
|
||||||
tags:
|
tags:
|
||||||
- b
|
- b
|
||||||
- label: d
|
- d
|
||||||
permalink: d-custom-permalink
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Standalone doc
|
# Standalone doc
|
||||||
|
|
|
@ -4,6 +4,7 @@ b:
|
||||||
label: 'Label for tag b'
|
label: 'Label for tag b'
|
||||||
c:
|
c:
|
||||||
permalink: '/permalink-for-tag-c'
|
permalink: '/permalink-for-tag-c'
|
||||||
|
d:
|
||||||
e:
|
e:
|
||||||
label: 'Label for tag e'
|
label: 'Label for tag e'
|
||||||
description: 'Description for tag e'
|
description: 'Description for tag e'
|
||||||
|
|
|
@ -93,6 +93,7 @@ export const dogfoodingPluginInstances: PluginConfig[] = [
|
||||||
? undefined
|
? undefined
|
||||||
: defaultReadingTime({content, options: {wordsPerMinute: 5}}),
|
: defaultReadingTime({content, options: {wordsPerMinute: 5}}),
|
||||||
onInlineTags: 'warn',
|
onInlineTags: 'warn',
|
||||||
|
onInlineAuthors: 'ignore',
|
||||||
tags: 'tags.yml',
|
tags: 'tags.yml',
|
||||||
} satisfies BlogOptions,
|
} satisfies BlogOptions,
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
---
|
---
|
||||||
title: How I Converted Profilo to Docusaurus in Under 2 Hours
|
title: How I Converted Profilo to Docusaurus in Under 2 Hours
|
||||||
author: Christine Abernathy
|
authors: [abernathyca]
|
||||||
authorURL: http://twitter.com/abernathyca
|
|
||||||
authorImageURL: https://github.com/caabernathy.png
|
|
||||||
authorTwitter: abernathyca
|
|
||||||
tags: [profilo, adoption]
|
tags: [profilo, adoption]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Towards Docusaurus 2
|
title: Towards Docusaurus 2
|
||||||
author: Endilie Yacop Sucipto
|
authors: endiliey
|
||||||
authorTitle: Maintainer of Docusaurus
|
|
||||||
authorURL: https://github.com/endiliey
|
|
||||||
authorImageURL: https://github.com/endiliey.png
|
|
||||||
authorTwitter: endiliey
|
|
||||||
tags: [new, adoption]
|
tags: [new, adoption]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
---
|
---
|
||||||
title: DocSearch migration
|
title: DocSearch migration
|
||||||
authors:
|
authors: [shortcuts, slorber]
|
||||||
- name: Clément Vannicatte
|
|
||||||
title: Software Engineer @ Algolia
|
|
||||||
url: https://github.com/shortcuts
|
|
||||||
image_url: https://github.com/shortcuts.png
|
|
||||||
twitter: sh0rtcts
|
|
||||||
- key: slorber
|
|
||||||
|
|
||||||
tags: [search]
|
tags: [search]
|
||||||
image: ./img/social-card.png
|
image: ./img/social-card.png
|
||||||
---
|
---
|
||||||
|
|
|
@ -43,3 +43,23 @@ Josh-Cena:
|
||||||
url: https://joshcena.com/
|
url: https://joshcena.com/
|
||||||
image_url: https://github.com/josh-cena.png
|
image_url: https://github.com/josh-cena.png
|
||||||
email: sidachen2003@gmail.com
|
email: sidachen2003@gmail.com
|
||||||
|
|
||||||
|
endiliey:
|
||||||
|
name: Endilie Yacop Sucipto
|
||||||
|
title: Maintainer of Docusaurus
|
||||||
|
url: https://github.com/endiliey
|
||||||
|
image_url: https://github.com/endiliey.png
|
||||||
|
twitter: endiliey
|
||||||
|
|
||||||
|
abernathyca:
|
||||||
|
name: Christine Abernathy
|
||||||
|
url: http://twitter.com/abernathyca
|
||||||
|
image_url: https://github.com/caabernathy.png
|
||||||
|
twitter: abernathyca
|
||||||
|
|
||||||
|
shortcuts:
|
||||||
|
name: Clément Vannicatte
|
||||||
|
title: Software Engineer @ Algolia
|
||||||
|
url: https://github.com/shortcuts
|
||||||
|
image_url: https://github.com/shortcuts.png
|
||||||
|
twitter: sh0rtcts
|
||||||
|
|
|
@ -291,6 +291,7 @@ export default async function createConfigAsync() {
|
||||||
copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`,
|
copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`,
|
||||||
language: defaultLocale,
|
language: defaultLocale,
|
||||||
},
|
},
|
||||||
|
onInlineAuthors: 'warn',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
Loading…
Add table
Reference in a new issue