2024-06-14 02:11:26 +08:00
|
|
|
import type { Literal, Node, Parent } from 'unist';
|
2024-06-21 04:17:36 +08:00
|
|
|
import { selectAll } from 'unist-util-select';
|
|
|
|
import options from '../options';
|
2024-06-14 02:11:26 +08:00
|
|
|
import { imageMetadata } from '../src/helpers/images';
|
2024-06-21 04:17:36 +08:00
|
|
|
import { urlJoin } from '../src/helpers/tools';
|
2024-06-14 02:11:26 +08:00
|
|
|
|
2024-06-21 04:17:36 +08:00
|
|
|
type ImageNode = Parent & {
|
2024-06-14 02:11:26 +08:00
|
|
|
url: string;
|
|
|
|
alt: string;
|
|
|
|
name: string;
|
|
|
|
width?: number;
|
|
|
|
height?: number;
|
|
|
|
attributes: (Literal & { name: string })[];
|
|
|
|
};
|
|
|
|
|
2024-06-21 04:17:36 +08:00
|
|
|
type MdxJsxAttribute = {
|
|
|
|
type: 'mdxJsxAttribute';
|
|
|
|
name: string;
|
|
|
|
value: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
type MdxJsxFlowElement = Parent & {
|
|
|
|
name: string;
|
|
|
|
attributes: MdxJsxAttribute[];
|
|
|
|
};
|
|
|
|
|
2024-06-14 02:11:26 +08:00
|
|
|
export const astroImage = () => {
|
|
|
|
return async (tree: Node) => {
|
2024-06-21 04:17:36 +08:00
|
|
|
// Find all the img node.
|
|
|
|
const imgs = selectAll('mdxJsxFlowElement', tree)
|
|
|
|
.map((node) => node as MdxJsxFlowElement)
|
|
|
|
.filter((node) => node.name === 'img');
|
|
|
|
for (const img of imgs) {
|
|
|
|
const srcAttribute = img.attributes.find((attribute) => attribute.name === 'src');
|
|
|
|
if (srcAttribute) {
|
|
|
|
const src = srcAttribute.value;
|
|
|
|
if (src.startsWith('/')) {
|
|
|
|
srcAttribute.value = urlJoin(options.assetsPrefix(), src);
|
|
|
|
}
|
2024-06-14 02:11:26 +08:00
|
|
|
}
|
2024-06-21 04:17:36 +08:00
|
|
|
}
|
2024-06-14 02:11:26 +08:00
|
|
|
|
2024-06-21 04:17:36 +08:00
|
|
|
// Find all the image node.
|
|
|
|
const imageNodes = selectAll('image', tree)
|
|
|
|
.map((node) => node as ImageNode)
|
|
|
|
.filter((imageNode) => !imageNode.url.startsWith('http'))
|
|
|
|
.map(transformAstroImage);
|
2024-06-14 02:11:26 +08:00
|
|
|
|
2024-06-21 04:17:36 +08:00
|
|
|
// Process image with blur metadata.
|
|
|
|
await Promise.all(imageNodes);
|
2024-06-14 02:11:26 +08:00
|
|
|
return tree;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const transformAstroImage = async (imageNode: ImageNode) => {
|
|
|
|
imageNode.type = 'mdxJsxFlowElement';
|
|
|
|
imageNode.name = 'Image';
|
2024-06-21 04:17:36 +08:00
|
|
|
|
|
|
|
try {
|
|
|
|
const metadata = await imageMetadata(imageNode.url);
|
|
|
|
if (metadata == null) {
|
|
|
|
throw new Error(`Failed to get image metadata: ${imageNode.url}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
imageNode.attributes = [
|
|
|
|
{ type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
|
|
|
|
{ type: 'mdxJsxAttribute', name: 'src', value: metadata.src },
|
|
|
|
{ type: 'mdxJsxAttribute', name: 'width', value: imageNode.width ?? metadata.width },
|
|
|
|
{ type: 'mdxJsxAttribute', name: 'height', value: imageNode.height ?? metadata.height },
|
|
|
|
{ type: 'mdxJsxAttribute', name: 'blurDataURL', value: metadata.blurDataURL },
|
|
|
|
{ type: 'mdxJsxAttribute', name: 'blurWidth', value: metadata.blurWidth },
|
|
|
|
{ type: 'mdxJsxAttribute', name: 'blurHeight', value: metadata.blurHeight },
|
|
|
|
];
|
|
|
|
} catch (error) {
|
|
|
|
imageNode.attributes = [
|
|
|
|
{ type: 'mdxJsxAttribute', name: 'alt', value: imageNode.alt },
|
|
|
|
{
|
|
|
|
type: 'mdxJsxAttribute',
|
|
|
|
name: 'src',
|
|
|
|
value: imageNode.url.startsWith('/') ? urlJoin(options.assetsPrefix(), imageNode.url) : imageNode.url,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
}
|
2024-06-14 02:11:26 +08:00
|
|
|
};
|