All files / kanbn/src parse-markdown.js

100% Statements 39/39
100% Branches 18/18
100% Functions 1/1
100% Lines 39/39

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 401x 796x 3x 3x 796x 1x 1x 792x 796x 4x 4x 788x 2073x 2073x 2073x 788x 796x 1x 1x 1x 1x 1x 1x 788x 796x 2074x 2074x 2074x 2074x 2074x 2074x 2074x 2074x 2074x 2074x 2074x 2074x 788x 1x  
module.exports = function parseMarkdown(markdown) {
  if (!markdown) {
    throw new Error('data is null, undefined or empty');
  }
  if (typeof markdown !== 'string') {
    throw new Error('data is not a string');
  }
  markdown = markdown.trim();
  if (markdown === '') {
    throw new Error('data is an empty string');
  }
  const headings = [...markdown.matchAll(/^#{1,6} (?<title>.+)/gm)].map(({ 0: heading, 1: title, index }) => ({
    heading,
    title,
    index
  }));
  if (headings.length > 0 && headings[0].index > 0) {
    headings.unshift({
      heading: '',
      title: 'raw',
      index: 0
    });
  }
  const parsed = {};
  for (let i = 0; i < headings.length; i++) {
    parsed[headings[i].title] = {
      heading: headings[i].heading,
      content: markdown.slice(
        // The + 1 skips the newline that ends the heading line; the synthetic 'raw' entry for
        // content before the first heading has no heading line, so it starts at index 0
        headings[i].index + headings[i].heading.length + (headings[i].heading ? 1 : 0),
        i < headings.length - 1
          ? headings[i + 1].index
          : undefined
      ).trim()
    };
  }
  return parsed;
};