mirror of
https://github.com/jsdoc/jsdoc.git
synced 2025-12-08 19:46:11 +00:00
use the markdown-it Markdown parser by default (#1243)
The marked parser is deprecated in JSDoc 3.6.0 and will be removed in JSDoc 3.7.0.
This commit is contained in:
parent
e6a313c4ca
commit
8191227607
@ -5,6 +5,10 @@
|
||||
'use strict';
|
||||
|
||||
var env = require('jsdoc/env');
|
||||
var logger = require('jsdoc/util/logger');
|
||||
var MarkdownIt = require('markdown-it');
|
||||
var marked = require('marked');
|
||||
var mda = require('markdown-it-anchor');
|
||||
var util = require('util');
|
||||
|
||||
/**
|
||||
@ -13,29 +17,37 @@ var util = require('util');
|
||||
*/
|
||||
var parserNames = {
|
||||
/**
|
||||
* The "[markdown-js](https://github.com/evilstreak/markdown-js)" (aka "evilstreak") parser.
|
||||
* The [`markdown-js`](https://github.com/evilstreak/markdown-js) (aka "evilstreak") parser.
|
||||
*
|
||||
* @deprecated Replaced by "marked," as markdown-js does not support inline HTML.
|
||||
* @deprecated Replaced by `markdown-it`.
|
||||
*/
|
||||
evilstreak: 'marked',
|
||||
evilstreak: 'markdownit',
|
||||
/**
|
||||
* The "GitHub-flavored Markdown" parser.
|
||||
* @deprecated Replaced by "marked."
|
||||
*
|
||||
* @deprecated Replaced by `markdown-it`.
|
||||
*/
|
||||
gfm: 'marked',
|
||||
gfm: 'markdownit',
|
||||
/**
|
||||
* The "[Marked](https://github.com/chjj/marked)" parser.
|
||||
* The `markdown-it` parser.
|
||||
*/
|
||||
markdownit: 'markdownit',
|
||||
/**
|
||||
* The [Marked](https://github.com/chjj/marked) parser.
|
||||
*
|
||||
* @deprecated Will be replaced by `markdown-it` in JSDoc 3.7.0.
|
||||
*/
|
||||
marked: 'marked'
|
||||
};
|
||||
|
||||
/**
|
||||
* Escape underscores that occur within {@ ... } in order to protect them
|
||||
* from the markdown parser(s).
|
||||
* @param {String} source the source text to sanitize.
|
||||
* @returns {String} `source` where underscores within {@ ... } have been
|
||||
* protected with a preceding backslash (i.e. \_) -- the markdown parsers
|
||||
* will strip the backslash and protect the underscore.
|
||||
* Escape underscores that occur within an inline tag in order to protect them from the `marked`
|
||||
* parser.
|
||||
*
|
||||
* @param {string} source - The source text to sanitize.
|
||||
* @return {string} The source text, where underscores within inline tags have been protected with a
|
||||
* preceding backslash (e.g., `\_`). The `marked` parser will strip the backslash and protect the
|
||||
* underscore.
|
||||
*/
|
||||
function escapeUnderscores(source) {
|
||||
return source.replace(/\{@[^}\r\n]+\}/g, function(wholeMatch) {
|
||||
@ -63,6 +75,18 @@ function unescapeUrls(source) {
|
||||
return source.replace(/(https?):\\\/\\\//g, '$1://');
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape backslashes within inline tags so that they are not stripped.
|
||||
*
|
||||
* @param {string} source - The source text to escape.
|
||||
* @return {string} The source text with backslashes escaped within inline tags.
|
||||
*/
|
||||
function escapeInlineTagBackslashes(source) {
|
||||
return source.replace(/\{@[^}\r\n]+\}/g, function(wholeMatch) {
|
||||
return wholeMatch.replace(/\\/g, '\\\\');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape characters in text within a code block.
|
||||
*
|
||||
@ -76,11 +100,25 @@ function escapeCode(source) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unencode quotes that occur within {@ ... } after the markdown parser has turned them
|
||||
* into html entities (unfortunately it isn't possible to escape them before parsing)
|
||||
* Wrap a code snippet in HTML tags that enable syntax highlighting.
|
||||
*
|
||||
* @param {string} code - The code snippet.
|
||||
* @param {string?} language - The language of the code snippet.
|
||||
* @return {string} The wrapped code snippet.
|
||||
*/
|
||||
function highlight(code, language) {
|
||||
var langClass = language ? ' lang-' + language : '';
|
||||
|
||||
return util.format('<pre class="prettyprint source%s"><code>%s</code></pre>', langClass,
|
||||
escapeCode(code));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unencode quotes that occur within {@ ... } after the Markdown parser has turned them into HTML
|
||||
* entities.
|
||||
*
|
||||
* @param {string} source - The source text to unencode.
|
||||
* @return {string} The source text with html entity `"` converted back to standard quotes
|
||||
* @return {string} The source text with HTML entity `"` converted back to standard quotes.
|
||||
*/
|
||||
function unencodeQuotes(source) {
|
||||
return source.replace(/\{@[^}\r\n]+\}/g, function(wholeMatch) {
|
||||
@ -88,7 +126,6 @@ function unencodeQuotes(source) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve a function that accepts a single parameter containing Markdown source. The function uses
|
||||
* the specified parser to transform the Markdown source to HTML, then returns the HTML as a string.
|
||||
@ -100,60 +137,82 @@ function unencodeQuotes(source) {
|
||||
* returns the resulting HTML.
|
||||
*/
|
||||
function getParseFunction(parserName, conf) {
|
||||
var logger = require('jsdoc/util/logger');
|
||||
var marked = require('marked');
|
||||
|
||||
var markedRenderer;
|
||||
var parserFunction;
|
||||
var renderer;
|
||||
|
||||
conf = conf || {};
|
||||
|
||||
if (parserName === parserNames.marked) {
|
||||
if (conf.hardwrap) {
|
||||
marked.setOptions({breaks: true});
|
||||
}
|
||||
switch (parserName) {
|
||||
case parserNames.marked:
|
||||
if (conf.hardwrap) {
|
||||
marked.setOptions({breaks: true});
|
||||
}
|
||||
|
||||
// Marked generates an "id" attribute for headers; this custom renderer suppresses it
|
||||
markedRenderer = new marked.Renderer();
|
||||
// Marked generates an "id" attribute for headers; this custom renderer suppresses it
|
||||
renderer = new marked.Renderer();
|
||||
|
||||
if (!conf.idInHeadings) {
|
||||
markedRenderer.heading = function(text, level) {
|
||||
return util.format('<h%s>%s</h%s>', level, text, level);
|
||||
if (!conf.idInHeadings) {
|
||||
renderer.heading = function(text, level) {
|
||||
return util.format('<h%s>%s</h%s>', level, text, level);
|
||||
};
|
||||
}
|
||||
|
||||
renderer.code = highlight;
|
||||
|
||||
parserFunction = function(source) {
|
||||
var result;
|
||||
|
||||
source = escapeUnderscores(source);
|
||||
source = escapeUrls(source);
|
||||
|
||||
result = marked(source, { renderer: renderer })
|
||||
.replace(/\s+$/, '')
|
||||
.replace(/'/g, "'");
|
||||
|
||||
result = unescapeUrls(result);
|
||||
result = unencodeQuotes(result);
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
parserFunction._parser = parserNames.marked;
|
||||
|
||||
// Allow prettyprint to work on inline code samples
|
||||
markedRenderer.code = function(code, language) {
|
||||
var langClass = language ? ' lang-' + language : '';
|
||||
return parserFunction;
|
||||
|
||||
return util.format( '<pre class="prettyprint source%s"><code>%s</code></pre>',
|
||||
langClass, escapeCode(code) );
|
||||
};
|
||||
case parserNames.markdownit:
|
||||
renderer = new MarkdownIt({
|
||||
breaks: Boolean(conf.hardwrap),
|
||||
highlight: highlight,
|
||||
html: true
|
||||
});
|
||||
|
||||
parserFunction = function(source) {
|
||||
var result;
|
||||
if (conf.idInHeadings) {
|
||||
renderer.use(mda);
|
||||
}
|
||||
|
||||
source = escapeUnderscores(source);
|
||||
source = escapeUrls(source);
|
||||
parserFunction = function(source) {
|
||||
var result;
|
||||
|
||||
result = marked(source, { renderer: markedRenderer })
|
||||
.replace(/\s+$/, '')
|
||||
.replace(/'/g, "'");
|
||||
source = escapeUrls(source);
|
||||
source = escapeInlineTagBackslashes(source);
|
||||
|
||||
result = unescapeUrls(result);
|
||||
result = unencodeQuotes(result);
|
||||
result = renderer.render(source)
|
||||
.replace(/\s+$/, '')
|
||||
.replace(/'/g, "'");
|
||||
|
||||
return result;
|
||||
};
|
||||
parserFunction._parser = parserNames.marked;
|
||||
result = unescapeUrls(result);
|
||||
result = unencodeQuotes(result);
|
||||
|
||||
return parserFunction;
|
||||
}
|
||||
else {
|
||||
logger.error('Unrecognized Markdown parser "%s". Markdown support is disabled.',
|
||||
parserName);
|
||||
return result;
|
||||
};
|
||||
parserFunction._parser = parserNames.markdownit;
|
||||
|
||||
return undefined;
|
||||
return parserFunction;
|
||||
|
||||
default:
|
||||
logger.error('Unrecognized Markdown parser "%s". Markdown support is disabled.',
|
||||
parserName);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,12 +227,7 @@ function getParseFunction(parserName, conf) {
|
||||
*/
|
||||
exports.getParser = function() {
|
||||
var conf = env.conf.markdown;
|
||||
var parser = (conf && conf.parser) ? parserNames[conf.parser] : parserNames.markdownit;
|
||||
|
||||
if (conf && conf.parser) {
|
||||
return getParseFunction(parserNames[conf.parser], conf);
|
||||
}
|
||||
else {
|
||||
// marked is the default parser
|
||||
return getParseFunction(parserNames.marked, conf);
|
||||
}
|
||||
return getParseFunction(parser, conf);
|
||||
};
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
"escape-string-regexp": "~1.0.5",
|
||||
"js2xmlparser": "~3.0.0",
|
||||
"klaw": "~2.0.0",
|
||||
"markdown-it": "~8.3.1",
|
||||
"markdown-it-anchor": "~4.0.0",
|
||||
"marked": "~0.3.6",
|
||||
"mkdirp": "~0.5.1",
|
||||
"requizzle": "~0.2.1",
|
||||
|
||||
@ -6,12 +6,12 @@ describe('jsdoc/util/markdown', function() {
|
||||
|
||||
it('should exist', function() {
|
||||
expect(markdown).toBeDefined();
|
||||
expect(typeof markdown).toEqual('object');
|
||||
expect(typeof markdown).toBe('object');
|
||||
});
|
||||
|
||||
it('should export a "getParser" function', function() {
|
||||
expect(markdown.getParser).toBeDefined();
|
||||
expect(typeof markdown.getParser).toEqual('function');
|
||||
expect(typeof markdown.getParser).toBe('function');
|
||||
});
|
||||
|
||||
describe('getParser', function() {
|
||||
@ -30,19 +30,23 @@ describe('jsdoc/util/markdown', function() {
|
||||
|
||||
setMarkdownConf({});
|
||||
parser = markdown.getParser();
|
||||
expect(typeof parser).toEqual('function');
|
||||
|
||||
setMarkdownConf({parser: 'marked'});
|
||||
parser = markdown.getParser();
|
||||
expect(typeof parser).toEqual('function');
|
||||
expect(typeof parser).toBe('function');
|
||||
});
|
||||
|
||||
it('should use the marked parser when evilstreak is requested', function() {
|
||||
it('should use the markdown-it parser by default', function() {
|
||||
var parser;
|
||||
|
||||
setMarkdownConf({});
|
||||
parser = markdown.getParser();
|
||||
expect(parser._parser).toBe('markdownit');
|
||||
});
|
||||
|
||||
it('should use the markdown-it parser when evilstreak is requested', function() {
|
||||
var parser;
|
||||
|
||||
setMarkdownConf({parser: 'evilstreak'});
|
||||
parser = markdown.getParser();
|
||||
expect(parser._parser).toEqual('marked');
|
||||
expect(parser._parser).toBe('markdownit');
|
||||
});
|
||||
|
||||
it('should use the marked parser when requested', function() {
|
||||
@ -50,15 +54,15 @@ describe('jsdoc/util/markdown', function() {
|
||||
|
||||
setMarkdownConf({parser: 'marked'});
|
||||
parser = markdown.getParser();
|
||||
expect(parser._parser).toEqual('marked');
|
||||
expect(parser._parser).toBe('marked');
|
||||
});
|
||||
|
||||
it('should use the marked parser when GFM is requested', function() {
|
||||
it('should use the markdown-it parser when GFM is requested', function() {
|
||||
var parser;
|
||||
|
||||
setMarkdownConf({parser: 'gfm'});
|
||||
parser = markdown.getParser();
|
||||
expect(parser._parser).toEqual('marked');
|
||||
expect(parser._parser).toBe('markdownit');
|
||||
});
|
||||
|
||||
it('should log an error if an unrecognized Markdown parser is requested', function() {
|
||||
@ -70,14 +74,10 @@ describe('jsdoc/util/markdown', function() {
|
||||
expect(logger.error).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not apply formatting to inline tags when the marked parser is enabled', function() {
|
||||
var parser;
|
||||
it('should not apply formatting to inline tags', function() {
|
||||
var parser = markdown.getParser();
|
||||
|
||||
setMarkdownConf({parser: 'marked'});
|
||||
parser = markdown.getParser();
|
||||
|
||||
// get the marked parser and do the test
|
||||
expect(parser('{@link MyClass#_x} and {@link MyClass#_y}')).toEqual(
|
||||
expect(parser('{@link MyClass#_x} and {@link MyClass#_y}')).toBe(
|
||||
'<p>{@link MyClass#_x} and {@link MyClass#_y}</p>');
|
||||
});
|
||||
|
||||
@ -98,7 +98,7 @@ describe('jsdoc/util/markdown', function() {
|
||||
'```';
|
||||
var convertedText = '' +
|
||||
'<pre class="prettyprint source lang-html"><code>' +
|
||||
'<p><a href="#">Sample \'HTML.\'</a></p>' +
|
||||
'<p><a href="#">Sample \'HTML.\'</a></p>\n' +
|
||||
'</code></pre>';
|
||||
|
||||
expect(parser(markdownText)).toBe(convertedText);
|
||||
@ -110,8 +110,17 @@ describe('jsdoc/util/markdown', function() {
|
||||
setMarkdownConf({hardwrap: true});
|
||||
parser = markdown.getParser();
|
||||
|
||||
expect(parser('line one\nline two')).toEqual(
|
||||
'<p>line one<br>line two</p>');
|
||||
expect(parser('line one\nline two')).toBe(
|
||||
'<p>line one<br>\nline two</p>');
|
||||
});
|
||||
|
||||
it('should add heading IDs when idInHeadings is enabled', function() {
|
||||
var parser;
|
||||
|
||||
setMarkdownConf({idInHeadings: true});
|
||||
parser = markdown.getParser();
|
||||
|
||||
expect(parser('# Hello')).toBe('<h1 id="hello">Hello</h1>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user