mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-28 05:58:38 +02:00
fix(blog): apply baseUrl to relative image in blog authors (#10440)
Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
parent
95880282b1
commit
02ed7d9132
7 changed files with 333 additions and 10 deletions
|
@ -1,4 +1,3 @@
|
|||
|
||||
JMarcey:
|
||||
name: Joel Marcey
|
||||
title: Technical Lead & Developer Advocate at Facebook
|
||||
|
@ -32,3 +31,13 @@ lex111:
|
|||
title: Open-source enthusiast
|
||||
url: https://github.com/lex111
|
||||
image_url: https://github.com/lex111.png
|
||||
|
||||
ozaki:
|
||||
name: ozaki
|
||||
title: ozaki
|
||||
image_url: /ozaki.png
|
||||
|
||||
ozakione:
|
||||
name: ozakione
|
||||
title: ozakione
|
||||
image_url: /img/ozaki.png
|
||||
|
|
|
@ -216,7 +216,7 @@ describe('getBlogPostAuthors', () => {
|
|||
authorsMap: {
|
||||
slorber: {
|
||||
name: 'Sébastien Lorber',
|
||||
imageURL: '/img/slorber.png',
|
||||
imageURL: '/baseUrl/img/slorber.png',
|
||||
key: 'slorber',
|
||||
page: null,
|
||||
},
|
||||
|
@ -419,7 +419,6 @@ describe('getBlogPostAuthors', () => {
|
|||
frontMatter: {
|
||||
authors: ['yangshun', 'jmarcey', 'slorber'],
|
||||
},
|
||||
|
||||
authorsMap: {
|
||||
yangshun: {name: 'Yangshun Tay', key: 'yangshun', page: null},
|
||||
jmarcey: {name: 'Joel Marcey', key: 'jmarcey', page: null},
|
||||
|
@ -486,6 +485,235 @@ describe('getBlogPostAuthors', () => {
|
|||
Don't mix 'authors' with other existing 'author_*' front matter. Choose one or the other, not both at the same time."
|
||||
`);
|
||||
});
|
||||
|
||||
// Global author without baseUrl
|
||||
it('getBlogPostAuthors do not modify global authors imageUrl without baseUrl', async () => {
|
||||
expect(
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: ['ozaki'],
|
||||
},
|
||||
authorsMap: {
|
||||
ozaki: {
|
||||
key: 'ozaki',
|
||||
imageURL: '/ozaki.png',
|
||||
page: null,
|
||||
},
|
||||
},
|
||||
baseUrl: '/',
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
imageURL: '/ozaki.png',
|
||||
key: 'ozaki',
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Global author with baseUrl
|
||||
it('getBlogPostAuthors do not modify global authors imageUrl with baseUrl', async () => {
|
||||
expect(
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: ['ozaki'],
|
||||
},
|
||||
authorsMap: {
|
||||
ozaki: {
|
||||
key: 'ozaki',
|
||||
imageURL: '/img/ozaki.png',
|
||||
page: null,
|
||||
},
|
||||
},
|
||||
baseUrl: '/img/',
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
imageURL: '/img/ozaki.png',
|
||||
key: 'ozaki',
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Global author without baseUrl with a subfolder in img
|
||||
it('getBlogPostAuthors do not modify globalAuthor imageUrl with subfolder without baseUrl', async () => {
|
||||
expect(
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: ['ozaki'],
|
||||
},
|
||||
authorsMap: {
|
||||
ozaki: {
|
||||
key: 'ozaki',
|
||||
imageURL: '/img/ozaki.png',
|
||||
page: null,
|
||||
},
|
||||
},
|
||||
baseUrl: '/',
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
imageURL: '/img/ozaki.png',
|
||||
key: 'ozaki',
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Global author with baseUrl with a subfolder in img
|
||||
it('getBlogPostAuthors do not modify globalAuthor imageUrl with subfolder with baseUrl', async () => {
|
||||
expect(
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: ['ozaki'],
|
||||
},
|
||||
authorsMap: {
|
||||
ozaki: {
|
||||
key: 'ozaki',
|
||||
imageURL: '/img/ozaki.png',
|
||||
page: null,
|
||||
},
|
||||
},
|
||||
baseUrl: '/img/',
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
imageURL: '/img/ozaki.png',
|
||||
key: 'ozaki',
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('getBlogPostAuthors throws if global author imageURL does not have baseUrl', async () => {
|
||||
expect(() =>
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: ['ozaki'],
|
||||
},
|
||||
authorsMap: {
|
||||
ozaki: {
|
||||
key: 'ozaki',
|
||||
imageURL: '/ozaki.png',
|
||||
page: null,
|
||||
},
|
||||
},
|
||||
baseUrl: '/baseUrl/',
|
||||
}),
|
||||
).toThrowErrorMatchingInlineSnapshot(
|
||||
`"Docusaurus internal bug: global authors image /ozaki.png should start with the expected baseUrl=/baseUrl/"`,
|
||||
);
|
||||
});
|
||||
|
||||
it('getBlogPostAuthors do not throws if inline author imageURL is a link to a file', async () => {
|
||||
const baseUrlTest = getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: [{imageURL: './ozaki.png'}],
|
||||
},
|
||||
authorsMap: undefined,
|
||||
baseUrl: '/baseUrl/',
|
||||
});
|
||||
const withoutBaseUrlTest = getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: [{imageURL: './ozaki.png'}],
|
||||
},
|
||||
authorsMap: undefined,
|
||||
baseUrl: '/',
|
||||
});
|
||||
expect(() => baseUrlTest).not.toThrow();
|
||||
expect(baseUrlTest).toEqual([
|
||||
{
|
||||
imageURL: './ozaki.png',
|
||||
key: null,
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
expect(() => withoutBaseUrlTest).not.toThrow();
|
||||
expect(withoutBaseUrlTest).toEqual([
|
||||
{
|
||||
imageURL: './ozaki.png',
|
||||
key: null,
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Inline author without baseUrl
|
||||
it('getBlogPostAuthors can return imageURL without baseUrl for inline authors', async () => {
|
||||
expect(
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: [{imageURL: '/ozaki.png'}],
|
||||
},
|
||||
authorsMap: undefined,
|
||||
baseUrl: '/',
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
imageURL: '/ozaki.png',
|
||||
key: null,
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Inline author with baseUrl
|
||||
it('getBlogPostAuthors normalize imageURL with baseUrl for inline authors', async () => {
|
||||
expect(
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: [{imageURL: '/ozaki.png'}],
|
||||
},
|
||||
authorsMap: undefined,
|
||||
baseUrl: '/img/',
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
imageURL: '/img/ozaki.png',
|
||||
key: null,
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Inline author without baseUrl with a subfolder in img
|
||||
it('getBlogPostAuthors normalize imageURL from subfolder without baseUrl for inline authors', async () => {
|
||||
expect(
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: [{imageURL: '/img/ozaki.png'}],
|
||||
},
|
||||
authorsMap: undefined,
|
||||
baseUrl: '/',
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
imageURL: '/img/ozaki.png',
|
||||
key: null,
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
// Inline author with baseUrl with a subfolder in img
|
||||
it('getBlogPostAuthors normalize imageURL from subfolder with baseUrl for inline authors', async () => {
|
||||
expect(
|
||||
getBlogPostAuthors({
|
||||
frontMatter: {
|
||||
authors: [{imageURL: '/img/ozaki.png'}],
|
||||
},
|
||||
authorsMap: undefined,
|
||||
baseUrl: '/img/',
|
||||
}),
|
||||
).toEqual([
|
||||
{
|
||||
imageURL: '/img/img/ozaki.png',
|
||||
key: null,
|
||||
page: null,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('groupBlogPostsByAuthorKey', () => {
|
||||
|
|
|
@ -80,6 +80,7 @@ describe('getAuthorsMap', () => {
|
|||
contentPaths,
|
||||
authorsMapPath: 'authors.yml',
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/',
|
||||
}),
|
||||
).resolves.toBeDefined();
|
||||
});
|
||||
|
@ -90,6 +91,7 @@ describe('getAuthorsMap', () => {
|
|||
contentPaths,
|
||||
authorsMapPath: 'authors.json',
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/',
|
||||
}),
|
||||
).resolves.toBeDefined();
|
||||
});
|
||||
|
@ -100,9 +102,60 @@ describe('getAuthorsMap', () => {
|
|||
contentPaths,
|
||||
authorsMapPath: 'authors_does_not_exist.yml',
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/',
|
||||
}),
|
||||
).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it('getAuthorsMap return imageURL with relative path', async () => {
|
||||
const authorsMap = await getAuthorsMap({
|
||||
contentPaths,
|
||||
authorsMapPath: 'authors.yml',
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/',
|
||||
});
|
||||
expect(authorsMap?.ozaki?.imageURL).toBe('/ozaki.png');
|
||||
});
|
||||
|
||||
it('getAuthorsMap normalize imageURL with baseUrl', async () => {
|
||||
const authorsMap = await getAuthorsMap({
|
||||
contentPaths,
|
||||
authorsMapPath: 'authors.yml',
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/baseUrl/',
|
||||
});
|
||||
expect(authorsMap?.ozaki?.imageURL).toBe('/baseUrl/ozaki.png');
|
||||
});
|
||||
|
||||
it('getAuthorsMap return imageURL with relative subdir path', async () => {
|
||||
const authorsMap = await getAuthorsMap({
|
||||
contentPaths,
|
||||
authorsMapPath: 'authors.yml',
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/',
|
||||
});
|
||||
expect(authorsMap?.ozakione?.imageURL).toBe('/img/ozaki.png');
|
||||
});
|
||||
|
||||
it('getAuthorsMap normalize imageURL with baseUrl and subdir same value', async () => {
|
||||
const authorsMap = await getAuthorsMap({
|
||||
contentPaths,
|
||||
authorsMapPath: 'authors.yml',
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/img/',
|
||||
});
|
||||
expect(authorsMap?.ozakione?.imageURL).toBe('/img/img/ozaki.png');
|
||||
});
|
||||
|
||||
it('getAuthorsMap normalize imageURL subdir with baseUrl', async () => {
|
||||
const authorsMap = await getAuthorsMap({
|
||||
contentPaths,
|
||||
authorsMapPath: 'authors.yml',
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/blog/',
|
||||
});
|
||||
expect(authorsMap?.ozakione?.imageURL).toBe('/blog/img/ozaki.png');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateAuthorsMapInput', () => {
|
||||
|
|
|
@ -67,6 +67,7 @@ async function testGenerateFeeds(
|
|||
contentPaths,
|
||||
authorsMapPath: options.authorsMapPath,
|
||||
authorsBaseRoutePath: '/authors',
|
||||
baseUrl: '/',
|
||||
});
|
||||
|
||||
const blogPosts = await generateBlogPosts(
|
||||
|
|
|
@ -21,18 +21,41 @@ type AuthorsParam = {
|
|||
baseUrl: string;
|
||||
};
|
||||
|
||||
function normalizeImageUrl({
|
||||
export function normalizeImageUrl({
|
||||
imageURL,
|
||||
baseUrl,
|
||||
}: {
|
||||
imageURL: string | undefined;
|
||||
baseUrl: string;
|
||||
}) {
|
||||
}): string | undefined {
|
||||
return imageURL?.startsWith('/')
|
||||
? normalizeUrl([baseUrl, imageURL])
|
||||
: imageURL;
|
||||
}
|
||||
|
||||
function normalizeAuthorUrl({
|
||||
author,
|
||||
baseUrl,
|
||||
}: {
|
||||
author: Author;
|
||||
baseUrl: string;
|
||||
}): string | undefined {
|
||||
if (author.key) {
|
||||
// Ensures invariant: global authors should have already been normalized
|
||||
if (
|
||||
author.imageURL?.startsWith('/') &&
|
||||
!author.imageURL.startsWith(baseUrl)
|
||||
) {
|
||||
throw new Error(
|
||||
`Docusaurus internal bug: global authors image ${author.imageURL} should start with the expected baseUrl=${baseUrl}`,
|
||||
);
|
||||
}
|
||||
|
||||
return author.imageURL;
|
||||
}
|
||||
return normalizeImageUrl({imageURL: author.imageURL, baseUrl});
|
||||
}
|
||||
|
||||
// Legacy v1/early-v2 front matter fields
|
||||
// We may want to deprecate those in favor of using only frontMatter.authors
|
||||
// TODO Docusaurus v4: remove this legacy front matter
|
||||
|
@ -116,13 +139,14 @@ ${Object.keys(authorsMap)
|
|||
// Author def from authorsMap can be locally overridden by front matter
|
||||
...getAuthorsMapAuthor(frontMatterAuthor.key),
|
||||
...frontMatterAuthor,
|
||||
};
|
||||
} as Author;
|
||||
|
||||
return {
|
||||
...author,
|
||||
key: author.key ?? null,
|
||||
page: author.page ?? null,
|
||||
imageURL: normalizeImageUrl({imageURL: author.imageURL, baseUrl}),
|
||||
// global author images have already been normalized
|
||||
imageURL: normalizeAuthorUrl({author, baseUrl}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,13 @@ import _ from 'lodash';
|
|||
import {readDataFile, normalizeUrl} from '@docusaurus/utils';
|
||||
import {Joi, URISchema} from '@docusaurus/utils-validation';
|
||||
import {AuthorSocialsSchema, normalizeSocials} from './authorsSocials';
|
||||
import {normalizeImageUrl} from './authors';
|
||||
import type {BlogContentPaths} from './types';
|
||||
import type {
|
||||
Author,
|
||||
AuthorAttributes,
|
||||
AuthorPage,
|
||||
AuthorsMap,
|
||||
AuthorWithKey,
|
||||
} from '@docusaurus/plugin-content-blog';
|
||||
|
||||
type AuthorInput = AuthorAttributes & {
|
||||
|
@ -93,12 +94,14 @@ export function checkAuthorsMapPermalinkCollisions(
|
|||
function normalizeAuthor({
|
||||
authorsBaseRoutePath,
|
||||
authorKey,
|
||||
baseUrl,
|
||||
author,
|
||||
}: {
|
||||
authorsBaseRoutePath: string;
|
||||
authorKey: string;
|
||||
baseUrl: string;
|
||||
author: AuthorInput;
|
||||
}): Author & {key: string} {
|
||||
}): AuthorWithKey {
|
||||
function getAuthorPage(): AuthorPage | null {
|
||||
if (!author.page) {
|
||||
return null;
|
||||
|
@ -114,6 +117,7 @@ function normalizeAuthor({
|
|||
...author,
|
||||
key: authorKey,
|
||||
page: getAuthorPage(),
|
||||
imageURL: normalizeImageUrl({imageURL: author.imageURL, baseUrl}),
|
||||
socials: author.socials ? normalizeSocials(author.socials) : undefined,
|
||||
};
|
||||
}
|
||||
|
@ -121,12 +125,14 @@ function normalizeAuthor({
|
|||
function normalizeAuthorsMap({
|
||||
authorsBaseRoutePath,
|
||||
authorsMapInput,
|
||||
baseUrl,
|
||||
}: {
|
||||
authorsBaseRoutePath: string;
|
||||
authorsMapInput: AuthorsMapInput;
|
||||
baseUrl: string;
|
||||
}): AuthorsMap {
|
||||
return _.mapValues(authorsMapInput, (author, authorKey) => {
|
||||
return normalizeAuthor({authorsBaseRoutePath, authorKey, author});
|
||||
return normalizeAuthor({authorsBaseRoutePath, authorKey, author, baseUrl});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -153,6 +159,7 @@ export async function getAuthorsMap(params: {
|
|||
authorsMapPath: string;
|
||||
authorsBaseRoutePath: string;
|
||||
contentPaths: BlogContentPaths;
|
||||
baseUrl: string;
|
||||
}): Promise<AuthorsMap | undefined> {
|
||||
const authorsMapInput = await getAuthorsMapInput(params);
|
||||
if (!authorsMapInput) {
|
||||
|
|
|
@ -271,6 +271,7 @@ export default async function pluginContentBlog(
|
|||
routeBasePath,
|
||||
authorsBasePath,
|
||||
]),
|
||||
baseUrl,
|
||||
});
|
||||
checkAuthorsMapPermalinkCollisions(authorsMap);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue