Skip to content

Instantly share code, notes, and snippets.

@VanTigranyan
Forked from plugnburn/README.md
Created February 19, 2020 09:24
Show Gist options
  • Select an option

  • Save VanTigranyan/651b7c77cfc149cb858a044c2108acbb to your computer and use it in GitHub Desktop.

Select an option

Save VanTigranyan/651b7c77cfc149cb858a044c2108acbb to your computer and use it in GitHub Desktop.
Landmark - a minimalistic and extensible Markdown compiler in JavaScript

Landmark: the simplest Markdown engine for the browser

Landmark is a small but extensible JavaScript library that allows to render Markdown documents into HTML. It's the primary engine for Sitemark and some other projects.

Usage

Landmark features only 2 methods:

  • Landmark.render(text) - takes the Markdown source and returns ready HTML code (you'll only need this most of the time)
  • Landmark.addRule(regexp, replacement) - adds a new processing rule that allows you to specify custom Markdown tags: regexp must be an escaped regular expression string in JavaScript syntax, and replacement can be either a string, or a function (see string replacement documentation for the reference)

Markdown syntax

Markdown syntax used in Landmark resembles both classical and GitHub-flavored Markdown versions, however it does not support some advanced features such as tables or reference-style links. Supported tags are:

  • Headings, single-line form: # Heading level 1, ## Heading level 2 and so on
  • Headings of levels 1 and 2, two-line form:
    Heading level 1
    ===============
    
    Heading level 2
    ---------------
    
  • Inline formatting: **bold** or __bold__, *italic* or _italic_, ~~strike~~, :"quote": and even non-standard ___underline___ (triple underscore) tag!
  • Preformatted inline blocks:`inline code`
  • Preformatted multiline blocks:
    ```
    first line of preformatted text
    second line of preformatted text
    etc...
    ```
  • Block quotes:
    > first line of quote
    > second line of quote
    > etc...
  • Lists:
    - unordered item 1
    - unordered item 2
    
    * another unordered item 1
    * another unordered item 2
    * another unordered item 3
    
    1. ordered item 1
    2. ordered item 2
    3. ordered item 3
    4. ordered item 4
  • Links: [link text](http://example.com)
  • Images: ![alt text](http://example.com/flower.jpg)
  • Horizontal rules: **************** (5 and more asterisks on a new line)
  • Paragraphs: just like in standard Markdown, any non-special text is enclosed into paragraphs by default, just surround it with newlines.

Of course, you can use plain old HTML in Markdown documents, and it will pass as is, unless it's enclosed into inline or multiline preformatted blocks, in which case it will be escaped appropriately.

(function(w, libName){
var esc=function(s){
s = s.replace(/\&/g, '&')
var escChars = '\'#<>`*-~_=:"![]()nt',c,l=escChars.length,i
for(i=0;i<l;i++) s=s.replace(RegExp('\\'+escChars[i], 'g'), function(m){return'&#'+m.charCodeAt(0)+';'})
return s
}, rules = [
{p:/\r\n/g, r:'\n'},
{p:/\n\s*```\n([^]*?)\n\s*```\s*\n/g, r:function(m,grp){return'<pre>'+esc(grp)+'</pre>'}},
{p:/`(.*?)`/g, r:function(m,grp){return'<code>'+esc(grp)+'</code>'}},
{p:/\n\s*(#+)(.*?)/g, r:function(m,hset,hval){m=hset.length;return'<h'+m+'>'+hval.trim()+'</h'+m+'>'}},
{p:/\n\s*(.*?)\n={3,}\n/g, r:'\n<h1>$1</h1>\n'},
{p:/\n\s*(.*?)\n-{3,}\n/g, r:'\n<h2>$1</h2>\n'},
{p:/___(.*?)___/g, r:'<u>$1</u>'},
{p:/(\*\*|__)(.*?)\1/g, r:'<strong>$2</strong>'},
{p:/(\*|_)(.*?)\1/g, r:'<em>$2</em>'},
{p:/~~(.*?)~~/g, r:'<del>$1</del>'},
{p:/:"(.*?)":/g, r:'<q>$1</q>'},
{p:/\!\[([^\[]+?)\]\s*\(([^\)]+?)\)/g, r:'<img src="$2" alt="$1">'},
{p:/\[([^\[]+?)\]\s*\(([^\)]+?)\)/g, r:'<a href="$2">$1</a>'},
{p:/\n\s*(\*|\-)\s*([^\n]*)/g, r:'\n<ul><li>$2</li></ul>'},
{p:/\n\s*\d+\.\s*([^\n]*)/g, r:'\n<ol><li>$1</li></ol>'},
{p:/\n\s*(\>|&gt;)\s*([^\n]*)/g, r:'\n<blockquote>$2</blockquote>'},
{p:/<\/(ul|ol|blockquote)>\s*<\1>/g, r: ' '},
{p:/\n\s*\*{5,}\s*\n/g, r:'\n<hr>'},
{p:/\n{3,}/g, r:'\n\n'},
{p:/\n([^\n]+)\n/g, r:function(m, grp){grp=grp.trim();return /^\<\/?(ul|ol|bl|h\d|p).*/.test(grp.slice(0,9)) ? grp : ('<p>'+grp+'</p>')}},
{p:/>\s+</g, r:'><'}
], l = rules.length, i
w[libName] = {
addRule:function(ruleString, replacement) {rules.push({p:RegExp(ruleString, 'g'),r:replacement})},
render:function(text) {
if(text = text || '') {
text = '\n' + text.trim() + '\n'
for(var i=0;i<l;i++) text = text.replace(rules[i].p, rules[i].r)
}
return text
}
}
})(self, 'Landmark')
@zrajm
Copy link

zrajm commented Mar 14, 2022

This is really cool! But there are improvements to make. If rules was a list of lists, then some bytes could be saved there. If also using reduce (introduced in ES2015) for the esc function and rule evaluation, and fat arrow functions (=> introduced in ES6) then some additional bytes can be shaved off. Some of the variable names could also be shortened with no loss of readability. I'm forking this and making some changes. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment