fix(blog): apply baseUrl to relative image in blog authors (#10440)

Co-authored-by: sebastien <lorber.sebastien@gmail.com>
This commit is contained in:
ozaki 2024-08-29 14:40:42 +02:00 committed by GitHub
parent 95880282b1
commit 02ed7d9132
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 333 additions and 10 deletions

View file

@ -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

View file

@ -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', () => {

View file

@ -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', () => {

View file

@ -67,6 +67,7 @@ async function testGenerateFeeds(
contentPaths,
authorsMapPath: options.authorsMapPath,
authorsBaseRoutePath: '/authors',
baseUrl: '/',
});
const blogPosts = await generateBlogPosts(

View file

@ -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}),
};
}
}

View file

@ -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) {

View file

@ -271,6 +271,7 @@ export default async function pluginContentBlog(
routeBasePath,
authorsBasePath,
]),
baseUrl,
});
checkAuthorsMapPermalinkCollisions(authorsMap);