Home Reference Source Test Repository

src/lib/markdown.js

const Util = require( './util.js' );

/**
 * Convert the HTML DOM Node to Markdown text.
 * @type {Array.<MdConverter>}
 * @see https://github.com/domchristie/to-markdown/blob/master/lib/md-converters.js
 */
const MarkdownConverters = [
  // Paragraph
  {
    filter: 'p',
    replacement: ( node, content ) => {
      return '\n\n' + content + '\n\n';
    }
  },
  // Line break
  {
    filter: 'br',
    replacement: () => {
      return '  \n';
    }
  },
  // Header
  {
    filter: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ],
    replacement: ( node, content, options ) => {
      const level  = node.nodeName.charAt( 1 );
      let   prefix = '';
      for( let i = 0; i < level; ++i ) {
        prefix += '#';
      }

      // Inter link ( Markdown Extra )
      if( node.id && !( options.noMELink ) ) {
        return '\n\n' + prefix + ' ' + content + ' {#' + node.id + '}\n\n';
      }

      return '\n\n' + prefix + ' ' + content + '\n\n';
    }
  },
  // Line
  {
    filter: 'hr',
    replacement: () => {
      return '\n\n* * *\n\n';
    }
  },
  // Italic
  {
    filter: ['em', 'i'],
    replacement: ( node, content ) => {
      return '_' + content + '_';
    }
  },
  // Strong
  {
    filter: [ 'strong', 'b' ],
    replacement: ( node, content ) => {
      return '**' + content + '**';
    }
  },
  // Inline code
  {
    filter: ( node ) => {
      const hasSiblings = node.previousSibling || node.nextSibling;
      const isCodeBlock = ( node.parentNode.nodeName === 'PRE' && !( hasSiblings ) );

      return ( node.nodeName === 'CODE' && !( isCodeBlock ) );
    },
    replacement: ( node, content ) => {
      return '`' + content + '`';
    }
  },
  // Link
  {
    filter: ( node ) => {
      return ( node.nodeName === 'A' && node.getAttribute( 'href' ) );
    },
    replacement: ( node, content ) => {
      const titlePart = node.title ? ' "' + node.title + '"' : '';
      return '[' + content + '](' + node.getAttribute( 'href' ) + titlePart + ')';
    }
  },
  // Image
  {
    filter: 'img',
    replacement: ( node ) => {
      const alt       = node.alt || '';
      const src       = node.getAttribute( 'src' ) || '';
      const title     = node.title || '';
      const titlePart = title ? ' "' + title + '"' : '';

      return src ? '![' + alt + '](' + src + titlePart + ')' : '';
    }
  },
  // Code blocks
  {
    filter: ( node ) => {
      return node.nodeName === 'PRE' && node.firstChild.nodeName === 'CODE';
    },
    replacement: ( node ) => {
      return '\n\n    ' + node.firstChild.textContent.replace( /\n/g, '\n    ' ) + '\n\n';
    }
  },
  // Block quote
  {
    filter: 'blockquote',
    replacement: ( node, content ) => {
      let result = Util.trim( content );
      result = result.replace( /\n{3,}/g, '\n\n' );
      result = result.replace( /^/gm, '> ' );
      return '\n\n' + result + '\n\n';
    }
  },
  // List item
  {
    filter: 'li',
    replacement: ( node, content ) => {
      const text   = content.replace( /^\s+/, '' ).replace( /\n/gm, '\n    ' );
      const parent = node.parentNode;
      const index  = Util.arrayIndexOf( parent.children, node ) + 1;
      const ol     = /ol/i;
      const prefix = ol.test( parent.nodeName ) ? index + '. ' : '* ';
      return prefix + text;
    }
  },
  // List
  {
    filter: [ 'ul', 'ol' ],
    replacement: ( node ) => {
      const strings = [];
      for( let i = 0, max = node.childNodes.length; i < max; ++i ) {
        if( node.childNodes[ i ]._replacement ) {
          strings.push( node.childNodes[ i ]._replacement );
        }
      }

      const li = /li/i;
      if( li.test( node.parentNode.nodeName ) ) {
        return '\n' + strings.join( '\n' );
      }

      return '\n\n' + strings.join( '\n' ) + '\n\n';
    }
  },
  // Block element
  {
    filter: ( node ) => {
      return Util.isBlockElement( node );
    },
    replacement: ( node, content ) => {
      return '\n\n' + Util.outerHTML( node, content ) + '\n\n';
    }
  },
  // Anything else!
  {
    filter: () => {
      return true;
    },
    replacement: ( node, content ) => {
      return Util.outerHTML( node, content );
    }
  }
];

module.exports = MarkdownConverters;