New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

commonmark

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

commonmark - npm Package Compare versions

Comparing version 0.15.0 to 0.16.0

changelog.js.txt

19

bench.js

@@ -5,6 +5,8 @@ var Benchmark = require('benchmark').Benchmark;

var sm = require('./lib/index.js');
// https://github.com/coreyti/showdown
var showdown = require('../../showdown/src/showdown');
// https://github.com/chjj/marked
var marked = require('../../marked/marked.min.js');
// npm install showdown
var Showdown = require('showdown').converter;
// npm install marked
var marked = require('marked');
// npm install markdown-it
var markdownit = require('markdown-it')('commonmark');

@@ -19,3 +21,3 @@ var benchfile = process.argv[2];

var renderer = new sm.HtmlRenderer();
renderer.renderBlock(doc);
renderer.render(doc);
})

@@ -25,3 +27,3 @@

"use strict";
var converter = new showdown.converter();
var converter = new Showdown();
converter.makeHtml(contents);

@@ -35,2 +37,7 @@ })

.add('markdown-it markdown->html', function() {
"use strict";
markdownit.render(contents);
})
.on('cycle', function(event) {

@@ -37,0 +44,0 @@ "use strict";

@@ -0,2 +1,8 @@

"use strict";
var Node = require('./node');
var unescapeString = require('./common').unescapeString;
var C_GREATERTHAN = 62;
var C_NEWLINE = 10;
var C_SPACE = 32;

@@ -6,21 +12,52 @@ var C_OPEN_BRACKET = 91;

var InlineParser = require('./inlines');
var unescapeString = new InlineParser().unescapeString;
var BLOCKTAGNAME = '(?:article|header|aside|hgroup|iframe|blockquote|hr|body|li|map|button|object|canvas|ol|caption|output|col|p|colgroup|pre|dd|progress|div|section|dl|table|td|dt|tbody|embed|textarea|fieldset|tfoot|figcaption|th|figure|thead|footer|footer|tr|form|ul|h1|h2|h3|h4|h5|h6|video|script|style)';
var HTMLBLOCKOPEN = "<(?:" + BLOCKTAGNAME + "[\\s/>]" + "|" +
"/" + BLOCKTAGNAME + "[\\s>]" + "|" + "[?!])";
var reHtmlBlockOpen = new RegExp('^' + HTMLBLOCKOPEN, 'i');
var reHrule = /^(?:(?:\* *){3,}|(?:_ *){3,}|(?:- *){3,}) *$/;
var reMaybeSpecial = /^[ #`~*+_=<>0-9-]/;
var reNonSpace = /[^ \t\n]/;
var reBulletListMarker = /^[*+-]( +|$)/;
var reOrderedListMarker = /^(\d+)([.)])( +|$)/;
var reATXHeaderMarker = /^#{1,6}(?: +|$)/;
var reCodeFence = /^`{3,}(?!.*`)|^~{3,}(?!.*~)/;
var reClosingCodeFence = /^(?:`{3,}|~{3,})(?= *$)/;
var reSetextHeaderLine = /^(?:=+|-+) *$/;
var reLineEnding = /\r\n|\n|\r/;
// Returns true if string contains only space characters.
var isBlank = function(s) {
return /^\s*$/.test(s);
return !(reNonSpace.test(s));
};
var tabSpaces = [' ', ' ', ' ', ' '];
// Convert tabs to spaces on each line using a 4-space tab stop.
var detabLine = function(text) {
if (text.indexOf('\t') === -1) {
return text;
} else {
var lastStop = 0;
return text.replace(/\t/g, function(match, offset) {
var result = ' '.slice((offset - lastStop) % 4);
lastStop = offset + 1;
return result;
});
var start = 0;
var offset;
var lastStop = 0;
while ((offset = text.indexOf('\t', start)) !== -1) {
var numspaces = (offset - lastStop) % 4;
var spaces = tabSpaces[numspaces];
text = text.slice(0, offset) + spaces + text.slice(offset + 1);
lastStop = offset + numspaces;
start = lastStop;
}
return text;
};

@@ -32,17 +69,18 @@

var res = s.slice(offset).match(re);
if (res) {
if (res === null) {
return -1;
} else {
return offset + res.index;
} else {
return -1;
}
};
var BLOCKTAGNAME = '(?:article|header|aside|hgroup|iframe|blockquote|hr|body|li|map|button|object|canvas|ol|caption|output|col|p|colgroup|pre|dd|progress|div|section|dl|table|td|dt|tbody|embed|textarea|fieldset|tfoot|figcaption|th|figure|thead|footer|footer|tr|form|ul|h1|h2|h3|h4|h5|h6|video|script|style)';
var HTMLBLOCKOPEN = "<(?:" + BLOCKTAGNAME + "[\\s/>]" + "|" +
"/" + BLOCKTAGNAME + "[\\s>]" + "|" + "[?!])";
var reHtmlBlockOpen = new RegExp('^' + HTMLBLOCKOPEN, 'i');
// destructively trip final blank lines in an array of strings
var stripFinalBlankLines = function(lns) {
var i = lns.length - 1;
while (!reNonSpace.test(lns[i])) {
lns.pop();
i--;
}
};
var reHrule = /^(?:(?:\* *){3,}|(?:_ *){3,}|(?:- *){3,}) *$/;
// DOC PARSER

@@ -52,18 +90,2 @@

var makeBlock = function(tag, start_line, start_column) {
return { t: tag,
open: true,
last_line_blank: false,
start_line: start_line,
start_column: start_column,
end_line: start_line,
children: [],
parent: null,
// string_content is formed by concatenating strings, in finalize:
string_content: "",
strings: [],
inline_content: []
};
};
// Returns true if parent block can contain child block.

@@ -73,4 +95,4 @@ var canContain = function(parent_type, child_type) {

parent_type === 'BlockQuote' ||
parent_type === 'ListItem' ||
(parent_type === 'List' && child_type === 'ListItem') );
parent_type === 'Item' ||
(parent_type === 'List' && child_type === 'Item') );
};

@@ -88,10 +110,13 @@

var endsWithBlankLine = function(block) {
if (block.last_line_blank) {
return true;
while (block) {
if (block.last_line_blank) {
return true;
}
if (block.t === 'List' || block.t === 'Item') {
block = block.lastChild;
} else {
break;
}
}
if ((block.t === 'List' || block.t === 'ListItem') && block.children.length > 0) {
return endsWithBlankLine(block.children[block.children.length - 1]);
} else {
return false;
}
return false;
};

@@ -103,3 +128,3 @@

// break of of all lists" feature.)
var breakOutOfLists = function(block, line_number) {
var breakOutOfLists = function(block) {
var b = block;

@@ -116,6 +141,6 @@ var last_list = null;

while (block !== last_list) {
this.finalize(block, line_number);
this.finalize(block, this.lineNumber);
block = block.parent;
}
this.finalize(last_list, line_number);
this.finalize(last_list, this.lineNumber);
this.tip = last_list.parent;

@@ -138,11 +163,12 @@ }

// and so on til we find a block that can accept children.
var addChild = function(tag, line_number, offset) {
var addChild = function(tag, offset) {
while (!canContain(this.tip.t, tag)) {
this.finalize(this.tip, line_number);
this.finalize(this.tip, this.lineNumber - 1);
}
var column_number = offset + 1; // offset 0 = column 1
var newBlock = makeBlock(tag, line_number, column_number);
this.tip.children.push(newBlock);
newBlock.parent = this.tip;
var newBlock = new Node(tag, [[this.lineNumber, column_number], [0, 0]]);
newBlock.strings = [];
newBlock.string_content = null;
this.tip.appendChild(newBlock);
this.tip = newBlock;

@@ -154,11 +180,17 @@ return newBlock;

// start, delimiter, bullet character, padding) or null.
var parseListMarker = function(ln, offset) {
var parseListMarker = function(ln, offset, indent) {
var rest = ln.slice(offset);
var match;
var spaces_after_marker;
var data = {};
var data = { type: null,
tight: true,
bullet_char: null,
start: null,
delimiter: null,
padding: null,
marker_offset: indent };
if (rest.match(reHrule)) {
return null;
}
if ((match = rest.match(/^[*+-]( +|$)/))) {
if ((match = rest.match(reBulletListMarker))) {
spaces_after_marker = match[1].length;

@@ -168,3 +200,3 @@ data.type = 'Bullet';

} else if ((match = rest.match(/^(\d+)([.)])( +|$)/))) {
} else if ((match = rest.match(reOrderedListMarker))) {
spaces_after_marker = match[3].length;

@@ -197,9 +229,17 @@ data.type = 'Ordered';

// Finalize and close any unmatched blocks. Returns true.
var closeUnmatchedBlocks = function() {
// finalize any blocks not matched
while (this.oldtip !== this.lastMatchedContainer) {
this.finalize(this.oldtip, this.lineNumber - 1);
this.oldtip = this.oldtip.parent;
}
return true;
};
// Analyze a line of text and update the document appropriately.
// We parse markdown text by calling this on each line of input,
// then finalizing the document.
var incorporateLine = function(ln, line_number) {
var incorporateLine = function(ln) {
var all_matched = true;
var last_child;
var first_nonspace;

@@ -211,9 +251,14 @@ var offset = 0;

var indent;
var last_matched_container;
var i;
var CODE_INDENT = 4;
var allClosed;
var container = this.doc;
var oldtip = this.tip;
this.oldtip = this.tip;
// replace NUL characters for security
if (ln.indexOf('\u0000') !== -1) {
ln = ln.replace(/\0/g, '\uFFFD');
}
// Convert tabs to spaces:

@@ -225,10 +270,9 @@ ln = detabLine(ln);

// Set all_matched to false if not all containers match.
while (container.children.length > 0) {
last_child = container.children[container.children.length - 1];
if (!last_child.open) {
while (container.lastChild) {
if (!container.lastChild.open) {
break;
}
container = last_child;
container = container.lastChild;
match = matchAt(/[^ ]/, ln, offset);
match = matchAt(reNonSpace, ln, offset);
if (match === -1) {

@@ -255,3 +299,3 @@ first_nonspace = ln.length;

case 'ListItem':
case 'Item':
if (indent >= container.list_data.marker_offset +

@@ -319,22 +363,8 @@ container.list_data.padding) {

last_matched_container = container;
allClosed = (container === this.oldtip);
this.lastMatchedContainer = container;
// This function is used to finalize and close any unmatched
// blocks. We aren't ready to do this now, because we might
// have a lazy paragraph continuation, in which case we don't
// want to close unmatched blocks. So we store this closure for
// use later, when we have more information.
var closeUnmatchedBlocks = function(mythis) {
var already_done = false;
// finalize any blocks not matched
while (!already_done && oldtip !== last_matched_container) {
mythis.finalize(oldtip, line_number);
oldtip = oldtip.parent;
}
already_done = true;
};
// Check to see if we've hit 2nd blank line; if so break out of list:
if (blank && container.last_line_blank) {
this.breakOutOfLists(container, line_number);
this.breakOutOfLists(container);
}

@@ -348,8 +378,9 @@

// this is a little performance optimization:
matchAt(/^[ #`~*+_=<>0-9-]/, ln, offset) !== -1) {
matchAt(reMaybeSpecial, ln, offset) !== -1) {
match = matchAt(/[^ ]/, ln, offset);
match = matchAt(reNonSpace, ln, offset);
if (match === -1) {
first_nonspace = ln.length;
blank = true;
break;
} else {

@@ -365,11 +396,16 @@ first_nonspace = match;

offset += CODE_INDENT;
closeUnmatchedBlocks(this);
container = this.addChild('IndentedCode', line_number, offset);
} else { // indent > 4 in a lazy paragraph continuation
break;
allClosed = allClosed ||
this.closeUnmatchedBlocks();
container = this.addChild('IndentedCode', offset);
}
break;
}
} else if (ln.charCodeAt(first_nonspace) === C_GREATERTHAN) {
offset = first_nonspace;
var cc = ln.charCodeAt(offset);
if (cc === C_GREATERTHAN) {
// blockquote
offset = first_nonspace + 1;
offset += 1;
// optional following space

@@ -379,10 +415,10 @@ if (ln.charCodeAt(offset) === C_SPACE) {

}
closeUnmatchedBlocks(this);
container = this.addChild('BlockQuote', line_number, offset);
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('BlockQuote', first_nonspace);
} else if ((match = ln.slice(first_nonspace).match(/^#{1,6}(?: +|$)/))) {
} else if ((match = ln.slice(offset).match(reATXHeaderMarker))) {
// ATX header
offset = first_nonspace + match[0].length;
closeUnmatchedBlocks(this);
container = this.addChild('Header', line_number, first_nonspace);
offset += match[0].length;
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('Header', first_nonspace);
container.level = match[0].trim().length; // number of #s

@@ -394,18 +430,18 @@ // remove trailing ###s:

} else if ((match = ln.slice(first_nonspace).match(/^`{3,}(?!.*`)|^~{3,}(?!.*~)/))) {
} else if ((match = ln.slice(offset).match(reCodeFence))) {
// fenced code block
var fence_length = match[0].length;
closeUnmatchedBlocks(this);
container = this.addChild('FencedCode', line_number, first_nonspace);
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('FencedCode', first_nonspace);
container.fence_length = fence_length;
container.fence_char = match[0][0];
container.fence_offset = first_nonspace - offset;
offset = first_nonspace + fence_length;
container.fence_offset = indent;
offset += fence_length;
break;
} else if (matchAt(reHtmlBlockOpen, ln, first_nonspace) !== -1) {
} else if (matchAt(reHtmlBlockOpen, ln, offset) !== -1) {
// html block
closeUnmatchedBlocks(this);
container = this.addChild('HtmlBlock', line_number, first_nonspace);
// note, we don't adjust offset because the tag is part of the text
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('HtmlBlock', offset);
offset -= indent; // back up so spaces are part of block
break;

@@ -415,21 +451,21 @@

container.strings.length === 1 &&
((match = ln.slice(first_nonspace).match(/^(?:=+|-+) *$/)))) {
((match = ln.slice(offset).match(reSetextHeaderLine)))) {
// setext header line
closeUnmatchedBlocks(this);
allClosed = allClosed || this.closeUnmatchedBlocks();
container.t = 'Header'; // convert Paragraph to SetextHeader
container.level = match[0][0] === '=' ? 1 : 2;
offset = ln.length;
break;
} else if (matchAt(reHrule, ln, first_nonspace) !== -1) {
} else if (matchAt(reHrule, ln, offset) !== -1) {
// hrule
closeUnmatchedBlocks(this);
container = this.addChild('HorizontalRule', line_number, first_nonspace);
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('HorizontalRule', first_nonspace);
offset = ln.length - 1;
break;
} else if ((data = parseListMarker(ln, first_nonspace))) {
} else if ((data = parseListMarker(ln, offset, indent))) {
// list item
closeUnmatchedBlocks(this);
data.marker_offset = indent;
offset = first_nonspace + data.padding;
allClosed = allClosed || this.closeUnmatchedBlocks();
offset += data.padding;

@@ -439,3 +475,3 @@ // add the list if needed

!(listsMatch(container.list_data, data))) {
container = this.addChild('List', line_number, first_nonspace);
container = this.addChild('List', first_nonspace);
container.list_data = data;

@@ -445,3 +481,3 @@ }

// add the list item
container = this.addChild('ListItem', line_number, first_nonspace);
container = this.addChild('Item', first_nonspace);
container.list_data = data;

@@ -454,6 +490,2 @@

if (acceptsLines(container.t)) {
// if it's a line container, it can't contain other containers
break;
}
}

@@ -464,3 +496,3 @@

match = matchAt(/[^ ]/, ln, offset);
match = matchAt(reNonSpace, ln, offset);
if (match === -1) {

@@ -476,4 +508,3 @@ first_nonspace = ln.length;

// First check for a lazy paragraph continuation:
if (this.tip !== last_matched_container &&
!blank &&
if (!allClosed && !blank &&
this.tip.t === 'Paragraph' &&

@@ -489,3 +520,3 @@ this.tip.strings.length > 0) {

// finalize any blocks not matched
closeUnmatchedBlocks(this);
allClosed = allClosed || this.closeUnmatchedBlocks();

@@ -500,5 +531,5 @@ // Block quote lines are never blank as they start with >

container.t === 'FencedCode' ||
(container.t === 'ListItem' &&
container.children.length === 0 &&
container.start_line === line_number));
(container.t === 'Item' &&
!container.firstChild &&
container.sourcepos[0][0] === this.lineNumber));

@@ -521,6 +552,6 @@ var cont = container;

ln.charAt(first_nonspace) === container.fence_char &&
ln.slice(first_nonspace).match(/^(?:`{3,}|~{3,})(?= *$)/));
ln.slice(first_nonspace).match(reClosingCodeFence));
if (match && match[0].length >= container.fence_length) {
// don't add closing fence to container; instead, close it:
this.finalize(container, line_number);
this.finalize(container, this.lineNumber);
} else {

@@ -541,15 +572,10 @@ this.addLine(ln, offset);

break;
} else if (container.t !== 'HorizontalRule' &&
container.t !== 'Header') {
} else {
// create paragraph container for line
container = this.addChild('Paragraph', line_number, first_nonspace);
container = this.addChild('Paragraph', this.lineNumber, first_nonspace);
this.addLine(ln, first_nonspace);
} else {
console.log("Line " + line_number.toString() +
" with container type " + container.t +
" did not match any condition.");
}
}
}
this.lastLineLength = ln.length - 1; // -1 for newline
};

@@ -562,3 +588,3 @@

// parent of the closed block.
var finalize = function(block, line_number) {
var finalize = function(block, lineNumber) {
var pos;

@@ -570,12 +596,7 @@ // don't do anything if the block is already closed

block.open = false;
if (line_number > block.start_line) {
block.end_line = line_number - 1;
} else {
block.end_line = line_number;
}
block.sourcepos[1] = [lineNumber, this.lastLineLength + 1];
switch (block.t) {
case 'Paragraph':
block.string_content = block.strings.join('\n').replace(/^ {2,}/m, '');
// delete block.strings;
block.string_content = block.strings.join('\n');

@@ -595,8 +616,12 @@ // try parsing the beginning as link reference definitions:

case 'Header':
case 'HtmlBlock':
block.string_content = block.strings.join('\n');
break;
case 'HtmlBlock':
block.literal = block.strings.join('\n');
break;
case 'IndentedCode':
block.string_content = block.strings.join('\n').replace(/(\n *)*$/, '\n');
stripFinalBlankLines(block.strings);
block.literal = block.strings.join('\n') + '\n';
block.t = 'CodeBlock';

@@ -609,5 +634,5 @@ break;

if (block.strings.length === 1) {
block.string_content = '';
block.literal = '';
} else {
block.string_content = block.strings.slice(1).join('\n') + '\n';
block.literal = block.strings.slice(1).join('\n') + '\n';
}

@@ -618,12 +643,9 @@ block.t = 'CodeBlock';

case 'List':
block.tight = true; // tight by default
block.list_data.tight = true; // tight by default
var numitems = block.children.length;
var i = 0;
while (i < numitems) {
var item = block.children[i];
var item = block.firstChild;
while (item) {
// check for non-final list item ending with blank line:
var last_item = i === numitems - 1;
if (endsWithBlankLine(item) && !last_item) {
block.tight = false;
if (endsWithBlankLine(item) && item.next) {
block.list_data.tight = false;
break;

@@ -633,14 +655,11 @@ }

// spaces between any of them:
var numsubitems = item.children.length;
var j = 0;
while (j < numsubitems) {
var subitem = item.children[j];
var last_subitem = j === numsubitems - 1;
if (endsWithBlankLine(subitem) && !(last_item && last_subitem)) {
block.tight = false;
var subitem = item.firstChild;
while (subitem) {
if (endsWithBlankLine(subitem) && (item.next || subitem.next)) {
block.list_data.tight = false;
break;
}
j++;
subitem = subitem.next;
}
i++;
item = item.next;
}

@@ -659,57 +678,46 @@ break;

var processInlines = function(block) {
var newblock = {};
newblock.t = block.t;
newblock.start_line = block.start_line;
newblock.start_column = block.start_column;
newblock.end_line = block.end_line;
switch(block.t) {
case 'Paragraph':
newblock.inline_content =
this.inlineParser.parse(block.string_content.trim(), this.refmap);
break;
case 'Header':
newblock.inline_content =
this.inlineParser.parse(block.string_content.trim(), this.refmap);
newblock.level = block.level;
break;
case 'List':
newblock.list_data = block.list_data;
newblock.tight = block.tight;
break;
case 'CodeBlock':
newblock.string_content = block.string_content;
newblock.info = block.info;
break;
case 'HtmlBlock':
newblock.string_content = block.string_content;
break;
default:
break;
}
if (block.children) {
var newchildren = [];
for (var i = 0; i < block.children.length; i++) {
newchildren.push(this.processInlines(block.children[i]));
var node, event;
var walker = block.walker();
while ((event = walker.next())) {
node = event.node;
if (!event.entering && (node.t === 'Paragraph' ||
node.t === 'Header')) {
this.inlineParser.parse(node, this.refmap);
}
newblock.children = newchildren;
}
return newblock;
};
var Document = function() {
var doc = new Node('Document', [[1, 1], [0, 0]]);
doc.string_content = null;
doc.strings = [];
return doc;
};
// The main parsing function. Returns a parsed document AST.
var parse = function(input) {
this.doc = makeBlock('Document', 1, 1);
this.doc = new Document();
this.tip = this.doc;
this.refmap = {};
var lines = input.replace(/\n$/, '').split(/\r\n|\n|\r/);
if (this.options.time) { console.time("preparing input"); }
var lines = input.split(reLineEnding);
var len = lines.length;
if (input.charCodeAt(input.length - 1) === C_NEWLINE) {
// ignore last blank line created by final newline
len -= 1;
}
if (this.options.time) { console.timeEnd("preparing input"); }
if (this.options.time) { console.time("block parsing"); }
for (var i = 0; i < len; i++) {
this.incorporateLine(lines[i], i + 1);
this.lineNumber += 1;
this.incorporateLine(lines[i]);
}
while (this.tip) {
this.finalize(this.tip, len - 1);
this.finalize(this.tip, len);
}
return this.processInlines(this.doc);
if (this.options.time) { console.timeEnd("block parsing"); }
if (this.options.time) { console.time("inline parsing"); }
this.processInlines(this.doc);
if (this.options.time) { console.timeEnd("inline parsing"); }
return this.doc;
};

@@ -719,7 +727,11 @@

// The DocParser object.
function DocParser(){
function DocParser(options){
return {
doc: makeBlock('Document', 1, 1),
doc: new Document(),
tip: this.doc,
oldtip: this.doc,
lineNumber: 0,
lastMatchedContainer: this.doc,
refmap: {},
lastLineLength: 0,
inlineParser: new InlineParser(),

@@ -732,3 +744,5 @@ breakOutOfLists: breakOutOfLists,

processInlines: processInlines,
parse: parse
closeUnmatchedBlocks: closeUnmatchedBlocks,
parse: parse,
options: options || {}
};

@@ -735,0 +749,0 @@ }

@@ -0,1 +1,3 @@

"use strict";
// derived from https://github.com/mathiasbynens/String.fromCodePoint

@@ -5,3 +7,2 @@ /*! http://mths.be/fromcodepoint v0.2.1 by @mathias */

module.exports = function (_) {
"use strict";
try {

@@ -22,3 +23,2 @@ return String.fromCodePoint(_);

var fromCodePoint = function() {
"use strict";
var MAX_SIZE = 0x4000;

@@ -25,0 +25,0 @@ var codeUnits = [];

@@ -0,1 +1,3 @@

"use strict";
var fromCodePoint = require('./from-code-point');

@@ -2131,3 +2133,2 @@

var entityToChar = function(m) {
"use strict";
var isNumeric = /^&#/.test(m);

@@ -2134,0 +2135,0 @@ var isHex = /^&#[Xx]/.test(m);

@@ -0,1 +1,3 @@

"use strict";
// commonmark.js - CommomMark in JavaScript

@@ -12,12 +14,5 @@ // Copyright (C) 2014 John MacFarlane

"use strict";
var util = require('util');
var renderAST = function(tree) {
return util.inspect(tree, {depth: null});
};
module.exports.Node = require('./node');
module.exports.DocParser = require('./blocks');
module.exports.HtmlRenderer = require('./html-renderer');
module.exports.ASTRenderer = renderAST;
module.exports.HtmlRenderer = require('./html');
module.exports.XmlRenderer = require('./xml');

@@ -0,1 +1,7 @@

"use strict";
var Node = require('./node');
var common = require('./common');
var normalizeURI = common.normalizeURI;
var unescapeString = common.unescapeString;
var fromCodePoint = require('./from-code-point.js');

@@ -7,3 +13,2 @@ var entityToChar = require('./html5-entities.js').entityToChar;

var C_NEWLINE = 10;
var C_SPACE = 32;
var C_ASTERISK = 42;

@@ -37,6 +42,6 @@ var C_UNDERSCORE = 95;

var CLOSETAG = "</" + TAGNAME + "\\s*[>]";
var HTMLCOMMENT = "<!--([^-]+|[-][^-]+)*-->";
var HTMLCOMMENT = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->";
var PROCESSINGINSTRUCTION = "[<][?].*?[?][>]";
var DECLARATION = "<![A-Z]+" + "\\s+[^>]*>";
var CDATA = "<!\\[CDATA\\[([^\\]]+|\\][^\\]]|\\]\\][^>])*\\]\\]>";
var CDATA = "<!\\[CDATA\\[[\\s\\S]*?\]\\]>";
var HTMLTAG = "(?:" + OPENTAG + "|" + CLOSETAG + "|" + HTMLCOMMENT + "|" +

@@ -65,19 +70,29 @@ PROCESSINGINSTRUCTION + "|" + DECLARATION + "|" + CDATA + ")";

var reAllEscapedChar = new RegExp('\\\\(' + ESCAPABLE + ')', 'g');
var reEntityHere = new RegExp('^' + ENTITY, 'i');
var reEntity = new RegExp(ENTITY, 'gi');
var reTicks = new RegExp('`+');
// Matches a character with a special meaning in markdown,
// or a string of non-special characters. Note: we match
// clumps of _ or * or `, because they need to be handled in groups.
var reMain = /^(?:[_*`\n]+|[\[\]\\!<&*_]|(?: *[^\n `\[\]\\!<&*_]+)+|[ \n]+)/m;
var reTicksHere = new RegExp('^`+');
// Replace entities and backslash escapes with literal characters.
var unescapeString = function(s) {
return s.replace(reAllEscapedChar, '$1')
.replace(reEntity, entityToChar);
};
var reEmailAutolink = /^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/;
var reAutolink = /^<(?:coap|doi|javascript|aaa|aaas|about|acap|cap|cid|crid|data|dav|dict|dns|file|ftp|geo|go|gopher|h323|http|https|iax|icap|im|imap|info|ipp|iris|iris.beep|iris.xpc|iris.xpcs|iris.lwz|ldap|mailto|mid|msrp|msrps|mtqp|mupdate|news|nfs|ni|nih|nntp|opaquelocktoken|pop|pres|rtsp|service|session|shttp|sieve|sip|sips|sms|snmp|soap.beep|soap.beeps|tag|tel|telnet|tftp|thismessage|tn3270|tip|tv|urn|vemmi|ws|wss|xcon|xcon-userid|xmlrpc.beep|xmlrpc.beeps|xmpp|z39.50r|z39.50s|adiumxtra|afp|afs|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|chrome|chrome-extension|com-eventbrite-attendee|content|cvs|dlna-playsingle|dlna-playcontainer|dtn|dvb|ed2k|facetime|feed|finger|fish|gg|git|gizmoproject|gtalk|hcp|icon|ipn|irc|irc6|ircs|itms|jar|jms|keyparc|lastfm|ldaps|magnet|maps|market|message|mms|ms-help|msnim|mumble|mvn|notes|oid|palm|paparazzi|platform|proxy|psyc|query|res|resource|rmi|rsync|rtmp|secondlife|sftp|sgn|skype|smb|soldat|spotify|ssh|steam|svn|teamspeak|things|udp|unreal|ut2004|ventrilo|view-source|webcal|wtai|wyciwyg|xfire|xri|ymsgr):[^<>\x00-\x20]*>/i;
var reSpnl = /^ *(?:\n *)?/;
var reWhitespaceChar = /^\s/;
var reWhitespace = /\s+/g;
var reFinalSpace = / *$/;
var reInitialSpace = /^ */;
var reAsciiAlnum = /[a-z0-9]/i;
var reLinkLabel = /^\[(?:[^\\\[\]]|\\[\[\]]){0,1000}\]/;
// Matches a string of non-special characters.
var reMain = /^[^\n`\[\]\\!<&*_]+/m;
// Normalize reference label: collapse internal whitespace

@@ -91,2 +106,8 @@ // to single space, remove leading/trailing whitespace, case fold.

var text = function(s) {
var node = new Node('Text');
node.literal = s;
return node;
};
// INLINE PARSER

@@ -122,3 +143,3 @@

var spnl = function() {
this.match(/^ *(?:\n *)?/);
this.match(reSpnl);
return 1;

@@ -131,6 +152,6 @@ };

// Attempt to parse backticks, returning either a backtick code span or a
// Attempt to parse backticks, adding either a backtick code span or a
// literal sequence of backticks.
var parseBackticks = function(inlines) {
var ticks = this.match(/^`+/);
var parseBackticks = function(block) {
var ticks = this.match(reTicksHere);
if (!ticks) {

@@ -142,8 +163,10 @@ return 0;

var matched;
while (!foundCode && (matched = this.match(/`+/m))) {
var node;
while (!foundCode && (matched = this.match(reTicks))) {
if (matched === ticks) {
inlines.push({ t: 'Code', c: this.subject.slice(afterOpenTicks,
this.pos - ticks.length)
.replace(/[ \n]+/g, ' ')
.trim() });
node = new Node('Code');
node.literal = this.subject.slice(afterOpenTicks,
this.pos - ticks.length)
.trim().replace(reWhitespace, ' ');
block.appendChild(node);
return true;

@@ -154,3 +177,3 @@ }

this.pos = afterOpenTicks;
inlines.push({ t: 'Text', c: ticks });
block.appendChild(text(ticks));
return true;

@@ -161,16 +184,18 @@ };

// character, a hard line break (if the backslash is followed by a newline),
// or a literal backslash to the 'inlines' list.
var parseBackslash = function(inlines) {
// or a literal backslash to the block's children.
var parseBackslash = function(block) {
var subj = this.subject,
pos = this.pos;
var node;
if (subj.charCodeAt(pos) === C_BACKSLASH) {
if (subj.charAt(pos + 1) === '\n') {
this.pos = this.pos + 2;
inlines.push({ t: 'Hardbreak' });
node = new Node('Hardbreak');
block.appendChild(node);
} else if (reEscapable.test(subj.charAt(pos + 1))) {
this.pos = this.pos + 2;
inlines.push({ t: 'Text', c: subj.charAt(pos + 1) });
block.appendChild(text(subj.charAt(pos + 1)));
} else {
this.pos++;
inlines.push({t: 'Text', c: '\\'});
block.appendChild(text('\\'));
}

@@ -184,18 +209,21 @@ return true;

// Attempt to parse an autolink (URL or email in pointy brackets).
var parseAutolink = function(inlines) {
var parseAutolink = function(block) {
var m;
var dest;
if ((m = this.match(/^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/))) { // email autolink
var node;
if ((m = this.match(reEmailAutolink))) {
dest = m.slice(1, -1);
inlines.push(
{t: 'Link',
label: [{ t: 'Text', c: dest }],
destination: 'mailto:' + encodeURI(unescape(dest)) });
node = new Node('Link');
node.destination = normalizeURI('mailto:' + dest);
node.title = '';
node.appendChild(text(dest));
block.appendChild(node);
return true;
} else if ((m = this.match(/^<(?:coap|doi|javascript|aaa|aaas|about|acap|cap|cid|crid|data|dav|dict|dns|file|ftp|geo|go|gopher|h323|http|https|iax|icap|im|imap|info|ipp|iris|iris.beep|iris.xpc|iris.xpcs|iris.lwz|ldap|mailto|mid|msrp|msrps|mtqp|mupdate|news|nfs|ni|nih|nntp|opaquelocktoken|pop|pres|rtsp|service|session|shttp|sieve|sip|sips|sms|snmp|soap.beep|soap.beeps|tag|tel|telnet|tftp|thismessage|tn3270|tip|tv|urn|vemmi|ws|wss|xcon|xcon-userid|xmlrpc.beep|xmlrpc.beeps|xmpp|z39.50r|z39.50s|adiumxtra|afp|afs|aim|apt|attachment|aw|beshare|bitcoin|bolo|callto|chrome|chrome-extension|com-eventbrite-attendee|content|cvs|dlna-playsingle|dlna-playcontainer|dtn|dvb|ed2k|facetime|feed|finger|fish|gg|git|gizmoproject|gtalk|hcp|icon|ipn|irc|irc6|ircs|itms|jar|jms|keyparc|lastfm|ldaps|magnet|maps|market|message|mms|ms-help|msnim|mumble|mvn|notes|oid|palm|paparazzi|platform|proxy|psyc|query|res|resource|rmi|rsync|rtmp|secondlife|sftp|sgn|skype|smb|soldat|spotify|ssh|steam|svn|teamspeak|things|udp|unreal|ut2004|ventrilo|view-source|webcal|wtai|wyciwyg|xfire|xri|ymsgr):[^<>\x00-\x20]*>/i))) {
} else if ((m = this.match(reAutolink))) {
dest = m.slice(1, -1);
inlines.push({
t: 'Link',
label: [{ t: 'Text', c: dest }],
destination: encodeURI(unescape(dest)) });
node = new Node('Link');
node.destination = normalizeURI(dest);
node.title = '';
node.appendChild(text(dest));
block.appendChild(node);
return true;

@@ -208,6 +236,9 @@ } else {

// Attempt to parse a raw HTML tag.
var parseHtmlTag = function(inlines) {
var parseHtmlTag = function(block) {
var m = this.match(reHtmlTag);
var node;
if (m) {
inlines.push({ t: 'Html', c: m });
node = new Node('Html');
node.literal = m;
block.appendChild(node);
return true;

@@ -243,13 +274,13 @@ } else {

var can_open = numdelims > 0 && !(/\s/.test(char_after)) &&
var can_open = numdelims > 0 && !(reWhitespaceChar.test(char_after)) &&
!(rePunctuation.test(char_after) &&
!(/\s/.test(char_before)) &&
!(rePunctuation.test(char_before)));
var can_close = numdelims > 0 && !(/\s/.test(char_before)) &&
var can_close = numdelims > 0 && !(reWhitespaceChar.test(char_before)) &&
!(rePunctuation.test(char_before) &&
!(/\s/.test(char_after)) &&
!(reWhitespaceChar.test(char_after)) &&
!(rePunctuation.test(char_after)));
if (cc === C_UNDERSCORE) {
can_open = can_open && !((/[a-z0-9]/i).test(char_before));
can_close = can_close && !((/[a-z0-9]/i).test(char_after));
can_open = can_open && !((reAsciiAlnum).test(char_before));
can_close = can_close && !((reAsciiAlnum).test(char_after));
}

@@ -262,17 +293,4 @@ this.pos = startpos;

var Emph = function(ils) {
return {t: 'Emph', c: ils};
};
var Strong = function(ils) {
return {t: 'Strong', c: ils};
};
var Str = function(s) {
return {t: 'Text', c: s};
};
// Attempt to parse emphasis or strong emphasis.
var parseEmphasis = function(cc, inlines) {
var parseEmphasis = function(cc, block) {
var res = this.scanDelims(cc);

@@ -287,3 +305,4 @@ var numdelims = res.numdelims;

this.pos += numdelims;
inlines.push(Str(this.subject.slice(startpos, this.pos)));
var node = text(this.subject.slice(startpos, this.pos));
block.appendChild(node);

@@ -293,3 +312,3 @@ // Add entry to stack for this opener

numdelims: numdelims,
pos: inlines.length - 1,
node: node,
previous: this.delimiters,

@@ -320,17 +339,3 @@ next: null,

var removeGaps = function(inlines) {
// remove gaps from inlines
var i, j;
j = 0;
for (i = 0 ; i < inlines.length; i++) {
if (inlines[i] !== null) {
inlines[j] = inlines[i];
j++;
}
}
inlines.splice(j);
};
var processEmphasis = function(inlines, stack_bottom) {
"use strict";
var processEmphasis = function(block, stack_bottom) {
var opener, closer;

@@ -340,5 +345,3 @@ var opener_inl, closer_inl;

var use_delims;
var contents;
var emph;
var i;
var tmp, next;

@@ -370,4 +373,4 @@ // find first closer above stack_bottom:

opener_inl = inlines[opener.pos];
closer_inl = inlines[closer.pos];
opener_inl = opener.node;
closer_inl = closer.node;

@@ -377,17 +380,22 @@ // remove used delimiters from stack elts and inlines

closer.numdelims -= use_delims;
opener_inl.c = opener_inl.c.slice(0, opener_inl.c.length - use_delims);
closer_inl.c = closer_inl.c.slice(0, closer_inl.c.length - use_delims);
opener_inl.literal =
opener_inl.literal.slice(0,
opener_inl.literal.length - use_delims);
closer_inl.literal =
closer_inl.literal.slice(0,
closer_inl.literal.length - use_delims);
// build contents for new emph element
contents = inlines.slice(opener.pos + 1, closer.pos);
removeGaps(contents);
var emph = new Node(use_delims === 1 ? 'Emph' : 'Strong');
emph = use_delims === 1 ? Emph(contents) : Strong(contents);
// insert into list of inlines
inlines[opener.pos + 1] = emph;
for (i = opener.pos + 2; i < closer.pos; i++) {
inlines[i] = null;
tmp = opener_inl.next;
while (tmp && tmp !== closer_inl) {
next = tmp.next;
tmp.unlink();
emph.appendChild(tmp);
tmp = next;
}
opener_inl.insertAfter(emph);
// remove elts btw opener and closer in delimiters stack

@@ -403,3 +411,3 @@ tempstack = closer.previous;

if (opener.numdelims === 0) {
inlines[opener.pos] = null;
opener_inl.unlink();
this.removeDelimiter(opener);

@@ -409,3 +417,3 @@ }

if (closer.numdelims === 0) {
inlines[closer.pos] = null;
closer_inl.unlink();
tempstack = closer.next;

@@ -416,3 +424,2 @@ this.removeDelimiter(closer);

} else {

@@ -428,4 +435,2 @@ closer = closer.next;

removeGaps(inlines);
// remove all delimiters

@@ -454,7 +459,7 @@ while (this.delimiters !== stack_bottom) {

if (res) { // chop off surrounding <..>:
return encodeURI(unescape(unescapeString(res.substr(1, res.length - 2))));
return normalizeURI(unescapeString(res.substr(1, res.length - 2)));
} else {
res = this.match(reLinkDestination);
if (res !== null) {
return encodeURI(unescape(unescapeString(res)));
return normalizeURI(unescapeString(res));
} else {

@@ -468,13 +473,13 @@ return null;

var parseLinkLabel = function() {
var m = this.match(/^\[(?:[^\\\[\]]|\\[\[\]]){0,1000}\]/);
var m = this.match(reLinkLabel);
return m === null ? 0 : m.length;
};
// Add open bracket to delimiter stack and add a Str to inlines.
var parseOpenBracket = function(inlines) {
// Add open bracket to delimiter stack and add a text node to block's children.
var parseOpenBracket = function(block) {
var startpos = this.pos;
this.pos += 1;
inlines.push(Str("["));
var node = text('[');
block.appendChild(node);

@@ -484,3 +489,3 @@ // Add entry to stack for this opener

numdelims: 1,
pos: inlines.length - 1,
node: node,
previous: this.delimiters,

@@ -501,5 +506,4 @@ next: null,

// IF next character is [, and ! delimiter to delimiter stack and
// add a Str to inlines. Otherwise just add a Str.
var parseBang = function(inlines) {
// add a text node to block's children. Otherwise just add a text node.
var parseBang = function(block) {
var startpos = this.pos;

@@ -509,8 +513,10 @@ this.pos += 1;

this.pos += 1;
inlines.push(Str("!["));
var node = text('![');
block.appendChild(node);
// Add entry to stack for this opener
this.delimiters = { cc: C_BANG,
numdelims: 1,
pos: inlines.length - 1,
node: node,
previous: this.delimiters,

@@ -526,3 +532,3 @@ next: null,

} else {
inlines.push(Str("!"));
block.appendChild(text('!'));
}

@@ -534,5 +540,5 @@ return true;

// stack. Add either a link or image, or a plain [ character,
// to the inlines stack. If there is a matching delimiter,
// to block's children. If there is a matching delimiter,
// remove it from the delimiter stack.
var parseCloseBracket = function(inlines) {
var parseCloseBracket = function(block) {
var startpos;

@@ -543,4 +549,2 @@ var is_image;

var matched = false;
var link_text;
var i;
var reflabel;

@@ -564,3 +568,3 @@ var opener;

// no matched opener, just return a literal
inlines.push(Str("]"));
block.appendChild(text(']'));
return true;

@@ -571,3 +575,3 @@ }

// no matched opener, just return a literal
inlines.push(Str("]"));
block.appendChild(text(']'));
// take opener off emphasis stack

@@ -580,11 +584,2 @@ this.removeDelimiter(opener);

is_image = opener.cc === C_BANG;
// instead of copying a slice, we null out the
// parts of inlines that don't correspond to link_text;
// later, we'll collapse them. This is awkward, and could
// be simplified if we made inlines a linked list rather than
// an array:
link_text = inlines.slice(0);
for (i = 0; i < opener.pos + 1; i++) {
link_text[i] = null;
}

@@ -600,6 +595,7 @@ // Check to see if we have a link/image

// make sure there's a space before the title:
(/^\s/.test(this.subject.charAt(this.pos - 1)) &&
(title = this.parseLinkTitle() || '') || true) &&
(reWhitespaceChar.test(this.subject.charAt(this.pos - 1)) &&
(title = this.parseLinkTitle()) || true) &&
this.spnl() &&
this.match(/^\)/)) {
this.subject.charAt(this.pos) === ')') {
this.pos += 1;
matched = true;

@@ -635,10 +631,19 @@ }

if (matched) {
this.processEmphasis(link_text, opener.previous);
var node = new Node(is_image ? 'Image' : 'Link');
node.destination = dest;
node.title = title || '';
// remove the part of inlines that became link_text.
// see note above on why we need to do this instead of splice:
for (i = opener.pos; i < inlines.length; i++) {
inlines[i] = null;
var tmp, next;
tmp = opener.node.next;
while (tmp) {
next = tmp.next;
tmp.unlink();
node.appendChild(tmp);
tmp = next;
}
block.appendChild(node);
this.processEmphasis(node, opener.previous);
opener.node.unlink();
// processEmphasis will remove this and later delimiters.

@@ -657,6 +662,2 @@ // Now, for a link, we also deactivate earlier link openers.

inlines.push({t: is_image ? 'Image' : 'Link',
destination: dest,
title: title,
label: link_text});
return true;

@@ -668,3 +669,3 @@

this.pos = startpos;
inlines.push(Str("]"));
block.appendChild(text(']'));
return true;

@@ -676,6 +677,6 @@ }

// Attempt to parse an entity, return Entity object if successful.
var parseEntity = function(inlines) {
var parseEntity = function(block) {
var m;
if ((m = this.match(reEntityHere))) {
inlines.push({ t: 'Text', c: entityToChar(m) });
block.appendChild(text(entityToChar(m)));
return true;

@@ -688,7 +689,7 @@ } else {

// Parse a run of ordinary characters, or a single character with
// a special meaning in markdown, as a plain string, adding to inlines.
var parseString = function(inlines) {
// a special meaning in markdown, as a plain string.
var parseString = function(block) {
var m;
if ((m = this.match(reMain))) {
inlines.push({ t: 'Text', c: m });
block.appendChild(text(m));
return true;

@@ -702,13 +703,17 @@ } else {

// line break; otherwise a soft line break.
var parseNewline = function(inlines) {
var m = this.match(/^ *\n/);
if (m) {
if (m.length > 2) {
inlines.push({ t: 'Hardbreak' });
} else if (m.length > 0) {
inlines.push({ t: 'Softbreak' });
var parseNewline = function(block) {
this.pos += 1; // assume we're at a \n
// check previous node for trailing spaces
var lastc = block.lastChild;
if (lastc && lastc.t === 'Text') {
var sps = reFinalSpace.exec(lastc.literal)[0].length;
if (sps > 0) {
lastc.literal = lastc.literal.replace(reFinalSpace, '');
}
return true;
block.appendChild(new Node(sps >= 2 ? 'Hardbreak' : 'Softbreak'));
} else {
block.appendChild(new Node('Softbreak'));
}
return false;
this.match(reInitialSpace); // gobble leading spaces in next line
return true;
};

@@ -720,3 +725,2 @@

this.pos = 0;
this.label_nest_level = 0;
var rawlabel;

@@ -777,6 +781,6 @@ var dest;

// Parse the next inline element in subject, advancing subject position.
// On success, add the result to the inlines list, and return true.
// On success, add the result to block's children and return true.
// On failure, return false.
var parseInline = function(inlines) {
"use strict";
var parseInline = function(block) {
var res;
var c = this.peek();

@@ -786,35 +790,33 @@ if (c === -1) {

}
var res;
switch(c) {
case C_NEWLINE:
case C_SPACE:
res = this.parseNewline(inlines);
res = this.parseNewline(block);
break;
case C_BACKSLASH:
res = this.parseBackslash(inlines);
res = this.parseBackslash(block);
break;
case C_BACKTICK:
res = this.parseBackticks(inlines);
res = this.parseBackticks(block);
break;
case C_ASTERISK:
case C_UNDERSCORE:
res = this.parseEmphasis(c, inlines);
res = this.parseEmphasis(c, block);
break;
case C_OPEN_BRACKET:
res = this.parseOpenBracket(inlines);
res = this.parseOpenBracket(block);
break;
case C_BANG:
res = this.parseBang(inlines);
res = this.parseBang(block);
break;
case C_CLOSE_BRACKET:
res = this.parseCloseBracket(inlines);
res = this.parseCloseBracket(block);
break;
case C_LESSTHAN:
res = this.parseAutolink(inlines) || this.parseHtmlTag(inlines);
res = this.parseAutolink(block) || this.parseHtmlTag(block);
break;
case C_AMPERSAND:
res = this.parseEntity(inlines);
res = this.parseEntity(block);
break;
default:
res = this.parseString(inlines);
res = this.parseString(block);
break;

@@ -824,3 +826,5 @@ }

this.pos += 1;
inlines.push({t: 'Text', c: fromCodePoint(c)});
var textnode = new Node('Text');
textnode.literal = fromCodePoint(c);
block.appendChild(textnode);
}

@@ -831,13 +835,12 @@

// Parse s as a list of inlines, using refmap to resolve references.
var parseInlines = function(s, refmap) {
this.subject = s;
// Parse string_content in block into inline children,
// using refmap to resolve references.
var parseInlines = function(block, refmap) {
this.subject = block.string_content.trim();
this.pos = 0;
this.refmap = refmap || {};
this.delimiters = null;
var inlines = [];
while (this.parseInline(inlines)) {
while (this.parseInline(block)) {
}
this.processEmphasis(inlines, null);
return inlines;
this.processEmphasis(block, null);
};

@@ -847,6 +850,4 @@

function InlineParser(){
"use strict";
return {
subject: '',
label_nest_level: 0, // used by parseLinkLabel method
delimiters: null, // used by parseEmphasis method

@@ -858,3 +859,2 @@ pos: 0,

spnl: spnl,
unescapeString: unescapeString,
parseBackticks: parseBackticks,

@@ -884,2 +884,1 @@ parseBackslash: parseBackslash,

module.exports = InlineParser;
{ "name": "commonmark",
"description": "a strongly specified, highly compatible variant of Markdown",
"version": "0.15.0",
"version": "0.16.0",
"homepage": "http://commonmark.org",

@@ -5,0 +5,0 @@ "keywords":

@@ -6,78 +6,163 @@ #!/usr/bin/env node

var commonmark = require('./lib/index.js');
var ansi = require('./ansi/ansi');
var cursor = ansi(process.stdout);
// Home made mini-version of the npm ansi module:
var escSeq = function(s) {
return function (){
process.stdout.write('\u001b' + s);
return this;
};
};
var repeat = function(pattern, count) {
if (count < 1) {
return '';
}
var result = '';
while (count > 1) {
if (count & 1) {
result += pattern;
}
count >>= 1;
pattern += pattern;
}
return result + pattern;
};
var cursor = {
write: function (s) {
process.stdout.write(s);
return this;
},
green: escSeq('[0;32m'),
red: escSeq('[0;31m'),
cyan: escSeq('[0;36m'),
reset: escSeq('[0m')
};
var writer = new commonmark.HtmlRenderer();
var reader = new commonmark.DocParser();
var passed = 0;
var failed = 0;
var results = {
passed: 0,
failed: 0
};
var showSpaces = function(s) {
var t = s;
return t.replace(/\t/g, '→')
.replace(/ /g, '␣');
var t = s;
return t.replace(/\t/g, '→')
.replace(/ /g, '␣');
};
var pathologicalTest = function(testcase, results) {
cursor.write(testcase.name + ' ');
console.time(' elapsed time');
var actual = writer.render(reader.parse(testcase.input));
if (actual === testcase.expected) {
cursor.green().write('✓\n').reset();
results.passed += 1;
} else {
cursor.red().write('✘\n');
cursor.cyan();
cursor.write('=== markdown ===============\n');
cursor.write(showSpaces(testcase.input));
cursor.write('=== expected ===============\n');
cursor.write(showSpaces(testcase.expected));
cursor.write('=== got ====================\n');
cursor.write(showSpaces(actual));
cursor.write('\n');
cursor.reset();
results.failed += 1;
}
console.timeEnd(' elapsed time');
};
fs.readFile('spec.txt', 'utf8', function(err, data) {
if (err) {
return console.log(err);
}
var i;
var examples = [];
var current_section = "";
var example_number = 0;
var tests = data
.replace(/\r\n?/g, "\n") // Normalize newlines for platform independence
.replace(/^<!-- END TESTS -->(.|[\n])*/m, '');
if (err) {
return console.log(err);
}
var i;
var examples = [];
var current_section = "";
var example_number = 0;
var tests = data
.replace(/\r\n?/g, "\n") // Normalize newlines for platform independence
.replace(/^<!-- END TESTS -->(.|[\n])*/m, '');
tests.replace(/^\.\n([\s\S]*?)^\.\n([\s\S]*?)^\.$|^#{1,6} *(.*)$/gm,
function(_, markdownSubmatch, htmlSubmatch, sectionSubmatch){
if (sectionSubmatch) {
current_section = sectionSubmatch;
} else {
example_number++;
examples.push({markdown: markdownSubmatch,
html: htmlSubmatch,
section: current_section,
number: example_number});
}
});
tests.replace(/^\.\n([\s\S]*?)^\.\n([\s\S]*?)^\.$|^#{1,6} *(.*)$/gm,
function(_, markdownSubmatch, htmlSubmatch, sectionSubmatch){
if (sectionSubmatch) {
current_section = sectionSubmatch;
} else {
example_number++;
examples.push({markdown: markdownSubmatch,
html: htmlSubmatch,
section: current_section,
number: example_number});
}
});
current_section = "";
current_section = "";
console.time("Elapsed time");
cursor.write('Spec tests:\n\n');
console.time("Elapsed time");
for (i = 0; i < examples.length; i++) {
var example = examples[i];
if (example.section !== current_section) {
if (current_section !== '') {
cursor.write('\n');
}
current_section = example.section;
cursor.reset().write(current_section).reset().write(' ');
for (i = 0; i < examples.length; i++) {
var example = examples[i];
if (example.section !== current_section) {
if (current_section !== '') {
cursor.write('\n');
}
current_section = example.section;
cursor.reset().write(current_section).reset().write(' ');
}
var actual = writer.render(reader.parse(example.markdown.replace(/→/g, '\t')));
if (actual === example.html) {
results.passed++;
cursor.green().write('✓').reset();
} else {
results.failed++;
cursor.write('\n');
cursor.red().write('✘ Example ' + example.number + '\n');
cursor.cyan();
cursor.write('=== markdown ===============\n');
cursor.write(showSpaces(example.markdown));
cursor.write('=== expected ===============\n');
cursor.write(showSpaces(example.html));
cursor.write('=== got ====================\n');
cursor.write(showSpaces(actual));
cursor.reset();
}
}
var actual = writer.renderBlock(reader.parse(example.markdown.replace(/→/g, '\t')));
if (actual === example.html) {
passed++;
cursor.green().write('✓').reset();
} else {
failed++;
cursor.write('\n');
cursor.write('\n');
console.timeEnd("Elapsed time");
cursor.red().write('✘ Example ' + example.number + '\n');
cursor.cyan();
cursor.write('=== markdown ===============\n');
cursor.write(showSpaces(example.markdown));
cursor.write('=== expected ===============\n');
cursor.write(showSpaces(example.html));
cursor.write('=== got ====================\n');
cursor.write(showSpaces(actual));
cursor.reset();
// pathological cases
cursor.write('\nPathological cases:\n');
var cases = [
{ name: 'U+0000 in input',
input: 'abc\u0000xyz\u0000\n',
expected: '<p>abc\ufffdxyz\ufffd</p>\n' },
{ name: 'nested strong emph 10000 deep',
input: repeat('*a **a ', 10000) + 'b' + repeat(' a** a*', 10000),
expected: '<p>' + repeat('<em>a <strong>a ', 10000) + 'b' +
repeat(' a</strong> a</em>', 10000) + '</p>\n' },
{ name: 'nested brackets 10000 deep',
input: repeat('[', 10000) + 'a' + repeat(']', 10000),
expected: '<p>' + repeat('[', 10000) + 'a' + repeat(']', 10000) +
'</p>\n' },
{ name: 'nested block quote 10000 deep',
input: repeat('> ', 10000) + 'a\n',
expected: repeat('<blockquote>\n', 10000) + '<p>a</p>\n' +
repeat('</blockquote>\n', 10000) }
];
for (i = 0; i < cases.length; i++) {
pathologicalTest(cases[i], results);
}
}
cursor.write('\n' + passed.toString() + ' tests passed, ' +
failed.toString() + ' failed.\n');
cursor.write('\n');
console.timeEnd("Elapsed time");
cursor.write(results.passed.toString() + ' tests passed, ' +
results.failed.toString() + ' failed.\n');
});

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc