mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-18 19:46:57 +02:00
feat(blog): author header social icons (#10222)
Co-authored-by: OzakIOne <OzakIOne@users.noreply.github.com> Co-authored-by: sebastien <lorber.sebastien@gmail.com> Co-authored-by: slorber <slorber@users.noreply.github.com>
This commit is contained in:
parent
8b877d27d4
commit
a6de0f2725
35 changed files with 1005 additions and 31 deletions
|
@ -9,9 +9,17 @@ yangshun:
|
||||||
title: Front End Engineer @ Facebook
|
title: Front End Engineer @ Facebook
|
||||||
url: https://github.com/yangshun
|
url: https://github.com/yangshun
|
||||||
image_url: https://github.com/yangshun.png
|
image_url: https://github.com/yangshun.png
|
||||||
|
socials:
|
||||||
|
x: yangshunz
|
||||||
|
github: yangshun
|
||||||
|
|
||||||
slorber:
|
slorber:
|
||||||
name: Sébastien Lorber
|
name: Sébastien Lorber
|
||||||
title: Docusaurus maintainer
|
title: Docusaurus maintainer
|
||||||
url: https://sebastienlorber.com
|
url: https://sebastienlorber.com
|
||||||
image_url: https://github.com/slorber.png
|
image_url: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
x: sebastienlorber
|
||||||
|
linkedin: sebastienlorber
|
||||||
|
github: slorber
|
||||||
|
newsletter: https://thisweekinreact.com
|
||||||
|
|
|
@ -4,15 +4,21 @@ JMarcey:
|
||||||
title: Technical Lead & Developer Advocate at Facebook
|
title: Technical Lead & Developer Advocate at Facebook
|
||||||
url: http://twitter.com/JoelMarcey
|
url: http://twitter.com/JoelMarcey
|
||||||
image_url: https://github.com/JoelMarcey.png
|
image_url: https://github.com/JoelMarcey.png
|
||||||
twitter: JoelMarcey
|
socials:
|
||||||
|
twitter: https://twitter.com/JoelMarcey
|
||||||
|
x: https://x.com/JoelMarcey
|
||||||
|
stackoverflow: https://stackoverflow.com/users/102705/Joel-Marcey
|
||||||
|
|
||||||
slorber:
|
slorber:
|
||||||
name: Sébastien Lorber
|
name: Sébastien Lorber
|
||||||
title: Docusaurus maintainer
|
title: Docusaurus maintainer
|
||||||
url: https://sebastienlorber.com
|
url: https://sebastienlorber.com
|
||||||
image_url: https://github.com/slorber.png
|
image_url: https://github.com/slorber.png
|
||||||
twitter: sebastienlorber
|
|
||||||
email: lorber.sebastien@gmail.com
|
email: lorber.sebastien@gmail.com
|
||||||
|
socials:
|
||||||
|
twitter: sebastienlorber
|
||||||
|
x: sebastienlorber
|
||||||
|
stackoverflow: 82609
|
||||||
|
|
||||||
yangshun:
|
yangshun:
|
||||||
name: Yangshun Tay
|
name: Yangshun Tay
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
title: Happy 1st Birthday Slash!
|
title: Happy 1st Birthday Slash!
|
||||||
authors:
|
authors:
|
||||||
- name: Yangshun Tay
|
- name: Yangshun Tay
|
||||||
|
socials:
|
||||||
|
x: https://x.com/yangshunz
|
||||||
|
github: yangshun
|
||||||
- slorber
|
- slorber
|
||||||
tags: [birthday,inlineTag,globalTag]
|
tags: [birthday,inlineTag,globalTag]
|
||||||
---
|
---
|
||||||
|
|
|
@ -3,3 +3,7 @@ slorber:
|
||||||
title: Docusaurus maintainer
|
title: Docusaurus maintainer
|
||||||
email: lorber.sebastien@gmail.com
|
email: lorber.sebastien@gmail.com
|
||||||
url: https://sebastienlorber.com
|
url: https://sebastienlorber.com
|
||||||
|
socials:
|
||||||
|
twitter: sebastienlorber
|
||||||
|
x: https://x.com/sebastienlorber
|
||||||
|
github: slorber
|
||||||
|
|
|
@ -255,6 +255,52 @@ describe('getBlogPostAuthors', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can normalize inline authors', () => {
|
||||||
|
expect(
|
||||||
|
getBlogPostAuthors({
|
||||||
|
frontMatter: {
|
||||||
|
authors: [
|
||||||
|
{
|
||||||
|
name: 'Seb1',
|
||||||
|
socials: {
|
||||||
|
x: 'https://x.com/sebastienlorber',
|
||||||
|
twitter: 'sebastienlorber',
|
||||||
|
github: 'slorber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Seb2',
|
||||||
|
socials: {
|
||||||
|
x: 'sebastienlorber',
|
||||||
|
twitter: 'https://twitter.com/sebastienlorber',
|
||||||
|
github: 'https://github.com/slorber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
authorsMap: {},
|
||||||
|
baseUrl: '/',
|
||||||
|
}),
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
name: 'Seb1',
|
||||||
|
socials: {
|
||||||
|
x: 'https://x.com/sebastienlorber',
|
||||||
|
twitter: 'https://twitter.com/sebastienlorber',
|
||||||
|
github: 'https://github.com/slorber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Seb2',
|
||||||
|
socials: {
|
||||||
|
x: 'https://x.com/sebastienlorber',
|
||||||
|
twitter: 'https://twitter.com/sebastienlorber',
|
||||||
|
github: 'https://github.com/slorber',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('throw when using author key with no authorsMap', () => {
|
it('throw when using author key with no authorsMap', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
getBlogPostAuthors({
|
getBlogPostAuthors({
|
||||||
|
@ -412,6 +458,29 @@ describe('getAuthorsMap', () => {
|
||||||
}),
|
}),
|
||||||
).resolves.toBeUndefined();
|
).resolves.toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getAuthorsMap returns normalized', () => {
|
||||||
|
it('socials', async () => {
|
||||||
|
const authorsMap = await getAuthorsMap({
|
||||||
|
contentPaths,
|
||||||
|
authorsMapPath: 'authors.yml',
|
||||||
|
});
|
||||||
|
expect(authorsMap.slorber.socials).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"stackoverflow": "https://stackoverflow.com/users/82609",
|
||||||
|
"twitter": "https://twitter.com/sebastienlorber",
|
||||||
|
"x": "https://x.com/sebastienlorber",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
expect(authorsMap.JMarcey.socials).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"stackoverflow": "https://stackoverflow.com/users/102705/Joel-Marcey",
|
||||||
|
"twitter": "https://twitter.com/JoelMarcey",
|
||||||
|
"x": "https://x.com/JoelMarcey",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('validateAuthorsMap', () => {
|
describe('validateAuthorsMap', () => {
|
||||||
|
@ -529,3 +598,68 @@ describe('validateAuthorsMap', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('authors socials', () => {
|
||||||
|
it('valid known author map socials', () => {
|
||||||
|
const authorsMap: AuthorsMap = {
|
||||||
|
ozaki: {
|
||||||
|
name: 'ozaki',
|
||||||
|
socials: {
|
||||||
|
twitter: 'ozakione',
|
||||||
|
github: 'ozakione',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throw socials that are not strings', () => {
|
||||||
|
const authorsMap: AuthorsMap = {
|
||||||
|
ozaki: {
|
||||||
|
name: 'ozaki',
|
||||||
|
socials: {
|
||||||
|
// @ts-expect-error: for tests
|
||||||
|
twitter: 42,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
validateAuthorsMap(authorsMap),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`""ozaki.socials.twitter" must be a string"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throw socials that are objects', () => {
|
||||||
|
const authorsMap: AuthorsMap = {
|
||||||
|
ozaki: {
|
||||||
|
name: 'ozaki',
|
||||||
|
socials: {
|
||||||
|
// @ts-expect-error: for tests
|
||||||
|
twitter: {link: 'ozakione'},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
validateAuthorsMap(authorsMap),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(
|
||||||
|
`""ozaki.socials.twitter" must be a string"`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('valid unknown author map socials', () => {
|
||||||
|
const authorsMap: AuthorsMap = {
|
||||||
|
ozaki: {
|
||||||
|
name: 'ozaki',
|
||||||
|
socials: {
|
||||||
|
random: 'ozakione',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(validateAuthorsMap(authorsMap)).toEqual(authorsMap);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
/**
|
||||||
|
* 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 {normalizeSocials} from '../authorsSocials';
|
||||||
|
import type {AuthorSocials} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
|
describe('normalizeSocials', () => {
|
||||||
|
it('only username', () => {
|
||||||
|
const socials: AuthorSocials = {
|
||||||
|
twitter: 'ozakione',
|
||||||
|
linkedin: 'ozakione',
|
||||||
|
github: 'ozakione',
|
||||||
|
stackoverflow: 'ozakione',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeSocials(socials)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"github": "https://github.com/ozakione",
|
||||||
|
"linkedin": "https://www.linkedin.com/in/ozakione/",
|
||||||
|
"stackoverflow": "https://stackoverflow.com/users/ozakione",
|
||||||
|
"twitter": "https://twitter.com/ozakione",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only username - case insensitive', () => {
|
||||||
|
const socials: AuthorSocials = {
|
||||||
|
Twitter: 'ozakione',
|
||||||
|
linkedIn: 'ozakione',
|
||||||
|
gitHub: 'ozakione',
|
||||||
|
STACKoverflow: 'ozakione',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeSocials(socials)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"github": "https://github.com/ozakione",
|
||||||
|
"linkedin": "https://www.linkedin.com/in/ozakione/",
|
||||||
|
"stackoverflow": "https://stackoverflow.com/users/ozakione",
|
||||||
|
"twitter": "https://twitter.com/ozakione",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('only links', () => {
|
||||||
|
const socials: AuthorSocials = {
|
||||||
|
twitter: 'https://x.com/ozakione',
|
||||||
|
linkedin: 'https://linkedin.com/ozakione',
|
||||||
|
github: 'https://github.com/ozakione',
|
||||||
|
stackoverflow: 'https://stackoverflow.com/ozakione',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeSocials(socials)).toEqual(socials);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('mixed links', () => {
|
||||||
|
const socials: AuthorSocials = {
|
||||||
|
twitter: 'ozakione',
|
||||||
|
linkedin: 'ozakione',
|
||||||
|
github: 'https://github.com/ozakione',
|
||||||
|
stackoverflow: 'https://stackoverflow.com/ozakione',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeSocials(socials)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"github": "https://github.com/ozakione",
|
||||||
|
"linkedin": "https://www.linkedin.com/in/ozakione/",
|
||||||
|
"stackoverflow": "https://stackoverflow.com/ozakione",
|
||||||
|
"twitter": "https://twitter.com/ozakione",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('one link', () => {
|
||||||
|
const socials: AuthorSocials = {
|
||||||
|
twitter: 'ozakione',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeSocials(socials)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"twitter": "https://twitter.com/ozakione",
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects strings that do not look like username/userId/handle or fully-qualified URLs', () => {
|
||||||
|
const socials: AuthorSocials = {
|
||||||
|
twitter: '/ozakione/XYZ',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(() => normalizeSocials(socials)).toThrowErrorMatchingInlineSnapshot(`
|
||||||
|
"Author socials should be usernames/userIds/handles, or fully qualified HTTP(s) absolute URLs.
|
||||||
|
Social platform 'twitter' has illegal value '/ozakione/XYZ'"
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allow other form of urls', () => {
|
||||||
|
const socials: AuthorSocials = {
|
||||||
|
twitter: 'https://bit.ly/sebastienlorber-twitter',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeSocials(socials)).toEqual(socials);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allow unknown social platforms urls', () => {
|
||||||
|
const socials: AuthorSocials = {
|
||||||
|
twitch: 'https://www.twitch.tv/sebastienlorber',
|
||||||
|
newsletter: 'https://thisweekinreact.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(normalizeSocials(socials)).toEqual(socials);
|
||||||
|
});
|
||||||
|
});
|
|
@ -5,8 +5,10 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import * as _ from 'lodash';
|
||||||
import {getDataFileData, normalizeUrl} from '@docusaurus/utils';
|
import {getDataFileData, normalizeUrl} from '@docusaurus/utils';
|
||||||
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
||||||
|
import {AuthorSocialsSchema, normalizeSocials} from './authorsSocials';
|
||||||
import type {BlogContentPaths} from './types';
|
import type {BlogContentPaths} from './types';
|
||||||
import type {
|
import type {
|
||||||
Author,
|
Author,
|
||||||
|
@ -20,12 +22,13 @@ export type AuthorsMap = {[authorKey: string]: Author};
|
||||||
const AuthorsMapSchema = Joi.object<AuthorsMap>()
|
const AuthorsMapSchema = Joi.object<AuthorsMap>()
|
||||||
.pattern(
|
.pattern(
|
||||||
Joi.string(),
|
Joi.string(),
|
||||||
Joi.object({
|
Joi.object<Author>({
|
||||||
name: Joi.string(),
|
name: Joi.string(),
|
||||||
url: URISchema,
|
url: URISchema,
|
||||||
imageURL: URISchema,
|
imageURL: URISchema,
|
||||||
title: Joi.string(),
|
title: Joi.string(),
|
||||||
email: Joi.string(),
|
email: Joi.string(),
|
||||||
|
socials: AuthorSocialsSchema,
|
||||||
})
|
})
|
||||||
.rename('image_url', 'imageURL')
|
.rename('image_url', 'imageURL')
|
||||||
.or('name', 'imageURL')
|
.or('name', 'imageURL')
|
||||||
|
@ -51,18 +54,32 @@ export function validateAuthorsMap(content: unknown): AuthorsMap {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeAuthor(author: Author): Author {
|
||||||
|
return {
|
||||||
|
...author,
|
||||||
|
socials: author.socials ? normalizeSocials(author.socials) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeAuthorsMap(authorsMap: AuthorsMap): AuthorsMap {
|
||||||
|
return _.mapValues(authorsMap, normalizeAuthor);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getAuthorsMap(params: {
|
export async function getAuthorsMap(params: {
|
||||||
authorsMapPath: string;
|
authorsMapPath: string;
|
||||||
contentPaths: BlogContentPaths;
|
contentPaths: BlogContentPaths;
|
||||||
}): Promise<AuthorsMap | undefined> {
|
}): Promise<AuthorsMap | undefined> {
|
||||||
return getDataFileData(
|
const authorsMap = await getDataFileData(
|
||||||
{
|
{
|
||||||
filePath: params.authorsMapPath,
|
filePath: params.authorsMapPath,
|
||||||
contentPaths: params.contentPaths,
|
contentPaths: params.contentPaths,
|
||||||
fileType: 'authors map',
|
fileType: 'authors map',
|
||||||
},
|
},
|
||||||
|
// TODO annoying to test: tightly coupled FS reads + validation...
|
||||||
validateAuthorsMap,
|
validateAuthorsMap,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return authorsMap ? normalizeAuthorsMap(authorsMap) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthorsParam = {
|
type AuthorsParam = {
|
||||||
|
@ -115,7 +132,7 @@ function getFrontMatterAuthorLegacy({
|
||||||
function normalizeFrontMatterAuthors(
|
function normalizeFrontMatterAuthors(
|
||||||
frontMatterAuthors: BlogPostFrontMatterAuthors = [],
|
frontMatterAuthors: BlogPostFrontMatterAuthors = [],
|
||||||
): BlogPostFrontMatterAuthor[] {
|
): BlogPostFrontMatterAuthor[] {
|
||||||
function normalizeAuthor(
|
function normalizeFrontMatterAuthor(
|
||||||
authorInput: string | Author,
|
authorInput: string | Author,
|
||||||
): BlogPostFrontMatterAuthor {
|
): BlogPostFrontMatterAuthor {
|
||||||
if (typeof authorInput === 'string') {
|
if (typeof authorInput === 'string') {
|
||||||
|
@ -128,8 +145,8 @@ function normalizeFrontMatterAuthors(
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.isArray(frontMatterAuthors)
|
return Array.isArray(frontMatterAuthors)
|
||||||
? frontMatterAuthors.map(normalizeAuthor)
|
? frontMatterAuthors.map(normalizeFrontMatterAuthor)
|
||||||
: [normalizeAuthor(frontMatterAuthors)];
|
: [normalizeFrontMatterAuthor(frontMatterAuthors)];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFrontMatterAuthors(params: AuthorsParam): Author[] {
|
function getFrontMatterAuthors(params: AuthorsParam): Author[] {
|
||||||
|
@ -158,11 +175,11 @@ ${Object.keys(authorsMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAuthor(frontMatterAuthor: BlogPostFrontMatterAuthor): Author {
|
function toAuthor(frontMatterAuthor: BlogPostFrontMatterAuthor): Author {
|
||||||
return {
|
return normalizeAuthor({
|
||||||
// Author def from authorsMap can be locally overridden by front matter
|
// Author def from authorsMap can be locally overridden by front matter
|
||||||
...getAuthorsMapAuthor(frontMatterAuthor.key),
|
...getAuthorsMapAuthor(frontMatterAuthor.key),
|
||||||
...frontMatterAuthor,
|
...frontMatterAuthor,
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return frontMatterAuthors.map(toAuthor);
|
return frontMatterAuthors.map(toAuthor);
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/**
|
||||||
|
* 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 type {
|
||||||
|
AuthorSocials,
|
||||||
|
SocialPlatformKey,
|
||||||
|
} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
|
export const AuthorSocialsSchema = Joi.object<AuthorSocials>({
|
||||||
|
twitter: Joi.string(),
|
||||||
|
github: Joi.string(),
|
||||||
|
linkedin: Joi.string(),
|
||||||
|
// StackOverflow userIds like '82609' are parsed as numbers by Yaml
|
||||||
|
stackoverflow: Joi.alternatives()
|
||||||
|
.try(Joi.number(), Joi.string())
|
||||||
|
.custom((val) => String(val)),
|
||||||
|
x: Joi.string(),
|
||||||
|
}).unknown();
|
||||||
|
|
||||||
|
type PredefinedPlatformNormalizer = (value: string) => string;
|
||||||
|
|
||||||
|
const PredefinedPlatformNormalizers: Record<
|
||||||
|
SocialPlatformKey | string,
|
||||||
|
PredefinedPlatformNormalizer
|
||||||
|
> = {
|
||||||
|
x: (handle: string) => `https://x.com/${handle}`,
|
||||||
|
twitter: (handle: string) => `https://twitter.com/${handle}`,
|
||||||
|
github: (handle: string) => `https://github.com/${handle}`,
|
||||||
|
linkedin: (handle: string) => `https://www.linkedin.com/in/${handle}/`,
|
||||||
|
stackoverflow: (userId: string) =>
|
||||||
|
`https://stackoverflow.com/users/${userId}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
type SocialEntry = [string, string];
|
||||||
|
|
||||||
|
function normalizeSocialEntry([platform, value]: SocialEntry): SocialEntry {
|
||||||
|
const normalizer = PredefinedPlatformNormalizers[platform.toLowerCase()];
|
||||||
|
const isAbsoluteUrl =
|
||||||
|
value.startsWith('http://') || value.startsWith('https://');
|
||||||
|
if (isAbsoluteUrl) {
|
||||||
|
return [platform, value];
|
||||||
|
} else if (value.includes('/')) {
|
||||||
|
throw new Error(
|
||||||
|
`Author socials should be usernames/userIds/handles, or fully qualified HTTP(s) absolute URLs.
|
||||||
|
Social platform '${platform}' has illegal value '${value}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (normalizer && !isAbsoluteUrl) {
|
||||||
|
const normalizedPlatform = platform.toLowerCase();
|
||||||
|
const normalizedValue = normalizer(value);
|
||||||
|
return [normalizedPlatform as SocialPlatformKey, normalizedValue];
|
||||||
|
}
|
||||||
|
return [platform, value];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const normalizeSocials = (socials: AuthorSocials): AuthorSocials => {
|
||||||
|
return Object.fromEntries(Object.entries(socials).map(normalizeSocialEntry));
|
||||||
|
};
|
|
@ -13,6 +13,7 @@ import {
|
||||||
URISchema,
|
URISchema,
|
||||||
validateFrontMatter,
|
validateFrontMatter,
|
||||||
} from '@docusaurus/utils-validation';
|
} from '@docusaurus/utils-validation';
|
||||||
|
import {AuthorSocialsSchema} from './authorsSocials';
|
||||||
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
const BlogPostFrontMatterAuthorSchema = Joi.object({
|
const BlogPostFrontMatterAuthorSchema = Joi.object({
|
||||||
|
@ -21,6 +22,7 @@ const BlogPostFrontMatterAuthorSchema = Joi.object({
|
||||||
title: Joi.string(),
|
title: Joi.string(),
|
||||||
url: URISchema,
|
url: URISchema,
|
||||||
imageURL: Joi.string(),
|
imageURL: Joi.string(),
|
||||||
|
socials: AuthorSocialsSchema,
|
||||||
})
|
})
|
||||||
.or('key', 'name', 'imageURL')
|
.or('key', 'name', 'imageURL')
|
||||||
.rename('image_url', 'imageURL', {alias: true});
|
.rename('image_url', 'imageURL', {alias: true});
|
||||||
|
|
|
@ -43,6 +43,29 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
|
||||||
authorsImageUrls: (string | undefined)[];
|
authorsImageUrls: (string | undefined)[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note we don't pre-define all possible platforms
|
||||||
|
* Users can add their own custom platforms if needed
|
||||||
|
*/
|
||||||
|
export type SocialPlatformKey =
|
||||||
|
| 'twitter'
|
||||||
|
| 'github'
|
||||||
|
| 'linkedin'
|
||||||
|
| 'stackoverflow'
|
||||||
|
| 'x';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Social platforms of the author.
|
||||||
|
* The record value is usually the fully qualified link of the social profile.
|
||||||
|
* For pre-defined platforms, it's possible to pass a handle instead
|
||||||
|
*/
|
||||||
|
export type AuthorSocials = Partial<Record<SocialPlatformKey, string>> & {
|
||||||
|
/**
|
||||||
|
* Unknown keys are allowed: users can pass additional social platforms
|
||||||
|
*/
|
||||||
|
[customAuthorSocialPlatform: string]: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type Author = {
|
export type Author = {
|
||||||
key?: string; // TODO temporary, need refactor
|
key?: string; // TODO temporary, need refactor
|
||||||
|
|
||||||
|
@ -70,10 +93,14 @@ yarn workspace v1.22.19image` is a collocated image path, this entry will be the
|
||||||
*/
|
*/
|
||||||
email?: string;
|
email?: string;
|
||||||
/**
|
/**
|
||||||
* Unknown keys are allowed, so that we can pass custom fields to authors,
|
* Social platforms of the author
|
||||||
* e.g., `twitter`.
|
* Usually displayed as a list of social icon links.
|
||||||
*/
|
*/
|
||||||
[key: string]: unknown;
|
socials?: AuthorSocials;
|
||||||
|
/**
|
||||||
|
* Unknown keys are allowed, so that we can pass custom fields to authors,
|
||||||
|
*/
|
||||||
|
[customAuthorAttribute: string]: unknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -289,6 +289,17 @@ export default function getSwizzleConfig(): SwizzleConfig {
|
||||||
},
|
},
|
||||||
description: 'The menu icon component',
|
description: 'The menu icon component',
|
||||||
},
|
},
|
||||||
|
'Icon/Socials': {
|
||||||
|
actions: {
|
||||||
|
// Forbidden because it's a parent folder, makes the CLI crash atm
|
||||||
|
// TODO the CLI should rather support --eject
|
||||||
|
// Subfolders can be swizzled
|
||||||
|
eject: 'forbidden',
|
||||||
|
wrap: 'forbidden',
|
||||||
|
},
|
||||||
|
description:
|
||||||
|
'The Icon/Socials folder is not directly swizzle-able, but you can swizzle its sub-components.',
|
||||||
|
},
|
||||||
MDXComponents: {
|
MDXComponents: {
|
||||||
actions: {
|
actions: {
|
||||||
eject: 'safe',
|
eject: 'safe',
|
||||||
|
|
|
@ -292,16 +292,30 @@ declare module '@theme/BlogPostItem/Header/Info' {
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@theme/BlogPostItem/Header/Author' {
|
declare module '@theme/BlogPostItem/Header/Author' {
|
||||||
import type {PropBlogPostContent} from '@docusaurus/plugin-content-blog';
|
import type {Author} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
readonly author: PropBlogPostContent['metadata']['authors'][number];
|
readonly author: Author;
|
||||||
|
readonly singleAuthor: boolean;
|
||||||
readonly className?: string;
|
readonly className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function BlogPostItemHeaderAuthor(props: Props): JSX.Element;
|
export default function BlogPostItemHeaderAuthor(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@theme/BlogPostItem/Header/Author/Socials' {
|
||||||
|
import type {Author} from '@docusaurus/plugin-content-blog';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
readonly author: Author;
|
||||||
|
readonly className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BlogPostItemHeaderAuthorSocials(
|
||||||
|
props: Props,
|
||||||
|
): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
declare module '@theme/BlogPostItem/Header/Authors' {
|
declare module '@theme/BlogPostItem/Header/Authors' {
|
||||||
export interface Props {
|
export interface Props {
|
||||||
readonly className?: string;
|
readonly className?: string;
|
||||||
|
@ -1514,6 +1528,54 @@ declare module '@theme/Icon/WordWrap' {
|
||||||
export default function IconWordWrap(props: Props): JSX.Element;
|
export default function IconWordWrap(props: Props): JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module '@theme/Icon/Socials/Twitter' {
|
||||||
|
import type {ComponentProps} from 'react';
|
||||||
|
|
||||||
|
export interface Props extends ComponentProps<'svg'> {}
|
||||||
|
|
||||||
|
export default function Twitter(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/Icon/Socials/GitHub' {
|
||||||
|
import type {ComponentProps} from 'react';
|
||||||
|
|
||||||
|
export interface Props extends ComponentProps<'svg'> {}
|
||||||
|
|
||||||
|
export default function Github(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/Icon/Socials/X' {
|
||||||
|
import type {ComponentProps} from 'react';
|
||||||
|
|
||||||
|
export interface Props extends ComponentProps<'svg'> {}
|
||||||
|
|
||||||
|
export default function X(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/Icon/Socials/LinkedIn' {
|
||||||
|
import type {ComponentProps} from 'react';
|
||||||
|
|
||||||
|
export interface Props extends ComponentProps<'svg'> {}
|
||||||
|
|
||||||
|
export default function LinkedIn(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/Icon/Socials/Default' {
|
||||||
|
import type {ComponentProps} from 'react';
|
||||||
|
|
||||||
|
export interface Props extends ComponentProps<'svg'> {}
|
||||||
|
|
||||||
|
export default function DefaultSocialIcon(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module '@theme/Icon/Socials/StackOverflow' {
|
||||||
|
import type {ComponentProps} from 'react';
|
||||||
|
|
||||||
|
export interface Props extends ComponentProps<'svg'> {}
|
||||||
|
|
||||||
|
export default function StackOverflow(props: Props): JSX.Element;
|
||||||
|
}
|
||||||
|
|
||||||
declare module '@theme/TagsListByLetter' {
|
declare module '@theme/TagsListByLetter' {
|
||||||
import type {TagsListItem} from '@docusaurus/utils';
|
import type {TagsListItem} from '@docusaurus/utils';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
/**
|
||||||
|
* 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 type {ComponentType} from 'react';
|
||||||
|
import React from 'react';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
|
import type {Props} from '@theme/BlogPostItem/Header/Author/Socials';
|
||||||
|
|
||||||
|
import Twitter from '@theme/Icon/Socials/Twitter';
|
||||||
|
import GitHub from '@theme/Icon/Socials/GitHub';
|
||||||
|
import X from '@theme/Icon/Socials/X';
|
||||||
|
import StackOverflow from '@theme/Icon/Socials/StackOverflow';
|
||||||
|
import LinkedIn from '@theme/Icon/Socials/LinkedIn';
|
||||||
|
import DefaultSocialIcon from '@theme/Icon/Socials/Default';
|
||||||
|
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
type SocialIcon = ComponentType<{className: string}>;
|
||||||
|
|
||||||
|
type SocialPlatformConfig = {Icon: SocialIcon; label: string};
|
||||||
|
|
||||||
|
const SocialPlatformConfigs: Record<string, SocialPlatformConfig> = {
|
||||||
|
twitter: {Icon: Twitter, label: 'Twitter'},
|
||||||
|
github: {Icon: GitHub, label: 'GitHub'},
|
||||||
|
stackoverflow: {Icon: StackOverflow, label: 'Stack Overflow'},
|
||||||
|
linkedin: {Icon: LinkedIn, label: 'LinkedIn'},
|
||||||
|
x: {Icon: X, label: 'X'},
|
||||||
|
};
|
||||||
|
|
||||||
|
function getSocialPlatformConfig(platformKey: string): SocialPlatformConfig {
|
||||||
|
return (
|
||||||
|
SocialPlatformConfigs[platformKey] ?? {
|
||||||
|
Icon: DefaultSocialIcon,
|
||||||
|
label: platformKey,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SocialLink({platform, link}: {platform: string; link: string}) {
|
||||||
|
const {Icon, label} = getSocialPlatformConfig(platform);
|
||||||
|
return (
|
||||||
|
<Link className={styles.authorSocialLink} href={link} title={label}>
|
||||||
|
<Icon className={clsx(styles.authorSocialLink)} />
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AuthorSocials({author}: {author: Props['author']}) {
|
||||||
|
return (
|
||||||
|
<div className={styles.authorSocials}>
|
||||||
|
{Object.entries(author.socials ?? {}).map(([platform, linkUrl]) => {
|
||||||
|
return <SocialLink key={platform} platform={platform} link={linkUrl} />;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--docusaurus-blog-social-icon-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authorSocials {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
line-clamp: 1;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authorSocialLink {
|
||||||
|
height: var(--docusaurus-blog-social-icon-size);
|
||||||
|
width: var(--docusaurus-blog-social-icon-size);
|
||||||
|
line-height: 0;
|
||||||
|
margin-right: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authorSocialIcon {
|
||||||
|
width: var(--docusaurus-blog-social-icon-size);
|
||||||
|
height: var(--docusaurus-blog-social-icon-size);
|
||||||
|
}
|
|
@ -8,8 +8,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import Link, {type Props as LinkProps} from '@docusaurus/Link';
|
import Link, {type Props as LinkProps} from '@docusaurus/Link';
|
||||||
|
import AuthorSocials from '@theme/BlogPostItem/Header/Author/Socials';
|
||||||
|
|
||||||
import type {Props} from '@theme/BlogPostItem/Header/Author';
|
import type {Props} from '@theme/BlogPostItem/Header/Author';
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
function MaybeLink(props: LinkProps): JSX.Element {
|
function MaybeLink(props: LinkProps): JSX.Element {
|
||||||
if (props.href) {
|
if (props.href) {
|
||||||
|
@ -18,12 +20,24 @@ function MaybeLink(props: LinkProps): JSX.Element {
|
||||||
return <>{props.children}</>;
|
return <>{props.children}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function AuthorTitle({title}: {title: string}) {
|
||||||
|
return (
|
||||||
|
<small className={styles.authorTitle} title={title}>
|
||||||
|
{title}
|
||||||
|
</small>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function BlogPostItemHeaderAuthor({
|
export default function BlogPostItemHeaderAuthor({
|
||||||
|
// singleAuthor, // may be useful in the future, or for swizzle users
|
||||||
author,
|
author,
|
||||||
className,
|
className,
|
||||||
}: Props): JSX.Element {
|
}: Props): JSX.Element {
|
||||||
const {name, title, url, imageURL, email} = author;
|
const {name, title, url, socials, imageURL, email} = author;
|
||||||
const link = url || (email && `mailto:${email}`) || undefined;
|
const link = url || (email && `mailto:${email}`) || undefined;
|
||||||
|
|
||||||
|
const hasSocials = socials && Object.keys(socials).length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={clsx('avatar margin-bottom--sm', className)}>
|
<div className={clsx('avatar margin-bottom--sm', className)}>
|
||||||
{imageURL && (
|
{imageURL && (
|
||||||
|
@ -32,14 +46,15 @@ export default function BlogPostItemHeaderAuthor({
|
||||||
</MaybeLink>
|
</MaybeLink>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{name && (
|
{(name || title) && (
|
||||||
<div className="avatar__intro">
|
<div className="avatar__intro">
|
||||||
<div className="avatar__name">
|
<div className="avatar__name">
|
||||||
<MaybeLink href={link}>
|
<MaybeLink href={link}>
|
||||||
<span>{name}</span>
|
<span className={styles.authorName}>{name}</span>
|
||||||
</MaybeLink>
|
</MaybeLink>
|
||||||
</div>
|
</div>
|
||||||
{title && <small className="avatar__subtitle">{title}</small>}
|
{!!title && <AuthorTitle title={title} />}
|
||||||
|
{hasSocials && <AuthorSocials author={author} />}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.authorName {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authorTitle {
|
||||||
|
margin-top: 0.06rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
line-height: 0.8rem;
|
||||||
|
display: -webkit-box;
|
||||||
|
overflow: hidden;
|
||||||
|
line-clamp: 1;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ export default function BlogPostItemHeaderAuthors({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const imageOnly = authors.every(({name}) => !name);
|
const imageOnly = authors.every(({name}) => !name);
|
||||||
|
const singleAuthor = authors.length === 1;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
@ -35,11 +36,12 @@ export default function BlogPostItemHeaderAuthors({
|
||||||
{authors.map((author, idx) => (
|
{authors.map((author, idx) => (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
!imageOnly && 'col col--6',
|
!imageOnly && (singleAuthor ? 'col col--12' : 'col col--6'),
|
||||||
imageOnly ? styles.imageOnlyAuthorCol : styles.authorCol,
|
imageOnly ? styles.imageOnlyAuthorCol : styles.authorCol,
|
||||||
)}
|
)}
|
||||||
key={idx}>
|
key={idx}>
|
||||||
<BlogPostItemHeaderAuthor
|
<BlogPostItemHeaderAuthor
|
||||||
|
singleAuthor={singleAuthor}
|
||||||
author={{
|
author={{
|
||||||
...author,
|
...author,
|
||||||
// Handle author images using relative paths
|
// Handle author images using relative paths
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
.authorCol {
|
.authorCol {
|
||||||
max-width: inherit !important;
|
max-width: inherit !important;
|
||||||
flex-grow: 1 !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.imageOnlyAuthorRow {
|
.imageOnlyAuthorRow {
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* 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 type {SVGProps} from 'react';
|
||||||
|
|
||||||
|
// SVG Source: https://tabler.io/
|
||||||
|
function DefaultSocial(props: SVGProps<SVGSVGElement>): JSX.Element {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
{...props}>
|
||||||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||||
|
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" />
|
||||||
|
<path d="M3.6 9h16.8" />
|
||||||
|
<path d="M3.6 15h16.8" />
|
||||||
|
<path d="M11.5 3a17 17 0 0 0 0 18" />
|
||||||
|
<path d="M12.5 3a17 17 0 0 1 0 18" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default DefaultSocial;
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* 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 type {SVGProps} from 'react';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
// SVG Source: https://svgl.app/
|
||||||
|
function GitHub(props: SVGProps<SVGSVGElement>): JSX.Element {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 256 250"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
{...props}
|
||||||
|
className={clsx(props.className, styles.githubSvg)}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
style={{'--dark': '#000', '--light': '#fff'} as React.CSSProperties}
|
||||||
|
preserveAspectRatio="xMidYMid">
|
||||||
|
<path d="M128.001 0C57.317 0 0 57.307 0 128.001c0 56.554 36.676 104.535 87.535 121.46 6.397 1.185 8.746-2.777 8.746-6.158 0-3.052-.12-13.135-.174-23.83-35.61 7.742-43.124-15.103-43.124-15.103-5.823-14.795-14.213-18.73-14.213-18.73-11.613-7.944.876-7.78.876-7.78 12.853.902 19.621 13.19 19.621 13.19 11.417 19.568 29.945 13.911 37.249 10.64 1.149-8.272 4.466-13.92 8.127-17.116-28.431-3.236-58.318-14.212-58.318-63.258 0-13.975 5-25.394 13.188-34.358-1.329-3.224-5.71-16.242 1.24-33.874 0 0 10.749-3.44 35.21 13.121 10.21-2.836 21.16-4.258 32.038-4.307 10.878.049 21.837 1.47 32.066 4.307 24.431-16.56 35.165-13.12 35.165-13.12 6.967 17.63 2.584 30.65 1.255 33.873 8.207 8.964 13.173 20.383 13.173 34.358 0 49.163-29.944 59.988-58.447 63.157 4.591 3.972 8.682 11.762 8.682 23.704 0 17.126-.148 30.91-.148 35.126 0 3.407 2.304 7.398 8.792 6.14C219.37 232.5 256 184.537 256 128.002 256 57.307 198.691 0 128.001 0Zm-80.06 182.34c-.282.636-1.283.827-2.194.39-.929-.417-1.45-1.284-1.15-1.922.276-.655 1.279-.838 2.205-.399.93.418 1.46 1.293 1.139 1.931Zm6.296 5.618c-.61.566-1.804.303-2.614-.591-.837-.892-.994-2.086-.375-2.66.63-.566 1.787-.301 2.626.591.838.903 1 2.088.363 2.66Zm4.32 7.188c-.785.545-2.067.034-2.86-1.104-.784-1.138-.784-2.503.017-3.05.795-.547 2.058-.055 2.861 1.075.782 1.157.782 2.522-.019 3.08Zm7.304 8.325c-.701.774-2.196.566-3.29-.49-1.119-1.032-1.43-2.496-.726-3.27.71-.776 2.213-.558 3.315.49 1.11 1.03 1.45 2.505.701 3.27Zm9.442 2.81c-.31 1.003-1.75 1.459-3.199 1.033-1.448-.439-2.395-1.613-2.103-2.626.301-1.01 1.747-1.484 3.207-1.028 1.446.436 2.396 1.602 2.095 2.622Zm10.744 1.193c.036 1.055-1.193 1.93-2.715 1.95-1.53.034-2.769-.82-2.786-1.86 0-1.065 1.202-1.932 2.733-1.958 1.522-.03 2.768.818 2.768 1.868Zm10.555-.405c.182 1.03-.875 2.088-2.387 2.37-1.485.271-2.861-.365-3.05-1.386-.184-1.056.893-2.114 2.376-2.387 1.514-.263 2.868.356 3.061 1.403Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default GitHub;
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[data-theme='dark'] .githubSvg {
|
||||||
|
fill: var(--light);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='light'] .githubSvg {
|
||||||
|
fill: var(--dark);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* 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 type {SVGProps} from 'react';
|
||||||
|
|
||||||
|
// SVG Source: https://svgl.app/
|
||||||
|
function LinkedIn(props: SVGProps<SVGSVGElement>): JSX.Element {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
preserveAspectRatio="xMidYMid"
|
||||||
|
viewBox="0 0 256 256"
|
||||||
|
{...props}>
|
||||||
|
<path
|
||||||
|
d="M218.123 218.127h-37.931v-59.403c0-14.165-.253-32.4-19.728-32.4-19.756 0-22.779 15.434-22.779 31.369v60.43h-37.93V95.967h36.413v16.694h.51a39.907 39.907 0 0 1 35.928-19.733c38.445 0 45.533 25.288 45.533 58.186l-.016 67.013ZM56.955 79.27c-12.157.002-22.014-9.852-22.016-22.009-.002-12.157 9.851-22.014 22.008-22.016 12.157-.003 22.014 9.851 22.016 22.008A22.013 22.013 0 0 1 56.955 79.27m18.966 138.858H37.95V95.967h37.97v122.16ZM237.033.018H18.89C8.58-.098.125 8.161-.001 18.471v219.053c.122 10.315 8.576 18.582 18.89 18.474h218.144c10.336.128 18.823-8.139 18.966-18.474V18.454c-.147-10.33-8.635-18.588-18.966-18.453"
|
||||||
|
fill="#0A66C2"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default LinkedIn;
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* 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 type {SVGProps} from 'react';
|
||||||
|
|
||||||
|
// SVG Source: https://svgl.app/
|
||||||
|
function StackOverflow(props: SVGProps<SVGSVGElement>): JSX.Element {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 169.61 200"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
{...props}>
|
||||||
|
<path
|
||||||
|
d="M140.44 178.38v-48.65h21.61V200H0v-70.27h21.61v48.65z"
|
||||||
|
fill="#bcbbbb"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M124.24 140.54l4.32-16.22-86.97-17.83-3.78 17.83zM49.7 82.16L130.72 120l7.56-16.22-81.02-37.83zm22.68-40l68.06 57.3 11.35-13.51-68.6-57.3-11.35 13.51zM116.14 0l-14.59 10.81 53.48 71.89 14.58-10.81zM37.81 162.16h86.43v-16.21H37.81z"
|
||||||
|
fill="#f48024"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default StackOverflow;
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* 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 type {SVGProps} from 'react';
|
||||||
|
|
||||||
|
// SVG Source: https://svgl.app/
|
||||||
|
function Twitter(props: SVGProps<SVGSVGElement>): JSX.Element {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 256 209"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
preserveAspectRatio="xMidYMid"
|
||||||
|
{...props}>
|
||||||
|
<path
|
||||||
|
d="M256 25.45c-9.42 4.177-19.542 7-30.166 8.27 10.845-6.5 19.172-16.793 23.093-29.057a105.183 105.183 0 0 1-33.351 12.745C205.995 7.201 192.346.822 177.239.822c-29.006 0-52.523 23.516-52.523 52.52 0 4.117.465 8.125 1.36 11.97-43.65-2.191-82.35-23.1-108.255-54.876-4.52 7.757-7.11 16.78-7.11 26.404 0 18.222 9.273 34.297 23.365 43.716a52.312 52.312 0 0 1-23.79-6.57c-.003.22-.003.44-.003.661 0 25.447 18.104 46.675 42.13 51.5a52.592 52.592 0 0 1-23.718.9c6.683 20.866 26.08 36.05 49.062 36.475-17.975 14.086-40.622 22.483-65.228 22.483-4.24 0-8.42-.249-12.529-.734 23.243 14.902 50.85 23.597 80.51 23.597 96.607 0 149.434-80.031 149.434-149.435 0-2.278-.05-4.543-.152-6.795A106.748 106.748 0 0 0 256 25.45"
|
||||||
|
fill="#55acee"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default Twitter;
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* 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 type {SVGProps} from 'react';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
// SVG Source: https://svgl.app/
|
||||||
|
function X(props: SVGProps<SVGSVGElement>): JSX.Element {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 1200 1227"
|
||||||
|
{...props}
|
||||||
|
className={clsx(props.className, styles.xSvg)}
|
||||||
|
style={{'--dark': '#000', '--light': '#fff'} as React.CSSProperties}>
|
||||||
|
<path d="M714.163 519.284 1160.89 0h-105.86L667.137 450.887 357.328 0H0l468.492 681.821L0 1226.37h105.866l409.625-476.152 327.181 476.152H1200L714.137 519.284h.026ZM569.165 687.828l-47.468-67.894-377.686-540.24h162.604l304.797 435.991 47.468 67.894 396.2 566.721H892.476L569.165 687.854v-.026Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default X;
|
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[data-theme='dark'] .xSvg {
|
||||||
|
fill: var(--light);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme='light'] .xSvg {
|
||||||
|
fill: var(--dark);
|
||||||
|
}
|
|
@ -63,6 +63,7 @@ export async function getDataFileData<T>(
|
||||||
try {
|
try {
|
||||||
const contentString = await fs.readFile(filePath, {encoding: 'utf8'});
|
const contentString = await fs.readFile(filePath, {encoding: 'utf8'});
|
||||||
const unsafeContent = Yaml.load(contentString);
|
const unsafeContent = Yaml.load(contentString);
|
||||||
|
// TODO we shouldn't validate here: it makes validation harder to test
|
||||||
return validate(unsafeContent);
|
return validate(unsafeContent);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error`The ${params.fileType} file at path=${filePath} looks invalid.`;
|
logger.error`The ${params.fileType} file at path=${filePath} looks invalid.`;
|
||||||
|
|
|
@ -145,6 +145,7 @@ javadoc
|
||||||
jiti
|
jiti
|
||||||
jmarcey
|
jmarcey
|
||||||
jodyheavener
|
jodyheavener
|
||||||
|
joelmarcey
|
||||||
joshcena
|
joshcena
|
||||||
jssdk
|
jssdk
|
||||||
Kaszubowski
|
Kaszubowski
|
||||||
|
@ -235,6 +236,7 @@ outerbounds
|
||||||
Outerbounds
|
Outerbounds
|
||||||
overrideable
|
overrideable
|
||||||
ozaki
|
ozaki
|
||||||
|
ozakione
|
||||||
pageview
|
pageview
|
||||||
palenight
|
palenight
|
||||||
Palenight
|
Palenight
|
||||||
|
@ -330,6 +332,7 @@ Solana
|
||||||
spâce
|
spâce
|
||||||
stackblitz
|
stackblitz
|
||||||
stackblitzrc
|
stackblitzrc
|
||||||
|
stackoverflow
|
||||||
Stormkit
|
Stormkit
|
||||||
Strikethrough
|
Strikethrough
|
||||||
strikethroughs
|
strikethroughs
|
||||||
|
|
26
website/_dogfooding/_blog tests/2024-07-03-dual-author.mdx
Normal file
26
website/_dogfooding/_blog tests/2024-07-03-dual-author.mdx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
title: Dual author socials
|
||||||
|
authors:
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
twitter: sebastienlorber
|
||||||
|
github: slorber
|
||||||
|
stackoverflow: 82609
|
||||||
|
linkedin: sebastienlorber
|
||||||
|
newsletter: https://thisweekinreact.com/newsletter
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
x: https://x.com/sebastienlorber
|
||||||
|
github: https://github.com/slorber
|
||||||
|
stackoverflow: 82609
|
||||||
|
linkedin: https://www.linkedin.com/in/sebastienlorber/
|
||||||
|
newsletter: https://thisweekinreact.com/newsletter
|
||||||
|
---
|
||||||
|
|
||||||
|
# Multiple authors
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
Content about the blog post
|
|
@ -0,0 +1,93 @@
|
||||||
|
---
|
||||||
|
title: How multiple authors with socials looks
|
||||||
|
authors:
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
title: Docusaurus Maintainer and This Week In React editor editor editor editor editor editor editor editor
|
||||||
|
socials:
|
||||||
|
twitter: https://twitter.com/sebastienlorber
|
||||||
|
x: https://x.com/sebastienlorber
|
||||||
|
github: https://github.com/slorber
|
||||||
|
stackoverflow: https://stackoverflow.com/users/82609/sebastien-lorber
|
||||||
|
linkedin: https://www.linkedin.com/in/sebastienlorber/
|
||||||
|
newsletter: https://thisweekinreact.com/newsletter
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
twitter: https://x.com/sebastienlorber
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
title: Docusaurus Maintainer and This Week In React editor
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
title: Docusaurus Maintainer and This Week In React editor editor editor editor editor editor editor editor
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
title: Docusaurus Maintainer and This Week In React editor
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
github: https://github.com/slorber
|
||||||
|
twitter: https://twitter.com/sebastienlorber
|
||||||
|
x: https://x.com/sebastienlorber
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
title: Docusaurus Maintainer and This Week In React editor editor editor editor editor editor editor editor
|
||||||
|
socials:
|
||||||
|
github: https://github.com/slorber
|
||||||
|
twitter: https://x.com/sebastienlorber
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
a: https://thisweekinreact.com/newsletter
|
||||||
|
b: https://thisweekinreact.com/newsletter
|
||||||
|
c: https://thisweekinreact.com/newsletter
|
||||||
|
d: https://thisweekinreact.com/newsletter
|
||||||
|
e: https://thisweekinreact.com/newsletter
|
||||||
|
f: https://thisweekinreact.com/newsletter
|
||||||
|
g: https://thisweekinreact.com/newsletter
|
||||||
|
h: https://thisweekinreact.com/newsletter
|
||||||
|
i: https://thisweekinreact.com/newsletter
|
||||||
|
j: https://thisweekinreact.com/newsletter
|
||||||
|
k: https://thisweekinreact.com/newsletter
|
||||||
|
l: https://thisweekinreact.com/newsletter
|
||||||
|
m: https://thisweekinreact.com/newsletter
|
||||||
|
n: https://thisweekinreact.com/newsletter
|
||||||
|
o: https://thisweekinreact.com/newsletter
|
||||||
|
p: https://thisweekinreact.com/newsletter
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
a: https://thisweekinreact.com/newsletter
|
||||||
|
b: https://thisweekinreact.com/newsletter
|
||||||
|
c: https://thisweekinreact.com/newsletter
|
||||||
|
d: https://thisweekinreact.com/newsletter
|
||||||
|
e: https://thisweekinreact.com/newsletter
|
||||||
|
f: https://thisweekinreact.com/newsletter
|
||||||
|
g: https://thisweekinreact.com/newsletter
|
||||||
|
h: https://thisweekinreact.com/newsletter
|
||||||
|
i: https://thisweekinreact.com/newsletter
|
||||||
|
j: https://thisweekinreact.com/newsletter
|
||||||
|
k: https://thisweekinreact.com/newsletter
|
||||||
|
l: https://thisweekinreact.com/newsletter
|
||||||
|
m: https://thisweekinreact.com/newsletter
|
||||||
|
n: https://thisweekinreact.com/newsletter
|
||||||
|
o: https://thisweekinreact.com/newsletter
|
||||||
|
p: https://thisweekinreact.com/newsletter
|
||||||
|
q: https://thisweekinreact.com/newsletter
|
||||||
|
r: https://thisweekinreact.com/newsletter
|
||||||
|
s: https://thisweekinreact.com/newsletter
|
||||||
|
t: https://thisweekinreact.com/newsletter
|
||||||
|
u: https://thisweekinreact.com/newsletter
|
||||||
|
v: https://thisweekinreact.com/newsletter
|
||||||
|
w: https://thisweekinreact.com/newsletter
|
||||||
|
x: https://thisweekinreact.com/newsletter
|
||||||
|
y: https://thisweekinreact.com/newsletter
|
||||||
|
z: https://thisweekinreact.com/newsletter
|
||||||
|
---
|
||||||
|
|
||||||
|
# Multiple authors
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
Content about the blog post
|
20
website/_dogfooding/_blog tests/2024-07-03-single-author.mdx
Normal file
20
website/_dogfooding/_blog tests/2024-07-03-single-author.mdx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: Single author socials
|
||||||
|
authors:
|
||||||
|
- name: Sébastien Lorber
|
||||||
|
imageURL: https://github.com/slorber.png
|
||||||
|
title: Docusaurus Maintainer and This Week In React editor editor editor editor editor editor editor editor editor editor editor editor editor editor
|
||||||
|
socials:
|
||||||
|
x: https://x.com/sebastienlorber
|
||||||
|
twitter: https://twitter.com/sebastienlorber
|
||||||
|
github: https://github.com/slorber
|
||||||
|
stackoverflow: 82609
|
||||||
|
linkedin: https://www.linkedin.com/in/sebastienlorber/
|
||||||
|
newsletter: https://thisweekinreact.com/newsletter
|
||||||
|
---
|
||||||
|
|
||||||
|
# Multiple authors
|
||||||
|
|
||||||
|
## Content
|
||||||
|
|
||||||
|
Content about the blog post
|
|
@ -83,6 +83,7 @@ export const dogfoodingPluginInstances: PluginConfig[] = [
|
||||||
editUrl:
|
editUrl:
|
||||||
'https://github.com/facebook/docusaurus/edit/main/website/_dogfooding/_blog-tests',
|
'https://github.com/facebook/docusaurus/edit/main/website/_dogfooding/_blog-tests',
|
||||||
postsPerPage: 3,
|
postsPerPage: 3,
|
||||||
|
blogSidebarCount: 'ALL',
|
||||||
feedOptions: {
|
feedOptions: {
|
||||||
type: 'all',
|
type: 'all',
|
||||||
title: 'Docusaurus Tests Blog',
|
title: 'Docusaurus Tests Blog',
|
||||||
|
|
|
@ -4,31 +4,39 @@ JMarcey:
|
||||||
url: https://twitter.com/JoelMarcey
|
url: https://twitter.com/JoelMarcey
|
||||||
image_url: https://github.com/JoelMarcey.png
|
image_url: https://github.com/JoelMarcey.png
|
||||||
email: jimarcey@gmail.com
|
email: jimarcey@gmail.com
|
||||||
twitter: JoelMarcey
|
socials:
|
||||||
|
x: joelmarcey
|
||||||
|
github: JoelMarcey
|
||||||
|
|
||||||
zpao:
|
zpao:
|
||||||
name: Paul O’Shannessy
|
name: Paul O’Shannessy
|
||||||
title: Engineering Manager at Meta
|
title: Engineering Manager at Meta
|
||||||
url: https://twitter.com/zpao
|
url: https://x.com/zpao
|
||||||
image_url: https://github.com/zpao.png
|
image_url: https://github.com/zpao.png
|
||||||
email: jimarcey@gmail.com
|
socials:
|
||||||
twitter: zpao
|
x: zpao
|
||||||
|
github: zpao
|
||||||
|
|
||||||
slorber:
|
slorber:
|
||||||
name: Sébastien Lorber
|
name: Sébastien Lorber
|
||||||
title: Docusaurus maintainer, This Week In React editor
|
title: Docusaurus maintainer, This Week In React editor
|
||||||
url: https://thisweekinreact.com
|
url: https://thisweekinreact.com
|
||||||
image_url: https://github.com/slorber.png
|
image_url: https://github.com/slorber.png
|
||||||
twitter: sebastienlorber
|
socials:
|
||||||
email: sebastien@thisweekinreact.com
|
x: sebastienlorber
|
||||||
|
linkedin: sebastienlorber
|
||||||
|
github: slorber
|
||||||
|
newsletter: https://thisweekinreact.com
|
||||||
|
|
||||||
yangshun:
|
yangshun:
|
||||||
name: Yangshun Tay
|
name: Yangshun Tay
|
||||||
title: Front End Engineer at Meta
|
title: Front End Engineer at Meta
|
||||||
url: https://github.com/yangshun
|
url: https://github.com/yangshun
|
||||||
image_url: https://github.com/yangshun.png
|
image_url: https://github.com/yangshun.png
|
||||||
twitter: yangshunz
|
|
||||||
email: tay.yang.shun@gmail.com
|
email: tay.yang.shun@gmail.com
|
||||||
|
socials:
|
||||||
|
x: yangshunz
|
||||||
|
github: yangshun
|
||||||
|
|
||||||
lex111:
|
lex111:
|
||||||
name: Alexey Pyltsyn
|
name: Alexey Pyltsyn
|
||||||
|
@ -49,17 +57,19 @@ endiliey:
|
||||||
title: Maintainer of Docusaurus
|
title: Maintainer of Docusaurus
|
||||||
url: https://github.com/endiliey
|
url: https://github.com/endiliey
|
||||||
image_url: https://github.com/endiliey.png
|
image_url: https://github.com/endiliey.png
|
||||||
twitter: endiliey
|
|
||||||
|
|
||||||
abernathyca:
|
abernathyca:
|
||||||
name: Christine Abernathy
|
name: Christine Abernathy
|
||||||
url: http://twitter.com/abernathyca
|
url: http://x.com/abernathyca
|
||||||
image_url: https://github.com/caabernathy.png
|
image_url: https://github.com/caabernathy.png
|
||||||
twitter: abernathyca
|
socials:
|
||||||
|
x: abernathyca
|
||||||
|
|
||||||
shortcuts:
|
shortcuts:
|
||||||
name: Clément Vannicatte
|
name: Clément Vannicatte
|
||||||
title: Software Engineer @ Algolia
|
title: Software Engineer @ Algolia
|
||||||
url: https://github.com/shortcuts
|
url: https://github.com/shortcuts
|
||||||
image_url: https://github.com/shortcuts.png
|
image_url: https://github.com/shortcuts.png
|
||||||
twitter: sh0rtcts
|
socials:
|
||||||
|
x: sh0rtcts
|
||||||
|
github: shortcuts
|
||||||
|
|
|
@ -251,12 +251,19 @@ type Tag = string | {label: string; permalink: string};
|
||||||
// An author key references an author from the global plugin authors.yml file
|
// An author key references an author from the global plugin authors.yml file
|
||||||
type AuthorKey = string;
|
type AuthorKey = string;
|
||||||
|
|
||||||
|
// Social platform name -> Social platform link
|
||||||
|
// Example: {MyPlatform: 'https://myplatform.com/myusername'}
|
||||||
|
// Pre-defined platforms ("x", "github", "twitter", "linkedin", "stackoverflow") accept handles:
|
||||||
|
// Example: {github: 'slorber'}
|
||||||
|
type AuthorSocials = Record<string, string>;
|
||||||
|
|
||||||
type Author = {
|
type Author = {
|
||||||
key?: AuthorKey;
|
key?: AuthorKey;
|
||||||
name: string;
|
name: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
image_url?: string;
|
image_url?: string;
|
||||||
|
socials?: AuthorSocials;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The front matter authors field allows various possible shapes
|
// The front matter authors field allows various possible shapes
|
||||||
|
@ -275,6 +282,9 @@ authors:
|
||||||
title: Co-creator of Docusaurus 1
|
title: Co-creator of Docusaurus 1
|
||||||
url: https://github.com/JoelMarcey
|
url: https://github.com/JoelMarcey
|
||||||
image_url: https://github.com/JoelMarcey.png
|
image_url: https://github.com/JoelMarcey.png
|
||||||
|
socials:
|
||||||
|
x: joelmarcey
|
||||||
|
github: JoelMarcey
|
||||||
tags: [docusaurus]
|
tags: [docusaurus]
|
||||||
description: This is my first post on Docusaurus.
|
description: This is my first post on Docusaurus.
|
||||||
image: https://i.imgur.com/mErPwqL.png
|
image: https://i.imgur.com/mErPwqL.png
|
||||||
|
|
|
@ -52,10 +52,16 @@ authors:
|
||||||
title: Co-creator of Docusaurus 1
|
title: Co-creator of Docusaurus 1
|
||||||
url: https://github.com/JoelMarcey
|
url: https://github.com/JoelMarcey
|
||||||
image_url: https://github.com/JoelMarcey.png
|
image_url: https://github.com/JoelMarcey.png
|
||||||
|
socials:
|
||||||
|
x: joelmarcey
|
||||||
|
github: JoelMarcey
|
||||||
- name: Sébastien Lorber
|
- name: Sébastien Lorber
|
||||||
title: Docusaurus maintainer
|
title: Docusaurus maintainer
|
||||||
url: https://sebastienlorber.com
|
url: https://sebastienlorber.com
|
||||||
image_url: https://github.com/slorber.png
|
image_url: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
x: sebastienlorber
|
||||||
|
github: slorber
|
||||||
tags: [hello, docusaurus-v2]
|
tags: [hello, docusaurus-v2]
|
||||||
image: https://i.imgur.com/mErPwqL.png
|
image: https://i.imgur.com/mErPwqL.png
|
||||||
hide_table_of_contents: false
|
hide_table_of_contents: false
|
||||||
|
@ -214,6 +220,9 @@ authors:
|
||||||
url: https://github.com/JoelMarcey
|
url: https://github.com/JoelMarcey
|
||||||
image_url: https://github.com/JoelMarcey.png
|
image_url: https://github.com/JoelMarcey.png
|
||||||
email: jimarcey@gmail.com
|
email: jimarcey@gmail.com
|
||||||
|
socials:
|
||||||
|
x: joelmarcey
|
||||||
|
github: JoelMarcey
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -230,10 +239,16 @@ authors:
|
||||||
url: https://github.com/JoelMarcey
|
url: https://github.com/JoelMarcey
|
||||||
image_url: https://github.com/JoelMarcey.png
|
image_url: https://github.com/JoelMarcey.png
|
||||||
email: jimarcey@gmail.com
|
email: jimarcey@gmail.com
|
||||||
|
socials:
|
||||||
|
x: joelmarcey
|
||||||
|
github: JoelMarcey
|
||||||
- name: Sébastien Lorber
|
- name: Sébastien Lorber
|
||||||
title: Docusaurus maintainer
|
title: Docusaurus maintainer
|
||||||
url: https://sebastienlorber.com
|
url: https://sebastienlorber.com
|
||||||
image_url: https://github.com/slorber.png
|
image_url: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
x: sebastienlorber
|
||||||
|
github: slorber
|
||||||
---
|
---
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -276,12 +291,18 @@ jmarcey:
|
||||||
url: https://github.com/JoelMarcey
|
url: https://github.com/JoelMarcey
|
||||||
image_url: https://github.com/JoelMarcey.png
|
image_url: https://github.com/JoelMarcey.png
|
||||||
email: jimarcey@gmail.com
|
email: jimarcey@gmail.com
|
||||||
|
socials:
|
||||||
|
x: joelmarcey
|
||||||
|
github: JoelMarcey
|
||||||
|
|
||||||
slorber:
|
slorber:
|
||||||
name: Sébastien Lorber
|
name: Sébastien Lorber
|
||||||
title: Docusaurus maintainer
|
title: Docusaurus maintainer
|
||||||
url: https://sebastienlorber.com
|
url: https://sebastienlorber.com
|
||||||
image_url: https://github.com/slorber.png
|
image_url: https://github.com/slorber.png
|
||||||
|
socials:
|
||||||
|
x: sebastienlorber
|
||||||
|
github: slorber
|
||||||
```
|
```
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue