blog/src/theme/BlogPostPage/index.tsx (126 lines of code) (raw):
/**
* 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 { HTMLAttributes } from 'react';
import React from 'react';
import Seo from '@theme/Seo';
import BlogLayout from '@theme/BlogLayout';
import BlogPostItem from '@theme-original/BlogPostItem';
import type { Props } from '@theme-original/BlogPostPage';
// eslint-disable-next-line import/no-extraneous-dependencies
import { ThemeClassNames } from '@docusaurus/theme-common';
import MDXComponents from '@theme-original/MDXComponents';
import type { ImageProps } from 'rc-image';
import Image from 'rc-image';
// eslint-disable-next-line import/no-extraneous-dependencies
import { MDXProvider } from '@mdx-js/react';
import { parseSrcset, stringifySrcset } from 'srcset';
import BlogPostPaginator from '../BlogPostPaginator';
const urlParse = (url: string) => {
const urlParseArr = url.split('/');
const name = urlParseArr.at(-1) as string;
return {
host: urlParseArr.slice(0, 3).join('/'),
folderPath: urlParseArr.slice(3, -1).join('/'),
name,
ext: name.split('.').at(-1) as string,
};
};
const imgPropsParse = (
props: Omit<ImageProps, 'placeholder'>,
Placeholder: |
((props: ImageProps) => JSX.Element) |
((props: HTMLAttributes<HTMLImageElement>) => JSX.Element),
): any => {
const {
src, srcSet, ...restProps
} = props;
const isFromCDN = src?.includes('static.apis');
if (!isFromCDN || !src || !['png', 'jpg', 'jpeg'].some((s) => src.endsWith(s))) {
return props;
}
const otherProps = {};
const u = urlParse(src);
const webpSrc = `${u.host}/apisix-webp/${u.folderPath}/${u.name.replace(u.ext, 'webp')}`;
const thumbnailWebpSrc = `${u.host}/apisix-thumbnail/${u.folderPath}/${u.name.replace(u.ext, 'webp')}`;
Object.assign(otherProps, {
src: webpSrc,
type: 'image/webp',
srcSet: stringifySrcset([
...parseSrcset(srcSet || ''),
{
url: webpSrc,
}, {
url: src,
},
]),
placeholderSrc: thumbnailWebpSrc,
placeholder: <Placeholder
{...restProps}
src={thumbnailWebpSrc}
srcSet={stringifySrcset([{
url: thumbnailWebpSrc,
}, {
url: `${u.host}/apisix-thumbnail/${u.folderPath}/${u.name}`,
}])}
/>,
});
return { ...restProps, ...otherProps };
};
const components = {
...MDXComponents,
img: (props: ImageProps) => (
<Image
{...imgPropsParse(props, (ps) => <Image {...ps} preview={false} />)}
preview={{ mask: 'Click to Preview' }}
loading="lazy"
/>
),
};
const BlogPostPage = (props: Props): JSX.Element => {
const { content: BlogPostContents, sidebar } = props;
const { frontMatter, assets, metadata } = BlogPostContents;
const {
title, description, nextItem, prevItem, date, tags, authors,
} = metadata;
const { hide_table_of_contents: hideTableOfContents, keywords } = frontMatter;
const image = assets.image ?? frontMatter.image;
return (
<BlogLayout
wrapperClassName={ThemeClassNames.wrapper.blogPages}
pageClassName={ThemeClassNames.page.blogPostPage}
sidebar={sidebar}
toc={!hideTableOfContents && BlogPostContents.toc ? BlogPostContents.toc : undefined}
frontMatter={frontMatter}
metadata={metadata}
>
<Seo
// TODO refactor needed: it's a bit annoying but Seo MUST be inside BlogLayout
// otherwise default image (set by BlogLayout) would shadow the custom blog post image
title={title}
description={description}
keywords={keywords}
image={image}
>
<meta property="og:type" content="article" />
<meta property="article:published_time" content={date} />
{/* TODO double check those article metas array syntaxes, see https://ogp.me/#array */}
{authors.some((author) => author.url) && (
<meta
property="article:author"
content={authors
.map((author) => author.url)
.filter(Boolean)
.join(',')}
/>
)}
{tags.length > 0 && (
<meta property="article:tag" content={tags.map((tag) => tag.label).join(',')} />
)}
</Seo>
<BlogPostItem frontMatter={frontMatter} assets={assets} metadata={metadata} isBlogPostPage>
<MDXProvider components={components}>
<BlogPostContents />
</MDXProvider>
</BlogPostItem>
{(nextItem || prevItem) && <BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />}
</BlogLayout>
);
};
export { imgPropsParse };
export default BlogPostPage;