Merge branch 'master' into update-templates

This commit is contained in:
Josh Bruce 2018-02-25 11:46:47 -05:00
commit a14ac2e2d0
9 changed files with 1856 additions and 274 deletions

27
.eslintrc.json Normal file
View File

@ -0,0 +1,27 @@
{
"extends": "standard",
"plugins": [
"standard"
],
"parserOptions": { "ecmaVersion": 5 },
"rules": {
"semi": "off",
"indent": ["warn", 2, {
"VariableDeclarator": { "var": 2 },
"SwitchCase": 1,
"outerIIFEBody": 0
}],
"space-before-function-paren": "off",
"operator-linebreak": ["error", "before", { "overrides": { "=": "after" } }],
"no-cond-assign": "off",
"no-useless-escape": "off",
"no-return-assign": "off",
"one-var": "off",
"no-control-regex": "off"
},
"env": {
"node": true,
"browser": true,
"amd": true
}
}

View File

@ -1,5 +1,16 @@
language: node_js language: node_js
node_js: node_js:
- "0.10" - "0.10"
- "0.8" - "4"
- "0.6" - "lts/*"
- "node"
script: |
if [ `node --version | cut -d . -f 1,2` = "v0.10" ]; then
sed -i s/0o755/0755/ test/index.js;
npm test;
else
npm run lint && npm test;
fi
cache:
directories:
- node_modules

View File

@ -4,7 +4,7 @@
* https://github.com/chjj/marked * https://github.com/chjj/marked
*/ */
;(function() { ;(function(root) {
'use strict'; 'use strict';
/** /**
@ -30,45 +30,45 @@ var block = {
block._label = /(?:\\[\[\]]|[^\[\]])+/; block._label = /(?:\\[\[\]]|[^\[\]])+/;
block._title = /(?:"(?:\\"|[^"]|"[^"\n]*")*"|'\n?(?:[^'\n]+\n?)*'|\([^()]*\))/; block._title = /(?:"(?:\\"|[^"]|"[^"\n]*")*"|'\n?(?:[^'\n]+\n?)*'|\([^()]*\))/;
block.def = replace(block.def) block.def = edit(block.def)
('label', block._label) .replace('label', block._label)
('title', block._title) .replace('title', block._title)
(); .getRegex();
block.bullet = /(?:[*+-]|\d+\.)/; block.bullet = /(?:[*+-]|\d+\.)/;
block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
block.item = replace(block.item, 'gm') block.item = edit(block.item, 'gm')
(/bull/g, block.bullet) .replace(/bull/g, block.bullet)
(); .getRegex();
block.list = replace(block.list) block.list = edit(block.list)
(/bull/g, block.bullet) .replace(/bull/g, block.bullet)
('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))') .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
('def', '\\n+(?=' + block.def.source + ')') .replace('def', '\\n+(?=' + block.def.source + ')')
(); .getRegex();
block._tag = '(?!(?:' block._tag = '(?!(?:'
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code' + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
+ '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo' + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
+ '|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b'; + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b';
block.html = replace(block.html) block.html = edit(block.html)
('comment', /<!--[\s\S]*?-->/) .replace('comment', /<!--[\s\S]*?-->/)
('closed', /<(tag)[\s\S]+?<\/\1>/) .replace('closed', /<(tag)[\s\S]+?<\/\1>/)
('closing', /<tag(?:"[^"]*"|'[^']*'|\s[^'"\/>]*)*?\/?>/) .replace('closing', /<tag(?:"[^"]*"|'[^']*'|\s[^'"\/>]*)*?\/?>/)
(/tag/g, block._tag) .replace(/tag/g, block._tag)
(); .getRegex();
block.paragraph = replace(block.paragraph) block.paragraph = edit(block.paragraph)
('hr', block.hr) .replace('hr', block.hr)
('heading', block.heading) .replace('heading', block.heading)
('lheading', block.lheading) .replace('lheading', block.lheading)
('tag', '<' + block._tag) .replace('tag', '<' + block._tag)
(); .getRegex();
block.blockquote = replace(block.blockquote) block.blockquote = edit(block.blockquote)
('paragraph', block.paragraph) .replace('paragraph', block.paragraph)
(); .getRegex();
/** /**
* Normal Block Grammar * Normal Block Grammar
@ -86,11 +86,11 @@ block.gfm = merge({}, block.normal, {
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
}); });
block.gfm.paragraph = replace(block.paragraph) block.gfm.paragraph = edit(block.paragraph)
('(?!', '(?!' .replace('(?!', '(?!'
+ block.gfm.fences.source.replace('\\1', '\\2') + '|' + block.gfm.fences.source.replace('\\1', '\\2') + '|'
+ block.list.source.replace('\\1', '\\3') + '|') + block.list.source.replace('\\1', '\\3') + '|')
(); .getRegex();
/** /**
* GFM + Tables Block Grammar * GFM + Tables Block Grammar
@ -154,17 +154,17 @@ Lexer.prototype.lex = function(src) {
*/ */
Lexer.prototype.token = function(src, top) { Lexer.prototype.token = function(src, top) {
var src = src.replace(/^ +$/gm, '') src = src.replace(/^ +$/gm, '');
, next var next,
, loose loose,
, cap cap,
, bull bull,
, b b,
, item item,
, space space,
, i i,
, tag tag,
, l; l;
while (src) { while (src) {
// newline // newline
@ -366,7 +366,7 @@ Lexer.prototype.token = function(src, top) {
// def // def
if (top && (cap = this.rules.def.exec(src))) { if (top && (cap = this.rules.def.exec(src))) {
src = src.substring(cap[0].length); src = src.substring(cap[0].length);
if (cap[3]) cap[3] = cap[3].substring(1,cap[3].length-1); if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
tag = cap[1].toLowerCase(); tag = cap[1].toLowerCase();
if (!this.tokens.links[tag]) { if (!this.tokens.links[tag]) {
this.tokens.links[tag] = { this.tokens.links[tag] = {
@ -446,8 +446,7 @@ Lexer.prototype.token = function(src, top) {
} }
if (src) { if (src) {
throw new throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
Error('Infinite loop on byte: ' + src.charCodeAt(0));
} }
} }
@ -477,22 +476,22 @@ var inline = {
inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/; inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
inline._email = /[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])?)+(?![-_])/; inline._email = /[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])?)+(?![-_])/;
inline.autolink = replace(inline.autolink) inline.autolink = edit(inline.autolink)
('scheme', inline._scheme) .replace('scheme', inline._scheme)
('email', inline._email) .replace('email', inline._email)
() .getRegex()
inline._inside = /(?:\[[^\]]*\]|\\[\[\]]|[^\[\]]|\](?=[^\[]*\]))*/; inline._inside = /(?:\[[^\]]*\]|\\[\[\]]|[^\[\]]|\](?=[^\[]*\]))*/;
inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/; inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
inline.link = replace(inline.link) inline.link = edit(inline.link)
('inside', inline._inside) .replace('inside', inline._inside)
('href', inline._href) .replace('href', inline._href)
(); .getRegex();
inline.reflink = replace(inline.reflink) inline.reflink = edit(inline.reflink)
('inside', inline._inside) .replace('inside', inline._inside)
(); .getRegex();
/** /**
* Normal Inline Grammar * Normal Inline Grammar
@ -514,16 +513,16 @@ inline.pedantic = merge({}, inline.normal, {
*/ */
inline.gfm = merge({}, inline.normal, { inline.gfm = merge({}, inline.normal, {
escape: replace(inline.escape)('])', '~|])')(), escape: edit(inline.escape).replace('])', '~|])').getRegex(),
url: replace(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/) url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/)
('email', inline._email) .replace('email', inline._email)
(), .getRegex(),
_backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/, _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
del: /^~~(?=\S)([\s\S]*?\S)~~/, del: /^~~(?=\S)([\s\S]*?\S)~~/,
text: replace(inline.text) text: edit(inline.text)
(']|', '~]|') .replace(']|', '~]|')
('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|') .replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|')
() .getRegex()
}); });
/** /**
@ -531,8 +530,8 @@ inline.gfm = merge({}, inline.normal, {
*/ */
inline.breaks = merge({}, inline.gfm, { inline.breaks = merge({}, inline.gfm, {
br: replace(inline.br)('{2,}', '*')(), br: edit(inline.br).replace('{2,}', '*').getRegex(),
text: replace(inline.gfm.text)('{2,}', '*')() text: edit(inline.gfm.text).replace('{2,}', '*').getRegex()
}); });
/** /**
@ -543,12 +542,11 @@ function InlineLexer(links, options) {
this.options = options || marked.defaults; this.options = options || marked.defaults;
this.links = links; this.links = links;
this.rules = inline.normal; this.rules = inline.normal;
this.renderer = this.options.renderer || new Renderer; this.renderer = this.options.renderer || new Renderer();
this.renderer.options = this.options; this.renderer.options = this.options;
if (!this.links) { if (!this.links) {
throw new throw new Error('Tokens array requires a `links` property.');
Error('Tokens array requires a `links` property.');
} }
if (this.options.gfm) { if (this.options.gfm) {
@ -582,11 +580,11 @@ InlineLexer.output = function(src, links, options) {
*/ */
InlineLexer.prototype.output = function(src) { InlineLexer.prototype.output = function(src) {
var out = '' var out = '',
, link link,
, text text,
, href href,
, cap; cap;
while (src) { while (src) {
// escape // escape
@ -717,8 +715,7 @@ InlineLexer.prototype.output = function(src) {
} }
if (src) { if (src) {
throw new throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
Error('Infinite loop on byte: ' + src.charCodeAt(0));
} }
} }
@ -730,8 +727,8 @@ InlineLexer.prototype.output = function(src) {
*/ */
InlineLexer.prototype.outputLink = function(cap, link) { InlineLexer.prototype.outputLink = function(cap, link) {
var href = escape(link.href) var href = escape(link.href),
, title = link.title ? escape(link.title) : null; title = link.title ? escape(link.title) : null;
return cap[0].charAt(0) !== '!' return cap[0].charAt(0) !== '!'
? this.renderer.link(href, title, this.output(cap[1])) ? this.renderer.link(href, title, this.output(cap[1]))
@ -767,10 +764,10 @@ InlineLexer.prototype.smartypants = function(text) {
InlineLexer.prototype.mangle = function(text) { InlineLexer.prototype.mangle = function(text) {
if (!this.options.mangle) return text; if (!this.options.mangle) return text;
var out = '' var out = '',
, l = text.length l = text.length,
, i = 0 i = 0,
, ch; ch;
for (; i < l; i++) { for (; i < l; i++) {
ch = text.charCodeAt(i); ch = text.charCodeAt(i);
@ -970,7 +967,7 @@ function Parser(options) {
this.tokens = []; this.tokens = [];
this.token = null; this.token = null;
this.options = options || marked.defaults; this.options = options || marked.defaults;
this.options.renderer = this.options.renderer || new Renderer; this.options.renderer = this.options.renderer || new Renderer();
this.renderer = this.options.renderer; this.renderer = this.options.renderer;
this.renderer.options = this.options; this.renderer.options = this.options;
} }
@ -991,7 +988,10 @@ Parser.parse = function(src, options) {
Parser.prototype.parse = function(src) { Parser.prototype.parse = function(src) {
this.inline = new InlineLexer(src.links, this.options); this.inline = new InlineLexer(src.links, this.options);
// use an InlineLexer with a TextRenderer to extract pure text // use an InlineLexer with a TextRenderer to extract pure text
this.inlineText = new InlineLexer(src.links, merge({}, this.options, {renderer: new TextRenderer})); this.inlineText = new InlineLexer(
src.links,
merge({}, this.options, {renderer: new TextRenderer()})
);
this.tokens = src.reverse(); this.tokens = src.reverse();
var out = ''; var out = '';
@ -1056,12 +1056,12 @@ Parser.prototype.tok = function() {
this.token.escaped); this.token.escaped);
} }
case 'table': { case 'table': {
var header = '' var header = '',
, body = '' body = '',
, i i,
, row row,
, cell cell,
, j; j;
// header // header
cell = ''; cell = '';
@ -1089,7 +1089,7 @@ Parser.prototype.tok = function() {
return this.renderer.table(header, body); return this.renderer.table(header, body);
} }
case 'blockquote_start': { case 'blockquote_start': {
var body = ''; body = '';
while (this.next().type !== 'blockquote_end') { while (this.next().type !== 'blockquote_end') {
body += this.tok(); body += this.tok();
@ -1098,8 +1098,8 @@ Parser.prototype.tok = function() {
return this.renderer.blockquote(body); return this.renderer.blockquote(body);
} }
case 'list_start': { case 'list_start': {
var body = '' body = '';
, ordered = this.token.ordered; var ordered = this.token.ordered;
while (this.next().type !== 'list_end') { while (this.next().type !== 'list_end') {
body += this.tok(); body += this.tok();
@ -1108,7 +1108,7 @@ Parser.prototype.tok = function() {
return this.renderer.list(body, ordered); return this.renderer.list(body, ordered);
} }
case 'list_item_start': { case 'list_item_start': {
var body = ''; body = '';
while (this.next().type !== 'list_item_end') { while (this.next().type !== 'list_item_end') {
body += this.token.type === 'text' body += this.token.type === 'text'
@ -1119,7 +1119,7 @@ Parser.prototype.tok = function() {
return this.renderer.listitem(body); return this.renderer.listitem(body);
} }
case 'loose_item_start': { case 'loose_item_start': {
var body = ''; body = '';
while (this.next().type !== 'list_item_end') { while (this.next().type !== 'list_item_end') {
body += this.tok(); body += this.tok();
@ -1156,7 +1156,7 @@ function escape(html, encode) {
} }
function unescape(html) { function unescape(html) {
// explicitly match decimal, hex, and named HTML entities // explicitly match decimal, hex, and named HTML entities
return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) { return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
n = n.toLowerCase(); n = n.toLowerCase();
if (n === 'colon') return ':'; if (n === 'colon') return ':';
@ -1169,15 +1169,19 @@ function unescape(html) {
}); });
} }
function replace(regex, opt) { function edit(regex, opt) {
regex = regex.source; regex = regex.source;
opt = opt || ''; opt = opt || '';
return function self(name, val) { return {
if (!name) return new RegExp(regex, opt); replace: function(name, val) {
val = val.source || val; val = val.source || val;
val = val.replace(/(^|[^\[])\^/g, '$1'); val = val.replace(/(^|[^\[])\^/g, '$1');
regex = regex.replace(name, val); regex = regex.replace(name, val);
return self; return this;
},
getRegex: function() {
return new RegExp(regex, opt);
}
}; };
} }
@ -1209,9 +1213,9 @@ function noop() {}
noop.exec = noop; noop.exec = noop;
function merge(obj) { function merge(obj) {
var i = 1 var i = 1,
, target target,
, key; key;
for (; i < arguments.length; i++) { for (; i < arguments.length; i++) {
target = arguments[i]; target = arguments[i];
@ -1225,19 +1229,19 @@ function merge(obj) {
return obj; return obj;
} }
/** /**
* Marked * Marked
*/ */
function marked(src, opt, callback) { function marked(src, opt, callback) {
// throw error in case of non string input // throw error in case of non string input
if (typeof src == 'undefined' || src === null) if (typeof src === 'undefined' || src === null) {
throw new Error('marked(): input parameter is undefined or null'); throw new Error('marked(): input parameter is undefined or null');
if (typeof src != 'string') }
throw new Error('marked(): input parameter is of type ' + if (typeof src !== 'string') {
Object.prototype.toString.call(src) + ', string expected'); throw new Error('marked(): input parameter is of type '
+ Object.prototype.toString.call(src) + ', string expected');
}
if (callback || typeof opt === 'function') { if (callback || typeof opt === 'function') {
if (!callback) { if (!callback) {
@ -1247,10 +1251,10 @@ function marked(src, opt, callback) {
opt = merge({}, marked.defaults, opt || {}); opt = merge({}, marked.defaults, opt || {});
var highlight = opt.highlight var highlight = opt.highlight,
, tokens tokens,
, pending pending,
, i = 0; i = 0;
try { try {
tokens = Lexer.lex(src, opt) tokens = Lexer.lex(src, opt)
@ -1346,7 +1350,7 @@ marked.defaults = {
langPrefix: 'lang-', langPrefix: 'lang-',
smartypants: false, smartypants: false,
headerPrefix: '', headerPrefix: '',
renderer: new Renderer, renderer: new Renderer(),
xhtml: false, xhtml: false,
baseUrl: null baseUrl: null
}; };
@ -1374,9 +1378,6 @@ if (typeof module !== 'undefined' && typeof exports === 'object') {
} else if (typeof define === 'function' && define.amd) { } else if (typeof define === 'function' && define.amd) {
define(function() { return marked; }); define(function() { return marked; });
} else { } else {
this.marked = marked; root.marked = marked;
} }
})(this || (typeof window !== 'undefined' ? window : global));
}).call(function() {
return this || (typeof window !== 'undefined' ? window : global);
}());

2
marked.min.js vendored

File diff suppressed because one or more lines are too long

1544
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -23,17 +23,27 @@
"html" "html"
], ],
"devDependencies": { "devDependencies": {
"markdown": "*", "eslint": "^4.15.0",
"showdown": "*", "eslint-config-standard": "^11.0.0-beta.0",
"markdown-it": "*", "eslint-plugin-import": "^2.8.0",
"eslint-plugin-node": "^5.2.1",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-standard": "^3.0.1",
"front-matter": "^2.3.0", "front-matter": "^2.3.0",
"glob-to-regexp": "0.3.0", "glob-to-regexp": "0.3.0",
"markdown": "*",
"markdown-it": "*",
"showdown": "*",
"uglify-js": "^3.3.10" "uglify-js": "^3.3.10"
}, },
"scripts": { "scripts": {
"test": "node test", "test": "node test",
"bench": "node test --bench", "bench": "node test --bench",
"lint": "eslint --fix lib/marked.js test/index.js",
"build": "uglifyjs lib/marked.js -cm --comments /Copyright/ -o marked.min.js", "build": "uglifyjs lib/marked.js -cm --comments /Copyright/ -o marked.min.js",
"preversion": "npm run build && (git diff --quiet || git commit -am 'minify')" "preversion": "npm run build && (git diff --quiet || git commit -am 'minify')"
},
"engines": {
"node": ">=0.10.0"
} }
} }

View File

@ -1,11 +1,13 @@
var fs = require('fs'); var fs = require('fs'),
path = require('path');
var test = require('../') var testMod = require('../'),
, runTests = test.runTests load = testMod.load;
, load = test.load;
var express = require('express') var express = require('express'),
, app = express(); app = express();
var files = load();
app.use(function(req, res, next) { app.use(function(req, res, next) {
var setHeader = res.setHeader; var setHeader = res.setHeader;
@ -21,21 +23,17 @@ app.use(function(req, res, next) {
next(); next();
}); });
var dir = __dirname + '/../tests'
, files = {};
app.get('/test.js', function(req, res, next) { app.get('/test.js', function(req, res, next) {
var test = fs.readFileSync(__dirname + '/test.js', 'utf8') var test = fs.readFileSync(path.join(__dirname, 'test.js'), 'utf8');
, files = load(); var testScript = test.replace('__TESTS__', JSON.stringify(files))
.replace('__MAIN__', testMod.runTests + '')
test = test.replace('__TESTS__', JSON.stringify(files)); .replace('__LIBS__', testMod.testFile + '');
test = test.replace('__MAIN__', runTests + '');
res.contentType('.js'); res.contentType('.js');
res.send(test); res.send(testScript);
}); });
app.use(express.static(__dirname + '/../../lib')); app.use(express.static(path.join(__dirname, '/../../lib'))) ;
app.use(express.static(__dirname)); app.use(express.static(__dirname));
app.listen(8080); app.listen(8080);

View File

@ -57,6 +57,8 @@ function escape(html, encode) {
.replace(/'/g, '&#39;'); .replace(/'/g, '&#39;');
} }
__LIBS__;
(__MAIN__)(); (__MAIN__)();
}).call(this); }).call(this);

249
test/index.js vendored
View File

@ -10,30 +10,28 @@
* Modules * Modules
*/ */
var fs = require('fs') var fs = require('fs'),
, path = require('path') path = require('path'),
, fm = require('front-matter') fm = require('front-matter'),
, g2r = require('glob-to-regexp') g2r = require('glob-to-regexp'),
, marked = require('../') marked = require('../'),
, markedMin = require('../marked.min.js'); markedMin = require('../marked.min.js');
/** /**
* Load Tests * Load Tests
*/ */
function load(options) { function load(options) {
var dir = __dirname + '/compiled_tests' options = options || {};
, files = {} var dir = path.join(__dirname, 'compiled_tests'),
, list files = {},
, file list,
, name file,
, content name,
, regex content,
, skip glob = g2r(options.glob || '*', { extended: true }),
, glob = g2r(options.glob || "*", { extended: true }) i,
, i l;
, j
, l;
list = fs list = fs
.readdirSync(dir) .readdirSync(dir)
@ -45,7 +43,7 @@ function load(options) {
l = list.length; l = list.length;
for (i = 0; i < l; i++) { for (i = 0; i < l; i++) {
name = path.basename(list[i], ".md"); name = path.basename(list[i], '.md');
if (glob.test(name)) { if (glob.test(name)) {
file = path.join(dir, list[i]); file = path.join(dir, list[i]);
content = fm(fs.readFileSync(file, 'utf8')); content = fm(fs.readFileSync(file, 'utf8'));
@ -62,7 +60,7 @@ function load(options) {
if (!options.glob) { if (!options.glob) {
// Change certain tests to allow // Change certain tests to allow
// comparison to older benchmark times. // comparison to older benchmark times.
fs.readdirSync(__dirname + '/new').forEach(function(name) { fs.readdirSync(path.join(__dirname, 'new')).forEach(function(name) {
if (path.extname(name) === '.html') return; if (path.extname(name) === '.html') return;
if (name === 'main.md') return; if (name === 'main.md') return;
delete files[name]; delete files[name];
@ -93,112 +91,118 @@ function runTests(engine, options) {
engine = null; engine = null;
} }
var engine = engine || marked engine = engine || marked;
, options = options || {} options = options || {};
, files = options.files || load(options) var succeeded = 0,
, complete = 0 failed = 0,
, failed = 0 files = options.files || load(options),
, failures = [] filenames = Object.keys(files),
, keys = Object.keys(files) len = filenames.length,
, i = 0 success,
, len = keys.length i,
, filename filename,
, file file;
, opts
, text
, html
, j
, l;
if (options.marked) { if (options.marked) {
marked.setOptions(options.marked); marked.setOptions(options.marked);
} }
main: for (i = 0; i < len; i++) {
for (; i < len; i++) { filename = filenames[i];
filename = keys[i];
file = files[filename]; file = files[filename];
opts = Object.keys(file.options); success = testFile(engine, file, filename, i + 1);
if (success) {
if (marked._original) { succeeded++;
marked.defaults = marked._original; } else {
delete marked._original; failed++;
} if (options.stop) {
break;
if (opts.length) {
marked._original = marked.defaults;
marked.defaults = {};
Object.keys(marked._original).forEach(function(key) {
marked.defaults[key] = marked._original[key];
});
opts.forEach(function(key) {
if (marked.defaults.hasOwnProperty(key)) {
marked.defaults[key] = file.options[key];
}
});
}
try {
text = engine(file.text).replace(/\s/g, '');
html = file.html.replace(/\s/g, '');
} catch (e) {
console.log('%s failed.', filename);
throw e;
}
j = 0;
l = html.length;
for (; j < l; j++) {
if (text[j] !== html[j]) {
failed++;
failures.push(filename);
text = text.substring(
Math.max(j - 30, 0),
Math.min(j + 30, text.length));
html = html.substring(
Math.max(j - 30, 0),
Math.min(j + 30, html.length));
console.log(
'\n#%d. %s failed at offset %d. Near: "%s".\n',
i + 1, filename, j, text);
console.log('\nGot:\n%s\n', text.trim() || text);
console.log('\nExpected:\n%s\n', html.trim() || html);
if (options.stop) {
break main;
}
continue main;
} }
} }
complete++;
console.log('#%d. %s completed.', i + 1, filename);
} }
console.log('%d/%d tests completed successfully.', complete, len); console.log('%d/%d tests completed successfully.', succeeded, len);
if (failed) console.log('%d/%d tests failed.', failed, len); if (failed) console.log('%d/%d tests failed.', failed, len);
return !failed; return !failed;
} }
/**
* Test a file
*/
function testFile(engine, file, filename, index) {
var opts = Object.keys(file.options),
text,
html,
j,
l;
if (marked._original) {
marked.defaults = marked._original;
delete marked._original;
}
if (opts.length) {
marked._original = marked.defaults;
marked.defaults = {};
Object.keys(marked._original).forEach(function(key) {
marked.defaults[key] = marked._original[key];
});
opts.forEach(function(key) {
if (marked.defaults.hasOwnProperty(key)) {
marked.defaults[key] = file.options[key];
}
});
}
try {
text = engine(file.text).replace(/\s/g, '');
html = file.html.replace(/\s/g, '');
} catch (e) {
console.log('%s failed.', filename);
throw e;
}
l = html.length;
for (j = 0; j < l; j++) {
if (text[j] !== html[j]) {
text = text.substring(
Math.max(j - 30, 0),
Math.min(j + 30, text.length));
html = html.substring(
Math.max(j - 30, 0),
Math.min(j + 30, l));
console.log(
'\n#%d. %s failed at offset %d. Near: "%s".\n',
index, filename, j, text);
console.log('\nGot:\n%s\n', text.trim() || text);
console.log('\nExpected:\n%s\n', html.trim() || html);
return false;
}
}
console.log('#%d. %s completed.', index, filename);
return true
}
/** /**
* Benchmark a function * Benchmark a function
*/ */
function bench(name, files, func) { function bench(name, files, func) {
var start = Date.now() var start = Date.now(),
, times = 1000 times = 1000,
, keys = Object.keys(files) keys = Object.keys(files),
, i i,
, l = keys.length l = keys.length,
, filename filename,
, file; file;
while (times--) { while (times--) {
for (i = 0; i < l; i++) { for (i = 0; i < l; i++) {
@ -216,8 +220,8 @@ function bench(name, files, func) {
*/ */
function runBench(options) { function runBench(options) {
var options = options || {} options = options || {};
, files = load(options); var files = load(options);
// Non-GFM, Non-pedantic // Non-GFM, Non-pedantic
marked.setOptions({ marked.setOptions({
@ -314,8 +318,8 @@ function runBench(options) {
*/ */
function time(options) { function time(options) {
var options = options || {} options = options || {};
, files = load(options); var files = load(options);
if (options.marked) { if (options.marked) {
marked.setOptions(options.marked); marked.setOptions(options.marked);
} }
@ -360,13 +364,13 @@ function fix() {
}); });
// node fix.js // node fix.js
var dir = __dirname + '/compiled_tests'; var dir = path.join(__dirname, 'compiled_tests');
fs.readdirSync(dir).filter(function(file) { fs.readdirSync(dir).filter(function(file) {
return path.extname(file) === '.html'; return path.extname(file) === '.html';
}).forEach(function(file) { }).forEach(function(file) {
var file = path.join(dir, file) file = path.join(dir, file);
, html = fs.readFileSync(file, 'utf8'); var html = fs.readFileSync(file, 'utf8');
// fix unencoded quotes // fix unencoded quotes
html = html html = html
@ -386,7 +390,7 @@ function fix() {
.replace(/&lt;/g, '<') .replace(/&lt;/g, '<')
.replace(/&amp;/g, '&'); .replace(/&amp;/g, '&');
id = id.toLowerCase().replace(/[^\w]+/g, '-'); id = id.toLowerCase().replace(/[^\w]+/g, '-');
return '<' + h + ' id="' + id + '">' + text + '</' + h + '>'; return '<' + h + ' id="' + id + '">' + text + '</' + h + '>';
}); });
@ -396,8 +400,8 @@ function fix() {
// turn <hr /> into <hr> // turn <hr /> into <hr>
fs.readdirSync(dir).forEach(function(file) { fs.readdirSync(dir).forEach(function(file) {
var file = path.join(dir, file) file = path.join(dir, file);
, text = fs.readFileSync(file, 'utf8'); var text = fs.readFileSync(file, 'utf8');
text = text.replace(/(<|&lt;)hr\s*\/(>|&gt;)/g, '$1hr$2'); text = text.replace(/(<|&lt;)hr\s*\/(>|&gt;)/g, '$1hr$2');
@ -425,12 +429,12 @@ function fix() {
* Argument Parsing * Argument Parsing
*/ */
function parseArg(argv) { function parseArg() {
var argv = process.argv.slice(2) var argv = process.argv.slice(2),
, options = {} options = {},
, opt = "" opt = '',
, orphans = [] orphans = [],
, arg; arg;
function getarg() { function getarg() {
var arg = argv.shift(); var arg = argv.shift();
@ -571,6 +575,7 @@ if (!module.parent) {
exports = main; exports = main;
exports.main = main; exports.main = main;
exports.runTests = runTests; exports.runTests = runTests;
exports.testFile = testFile;
exports.runBench = runBench; exports.runBench = runBench;
exports.load = load; exports.load = load;
exports.bench = bench; exports.bench = bench;