@yozora/gatsby-images
Version:
Transform markdown files to Yozora AST
138 lines (133 loc) • 5.11 kB
JavaScript
import { ImageReferenceType, ImageType } from '@yozora/ast';
import { calcDefinitionMap, traverseAst } from '@yozora/ast-util';
import chalk from 'chalk';
import { fluid } from 'gatsby-plugin-sharp';
import path from 'node:path';
import queryString from 'query-string';
const DEFAULT_OPTIONS = {
maxWidth: 650,
wrapperStyle: '',
backgroundColor: 'white',
decoding: 'async',
disableBgImageOnAlpha: false,
disableBgImage: false,
linkImagesToOriginal: true,
loading: 'lazy',
markdownCaptions: false,
showCaptions: false,
withWebp: false,
withAvif: false,
tracedSVG: false,
};
const EMPTY_ALT = 'YOZORA_EMPTY_ALT';
const supportedImgExts = new Set([
'jpeg',
'jpg',
'png',
'webp',
'tif',
'tiff',
'avif',
]);
function getImageInfo(uri) {
const { url, query } = queryString.parseUrl(uri);
const ext = path.extname(url).split(`.`).pop() ?? '';
return { ext, url, query };
}
function isRelativeUrl(url) {
if (/^[a-zA-Z]:\\/.test(url))
return true;
return !/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(url);
}
function slash(urlPath) {
const isExtendedLengthPath = /^\\\\\?\\/.test(urlPath);
if (isExtendedLengthPath) {
return urlPath;
}
return urlPath.replace(/\\/g, `/`);
}
async function mutateYozoraAst(api, pluginOptions = {}) {
const { files, markdownNode, markdownAST, pathPrefix, reporter, cache, getNode } = api;
const options = { ...DEFAULT_OPTIONS, pathPrefix, ...pluginOptions };
const { definitionMap } = calcDefinitionMap(markdownAST, undefined, options.presetDefinitions);
const markdownImageNodes = [];
traverseAst(markdownAST, [ImageReferenceType, ImageType], node => markdownImageNodes.push(node));
async function generateImagesAndUpdateNode(node, overWrites = {}) {
if (markdownNode.parent == null)
return null;
const parentNode = getNode(markdownNode.parent);
if (parentNode == null || parentNode.dir == null)
return null;
const imagePath = slash(path.join(parentNode.dir, getImageInfo(node.url).url));
const imageNode = files.find(file => file != null && file.absolutePath === imagePath);
if (imageNode == null || imageNode.absolutePath == null)
return null;
const fluidResult = await fluid({
file: imageNode,
args: { ...options },
reporter,
cache,
});
if (!fluidResult)
return null;
const srcSplit = getImageInfo(node.url).url.split(`/`);
const fileName = srcSplit[srcSplit.length - 1];
const fileNameNoExt = fileName.replace(/\.[^/.]+$/, ``);
const defaultAlt = fileNameNoExt.replace(/[^A-Z0-9]/gi, ` `);
const isEmptyAlt = node.alt === EMPTY_ALT;
const alt = isEmptyAlt ? '' : overWrites.alt ?? node.alt ?? defaultAlt;
const title = node.title ?? alt;
const loading = options.loading;
if (![`lazy`, `eager`, `auto`].includes(loading)) {
reporter.warn(reporter.stripIndent(`
${chalk.bold(loading)} is an invalid value for the ${chalk.bold(`loading`)} option. Please pass one of "lazy", "eager" or "auto".
`));
}
const decoding = options.decoding;
if (![`async`, `sync`, `auto`].includes(decoding)) {
reporter.warn(reporter.stripIndent(`
${chalk.bold(decoding)} is an invalid value for the ${chalk.bold(`decoding`)} option. Please pass one of "async", "sync" or "auto".
`));
}
return {
alt: alt,
title: title,
src: fluidResult.src,
srcSet: fluidResult.srcSet,
sizes: fluidResult.sizes,
loading: loading,
decoding: decoding,
};
}
async function process(node) {
let originalNode = node;
const overWrites = {};
if (!node.url && node.identifier) {
originalNode = node;
node = definitionMap[originalNode.identifier];
if (!node)
return null;
overWrites.alt = originalNode.alt;
}
const fileType = getImageInfo(node.url).ext;
if (isRelativeUrl(node.url) && supportedImgExts.has(fileType)) {
const imageData = await generateImagesAndUpdateNode(node, overWrites);
if (imageData == null)
return null;
node.url = imageData.src;
originalNode.alt = imageData.alt;
originalNode.src = imageData.src;
originalNode.srcSet = imageData.srcSet;
originalNode.sizes = imageData.sizes;
if (imageData.loading)
originalNode.loading = imageData.loading;
if (imageData.decoding)
originalNode.decoding = imageData.decoding;
return originalNode;
}
return null;
}
return Promise.all(markdownImageNodes.map(process)).then(nodes => nodes.filter((node) => node != null));
}
export { mutateYozoraAst as default };
//# sourceMappingURL=index.mjs.map