UNPKG

markdown-notes-tree

Version:

Generate Markdown trees that act as a table of contents for a folder structure with Markdown notes

234 lines (194 loc) 6.36 kB
"use strict"; const mdastUtilFromMarkdown = require("mdast-util-from-markdown"); const mdastUtilToMarkdown = require("mdast-util-to-markdown"); module.exports = { getAstNodeFromMarkdown, getFirstLevel1HeadingChild, hasLinkDescendant, getFirstHtmlChildWithValue, getAllHtmlChildrenWithValues, getStartIndex, getEndIndex, extractParagraphFromHeadingNode, isSingleMarkdownParagraph, removeStrongFromMarkdown, escapeText, generateLinkFromMarkdownParagraphAndUrl, generateStrongParagraphFromMarkdownParagraph, generateLevel1HeadingFromMarkdownParagraph }; const mdastCache = new Map(); function getAstNodeFromMarkdown(contents) { if (mdastCache.has(contents)) { return mdastCache.get(contents); } // note that this should never fail for any kind of string, as all strings are valid Markdown const mdast = mdastUtilFromMarkdown(contents); mdastCache.set(contents, mdast); return mdast; } function getFirstLevel1HeadingChild(node) { if (!node.children) { return undefined; } return node.children.find(node => node.type === "heading" && node.depth === 1); } function hasLinkDescendant(node) { if (!node.children) { return false; } return node.children.some(child => { if (child.type === "link") { return true; } return hasLinkDescendant(child); }); } function getFirstHtmlChildWithValue(value, node) { const allHtmlChildrenWithValue = getAllHtmlChildrenWithValues([value], node); return allHtmlChildrenWithValue[0]; } function getAllHtmlChildrenWithValues(values, node) { if (!node.children) { return []; } return node.children.filter(node => node.type === "html" && values.includes(node.value)); } function getStartIndex(node) { return node.position.start.offset; } function getEndIndex(node) { return node.position.end.offset; } function extractParagraphFromHeadingNode(node) { if (node.type !== "heading") { throw new Error("Provided node must be a heading"); } const extracted = { type: "root", children: [ { type: "paragraph", children: node.children.map(markAsGenerated) } ] }; return mdastUtilToMarkdown(extracted).trim(); } function isSingleMarkdownParagraph(markdown) { const node = getAstNodeFromMarkdown(markdown); return node.children.length === 1 && node.children[0].type === "paragraph"; } /** * Note: this might alter other Markdown formatting syntax (for example, * vs _ for emphasis) */ function removeStrongFromMarkdown(markdown) { const node = getAstNodeFromMarkdown(markdown); const nodeWithoutStrong = replaceStrongDescendantsByChildren(node); return mdastUtilToMarkdown(nodeWithoutStrong).trim(); } function replaceStrongDescendantsByChildren(node) { return { ...node, position: undefined, // position must not be defined for generated/altered node children: node.children ? replaceStrongNodesByChildrenDeep(node.children) : undefined }; } function replaceStrongNodesByChildrenDeep(nodes) { const newNodes = []; for (const node of nodes) { if (node.type === "strong") { newNodes.push(...replaceStrongNodesByChildrenDeep(node.children)); } else { newNodes.push(replaceStrongDescendantsByChildren(node)); } } return newNodes; } function escapeText(text) { const generated = { type: "root", children: [ { type: "paragraph", children: [ { type: "text", value: text } ] } ] }; return mdastUtilToMarkdown(generated).trim(); } function generateLinkFromMarkdownParagraphAndUrl(markdown, url) { if (!isSingleMarkdownParagraph(markdown)) { throw new Error("Link text must be a single paragraph"); } const node = getAstNodeFromMarkdown(markdown); const paragraphNode = node.children[0]; const generated = { type: "root", children: [ { type: "paragraph", children: [ { type: "link", url: url, children: paragraphNode.children.map(markAsGenerated) } ] } ] }; return mdastUtilToMarkdown(generated).trim(); } function generateStrongParagraphFromMarkdownParagraph(markdown) { if (!isSingleMarkdownParagraph(markdown)) { throw new Error("Paragraph text must be a single paragraph"); } const node = getAstNodeFromMarkdown(markdown); const paragraphNode = node.children[0]; const generated = { type: "root", children: [ { type: "paragraph", children: [ { type: "strong", children: paragraphNode.children.map(markAsGenerated) } ] } ] }; return mdastUtilToMarkdown(generated).trim(); } function generateLevel1HeadingFromMarkdownParagraph(markdown) { if (!isSingleMarkdownParagraph(markdown)) { throw new Error("Heading text must be a single paragraph"); } const node = getAstNodeFromMarkdown(markdown); const paragraphNode = node.children[0]; const generated = { type: "root", children: [ { type: "heading", depth: 1, children: paragraphNode.children.map(markAsGenerated) } ] }; return mdastUtilToMarkdown(generated).trim(); } function markAsGenerated(node) { return { ...node, position: undefined, // position must not be defined for generated/altered node children: node.children ? node.children.map(markAsGenerated) : undefined }; }