npm/marky-markdown

View on GitHub
lib/render.js

Summary

Maintainability
A
2 hrs
Test Coverage
var pickBy = require('lodash.pickby')
var MD = require('markdown-it')
var lazyHeaders = require('markdown-it-lazy-headers')
var emoji = require('markdown-it-emoji')
var expandTabs = require('markdown-it-expand-tabs')
var githubTaskList = require('markdown-it-task-lists')

var cleanup = require('./cleanup')
var githubLinkify = require('./linkify')

var codeWrap = require('./plugin/code-wrap')
var headingLinks = require('./plugin/heading-links')
var gravatar = require('./plugin/gravatar')
var github = require('./plugin/github')
var youtube = require('./plugin/youtube')
var cdnImages = require('./plugin/cdn')
var packagize = require('./plugin/packagize')
var htmlHeading = require('./plugin/html-heading')
var fenceLanguageAliasing = require('./plugin/language-alias')
var relaxedLinkRefs = require('./gfm/relaxed-link-reference')
var githubHeadings = require('./gfm/indented-headings')
var overrideLinkDestinationParser = require('./gfm/override-link-destination-parser')
var looseLinkParsing = require('./gfm/link')
var looseImageParsing = require('./gfm/image')
var relNoFollow = require('./plugin/nofollow')

if (typeof process.browser === 'undefined') {
  var Highlights = require('highlights')
  var highlighter = new Highlights()

  var languages = [
    'atom-language-nginx',
    'atom-language-diff',
    'language-dart',
    'language-rust',
    'language-erlang',
    'language-glsl',
    'language-haxe',
    'language-ini',
    'language-stylus'
  ]

  languages.forEach(function (language) {
    highlighter.requireGrammarsSync({
      modulePath: require.resolve(language + '/package.json')
    })
  })

  // cleanup generated rules in the highlighter registry if they are idle for 2000ms
  // they take a tremendous amount of memory if you process many languages in a server type environment.
  cleanup(highlighter.registry.grammars)
}

var render = module.exports = function (markdown, options) {
  return render.getParser(options).render(markdown)
}

render.getParser = function (options) {
  var mdOptions = {
    html: true,
    langPrefix: 'highlight ',
    linkify: options.linkify
  }

  if (options.highlightSyntax) {
    mdOptions.highlight = function (code, lang) {
      if (!lang) { return '' }
      return highlighter.highlightSync({
        fileContents: code,
        scopeName: scopeNameFromLang(highlighter, lang)
      })
    }
  }

  var parser = MD(mdOptions)
    .use(lazyHeaders)
    .use(emoji, {shortcuts: {}})
    .use(expandTabs, {tabWidth: 4})
    .use(githubTaskList)
    .use(headingLinks, options)
    .use(githubHeadings)
    .use(relaxedLinkRefs)
    .use(gravatar)
    .use(github, {package: options.package})
    .use(youtube)
    .use(packagize, {package: options.package})
    .use(htmlHeading)
    .use(overrideLinkDestinationParser)
    .use(looseLinkParsing)
    .use(looseImageParsing)

  if (options.nofollow) {
    parser.use(relNoFollow)
  }

  if (options.highlightSyntax) {
    parser.use(codeWrap)
          .use(fenceLanguageAliasing)
  }
  if (options.serveImagesWithCDN) parser.use(cdnImages, {package: options.package})

  return githubLinkify(parser)
}

render.renderPackageDescription = function (description) {
  return MD({html: true}).renderInline(description)
}

var mappings = {
  jsx: 'source.js.jsx',
  sh: 'source.shell',
  markdown: 'source.gfm',
  erb: 'text.html.erb'
}

// attempt to look up by the long language name, e.g. Ruby, JavaScript.
// fallback to assuming that lang is the extension of the code snippet.
function scopeNameFromLang (highlighter, lang) {
  // alias language names.

  if (mappings[lang]) return mappings[lang]

  var grammar = pickBy(highlighter.registry.grammarsByScopeName, function (val, key) {
    return val.name.toLowerCase() === lang
  })

  if (Object.keys(grammar).length) {
    return Object.keys(grammar)[0]
  }

  var name = 'source.' + lang
  // mappings[lang] = name

  return name
}