Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 10x 10x 45x 45x 45x 45x 45x 45x 12x 12x 12x 45x 45x 61x 61x 61x 61x 206x 45x 45x 45x 61x 190x 190x 190x 115x 112x 111x 13x 13x 13x 13x 22x 22x 13x 13x 13x 98x 45x 61x 61x 54x 54x 54x 54x 54x 10x | // resilient parser factory: try unified/remark at runtime, fall back to a synchronous AST builder
let parserFactory = null;
try { parserFactory = require('unified'); } catch (e) { parserFactory = null; }
function parseParagraphLines(lines) {
const text = lines.join(' ').trim();
Iif (!text) return null;
const parts = [];
let lastIndex = 0;
const linkRe = /\[([^\]]+)\]\((https?:\/\/[^)]+|\.?\/\/+[^)]+|[^)]+\.md)\)/g;
let m;
while ((m = linkRe.exec(text)) !== null) {
if (m.index > lastIndex) parts.push({ type: 'text', value: text.slice(lastIndex, m.index) });
parts.push({ type: 'link', url: m[2], children: [{ type: 'text', value: m[1] }] });
lastIndex = m.index + m[0].length;
}
if (lastIndex < text.length) parts.push({ type: 'text', value: text.slice(lastIndex) });
return { type: 'paragraph', children: parts.length ? parts : [{ type: 'text', value: text }] };
}
/**
* Minimal synchronous AST builder used when remark is not installed.
* Produces a root node with heading, paragraph, list, image, and html children.
* Does not handle nested block elements — intentionally simple to stay dependency-free.
*
* @param {string} text - Raw markdown string
* @returns {{ type: 'root', children: object[] }}
*/
function buildFallbackAst(text) {
const lines = String(text || '').split(/\r?\n/);
const children = [];
let paraBuf = [];
const flush = () => {
if (!paraBuf.length) return;
const node = parseParagraphLines(paraBuf);
Eif (node) children.push(node);
paraBuf = [];
};
for (let i = 0; i < lines.length; i++) {
const l = lines[i];
const h = l.match(/^\s*(#{1,6})\s*(.*)$/);
if (h) { flush(); children.push({ type: 'heading', depth: h[1].length, children: [{ type: 'text', value: h[2].trim() }] }); continue; }
if (l.match(/!\[[^\]]*\]\(([^)]+)\)/)) { flush(); children.push({ type: 'image', url: l.match(/!\[[^\]]*\]\(([^)]+)\)/)[1].trim(), alt: '' }); continue; }
if (l.match(/<img[^>]+src=["']([^"']+)["'][^>]*>/i)) { flush(); children.push({ type: 'html', value: l }); continue; }
if (/^\s*[-*+]\s+/.test(l)) {
flush();
const items = [];
let j = i;
while (j < lines.length && /^\s*[-*+]\s+/.test(lines[j])) {
items.push({ type: 'listItem', children: [{ type: 'paragraph', children: [{ type: 'text', value: lines[j].replace(/^\s*[-*+]\s+/, '').trim() }] }] });
j++;
}
children.push({ type: 'list', children: items });
i = j - 1;
continue;
}
if (l.trim() === '') { flush(); continue; }
paraBuf.push(l.trim());
}
flush();
return { type: 'root', children };
}
/**
* Parses a README markdown string into a unified/remark AST.
* Attempts unified → remark → buildFallbackAst in that order so parsing
* succeeds even when the optional remark packages are not installed.
*
* @param {string} text - Raw markdown string
* @returns {{ type: 'root', children: object[] }|null}
*/
function parseMarkdown(text) {
Iif (!text) return null;
Iif (parserFactory) {
try {
const factory = (typeof parserFactory === 'function') ? parserFactory : (parserFactory && parserFactory.default) ? parserFactory.default : null;
if (factory && typeof factory === 'function') {
let remarkParse = null;
try { remarkParse = require('remark-parse'); } catch (e) { remarkParse = null; }
if (remarkParse) return factory().use(remarkParse).parse(text);
}
} catch (e) { /* fall through */ }
}
try {
const remarkPkg = require('remark');
const remarkFactory = (typeof remarkPkg === 'function') ? remarkPkg : (remarkPkg && remarkPkg.default) ? remarkPkg.default : null;
let remarkParse = null;
try { remarkParse = require('remark-parse'); } catch (e) { remarkParse = null; }
if (remarkFactory && remarkParse) return remarkFactory().use(remarkParse).parse(text);
} catch (e) { /* fall through */ }
return buildFallbackAst(text);
}
module.exports = { buildFallbackAst, parseMarkdown };
|