Huge News!Announcing our $40M Series B led by Abstract Ventures.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.17.0 to 0.17.1

bench.html

20

bench/bench.js

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

"use strict";
var Benchmark = require('benchmark').Benchmark;

@@ -6,3 +8,3 @@ var suite = new Benchmark.Suite();

// npm install showdown
var Showdown = require('showdown').converter;
var Showdown = require('showdown');
// npm install marked

@@ -13,2 +15,6 @@ var marked = require('marked');

var showdown = new Showdown.converter();
var parser = new commonmark.Parser();
var renderer = new commonmark.HtmlRenderer();
var benchfile = process.argv[2];

@@ -19,16 +25,10 @@

suite.add('commonmark.js markdown->html', function() {
"use strict";
var doc = new commonmark.Parser().parse(contents);
var renderer = new commonmark.HtmlRenderer();
renderer.render(doc);
renderer.render(parser.parse(contents));
})
.add('showdown.js markdown->html', function() {
"use strict";
var converter = new Showdown();
converter.makeHtml(contents);
showdown.makeHtml(contents);
})
.add('marked.js markdown->html', function() {
"use strict";
marked(contents);

@@ -38,3 +38,2 @@ })

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

@@ -44,5 +43,4 @@ })

.on('cycle', function(event) {
"use strict";
console.log(String(event.target));
})
.run();
{
"name": "commonmark",
"version": "0.17.0",
"main": "dist/commonmark.js",
"homepage": "https://github.com/jgm/commonmark.js",
"description": "CommonMark parsing and rendering library",
"license": "BSD3",
"license": "BSD-2-Clause",
"ignore": [

@@ -9,0 +8,0 @@ "**/.*",

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

[0.17]
[0.17.1]
* Reorganized block parsing in a more modular way. There is now
a `blocks` property of the parser that contains information
about each type of block, which is used in parsing. Ultimately
this will make it easier to extend the library, but the project
is still only partially completed.
* Code cleanup and simplification, with some performance optimizations.
* Removed version from `bower.json`. Bower takes version from tags.
* Initialize some properties at beginning of 'parse'.
This fixes some mistakes in source position when the
same Parser object was used to parse multiple times (#3).
* Made parsing of backslash escapes a bit more efficient.
* Removed refmap parameter of InlineParser.parse().
Instead, set the refmap property before running the parser.
* Set `_string_content` to null after using, allowing it to be GCd.
* Removed `_strings`; just append to `_string_content`.
This gives better performance with v8.
* Format benchmarks so that samples are linked.
* Added in-browser benchmark.
* Added API documentation to README.
* xml renderer: use `sourcepos` attribute, not `data-sourcepos`.
* Changed license to 2-clause BSD. Added clause for spec.
[0.17.0]
* Renamed `DocParser` -> `Parser`.

@@ -4,0 +28,0 @@ Note: library users should update their code or it will break.

@@ -6,7 +6,2 @@ "use strict";

var C_GREATERTHAN = 62;
var C_NEWLINE = 10;
var C_SPACE = 32;
var C_OPEN_BRACKET = 91;
var CODE_INDENT = 4;

@@ -78,11 +73,2 @@

// 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--;
}
};
// DOC PARSER

@@ -92,16 +78,2 @@

// Returns true if parent block can contain child block.
var canContain = function(parent_type, child_type) {
return ( parent_type === 'Document' ||
parent_type === 'BlockQuote' ||
parent_type === 'Item' ||
(parent_type === 'List' && child_type === 'Item') );
};
// Returns true if block type can accept lines of text.
var acceptsLines = function(block_type) {
return ( block_type === 'Paragraph' ||
block_type === 'CodeBlock' );
};
// Returns true if block ends with a blank line, descending if needed

@@ -151,3 +123,3 @@ // into lists and sublists.

var addLine = function(ln) {
this.tip._strings.push(ln.slice(this.offset));
this.tip._string_content += ln.slice(this.offset) + '\n';
};

@@ -159,3 +131,3 @@

var addChild = function(tag, offset) {
while (!canContain(this.tip.type, tag)) {
while (!this.blocks[this.tip.type].canContain(tag)) {
this.finalize(this.tip, this.lineNumber - 1);

@@ -166,4 +138,3 @@ }

var newBlock = new Node(tag, [[this.lineNumber, column_number], [0, 0]]);
newBlock._strings = [];
newBlock._string_content = null;
newBlock._string_content = '';
this.tip.appendChild(newBlock);

@@ -225,9 +196,11 @@ this.tip = newBlock;

var closeUnmatchedBlocks = function() {
// finalize any blocks not matched
while (this.oldtip !== this.lastMatchedContainer) {
var parent = this.oldtip._parent;
this.finalize(this.oldtip, this.lineNumber - 1);
this.oldtip = parent;
if (!this.allClosed) {
// finalize any blocks not matched
while (this.oldtip !== this.lastMatchedContainer) {
var parent = this.oldtip._parent;
this.finalize(this.oldtip, this.lineNumber - 1);
this.oldtip = parent;
}
this.allClosed = true;
}
return true;
};

@@ -243,3 +216,5 @@

continue: function() { return 0; },
finalize: function() { return; }
finalize: function() { return; },
canContain: function(t) { return (t !== 'Item'); },
acceptsLines: false
},

@@ -269,11 +244,13 @@ List: {

}
}
},
canContain: function(t) { return (t === 'Item'); },
acceptsLines: false
},
BlockQuote: {
continue: function(parser, container, next_nonspace) {
continue: function(parser, container, nextNonspace) {
var ln = parser.currentLine;
if (next_nonspace - parser.offset <= 3 &&
ln.charCodeAt(next_nonspace) === C_GREATERTHAN) {
parser.offset = next_nonspace + 1;
if (ln.charCodeAt(parser.offset) === C_SPACE) {
if (nextNonspace - parser.offset <= 3 &&
ln.charAt(nextNonspace) === '>') {
parser.offset = nextNonspace + 1;
if (ln.charAt(parser.offset) === ' ') {
parser.offset++;

@@ -286,9 +263,11 @@ }

},
finalize: function() { return; }
finalize: function() { return; },
canContain: function(t) { return (t !== 'Item'); },
acceptsLines: false
},
Item: {
continue: function(parser, container, next_nonspace) {
if (next_nonspace === parser.currentLine.length) { // blank
parser.offset = next_nonspace;
} else if (next_nonspace - parser.offset >=
continue: function(parser, container, nextNonspace) {
if (nextNonspace === parser.currentLine.length) { // blank
parser.offset = nextNonspace;
} else if (nextNonspace - parser.offset >=
container._listData.markerOffset +

@@ -303,3 +282,5 @@ container._listData.padding) {

},
finalize: function() { return; }
finalize: function() { return; },
canContain: function(t) { return (t !== 'Item'); },
acceptsLines: false
},

@@ -311,5 +292,5 @@ Header: {

},
finalize: function(parser, block) {
block._string_content = block._strings.join('\n');
}
finalize: function() { return; },
canContain: function() { return false; },
acceptsLines: false
},

@@ -321,12 +302,14 @@ HorizontalRule: {

},
finalize: function() { return; }
finalize: function() { return; },
canContain: function() { return false; },
acceptsLines: false
},
CodeBlock: {
continue: function(parser, container, next_nonspace) {
continue: function(parser, container, nextNonspace) {
var ln = parser.currentLine;
var indent = next_nonspace - parser.offset;
var indent = nextNonspace - parser.offset;
if (container._isFenced) { // fenced
var match = (indent <= 3 &&
ln.charAt(next_nonspace) === container._fenceChar &&
ln.slice(next_nonspace).match(reClosingCodeFence));
ln.charAt(nextNonspace) === container._fenceChar &&
ln.slice(nextNonspace).match(reClosingCodeFence));
if (match && match[0].length >= container._fenceLength) {

@@ -339,3 +322,3 @@ // closing fence - we're at end of line, so we can return

var i = container._fenceOffset;
while (i > 0 && ln.charCodeAt(parser.offset) === C_SPACE) {
while (i > 0 && ln.charAt(parser.offset) === ' ') {
parser.offset++;

@@ -348,4 +331,4 @@ i--;

parser.offset += CODE_INDENT;
} else if (next_nonspace === ln.length) { // blank
parser.offset = next_nonspace;
} else if (nextNonspace === ln.length) { // blank
parser.offset = nextNonspace;
} else {

@@ -360,32 +343,37 @@ return 1;

// first line becomes info string
block.info = unescapeString(block._strings[0].trim());
if (block._strings.length === 1) {
block._literal = '';
} else {
block._literal = block._strings.slice(1).join('\n') + '\n';
}
var content = block._string_content;
var newlinePos = content.indexOf('\n');
var firstLine = content.slice(0, newlinePos);
var rest = content.slice(newlinePos + 1);
block.info = unescapeString(firstLine.trim());
block._literal = rest;
} else { // indented
stripFinalBlankLines(block._strings);
block._literal = block._strings.join('\n') + '\n';
block._literal = block._string_content.replace(/(\n *)+$/, '\n');
}
}
block._string_content = null; // allow GC
},
canContain: function() { return false; },
acceptsLines: true
},
HtmlBlock: {
continue: function(parser, container, next_nonspace) {
return (next_nonspace === parser.currentLine.length ? 1 : 0);
continue: function(parser, container, nextNonspace) {
return (nextNonspace === parser.currentLine.length ? 1 : 0);
},
finalize: function(parser, block) {
block._literal = block._strings.join('\n');
}
block._literal = block._string_content.replace(/(\n *)+$/, '');
block._string_content = null; // allow GC
},
canContain: function() { return false; },
acceptsLines: true
},
Paragraph: {
continue: function(parser, container, next_nonspace) {
return (next_nonspace === parser.currentLine.length ? 1 : 0);
continue: function(parser, container, nextNonspace) {
return (nextNonspace === parser.currentLine.length ? 1 : 0);
},
finalize: function(parser, block) {
var pos;
block._string_content = block._strings.join('\n');
var hasReferenceDefs = false;
// try parsing the beginning as link reference definitions:
while (block._string_content.charCodeAt(0) === C_OPEN_BRACKET &&
while (block._string_content.charAt(0) === '[' &&
(pos =

@@ -395,8 +383,10 @@ parser.inlineParser.parseReference(block._string_content,

block._string_content = block._string_content.slice(pos);
if (isBlank(block._string_content)) {
block.unlink();
break;
}
hasReferenceDefs = true;
}
}
if (hasReferenceDefs && isBlank(block._string_content)) {
block.unlink();
}
},
canContain: function() { return false; },
acceptsLines: true
}

@@ -410,3 +400,3 @@ };

var all_matched = true;
var next_nonspace;
var nextNonspace;
var match;

@@ -416,3 +406,3 @@ var data;

var indent;
var allClosed;
var t;

@@ -442,8 +432,8 @@ var container = this.doc;

if (match === -1) {
next_nonspace = ln.length;
nextNonspace = ln.length;
} else {
next_nonspace = match;
nextNonspace = match;
}
switch (this.blocks[container.type].continue(this, container, next_nonspace)) {
switch (this.blocks[container.type].continue(this, container, nextNonspace)) {
case 0: // we've matched, keep going

@@ -455,3 +445,3 @@ break;

case 2: // we've hit end of line for fenced code close and can return
this.lastLineLength = ln.length - 1; // -1 for newline
this.lastLineLength = ln.length;
return;

@@ -467,5 +457,5 @@ default:

blank = next_nonspace === ln.length;
blank = nextNonspace === ln.length;
allClosed = (container === this.oldtip);
this.allClosed = (container === this.oldtip);
this.lastMatchedContainer = container;

@@ -480,17 +470,18 @@

// adding children to the last matched container:
while (true) {
var t = container.type;
while ((t = container.type) && !(t === 'CodeBlock' || t === 'HtmlBlock')) {
match = matchAt(reNonSpace, ln, this.offset);
if (match === -1) {
next_nonspace = ln.length;
nextNonspace = ln.length;
blank = true;
break;
} else {
next_nonspace = match;
nextNonspace = match;
blank = false;
}
indent = next_nonspace - this.offset;
indent = nextNonspace - this.offset;
if (t === 'CodeBlock' || t === 'HtmlBlock') {
// this is a little performance optimization:
if (indent < CODE_INDENT && !reMaybeSpecial.test(ln.slice(nextNonspace))) {
this.offset = nextNonspace;
break;

@@ -500,47 +491,40 @@ }

if (indent >= CODE_INDENT) {
// indented code
if (this.tip.type !== 'Paragraph' && !blank) {
// indented code
this.offset += CODE_INDENT;
allClosed = allClosed ||
this.closeUnmatchedBlocks();
this.closeUnmatchedBlocks();
container = this.addChild('CodeBlock', this.offset);
} else {
// lazy paragraph continuation
this.offset = nextNonspace;
}
break;
}
// this is a little performance optimization:
if (matchAt(reMaybeSpecial, ln, next_nonspace) === -1) {
break;
}
this.offset = next_nonspace;
var cc = ln.charCodeAt(this.offset);
if (cc === C_GREATERTHAN) {
} else if (ln.charAt(nextNonspace) === '>') {
// blockquote
this.offset += 1;
this.offset = nextNonspace + 1;
// optional following space
if (ln.charCodeAt(this.offset) === C_SPACE) {
if (ln.charAt(this.offset) === ' ') {
this.offset++;
}
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('BlockQuote', next_nonspace);
this.closeUnmatchedBlocks();
container = this.addChild('BlockQuote', nextNonspace);
} else if ((match = ln.slice(this.offset).match(reATXHeaderMarker))) {
} else if ((match = ln.slice(nextNonspace).match(reATXHeaderMarker))) {
// ATX header
this.offset += match[0].length;
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('Header', next_nonspace);
this.offset = nextNonspace + match[0].length;
this.closeUnmatchedBlocks();
container = this.addChild('Header', nextNonspace);
container.level = match[0].trim().length; // number of #s
// remove trailing ###s:
container._strings =
[ln.slice(this.offset).replace(/^ *#+ *$/, '').replace(/ +#+ *$/, '')];
container._string_content =
ln.slice(this.offset).replace(/^ *#+ *$/, '').replace(/ +#+ *$/, '');
this.offset = ln.length;
break;
} else if ((match = ln.slice(this.offset).match(reCodeFence))) {
} else if ((match = ln.slice(nextNonspace).match(reCodeFence))) {
// fenced code block
var fenceLength = match[0].length;
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('CodeBlock', next_nonspace);
this.closeUnmatchedBlocks();
container = this.addChild('CodeBlock', nextNonspace);
container._isFenced = true;

@@ -550,19 +534,20 @@ container._fenceLength = fenceLength;

container._fenceOffset = indent;
this.offset += fenceLength;
this.offset = nextNonspace + fenceLength;
} else if (matchAt(reHtmlBlockOpen, ln, this.offset) !== -1) {
} else if (matchAt(reHtmlBlockOpen, ln, nextNonspace) !== -1) {
// html block
allClosed = allClosed || this.closeUnmatchedBlocks();
this.closeUnmatchedBlocks();
container = this.addChild('HtmlBlock', this.offset);
this.offset -= indent; // back up so spaces are part of block
// don't adjust this.offset; spaces are part of block
break;
} else if (t === 'Paragraph' &&
container._strings.length === 1 &&
((match = ln.slice(this.offset).match(reSetextHeaderLine)))) {
(container._string_content.indexOf('\n') ===
container._string_content.length - 1) &&
((match = ln.slice(nextNonspace).match(reSetextHeaderLine)))) {
// setext header line
allClosed = allClosed || this.closeUnmatchedBlocks();
this.closeUnmatchedBlocks();
var header = new Node('Header', container.sourcepos);
header.level = match[0][0] === '=' ? 1 : 2;
header._strings = container._strings;
header._string_content = container._string_content;
container.insertAfter(header);

@@ -575,13 +560,13 @@ container.unlink();

} else if (matchAt(reHrule, ln, this.offset) !== -1) {
} else if (matchAt(reHrule, ln, nextNonspace) !== -1) {
// hrule
allClosed = allClosed || this.closeUnmatchedBlocks();
container = this.addChild('HorizontalRule', next_nonspace);
this.offset = ln.length - 1;
this.closeUnmatchedBlocks();
container = this.addChild('HorizontalRule', nextNonspace);
this.offset = ln.length;
break;
} else if ((data = parseListMarker(ln, this.offset, indent))) {
} else if ((data = parseListMarker(ln, nextNonspace, indent))) {
// list item
allClosed = allClosed || this.closeUnmatchedBlocks();
this.offset += data.padding;
this.closeUnmatchedBlocks();
this.offset = nextNonspace + data.padding;

@@ -591,3 +576,3 @@ // add the list if needed

!(listsMatch(container._listData, data))) {
container = this.addChild('List', next_nonspace);
container = this.addChild('List', nextNonspace);
container._listData = data;

@@ -597,6 +582,7 @@ }

// add the list item
container = this.addChild('Item', next_nonspace);
container = this.addChild('Item', nextNonspace);
container._listData = data;
} else {
this.offset = nextNonspace;
break;

@@ -612,7 +598,5 @@

// First check for a lazy paragraph continuation:
if (!allClosed && !blank &&
this.tip.type === 'Paragraph' &&
this.tip._strings.length > 0) {
if (!this.allClosed && !blank &&
this.tip.type === 'Paragraph') {
// lazy paragraph continuation
this.addLine(ln);

@@ -623,3 +607,3 @@

// finalize any blocks not matched
allClosed = allClosed || this.closeUnmatchedBlocks();
this.closeUnmatchedBlocks();
if (blank && container.lastChild) {

@@ -649,27 +633,12 @@ container.lastChild._lastLineBlank = true;

switch (t) {
case 'HtmlBlock':
case 'CodeBlock':
if (this.blocks[t].acceptsLines) {
this.addLine(ln);
break;
case 'Header':
case 'HorizontalRule':
// nothing to do; we already added the contents.
break;
default:
this.offset = next_nonspace;
if (acceptsLines(t)) {
this.addLine(ln);
} else if (blank) {
break;
} else {
// create paragraph container for line
container = this.addChild('Paragraph', this.offset);
this.addLine(ln);
}
} else if (this.offset < ln.length && !blank) {
// create paragraph container for line
container = this.addChild('Paragraph', this.offset);
this.offset = nextNonspace;
this.addLine(ln);
}
}
this.lastLineLength = ln.length - 1; // -1 for newline
this.lastLineLength = ln.length;
};

@@ -685,3 +654,3 @@

block._open = false;
block.sourcepos[1] = [lineNumber, this.lastLineLength + 1];
block.sourcepos[1] = [lineNumber, this.lastLineLength];

@@ -698,2 +667,3 @@ this.blocks[block.type].finalize(this, block);

var walker = block.walker();
this.inlineParser.refmap = this.refmap;
while ((event = walker.next())) {

@@ -703,3 +673,3 @@ node = event.node;

if (!event.entering && (t === 'Paragraph' || t === 'Header')) {
this.inlineParser.parse(node, this.refmap);
this.inlineParser.parse(node);
}

@@ -711,4 +681,2 @@ }

var doc = new Node('Document', [[1, 1], [0, 0]]);
doc._string_content = null;
doc._strings = [];
return doc;

@@ -722,6 +690,11 @@ };

this.refmap = {};
this.lineNumber = 0;
this.lastLineLength = 0;
this.offset = 0;
this.lastMatchedContainer = this.doc;
this.currentLine = "";
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) {
if (input.charAt(input.length - 1) === '\n') {
// ignore last blank line created by final newline

@@ -756,2 +729,3 @@ len -= 1;

offset: 0,
allClosed: true,
lastMatchedContainer: this.doc,

@@ -758,0 +732,0 @@ refmap: {},

@@ -68,9 +68,9 @@ "use strict";

var reEscapable = new RegExp(ESCAPABLE);
var reEscapable = new RegExp('^' + ESCAPABLE);
var reEntityHere = new RegExp('^' + ENTITY, 'i');
var reTicks = new RegExp('`+');
var reTicks = /`+/;
var reTicksHere = new RegExp('^`+');
var reTicksHere = /^`+/;

@@ -144,10 +144,9 @@ 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 ticks = this.match(reTicksHere);
if (!ticks) {
if (ticks === null) {
return 0;
}
var afterOpenTicks = this.pos;
var foundCode = false;
var matched;
var node;
while (!foundCode && (matched = this.match(reTicks))) {
while ((matched = this.match(reTicks)) !== null) {
if (matched === ticks) {

@@ -170,3 +169,4 @@ node = new Node('Code');

// character, a hard line break (if the backslash is followed by a newline),
// or a literal backslash to the block's children.
// or a literal backslash to the block's children. Assumes current character
// is a backslash.
var parseBackslash = function(block) {

@@ -176,18 +176,14 @@ var subj = this.subject,

var node;
if (subj.charCodeAt(pos) === C_BACKSLASH) {
if (subj.charAt(pos + 1) === '\n') {
this.pos = this.pos + 2;
node = new Node('Hardbreak');
block.appendChild(node);
} else if (reEscapable.test(subj.charAt(pos + 1))) {
this.pos = this.pos + 2;
block.appendChild(text(subj.charAt(pos + 1)));
} else {
this.pos++;
block.appendChild(text('\\'));
}
return true;
if (subj.charAt(pos + 1) === '\n') {
this.pos = this.pos + 2;
node = new Node('Hardbreak');
block.appendChild(node);
} else if (reEscapable.test(subj.charAt(pos + 1))) {
this.pos = this.pos + 2;
block.appendChild(text(subj.charAt(pos + 1)));
} else {
return false;
this.pos++;
block.appendChild(text('\\'));
}
return true;
};

@@ -224,10 +220,9 @@

var m = this.match(reHtmlTag);
var node;
if (m) {
node = new Node('Html');
if (m === null) {
return false;
} else {
var node = new Node('Html');
node._literal = m;
block.appendChild(node);
return true;
} else {
return false;
}

@@ -264,3 +259,3 @@ };

!(rePunctuation.test(char_after) &&
!(/\s/.test(char_before)) &&
!(reWhitespaceChar.test(char_before)) &&
!(rePunctuation.test(char_before)));

@@ -427,7 +422,7 @@ right_flanking = numdelims > 0 &&

var title = this.match(reLinkTitle);
if (title) {
if (title === null) {
return null;
} else {
// chop off quotes from title and unescape:
return unescapeString(title.substr(1, title.length - 2));
} else {
return null;
}

@@ -440,11 +435,11 @@ };

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

@@ -798,9 +793,9 @@ };

// using refmap to resolve references.
var parseInlines = function(block, refmap) {
var parseInlines = function(block) {
this.subject = block._string_content.trim();
this.pos = 0;
this.refmap = refmap || {};
this.delimiters = null;
while (this.parseInline(block)) {
}
block._string_content = null; // allow raw string to be garbage collected
this.processEmphasis(block, null);

@@ -807,0 +802,0 @@ };

@@ -75,3 +75,2 @@ "use strict";

this._open = true;
this._strings = null;
this._string_content = null;

@@ -78,0 +77,0 @@ this._literal = null;

@@ -128,3 +128,3 @@ "use strict";

if (pos) {
attrs.push(['data-sourcepos', String(pos[0][0]) + ':' +
attrs.push(['sourcepos', String(pos[0][0]) + ':' +
String(pos[0][1]) + '-' + String(pos[1][0]) + ':' +

@@ -131,0 +131,0 @@ String(pos[1][1])]);

{ "name": "commonmark",
"description": "a strongly specified, highly compatible variant of Markdown",
"version": "0.17.0",
"version": "0.17.1",
"homepage": "http://commonmark.org",

@@ -15,3 +15,3 @@ "keywords":

"bugs": { "url": "https://github.com/jgm/commonmark.js/issues" },
"license": "BSD-3-Clause",
"license": "BSD-2-Clause",
"main": "./lib/index.js",

@@ -18,0 +18,0 @@ "bin": { "commonmark": "./bin/commonmark" },

@@ -5,3 +5,3 @@ commonmark.js

CommonMark is a rationalized version of Markdown syntax,
with a [spec][the spec] and BSD3-licensed reference
with a [spec][the spec] and BSD-licensed reference
implementations in C and JavaScript.

@@ -34,4 +34,5 @@

a standalone JavaScript file `js/dist/commonmark.js`,
suitable for linking into a web page, or just fetch
<http://spec.commonmark.org/js/commonmark.js>.
suitable for linking into a web page, or fetch the latest
from <http://spec.commonmark.org/js/commonmark.js>, or
`bower install commonmark`.

@@ -63,88 +64,162 @@ To run tests for the JavaScript library:

var reader = new commonmark.Parser();
var writer = new commonmark.HtmlRenderer();
var parsed = reader.parse("Hello *world*"); // parsed is a 'Node' tree
// transform parsed if you like...
var result = writer.render(parsed); // result is a string
``` js
var reader = new commonmark.Parser();
var writer = new commonmark.HtmlRenderer();
var parsed = reader.parse("Hello *world*"); // parsed is a 'Node' tree
// transform parsed if you like...
var result = writer.render(parsed); // result is a String
```
**A note on security:**
THe library does not attempt to sanitize link attributes or
raw HTML. If you use this library in applications that accept
untrusted user input, you must run the output through an HTML
sanitizer to protect against
[XSS attacks](http://en.wikipedia.org/wiki/Cross-site_scripting).
The constructors for `Parser` and `HtmlRenderer` take an optional
`options` parameter:
<!-- TODO
``` js
var writer = new commonmark.HtmlRenderer({sourcepos: true});
```
Public API
----------
THe following options are currently supported:
### Parser
- `sourcepos`: if `true`, source position information for block-level
elements will be rendered in the `data-sourcepos` attribute (for
HTML) or the `sourcepos` attribute (for XML).
constructor takes options param
explain what can go into options
It is also possible to override the `escape` and `softbreak`
properties of a renderer. So, to make soft breaks render as hard
breaks in HTML:
public
properties: options
methods: parse
``` js
var writer = new commonmark.HtmlRenderer;
writer.softbreak = "<br />";
```
?? should we add a filters [] option?
a filter could be a function that transforms a node,
and the parser could automatically run a walker with
each filter
To make them render as spaces:
### Node
``` js
writer.softbreak = " ";
```
getters: type, firstChild, lastChild,,
next, prev, parent, sourcepos, isContainer
getters+setters: literal, destination, title,
info, level, listType, listTight, listStart,
listDelimiter
methods: appendChild(child),
prependChild(child),
unlink(),
insertAfter(sibling),
insertBefore(sibling),
walker()
To override `escape`, pass it a function with two parameters:
the first is the string to be escaped, the second is a boolean
that is `true` if the escaped string is to be included in an
attribute.
walker returns NodeWalker object with methods:
resumeAt(node, entering)
next() - returns an objcet with properties 'entering' and 'node'
In addition to the `HtmlRenderer`, there is an `XmlRenderer`, which
will produce an XML representation of the AST:
examples:
capitalize every string
changing emphasis to ALL CAPS
de-linkifying
running all the code samples through a highlighter or other
transform (svg?)
``` js
var writer = new commonmark.XmlRenderer({sourcepos: true});
```
?? would it be better to include NodeWalker in the API
and have people do walker = new NodeWalker(node)?
probably.
The parser returns a Node. The following public properties are defined
(those marked "read-only" have only a getter, not a setter):
### HtmlRenderer
- `type` (read-only): a String, one of
`Text`, `Softbreak`, `Hardbreak`, `Emph`, `Strong`,
`Html`, `Link`, `Image`, `Code`, `Document`, `Paragraph`,
`BlockQuote`, `Item`, `List`, `Header`, `CodeBlock`,
`HtmlBlock` `HorizontalRule`.
- `firstChild` (read-only): a Node or null.
- `lastChild` (read-only): a Node or null.
- `next` (read-only): a Node or null.
- `prev` (read-only): a Node or null.
- `parent` (read-only): a Node or null.
- `sourcepos` (read-only): an Array with the following form:
`[[startline, startcolumn], [endline, endcolumn]]`.
- `isContainer` (read-only): `true` if the Node can contain other
Nodes as children.
- `literal`: the literal String content of the node or null.
- `destination`: link or image destination (String) or null.
- `title`: link or image title (String) or null.
- `info`: fenced code block info string (String) or null.
- `level`: header level (Number).
- `listType`: a String, either `Bullet` or `Ordered`.
- `listTight`: `true` if list is tight.
- `listStart`: a Number, the starting number of an ordered list.
- `listDelimiter`: a String, either `)` or `.` for an ordered list.
constructor takes options param
document relevant options
Nodes have the following public methods:
methods:
escapeXml(string, isAttribute)
render(node)
properties:
options
- `appendChild(child)`: Append a Node `child` to the end of the
Node's children.
- `prependChild(child)`: Prepend a Node `child` to the end of the
Node's children.
- `unlink()`: Remove the Node from the tree, severing its links
with siblings and parents, and closing up gaps as needed.
- `insertAfter(sibling)`: Insert a Node `sibling` after the Node.
- `insertBefore(sibling)`: Insert a Node `sibling` before the Node.
- `walker()`: Returns a NodeWalker that can be used to iterate through
the Node tree rooted in the Node.
The NodeWalker returned by `walker()` has two methods:
### XmlRenderer
- `next()`: Returns an object with properties `entering` (a boolean,
which is `true` when we enter a Node from a parent or sibling, and
`false` when we reenter it from a child). Returns `null` when
we have finished walking the tree.
- `resumeAt(node, entering)`: Resets the iterator to resume at the
specified node and setting for `entering`. (Normally this isn't
needed unless you do destructive updates to the Node tree.)
constructor takes options param
document relevant options
Here is an example of the use of a NodeWalker to iterate through
the tree, making transformations. This simple example converts
the contents of all `Text` nodes to ALL CAPS:
methods:
escapeXml(string, isAttribute)
render(node)
properties:
options
``` js
var walker = parsed.walker();
var event, node;
-->
while ((event = walker.next())) {
node = event.node;
if (event.entering && node.type === 'Text') {
node.literal = node.literal.toUpperCase();
}
}
```
This more complex example converts emphasis to ALL CAPS:
``` js
var walker = parsed.walker();
var event, node;
var inEmph = false;
while ((event = walker.next())) {
node = event.node;
if (node.type === 'Emph') {
if (event.entering) {
inEmph = true;
} else {
inEmph = false;
// add Emph node's children as siblings
while (node.firstChild) {
node.insertBefore(node.firstChild);
}
// remove the empty Emph node
node.unlink()
}
} else if (inEmph && node.type === 'Text') {
node.literal = node.literal.toUpperCase();
}
}
```
Exercises for the reader: write a transform to
1. De-linkify a document, transforming links to regular text.
2. Remove all raw HTML (`Html` and `HtmlBlock` nodes).
3. Run fenced code blocks marked with a language name through
a syntax highlighting library, replacing them with an `HtmlBlock`
containing the highlighted code.
4. Print warnings to the console for images without image
descriptions or titles.
A note on security
------------------
The library does not attempt to sanitize link attributes or
raw HTML. If you use this library in applications that accept
untrusted user input, you must run the output through an HTML
sanitizer to protect against
[XSS attacks](http://en.wikipedia.org/wiki/Cross-site_scripting).
Performance

@@ -168,33 +243,60 @@ -----------

repository.) Results show a ratio of ops/second (higher is better)
against the slowest implementation (always showdown).
against showdown (which is usually the slowest implementation).
| Sample |showdown |commonmark|marked |markdown-it|
|------------------------|---------:|---------:|---------:|----------:|
|README.md | 1| 3.3| 3.1| 4.3|
|block-bq-flat.md | 1| 9.3| 13.6| 13.7|
|block-bq-nested.md | 1| 12.5| 10.6| 13.2|
|block-code.md | 1| 28.8| 64.7| 95.4|
|block-fences.md | 1| 20.7| 67.9| 72.9|
|block-heading.md | 1| 11.1| 11.8| 19.6|
|block-hr.md | 1| 15.0| 16.0| 41.4|
|block-html.md | 1| 8.2| 3.0| 15.9|
|block-lheading.md | 1| 15.3| 19.2| 16.8|
|block-list-flat.md | 1| 4.6| 4.4| 10.7|
|block-list-nested.md | 1| 7.7| 6.0| 19.3|
|block-ref-flat.md | 1| 2.0| 1.3| 1.7|
|block-ref-nested.md | 1| 1.7| 1.6| 2.9|
|inline-autolink.md | 1| 4.4| 7.4| 4.7|
|inline-backticks.md | 1| 16.3| 14.3| 30.5|
|inline-em-flat.md | 1| 4.1| 3.5| 9.2|
|inline-em-nested.md | 1| 5.2| 5.1| 7.9|
|inline-em-worst.md | 1| 5.7| 5.4| 3.7|
|inline-entity.md | 1| 5.3| 10.5| 8.5|
|inline-escape.md | 1| 4.8| 3.1| 13.1|
|inline-html.md | 1| 3.6| 5.4| 5.1|
|inline-links-flat.md | 1| 3.5| 4.2| 4.1|
|inline-links-nested.md | 1| 4.1| 1.1| 1.6|
|inline-newlines.md | 1| 7.6| 7.3| 15.3|
|lorem1.md | 1| 8.9| 5.1| 5.7|
|rawtabs.md | 1| 9.7| 10.6| 15.4|
| Sample |showdown |commonmark|marked |markdown-it|
|--------------------------|---------:|---------:|---------:|----------:|
|[README.md] | 1| 2.9| 2.7| 3.7|
|[block-bq-flat.md] | 1| 2.6| 3.6| 3.3|
|[block-bq-nested.md] | 1| 8.1| 6.1| 7.4|
|[block-code.md] | 1| 3.3| 7.3| 10.8|
|[block-fences.md] | 1| 4.6| 13.7| 13.4|
|[block-heading.md] | 1| 3.1| 3.1| 5.0|
|[block-hr.md] | 1| 2.1| 2.0| 5.2|
|[block-html.md] | 1| 1.8| 0.6| 3.3|
|[block-lheading.md] | 1| 2.7| 3.0| 2.5|
|[block-list-flat.md] | 1| 3.1| 2.8| 7.2|
|[block-list-nested.md] | 1| 6.3| 4.9| 14.3|
|[block-ref-flat.md] | 1| 0.6| 0.3| 0.4|
|[block-ref-nested.md] | 1| 0.4| 0.4| 0.7|
|[inline-autolink.md] | 1| 1.6| 2.7| 1.7|
|[inline-backticks.md] | 1| 5.0| 3.9| 8.2|
|[inline-em-flat.md] | 1| 0.7| 0.7| 1.5|
|[inline-em-nested.md] | 1| 0.9| 0.8| 1.3|
|[inline-em-worst.md] | 1| 1.1| 0.9| 0.6|
|[inline-entity.md] | 1| 1.3| 2.6| 1.9|
|[inline-escape.md] | 1| 1.6| 1| 4.3|
|[inline-html.md] | 1| 1.8| 2.8| 2.6|
|[inline-links-flat.md] | 1| 1.9| 1.9| 1.9|
|[inline-links-nested.md] | 1| 1.4| 0.4| 0.5|
|[inline-newlines.md] | 1| 1.2| 1.2| 2.4|
|[lorem1.md] | 1| 5.4| 2.9| 3.2|
|[rawtabs.md] | 1| 3.2| 3.1| 4.4|
[block-html.md]: bench/samples/block-html.md
[inline-links-nested.md]: bench/samples/inline-links-nested.md
[inline-em-flat.md]: bench/samples/inline-em-flat.md
[inline-autolink.md]: bench/samples/inline-autolink.md
[inline-html.md]: bench/samples/inline-html.md
[rawtabs.md]: bench/samples/rawtabs.md
[inline-escape.md]: bench/samples/inline-escape.md
[inline-em-worst.md]: bench/samples/inline-em-worst.md
[block-list-nested.md]: bench/samples/block-list-nested.md
[block-bq-nested.md]: bench/samples/block-bq-nested.md
[block-bq-flat.md]: bench/samples/block-bq-flat.md
[inline-newlines.md]: bench/samples/inline-newlines.md
[block-ref-nested.md]: bench/samples/block-ref-nested.md
[block-fences.md]: bench/samples/block-fences.md
[lorem1.md]: bench/samples/lorem1.md
[README.md]: bench/samples/README.md
[inline-links-flat.md]: bench/samples/inline-links-flat.md
[block-heading.md]: bench/samples/block-heading.md
[inline-em-nested.md]: bench/samples/inline-em-nested.md
[inline-entity.md]: bench/samples/inline-entity.md
[block-list-flat.md]: bench/samples/block-list-flat.md
[block-hr.md]: bench/samples/block-hr.md
[block-lheading.md]: bench/samples/block-lheading.md
[block-code.md]: bench/samples/block-code.md
[inline-backticks.md]: bench/samples/inline-backticks.md
[block-ref-flat.md]: bench/samples/block-ref-flat.md
To generate this table,

@@ -201,0 +303,0 @@

@@ -150,3 +150,4 @@ #!/usr/bin/env node

for (var x = 1000; x <= 10000; x *= 10) {
var x;
for (x = 1000; x <= 10000; x *= 10) {
cases.push(

@@ -158,3 +159,3 @@ { name: 'nested strong emph ' + x + ' deep',

}
for (var x = 1000; x <= 10000; x *= 10) {
for (x = 1000; x <= 10000; x *= 10) {
cases.push(

@@ -166,3 +167,3 @@ { name: 'nested brackets ' + x + ' deep',

}
for (var x = 1000; x <= 10000; x *= 10) {
for (x = 1000; x <= 10000; x *= 10) {
cases.push(

@@ -169,0 +170,0 @@ { name: 'nested block quote ' + x + ' deep',

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

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