@diplodoc/transform
Version:
A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML
155 lines • 6.37 kB
JavaScript
;
const path_1 = require("path");
const chalk_1 = require("chalk");
const svgo_1 = require("svgo");
const fs_1 = require("fs");
const utilsFS_1 = require("../../utilsFS");
const utils_1 = require("../../utils");
const sanitizeAttribute = (value) => value.replace(/(\d*[%a-z]{0,5}).*/gi, '$1');
function replaceImageSrc(state, currentPath, path, imgSrc, { assetsPublicPath = path_1.sep, root = '', log }) {
var _a;
if ((0, utilsFS_1.isFileExists)(path)) {
(_a = state.md.assets) === null || _a === void 0 ? void 0 : _a.push(imgSrc);
}
else {
log.error(`Asset not found: ${(0, chalk_1.bold)(imgSrc)} in ${(0, chalk_1.bold)(currentPath)}`);
}
const relativeToRoot = path.replace(root + path_1.sep, '');
const publicSrc = (0, path_1.join)(assetsPublicPath, relativeToRoot);
return publicSrc;
}
function getSvgContent(file, from, { rawContent, notFoundCb, log, root = '' }) {
try {
return rawContent(file);
}
catch (e) {
const path = file.replace(root, '');
log.error(`SVG ${path} from ${from} not found`);
if (notFoundCb) {
notFoundCb(path);
}
return null;
}
}
function shouldBeInlined(token, opts) {
var _a;
if (!((_a = token.attrGet('src')) === null || _a === void 0 ? void 0 : _a.endsWith('.svg'))) {
return false;
}
const forceInlineSvg = token.attrGet('inline') === 'true';
const shouldInlineSvg = forceInlineSvg || (token.attrGet('inline') !== 'false' && opts.enabled !== false);
return shouldInlineSvg;
}
const getRawFile = (path) => {
return (0, fs_1.readFileSync)(path, 'utf8').toString();
};
const index = (md, opts) => {
const { rawContent = getRawFile, calcPath = utilsFS_1.resolveRelativePath, replaceImageSrc: replaceImage = replaceImageSrc, } = opts;
// TODO:goldserg need remove support opts.inlineSvg
if (opts.inlineSvg !== undefined) {
opts.svgInline = Object.assign(Object.assign({}, opts.svgInline), { enabled: opts.inlineSvg });
}
md.assets = [];
const plugin = (state) => {
const tokens = state.tokens;
(0, utils_1.filterTokens)(tokens, 'inline', (inline, { commented }) => {
if (commented || !inline.children) {
return;
}
const childrenTokens = inline.children || [];
(0, utils_1.filterTokens)(childrenTokens, 'image', (image, { commented, index }) => {
const didPatch = image.attrGet('yfm_patched') || false;
if (didPatch || commented) {
return;
}
const imgSrc = (0, utils_1.getSrcTokenAttr)(image);
if ((0, utils_1.isExternalHref)(imgSrc)) {
return;
}
const forceInlineSvg = image.attrGet('inline') === 'true';
const shouldInlineSvg = shouldBeInlined(image, opts.svgInline);
const imageOpts = {
width: image.attrGet('width'),
height: image.attrGet('height'),
};
const from = state.env.path || opts.path;
const file = calcPath(from, imgSrc);
if (shouldInlineSvg) {
const svgContent = getSvgContent(file, from, Object.assign(Object.assign({}, opts), { rawContent }));
if (svgContent) {
if (svgContent.length > opts.svgInline.maxFileSize && !forceInlineSvg) {
image.attrSet('YFM011', `Svg size: ${svgContent.length}; Config size: ${opts.svgInline.maxFileSize}; Src: ${(0, chalk_1.bold)(file)}`);
}
else {
const svgToken = new state.Token('image_svg', '', 0);
svgToken.attrSet('content', replaceSvgContent(svgContent, imageOpts));
childrenTokens[index] = svgToken;
}
}
}
if (childrenTokens[index].type === 'image') {
image.attrSet('src', replaceImage(state, from, file, imgSrc, opts));
image.attrSet('yfm_patched', '1');
}
});
});
};
try {
md.core.ruler.before('includes', 'images', plugin);
}
catch (e) {
md.core.ruler.push('images', plugin);
}
md.renderer.rules.image_svg = (tokens, index) => {
const token = tokens[index];
return token.attrGet('content') || '';
};
};
function replaceSvgContent(content, options) {
var _a;
if (!content) {
return '';
}
// monoline
content = content.replace(/>\r?\n</g, '><').replace(/\r?\n/g, ' ');
// remove <?xml...?>
content = content.replace(/<\?xml.*?\?>.*?(<svg.*)/g, '$1');
// width, height
let svgRoot = content.replace(/.*?<svg([^>]*)>.*/g, '$1');
const { width, height } = ((_a = svgRoot
.match(/(?:width="(.*?)")|(?:height="(.*?)")/g)) === null || _a === void 0 ? void 0 : _a.reduce((acc, val) => {
const [key, value] = val.split('=');
acc[key] = value;
return acc;
}, {})) || { width: undefined, height: undefined };
if (!width && options.width) {
const sanitizedWidth = sanitizeAttribute(options.width.toString());
svgRoot = `${svgRoot} width="${sanitizedWidth}"`;
}
if (!height && options.height) {
const sanitizedHeight = sanitizeAttribute(options.height.toString());
svgRoot = `${svgRoot} height="${sanitizedHeight}"`;
}
if ((!width && options.width) || (!height && options.height)) {
content = content.replace(/.*?<svg([^>]*)>/, `<svg${svgRoot}>`);
}
// randomize ids
content = (0, svgo_1.optimize)(content, {
plugins: [
{
name: 'prefixIds',
params: {
prefix: 'rnd-' + Math.floor(Math.random() * 1e9).toString(16),
prefixClassNames: false,
},
},
],
}).data;
return content;
}
// Create an object that is the index function with an additional replaceSvgContent property
const imagesPlugin = Object.assign(index, {
replaceSvgContent,
});
module.exports = imagesPlugin;
//# sourceMappingURL=index.js.map