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
|
||||
url: https://github.com/yangshun
|
||||
image_url: https://github.com/yangshun.png
|
||||
socials:
|
||||
x: yangshunz
|
||||
github: yangshun
|
||||
|
||||
slorber:
|
||||
name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
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
|
||||
url: http://twitter.com/JoelMarcey
|
||||
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:
|
||||
name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
twitter: sebastienlorber
|
||||
email: lorber.sebastien@gmail.com
|
||||
socials:
|
||||
twitter: sebastienlorber
|
||||
x: sebastienlorber
|
||||
stackoverflow: 82609
|
||||
|
||||
yangshun:
|
||||
name: Yangshun Tay
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
title: Happy 1st Birthday Slash!
|
||||
authors:
|
||||
- name: Yangshun Tay
|
||||
socials:
|
||||
x: https://x.com/yangshunz
|
||||
github: yangshun
|
||||
- slorber
|
||||
tags: [birthday,inlineTag,globalTag]
|
||||
---
|
||||
|
|
|
@ -3,3 +3,7 @@ slorber:
|
|||
title: Docusaurus maintainer
|
||||
email: lorber.sebastien@gmail.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', () => {
|
||||
expect(() =>
|
||||
getBlogPostAuthors({
|
||||
|
@ -412,6 +458,29 @@ describe('getAuthorsMap', () => {
|
|||
}),
|
||||
).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', () => {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
import * as _ from 'lodash';
|
||||
import {getDataFileData, normalizeUrl} from '@docusaurus/utils';
|
||||
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
||||
import {AuthorSocialsSchema, normalizeSocials} from './authorsSocials';
|
||||
import type {BlogContentPaths} from './types';
|
||||
import type {
|
||||
Author,
|
||||
|
@ -20,12 +22,13 @@ export type AuthorsMap = {[authorKey: string]: Author};
|
|||
const AuthorsMapSchema = Joi.object<AuthorsMap>()
|
||||
.pattern(
|
||||
Joi.string(),
|
||||
Joi.object({
|
||||
Joi.object<Author>({
|
||||
name: Joi.string(),
|
||||
url: URISchema,
|
||||
imageURL: URISchema,
|
||||
title: Joi.string(),
|
||||
email: Joi.string(),
|
||||
socials: AuthorSocialsSchema,
|
||||
})
|
||||
.rename('image_url', 'imageURL')
|
||||
.or('name', 'imageURL')
|
||||
|
@ -51,18 +54,32 @@ export function validateAuthorsMap(content: unknown): AuthorsMap {
|
|||
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: {
|
||||
authorsMapPath: string;
|
||||
contentPaths: BlogContentPaths;
|
||||
}): Promise<AuthorsMap | undefined> {
|
||||
return getDataFileData(
|
||||
const authorsMap = await getDataFileData(
|
||||
{
|
||||
filePath: params.authorsMapPath,
|
||||
contentPaths: params.contentPaths,
|
||||
fileType: 'authors map',
|
||||
},
|
||||
// TODO annoying to test: tightly coupled FS reads + validation...
|
||||
validateAuthorsMap,
|
||||
);
|
||||
|
||||
return authorsMap ? normalizeAuthorsMap(authorsMap) : undefined;
|
||||
}
|
||||
|
||||
type AuthorsParam = {
|
||||
|
@ -115,7 +132,7 @@ function getFrontMatterAuthorLegacy({
|
|||
function normalizeFrontMatterAuthors(
|
||||
frontMatterAuthors: BlogPostFrontMatterAuthors = [],
|
||||
): BlogPostFrontMatterAuthor[] {
|
||||
function normalizeAuthor(
|
||||
function normalizeFrontMatterAuthor(
|
||||
authorInput: string | Author,
|
||||
): BlogPostFrontMatterAuthor {
|
||||
if (typeof authorInput === 'string') {
|
||||
|
@ -128,8 +145,8 @@ function normalizeFrontMatterAuthors(
|
|||
}
|
||||
|
||||
return Array.isArray(frontMatterAuthors)
|
||||
? frontMatterAuthors.map(normalizeAuthor)
|
||||
: [normalizeAuthor(frontMatterAuthors)];
|
||||
? frontMatterAuthors.map(normalizeFrontMatterAuthor)
|
||||
: [normalizeFrontMatterAuthor(frontMatterAuthors)];
|
||||
}
|
||||
|
||||
function getFrontMatterAuthors(params: AuthorsParam): Author[] {
|
||||
|
@ -158,11 +175,11 @@ ${Object.keys(authorsMap)
|
|||
}
|
||||
|
||||
function toAuthor(frontMatterAuthor: BlogPostFrontMatterAuthor): Author {
|
||||
return {
|
||||
return normalizeAuthor({
|
||||
// Author def from authorsMap can be locally overridden by front matter
|
||||
...getAuthorsMapAuthor(frontMatterAuthor.key),
|
||||
...frontMatterAuthor,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
validateFrontMatter,
|
||||
} from '@docusaurus/utils-validation';
|
||||
import {AuthorSocialsSchema} from './authorsSocials';
|
||||
import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog';
|
||||
|
||||
const BlogPostFrontMatterAuthorSchema = Joi.object({
|
||||
|
@ -21,6 +22,7 @@ const BlogPostFrontMatterAuthorSchema = Joi.object({
|
|||
title: Joi.string(),
|
||||
url: URISchema,
|
||||
imageURL: Joi.string(),
|
||||
socials: AuthorSocialsSchema,
|
||||
})
|
||||
.or('key', 'name', 'imageURL')
|
||||
.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)[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
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;
|
||||
/**
|
||||
* Unknown keys are allowed, so that we can pass custom fields to authors,
|
||||
* e.g., `twitter`.
|
||||
* Social platforms of the author
|
||||
* 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',
|
||||
},
|
||||
'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: {
|
||||
actions: {
|
||||
eject: 'safe',
|
||||
|
|
|
@ -292,16 +292,30 @@ declare module '@theme/BlogPostItem/Header/Info' {
|
|||
}
|
||||
|
||||
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 {
|
||||
readonly author: PropBlogPostContent['metadata']['authors'][number];
|
||||
readonly author: Author;
|
||||
readonly singleAuthor: boolean;
|
||||
readonly className?: string;
|
||||
}
|
||||
|
||||
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' {
|
||||
export interface Props {
|
||||
readonly className?: string;
|
||||
|
@ -1514,6 +1528,54 @@ declare module '@theme/Icon/WordWrap' {
|
|||
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' {
|
||||
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 clsx from 'clsx';
|
||||
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 styles from './styles.module.css';
|
||||
|
||||
function MaybeLink(props: LinkProps): JSX.Element {
|
||||
if (props.href) {
|
||||
|
@ -18,12 +20,24 @@ function MaybeLink(props: LinkProps): JSX.Element {
|
|||
return <>{props.children}</>;
|
||||
}
|
||||
|
||||
function AuthorTitle({title}: {title: string}) {
|
||||
return (
|
||||
<small className={styles.authorTitle} title={title}>
|
||||
{title}
|
||||
</small>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BlogPostItemHeaderAuthor({
|
||||
// singleAuthor, // may be useful in the future, or for swizzle users
|
||||
author,
|
||||
className,
|
||||
}: 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 hasSocials = socials && Object.keys(socials).length > 0;
|
||||
|
||||
return (
|
||||
<div className={clsx('avatar margin-bottom--sm', className)}>
|
||||
{imageURL && (
|
||||
|
@ -32,14 +46,15 @@ export default function BlogPostItemHeaderAuthor({
|
|||
</MaybeLink>
|
||||
)}
|
||||
|
||||
{name && (
|
||||
{(name || title) && (
|
||||
<div className="avatar__intro">
|
||||
<div className="avatar__name">
|
||||
<MaybeLink href={link}>
|
||||
<span>{name}</span>
|
||||
<span className={styles.authorName}>{name}</span>
|
||||
</MaybeLink>
|
||||
</div>
|
||||
{title && <small className="avatar__subtitle">{title}</small>}
|
||||
{!!title && <AuthorTitle title={title} />}
|
||||
{hasSocials && <AuthorSocials author={author} />}
|
||||
</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;
|
||||
}
|
||||
const imageOnly = authors.every(({name}) => !name);
|
||||
const singleAuthor = authors.length === 1;
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
|
@ -35,11 +36,12 @@ export default function BlogPostItemHeaderAuthors({
|
|||
{authors.map((author, idx) => (
|
||||
<div
|
||||
className={clsx(
|
||||
!imageOnly && 'col col--6',
|
||||
!imageOnly && (singleAuthor ? 'col col--12' : 'col col--6'),
|
||||
imageOnly ? styles.imageOnlyAuthorCol : styles.authorCol,
|
||||
)}
|
||||
key={idx}>
|
||||
<BlogPostItemHeaderAuthor
|
||||
singleAuthor={singleAuthor}
|
||||
author={{
|
||||
...author,
|
||||
// Handle author images using relative paths
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
.authorCol {
|
||||
max-width: inherit !important;
|
||||
flex-grow: 1 !important;
|
||||
}
|
||||
|
||||
.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 {
|
||||
const contentString = await fs.readFile(filePath, {encoding: 'utf8'});
|
||||
const unsafeContent = Yaml.load(contentString);
|
||||
// TODO we shouldn't validate here: it makes validation harder to test
|
||||
return validate(unsafeContent);
|
||||
} catch (err) {
|
||||
logger.error`The ${params.fileType} file at path=${filePath} looks invalid.`;
|
||||
|
|
|
@ -145,6 +145,7 @@ javadoc
|
|||
jiti
|
||||
jmarcey
|
||||
jodyheavener
|
||||
joelmarcey
|
||||
joshcena
|
||||
jssdk
|
||||
Kaszubowski
|
||||
|
@ -235,6 +236,7 @@ outerbounds
|
|||
Outerbounds
|
||||
overrideable
|
||||
ozaki
|
||||
ozakione
|
||||
pageview
|
||||
palenight
|
||||
Palenight
|
||||
|
@ -330,6 +332,7 @@ Solana
|
|||
spâce
|
||||
stackblitz
|
||||
stackblitzrc
|
||||
stackoverflow
|
||||
Stormkit
|
||||
Strikethrough
|
||||
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:
|
||||
'https://github.com/facebook/docusaurus/edit/main/website/_dogfooding/_blog-tests',
|
||||
postsPerPage: 3,
|
||||
blogSidebarCount: 'ALL',
|
||||
feedOptions: {
|
||||
type: 'all',
|
||||
title: 'Docusaurus Tests Blog',
|
||||
|
|
|
@ -4,31 +4,39 @@ JMarcey:
|
|||
url: https://twitter.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
email: jimarcey@gmail.com
|
||||
twitter: JoelMarcey
|
||||
socials:
|
||||
x: joelmarcey
|
||||
github: JoelMarcey
|
||||
|
||||
zpao:
|
||||
name: Paul O’Shannessy
|
||||
title: Engineering Manager at Meta
|
||||
url: https://twitter.com/zpao
|
||||
url: https://x.com/zpao
|
||||
image_url: https://github.com/zpao.png
|
||||
email: jimarcey@gmail.com
|
||||
twitter: zpao
|
||||
socials:
|
||||
x: zpao
|
||||
github: zpao
|
||||
|
||||
slorber:
|
||||
name: Sébastien Lorber
|
||||
title: Docusaurus maintainer, This Week In React editor
|
||||
url: https://thisweekinreact.com
|
||||
image_url: https://github.com/slorber.png
|
||||
twitter: sebastienlorber
|
||||
email: sebastien@thisweekinreact.com
|
||||
socials:
|
||||
x: sebastienlorber
|
||||
linkedin: sebastienlorber
|
||||
github: slorber
|
||||
newsletter: https://thisweekinreact.com
|
||||
|
||||
yangshun:
|
||||
name: Yangshun Tay
|
||||
title: Front End Engineer at Meta
|
||||
url: https://github.com/yangshun
|
||||
image_url: https://github.com/yangshun.png
|
||||
twitter: yangshunz
|
||||
email: tay.yang.shun@gmail.com
|
||||
socials:
|
||||
x: yangshunz
|
||||
github: yangshun
|
||||
|
||||
lex111:
|
||||
name: Alexey Pyltsyn
|
||||
|
@ -49,17 +57,19 @@ endiliey:
|
|||
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
|
||||
url: http://x.com/abernathyca
|
||||
image_url: https://github.com/caabernathy.png
|
||||
twitter: abernathyca
|
||||
socials:
|
||||
x: abernathyca
|
||||
|
||||
shortcuts:
|
||||
name: Clément Vannicatte
|
||||
title: Software Engineer @ Algolia
|
||||
url: https://github.com/shortcuts
|
||||
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
|
||||
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 = {
|
||||
key?: AuthorKey;
|
||||
name: string;
|
||||
title?: string;
|
||||
url?: string;
|
||||
image_url?: string;
|
||||
socials?: AuthorSocials;
|
||||
};
|
||||
|
||||
// The front matter authors field allows various possible shapes
|
||||
|
@ -275,6 +282,9 @@ authors:
|
|||
title: Co-creator of Docusaurus 1
|
||||
url: https://github.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
socials:
|
||||
x: joelmarcey
|
||||
github: JoelMarcey
|
||||
tags: [docusaurus]
|
||||
description: This is my first post on Docusaurus.
|
||||
image: https://i.imgur.com/mErPwqL.png
|
||||
|
|
|
@ -52,10 +52,16 @@ authors:
|
|||
title: Co-creator of Docusaurus 1
|
||||
url: https://github.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
socials:
|
||||
x: joelmarcey
|
||||
github: JoelMarcey
|
||||
- name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
socials:
|
||||
x: sebastienlorber
|
||||
github: slorber
|
||||
tags: [hello, docusaurus-v2]
|
||||
image: https://i.imgur.com/mErPwqL.png
|
||||
hide_table_of_contents: false
|
||||
|
@ -214,6 +220,9 @@ authors:
|
|||
url: https://github.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
email: jimarcey@gmail.com
|
||||
socials:
|
||||
x: joelmarcey
|
||||
github: JoelMarcey
|
||||
---
|
||||
```
|
||||
|
||||
|
@ -230,10 +239,16 @@ authors:
|
|||
url: https://github.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
email: jimarcey@gmail.com
|
||||
socials:
|
||||
x: joelmarcey
|
||||
github: JoelMarcey
|
||||
- name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
socials:
|
||||
x: sebastienlorber
|
||||
github: slorber
|
||||
---
|
||||
```
|
||||
|
||||
|
@ -276,12 +291,18 @@ jmarcey:
|
|||
url: https://github.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
email: jimarcey@gmail.com
|
||||
socials:
|
||||
x: joelmarcey
|
||||
github: JoelMarcey
|
||||
|
||||
slorber:
|
||||
name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
socials:
|
||||
x: sebastienlorber
|
||||
github: slorber
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue