UNPKG

markdown-code-example-inserter

Version:
100 lines (99 loc) 3.97 kB
import { InvalidNodeError } from '../errors/invalid-node.error.js'; import { linkCommentTriggerPhrase, startsWithTriggerPhraseRegExp } from '../trigger-phrase.js'; import { isCodeBlock, isCommentNode, parseMarkdownContents } from './parse-markdown.js'; import { walk } from './walk.js'; function isExampleLinkComment(input) { return isCommentNode(input) && input.value.trim().startsWith(linkCommentTriggerPhrase); } export function extractIndent(line, node) { if (typeof node.value === 'string' && line.trim().startsWith(node.value)) { return line.slice(0, // column is 1 indexed so we must remove 1 from it node.position.start.column - 1); } return ''; } export function extractLinks(markdownFileContents) { const parsedRoot = parseMarkdownContents(markdownFileContents); const markdownLines = markdownFileContents.toString().split('\n'); const commentData = []; let lastNode; let lastHtmlNode; walk(parsedRoot, 'markdown', (node, language) => { const lastComment = commentData[commentData.length - 1]; if (language === 'markdown' && isHtmlNode(node)) { assertFullyPositionedNode(node); const htmlLine = markdownLines[ // line is 1 indexed node.position.start.line - 1]; if (!htmlLine) { throw new InvalidNodeError(node, `this Html node's position.start.line is not actually a valid line number from the file it's in`); } lastHtmlNode = { htmlNode: node, indent: extractIndent(htmlLine, node), }; } else if (language === 'html' && isExampleLinkComment(node)) { if (!lastHtmlNode) { throw new InvalidNodeError(node, 'encountered html node without first encountering html root node'); } assertFullyPositionedNode(node); const newNode = offsetNodePosition(node, lastHtmlNode.htmlNode); node = newNode; commentData.push({ comment: newNode, indent: lastHtmlNode.indent }); } else if (language === 'markdown' && lastComment && lastNode === lastComment.comment && isCodeBlock(node)) { assertFullyPositionedNode(node); lastComment.codeBlock = node; } lastNode = node; }); return commentData.map((comment) => { assertFullyPositionedNode(comment.comment); return { node: comment.comment, indent: comment.indent, linkPath: comment.comment.value .trim() .replace(startsWithTriggerPhraseRegExp, '') .trim(), linkedCodeBlock: comment.codeBlock, }; }); } function offsetNodePosition(needsOffset, offsetBase) { return { ...needsOffset, position: { ...needsOffset.position, start: { ...needsOffset.position.start, line: offsetBase.position.start.line + needsOffset.position.start.line - 1, offset: offsetBase.position.start.offset + needsOffset.position.start.offset, }, end: { ...needsOffset.position.end, line: offsetBase.position.start.line + needsOffset.position.end.line - 1, offset: offsetBase.position.start.offset + needsOffset.position.end.offset, }, }, }; } function isHtmlNode(input) { return input.type === 'html'; } export function assertFullyPositionedNode(node) { if (!node.position) { throw new InvalidNodeError(node, 'missing position property'); } if (node.position.end.offset == undefined) { throw new InvalidNodeError(node, 'missing end position offset'); } if (node.position.start.offset == undefined) { throw new InvalidNodeError(node, 'missing start position offset'); } }