astro-loader-obsidian
Version:
Use your Obsidian Vault in Astro projects
104 lines (79 loc) • 3.21 kB
text/typescript
import he from "he";
import type { Wikilink } from "./wikiLink";
import type { AstroIntegrationLogger } from "astro";
const imageDisplayClassNames: Record<string, string> = {
'float-left': 'figure-image-float-left',
'float-right': 'figure-image-float-right',
'left': 'figure-image-float-left',
'right': 'figure-image-float-right',
};
const imageSizing = (text: string): {
width: number;
height?: number;
}|null => {
if (!Number.isNaN(+text)) {
return { width: Number.parseInt(text) };
}
const [w, h] = text.split('x') as [string, string];
if (!w || !h) {
return null;
}
if (!Number.isNaN(+w) && !Number.isNaN(+h)) {
return { width: Number.parseInt(w as string), height: Number.parseInt(h as string) };
}
return null;
}
const URL_REGEX = /(?<!@)\b(?:https?:\/\/(?:[\w]*:[\w]*@)?)?[\w\-\.]+\.(?:com|org|co\.uk|net|de|com\.br|io|com\.au|it|fr|nl|co|in|ca|github\.io|co\.jp|ch|se|es|eu|edu|ai|be|me|us|blogspot\.com|jp|dk|ru|app|co\.za|dev|pl|co\.kr|co\.in|com\.mx|no|info|pt|fi|cn|cz|tech|org\.uk|ro|biz|ie|gov|co\.nz|com\.cn|xyz|com\.tr|gr|tv|mx|ae|at)\b(?:\/[\w.,?^=%&:\/~+#\-]*[\w?^=%&\/~+#\-])?/gim;
const captionChildren = (caption: string): string => {
const matches = Array.from(caption.matchAll(URL_REGEX));
if (matches.length === 0) {
return caption;
}
let captionTpl = caption;
for (const match of matches) {
const [url] = match;
const urlObj = new URL(url.startsWith('http') ? url : `https://${url}`);
captionTpl = captionTpl.replace(url, `<a href="${urlObj.toString()}">${urlObj.hostname}</a>`);
}
return captionTpl;
}
export const renderImage = async (
rendered: {
metadata: {
imagePaths: string[],
}
},
link: Wikilink,
logger: AstroIntegrationLogger
) => {
const title = (link.link.href as string).split('/').reverse()?.[0];
let caption: string|undefined = undefined;
const altFragments = ((link.link.caption as string) ?? '').split('|');
const figureProps: Record<string, unknown> = {
alt: title,
class: 'figure-image',
};
const imageProps: Record<string, unknown> = {
src: link.link.href,
alt: link.link.title,
index: rendered.metadata.imagePaths.indexOf(link.link.href as string)
};
for (const fragment of altFragments) {
const sizing = imageSizing(fragment);
if (sizing) {
imageProps.width = sizing.width ?? undefined;
imageProps.height = sizing.height ?? undefined;
figureProps.style = imageProps.style = `${sizing.width ? `width: ${sizing.width}px; ` : ''}${sizing.height ? `height: ${sizing.height}px; ` : ''}`
continue;
}
const layoutClassName = imageDisplayClassNames[fragment];
if (layoutClassName) {
figureProps.class = [figureProps.class, layoutClassName].join(' ');
continue;
}
caption = fragment;
}
const imageElement = `<img __ASTRO_IMAGE_="${he.encode(JSON.stringify(imageProps))}" />`;
const captionElement = caption && caption !== title ? `<figcaption>${captionChildren(caption)}</figcaption>` : ''
return `<figure alt="${figureProps.alt}" class="${figureProps.class}" style="${figureProps.style ?? ''}">${imageElement}${captionElement}</figure>`;
}