@diplodoc/transform
Version:
A simple transformer of text in YFM (Yandex Flavored Markdown) to HTML
153 lines (120 loc) • 3.96 kB
text/typescript
import type Token from 'markdown-it/lib/token';
import url from 'url';
export function isLocalUrl(url: string) {
return !/^(?:[a-z]+:)?\/\//i.test(url);
}
export function findBlockTokens(tokens: Token[], id: string) {
let blockTokens: Token[] = [];
let i = 0,
startToken,
start,
end;
while (i < tokens.length) {
const token = tokens[i];
if (typeof start === 'number' && startToken) {
if (startToken.type === 'paragraph_open' && token.type === 'paragraph_close') {
end = i + 1;
break;
} else if (startToken.type === 'heading_open') {
if (token.type === 'heading_open' && token.tag === startToken.tag) {
end = i;
break;
} else if (i === tokens.length - 1) {
end = tokens.length;
}
}
}
if (
(token.type === 'paragraph_open' || token.type === 'heading_open') &&
token.attrGet('id') === id &&
typeof start === 'undefined'
) {
startToken = token;
start = i;
}
i++;
}
if (typeof start === 'number' && typeof end === 'number') {
blockTokens = tokens.slice(start, end);
}
return blockTokens;
}
export function headingInfo(tokens: Token[], idx: number) {
const openToken = tokens[idx];
const inlineToken = tokens[idx + 1];
let title = '',
i = 0;
while (inlineToken.children && i < inlineToken.children.length) {
const token = inlineToken.children[i];
if (token.type === 'text') {
title += token.content;
}
i++;
}
const level = Number.parseInt(openToken.tag.slice(1), 10);
title ||= inlineToken.content;
return {
level,
title,
};
}
export function isExternalHref(href: string) {
return /^(\w{1,10}:)?\/\//.test(href) || /^([+\w]{1,10}:)/.test(href);
}
export function transformLinkToOriginalArticle(opts: {root: string; currentPath: string}) {
const {root, currentPath} = opts;
return currentPath.replace(root, '').replace(/\.(md|ya?ml|html)$/i, '');
}
export function getHrefTokenAttr(token: Token) {
let href = token.attrGet('href') || '';
try {
href = decodeURI(href);
} catch (e) {}
return href;
}
export function getSrcTokenAttr(token: Token) {
let src = token.attrGet('src') || '';
try {
// decodeURI can throw an error https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/URIError
src = decodeURI(src);
} catch (e) {}
return src;
}
export const PAGE_LINK_REGEXP = /\.(md|ya?ml)$/i;
export function defaultTransformLink(href: string) {
const parsed = url.parse(href);
href = url.format({
...parsed,
pathname: parsed.pathname?.replace(PAGE_LINK_REGEXP, '.html'),
});
return href;
}
type TokenWalker = (
token: Token,
state: {commented: boolean; index: number},
) => void | undefined | {skip: number};
export function filterTokens(tokens: Token[], type: string, handler: TokenWalker) {
let commented = false;
if (!tokens || !tokens.length) {
return;
}
for (let index = 0; index < tokens.length; index++) {
const token = tokens[index];
if (token.type === 'html_block') {
const commentStart = token.content.match('<!--');
const commentEnd = token.content.match('-->');
if (commentStart && !commentEnd) {
commented = true;
}
if (!commentStart && commentEnd) {
commented = false;
}
}
if (token.type === type) {
const result = handler(token, {commented, index});
if (result?.skip) {
index += result?.skip - 1;
}
}
}
}