extra-markdown-text
Version:
Utility methods for Markdown text.
95 lines (93 loc) • 3.42 kB
JavaScript
const RCODEBLOCK = /^ {0,3}```(\w*)\s*\n([\s\S]*?)^ {0,3}```[ \t]*\n|((?:^(?: {4}|\t)[\s\S]*?\n)+)/gm;
function unindentCodeBlock(txt) {
return txt.replace(/^( {4}|\t)/gm, '');
}
function forEachCodeBlock(txt, fn) {
var m = null;
while ((m = RCODEBLOCK.exec(txt)) != null)
fn(m[0], m[1] || '', m[2] != null ? m[2] : unindentCodeBlock(m[3]));
}
function codeBlocks(txt) {
var a = [];
forEachCodeBlock(txt, (full, language, body) => a.push({ full, language, body }));
return a;
}
function replaceCodeBlocks(txt, fn) {
return txt.replace(RCODEBLOCK, (m, p1, p2, p3) => {
return fn(m, p1 || '', p2 != null ? p2 : unindentCodeBlock(p3));
});
}
function tagCodeBlocks(txt) {
var tags = new Map(), i = -1;
var txt = replaceCodeBlocks(txt, full => {
var k = `AUTO_CODE_BLOCK_${++i}`;
tags.set(k, full);
return '```\n' + `${k}\n` + '```\n';
});
return [txt, tags];
}
function untagCodeBlocks(txt, tags) {
for (var [tag, full] of tags)
txt = txt.replace('```\n' + `${tag}\n` + '```\n', full);
return txt;
}
const RLINK = /!?\[(.*?)\](?:\((.*?)\)| ?\[(.*?)\]|(?!:))/gm;
function forEachLink(txt, fn) {
var txt = replaceCodeBlocks(txt, () => ''), m = null;
while ((m = RLINK.exec(txt)) != null)
if (!m[0].startsWith('!'))
fn(m[0], m[1], m[3] || '', m[2] || '');
}
function links(txt) {
var a = [];
forEachLink(txt, (full, name, reference, url) => a.push({ full, name, reference, url }));
return a;
}
function replaceLinks(txt, fn) {
var [txt, tags] = tagCodeBlocks(txt);
txt = txt.replace(RLINK, (m, p1, p2, p3) => {
return m.startsWith('!') ? m : fn(m, p1, p3 || '', p2 || '');
});
return untagCodeBlocks(txt, tags);
}
const RLINKREFERENCE = /^[ \t]*\[(.*?)\]:[ \t]*<?([>\S]*?)>?(?:[ \t]*['"(](.*?)['")])?[ \t]*$/gm;
function forEachLinkReference(txt, fn) {
var txt = replaceCodeBlocks(txt, () => ''), m = null;
while ((m = RLINKREFERENCE.exec(txt)) != null)
fn(m[0], m[1], m[2], m[3] || '');
}
function linkReferences(txt) {
var a = [];
forEachLinkReference(txt, (full, name, url, title) => a.push({ full, name, url, title }));
return a;
}
function replaceLinkReferences(txt, fn) {
var [txt, tags] = tagCodeBlocks(txt);
txt = txt.replace(RLINKREFERENCE, (m, p1, p2, p3) => fn(m, p1, p2, p3 || ''));
return untagCodeBlocks(txt, tags);
}
const RTABLE = /.*?\n.*?(?:\|[ \t]*---|---[ \t]*\|)[\s\S]*?(?:\n(?=[ \t]*\n)|$)/g;
function tableRows(full) {
var rows = [];
var ls = full.trim().split('\n');
ls.splice(1, 1);
for (var l of ls)
rows.push(l.replace(/(^\s*\|)|(\|\s*$)/g, '').split('|'));
return rows;
}
function forEachTable(txt, fn) {
var txt = replaceCodeBlocks(txt, () => ''), m = null;
while ((m = RTABLE.exec(txt)) != null)
fn(m[0], tableRows(m[0]));
}
function tables(txt) {
var a = [];
forEachTable(txt, (full, rows) => a.push({ full, rows }));
return a;
}
function replaceTables(txt, fn) {
var [txt, tags] = tagCodeBlocks(txt);
txt = txt.replace(RTABLE, m => fn(m, tableRows(m)));
return untagCodeBlocks(txt, tags);
}
export { codeBlocks, forEachCodeBlock, forEachLink, forEachLinkReference, forEachTable, linkReferences, links, replaceCodeBlocks, replaceLinkReferences, replaceLinks, replaceTables, tables, tagCodeBlocks, untagCodeBlocks };