fix: remove deprecated options (#2936)
BREAKING CHANGE: deprecated options removed. See https://marked.js.org/using_advanced#options to see how to enable the removed options with extensions.
This commit is contained in:
parent
fc643a2625
commit
22ebdb2507
@ -54,16 +54,21 @@ console.log(marked.parse(markdownString));
|
||||
|silent |`boolean` |`false` |v0.2.7 |If true, the parser does not throw any exception or log any warning. Any error will be returned as a string.|
|
||||
|tokenizer |`object` |`new Tokenizer()`|v1.0.0|An object containing functions to create tokens from markdown. See [extensibility](/using_pro) for more details.|
|
||||
|walkTokens |`function` |`null`|v1.1.0|A function which is called for every token. See [extensibility](/using_pro) for more details.|
|
||||
|baseUrl (**deprecated**)|`string` |`null` |v0.3.9 |Deprecated in v5.0.0 use [`marked-base-url`](https://www.npmjs.com/package/marked-base-url) to prefix url for any relative link. |
|
||||
|headerIds (**deprecated**)|`boolean` |`true` |v0.4.0 |Deprecated in v5.0.0 use [`marked-gfm-heading-id`](https://www.npmjs.com/package/marked-gfm-heading-id) to include an `id` attribute when emitting headings (h1, h2, h3, etc).|
|
||||
|headerPrefix (**deprecated**)|`string` |`''` |v0.3.0 |Deprecated in v5.0.0 use [`marked-gfm-heading-id`](https://www.npmjs.com/package/marked-gfm-heading-id) to add a string to prefix the `id` attribute when emitting headings (h1, h2, h3, etc).|
|
||||
|highlight (**deprecated**)|`function`|`null` |v0.3.0 |Deprecated in v5.0.0 use [`marked-highlight`](https://www.npmjs.com/package/marked-highlight) to add highlighting to code blocks. |
|
||||
|langPrefix (**deprecated**)|`string` |`'language-'`|v0.3.0|Deprecated in v5.0.0 use [`marked-highlight`](https://www.npmjs.com/package/marked-highlight) to prefix the className in a `<code>` block. Useful for syntax highlighting.|
|
||||
|mangle (**deprecated**)|`boolean` |`true` |v0.3.4 |Deprecated in v5.0.0 use [`marked-mangle`](https://www.npmjs.com/package/marked-mangle) to mangle email addresses.|
|
||||
|sanitize (**deprecated**)|`boolean` |`false` |v0.2.1 |If true, sanitize the HTML passed into `markdownString` with the `sanitizer` function.<br>**Warning**: This feature is deprecated and it should NOT be used as it cannot be considered secure.<br>Instead use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! |
|
||||
|sanitizer (**deprecated**)|`function`|`null` |v0.3.4 |A function to sanitize the HTML passed into `markdownString`.|
|
||||
|smartypants (**deprecated**)|`boolean` |`false` |v0.2.9 |Deprecated in v5.0.0 use [`marked-smartypants`](https://www.npmjs.com/package/marked-smartypants) to use "smart" typographic punctuation for things like quotes and dashes.|
|
||||
|xhtml (**deprecated**)|`boolean` |`false` |v0.3.2 |Deprecated in v5.0.0 use [`marked-xhtml`](https://www.npmjs.com/package/marked-xhtml) to emit self-closing HTML tags for void elements (<br/>, <img/>, etc.) with a "/" as required by XHTML.|
|
||||
|
||||
## Old Options
|
||||
|
||||
|Member |Type |Default |Since |Notes |
|
||||
|:-----------|:---------|:--------|:--------|:-------------|
|
||||
|baseUrl (**removed**)|`string` |`null` |v0.3.9 |Removed in v8.0.0 use [`marked-base-url`](https://www.npmjs.com/package/marked-base-url) to prefix url for any relative link. |
|
||||
|headerIds (**removed**)|`boolean` |`true` |v0.4.0 |Removed in v8.0.0 use [`marked-gfm-heading-id`](https://www.npmjs.com/package/marked-gfm-heading-id) to include an `id` attribute when emitting headings (h1, h2, h3, etc).|
|
||||
|headerPrefix (**removed**)|`string` |`''` |v0.3.0 |Removed in v8.0.0 use [`marked-gfm-heading-id`](https://www.npmjs.com/package/marked-gfm-heading-id) to add a string to prefix the `id` attribute when emitting headings (h1, h2, h3, etc).|
|
||||
|highlight (**removed**)|`function`|`null` |v0.3.0 |Removed in v8.0.0 use [`marked-highlight`](https://www.npmjs.com/package/marked-highlight) to add highlighting to code blocks. |
|
||||
|langPrefix (**removed**)|`string` |`'language-'`|v0.3.0|Removed in v8.0.0 use [`marked-highlight`](https://www.npmjs.com/package/marked-highlight) to prefix the className in a `<code>` block. Useful for syntax highlighting.|
|
||||
|mangle (**removed**)|`boolean` |`true` |v0.3.4 |Removed in v8.0.0 use [`marked-mangle`](https://www.npmjs.com/package/marked-mangle) to mangle email addresses.|
|
||||
|sanitize (**removed**)|`boolean` |`false` |v0.2.1 |Removed in v8.0.0 use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML! |
|
||||
|sanitizer (**removed**)|`function`|`null` |v0.3.4 |Removed in v8.0.0 use a sanitize library, like [DOMPurify](https://github.com/cure53/DOMPurify) (recommended), [sanitize-html](https://github.com/apostrophecms/sanitize-html) or [insane](https://github.com/bevacqua/insane) on the output HTML!|
|
||||
|smartypants (**removed**)|`boolean` |`false` |v0.2.9 |Removed in v8.0.0 use [`marked-smartypants`](https://www.npmjs.com/package/marked-smartypants) to use "smart" typographic punctuation for things like quotes and dashes.|
|
||||
|xhtml (**removed**)|`boolean` |`false` |v0.3.2 |Removed in v8.0.0 use [`marked-xhtml`](https://www.npmjs.com/package/marked-xhtml) to emit self-closing HTML tags for void elements (<br/>, <img/>, etc.) with a "/" as required by XHTML.|
|
||||
|
||||
<h2 id="extensions">Known Extensions</h2>
|
||||
|
||||
|
@ -13,10 +13,7 @@ import { marked } from 'marked';
|
||||
marked.use({
|
||||
pedantic: false,
|
||||
gfm: true,
|
||||
breaks: false,
|
||||
sanitize: false,
|
||||
smartypants: false,
|
||||
xhtml: false
|
||||
breaks: false
|
||||
});
|
||||
```
|
||||
|
||||
@ -249,25 +246,9 @@ console.log(marked.parse('$ latex code $\n\n` other code `'));
|
||||
- <code>**codespan**(*string* src)</code>
|
||||
- <code>**br**(*string* src)</code>
|
||||
- <code>**del**(*string* src)</code>
|
||||
- <code>**autolink**(*string* src, *function* mangle)</code>
|
||||
- <code>**url**(*string* src, *function* mangle)</code>
|
||||
- <code>**inlineText**(*string* src, *function* smartypants)</code>
|
||||
|
||||
`mangle` is a method that changes text to HTML character references:
|
||||
|
||||
```js
|
||||
mangle('test@example.com')
|
||||
// "test@example.com"
|
||||
```
|
||||
|
||||
`smartypants` is a method that translates plain ASCII punctuation characters into “smart” typographic punctuation HTML entities:
|
||||
|
||||
https://daringfireball.net/projects/smartypants/
|
||||
|
||||
```js
|
||||
smartypants('"this ... string"')
|
||||
// "“this … string”"
|
||||
```
|
||||
- <code>**autolink**(*string* src)</code>
|
||||
- <code>**url**(*string* src)</code>
|
||||
- <code>**inlineText**(*string* src)</code>
|
||||
|
||||
***
|
||||
|
||||
@ -337,17 +318,18 @@ marked.use({ hooks: { preprocess } });
|
||||
// Run marked
|
||||
console.log(marked.parse(`
|
||||
---
|
||||
headerIds: false
|
||||
breaks: true
|
||||
---
|
||||
|
||||
## test
|
||||
line1
|
||||
line2
|
||||
`.trim()));
|
||||
```
|
||||
|
||||
**Output:**
|
||||
|
||||
```html
|
||||
<h2>test</h2>
|
||||
<p>line1<br>line2</p>
|
||||
```
|
||||
|
||||
**Example:** Sanitize HTML with [isomorphic-dompurify](https://www.npmjs.com/package/isomorphic-dompurify)
|
||||
|
343
lib/marked.cjs
generated
343
lib/marked.cjs
generated
@ -17,25 +17,15 @@
|
||||
function _getDefaults() {
|
||||
return {
|
||||
async: false,
|
||||
baseUrl: null,
|
||||
breaks: false,
|
||||
extensions: null,
|
||||
gfm: true,
|
||||
headerIds: false,
|
||||
headerPrefix: '',
|
||||
highlight: null,
|
||||
hooks: null,
|
||||
langPrefix: 'language-',
|
||||
mangle: false,
|
||||
pedantic: false,
|
||||
renderer: null,
|
||||
sanitize: false,
|
||||
sanitizer: null,
|
||||
silent: false,
|
||||
smartypants: false,
|
||||
tokenizer: null,
|
||||
walkTokens: null,
|
||||
xhtml: false
|
||||
walkTokens: null
|
||||
};
|
||||
}
|
||||
exports.defaults = _getDefaults();
|
||||
@ -103,26 +93,7 @@ function edit(regex, opt) {
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
const nonWordAndColonTest = /[^\w:]/g;
|
||||
const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
|
||||
function cleanUrl(sanitize, base, href) {
|
||||
if (sanitize) {
|
||||
let prot;
|
||||
try {
|
||||
prot = decodeURIComponent(unescape(href))
|
||||
.replace(nonWordAndColonTest, '')
|
||||
.toLowerCase();
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (base && !originIndependentUrl.test(href)) {
|
||||
href = resolveUrl(base, href);
|
||||
}
|
||||
function cleanUrl(href) {
|
||||
try {
|
||||
href = encodeURI(href).replace(/%25/g, '%');
|
||||
}
|
||||
@ -131,40 +102,6 @@ function cleanUrl(sanitize, base, href) {
|
||||
}
|
||||
return href;
|
||||
}
|
||||
const baseUrls = {};
|
||||
const justDomain = /^[^:]+:\/*[^/]*$/;
|
||||
const protocol = /^([^:]+:)[\s\S]*$/;
|
||||
const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
|
||||
function resolveUrl(base, href) {
|
||||
if (!baseUrls[' ' + base]) {
|
||||
// we can ignore everything in base after the last slash of its path component,
|
||||
// but we might need to add _that_
|
||||
// https://tools.ietf.org/html/rfc3986#section-3
|
||||
if (justDomain.test(base)) {
|
||||
baseUrls[' ' + base] = base + '/';
|
||||
}
|
||||
else {
|
||||
baseUrls[' ' + base] = rtrim(base, '/', true);
|
||||
}
|
||||
}
|
||||
base = baseUrls[' ' + base];
|
||||
const relativeBase = base.indexOf(':') === -1;
|
||||
if (href.substring(0, 2) === '//') {
|
||||
if (relativeBase) {
|
||||
return href;
|
||||
}
|
||||
return base.replace(protocol, '$1') + href;
|
||||
}
|
||||
else if (href.charAt(0) === '/') {
|
||||
if (relativeBase) {
|
||||
return href;
|
||||
}
|
||||
return base.replace(domain, '$1') + href;
|
||||
}
|
||||
else {
|
||||
return base + href;
|
||||
}
|
||||
}
|
||||
const noopTest = { exec: () => null };
|
||||
function splitCells(tableRow, count) {
|
||||
// ensure that every cell-delimiting pipe has a space
|
||||
@ -258,35 +195,6 @@ function findClosingBracket(str, b) {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function checkDeprecations(opt, callback) {
|
||||
if (!opt || opt.silent) {
|
||||
return;
|
||||
}
|
||||
if (callback) {
|
||||
console.warn('marked(): callback is deprecated since version 5.0.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/using_pro#async');
|
||||
}
|
||||
if (opt.sanitize || opt.sanitizer) {
|
||||
console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options');
|
||||
}
|
||||
if (opt.highlight || opt.langPrefix !== 'language-') {
|
||||
console.warn('marked(): highlight and langPrefix parameters are deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-highlight.');
|
||||
}
|
||||
if (opt.mangle) {
|
||||
console.warn('marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.');
|
||||
}
|
||||
if (opt.baseUrl) {
|
||||
console.warn('marked(): baseUrl parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-base-url.');
|
||||
}
|
||||
if (opt.smartypants) {
|
||||
console.warn('marked(): smartypants parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-smartypants.');
|
||||
}
|
||||
if (opt.xhtml) {
|
||||
console.warn('marked(): xhtml parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-xhtml.');
|
||||
}
|
||||
if (opt.headerIds || opt.headerPrefix) {
|
||||
console.warn('marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.');
|
||||
}
|
||||
}
|
||||
|
||||
function outputLink(cap, link, raw, lexer) {
|
||||
const href = link.href;
|
||||
@ -603,17 +511,9 @@ class _Tokenizer {
|
||||
type: 'html',
|
||||
block: true,
|
||||
raw: cap[0],
|
||||
pre: !this.options.sanitizer
|
||||
&& (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
|
||||
pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
|
||||
text: cap[0]
|
||||
};
|
||||
if (this.options.sanitize) {
|
||||
const text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]);
|
||||
const paragraph = token;
|
||||
paragraph.type = 'paragraph';
|
||||
paragraph.text = text;
|
||||
paragraph.tokens = this.lexer.inline(text);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@ -751,18 +651,12 @@ class _Tokenizer {
|
||||
this.lexer.state.inRawBlock = false;
|
||||
}
|
||||
return {
|
||||
type: this.options.sanitize
|
||||
? 'text'
|
||||
: 'html',
|
||||
type: 'html',
|
||||
raw: cap[0],
|
||||
inLink: this.lexer.state.inLink,
|
||||
inRawBlock: this.lexer.state.inRawBlock,
|
||||
block: false,
|
||||
text: this.options.sanitize
|
||||
? (this.options.sanitizer
|
||||
? this.options.sanitizer(cap[0])
|
||||
: escape(cap[0]))
|
||||
: cap[0]
|
||||
text: cap[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -933,12 +827,12 @@ class _Tokenizer {
|
||||
};
|
||||
}
|
||||
}
|
||||
autolink(src, mangle) {
|
||||
autolink(src) {
|
||||
const cap = this.rules.inline.autolink.exec(src);
|
||||
if (cap) {
|
||||
let text, href;
|
||||
if (cap[2] === '@') {
|
||||
text = escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
|
||||
text = escape(cap[1]);
|
||||
href = 'mailto:' + text;
|
||||
}
|
||||
else {
|
||||
@ -960,12 +854,12 @@ class _Tokenizer {
|
||||
};
|
||||
}
|
||||
}
|
||||
url(src, mangle) {
|
||||
url(src) {
|
||||
let cap;
|
||||
if (cap = this.rules.inline.url.exec(src)) {
|
||||
let text, href;
|
||||
if (cap[2] === '@') {
|
||||
text = escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
|
||||
text = escape(cap[0]);
|
||||
href = 'mailto:' + text;
|
||||
}
|
||||
else {
|
||||
@ -998,15 +892,15 @@ class _Tokenizer {
|
||||
};
|
||||
}
|
||||
}
|
||||
inlineText(src, smartypants) {
|
||||
inlineText(src) {
|
||||
const cap = this.rules.inline.text.exec(src);
|
||||
if (cap) {
|
||||
let text;
|
||||
if (this.lexer.state.inRawBlock) {
|
||||
text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0];
|
||||
text = cap[0];
|
||||
}
|
||||
else {
|
||||
text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
|
||||
text = escape(cap[0]);
|
||||
}
|
||||
return {
|
||||
type: 'text',
|
||||
@ -1294,39 +1188,6 @@ inline.breaks = {
|
||||
.getRegex()
|
||||
};
|
||||
|
||||
/**
|
||||
* smartypants text replacement
|
||||
*/
|
||||
function smartypants(text) {
|
||||
return text
|
||||
// em-dashes
|
||||
.replace(/---/g, '\u2014')
|
||||
// en-dashes
|
||||
.replace(/--/g, '\u2013')
|
||||
// opening singles
|
||||
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
|
||||
// closing singles & apostrophes
|
||||
.replace(/'/g, '\u2019')
|
||||
// opening doubles
|
||||
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
|
||||
// closing doubles
|
||||
.replace(/"/g, '\u201d')
|
||||
// ellipses
|
||||
.replace(/\.{3}/g, '\u2026');
|
||||
}
|
||||
/**
|
||||
* mangle email addresses
|
||||
*/
|
||||
function mangle(text) {
|
||||
let out = '';
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const ch = Math.random() > 0.5
|
||||
? 'x' + text.charCodeAt(i).toString(16)
|
||||
: text.charCodeAt(i).toString();
|
||||
out += '&#' + ch + ';';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Block Lexer
|
||||
*/
|
||||
@ -1700,13 +1561,13 @@ class _Lexer {
|
||||
continue;
|
||||
}
|
||||
// autolink
|
||||
if (token = this.tokenizer.autolink(src, mangle)) {
|
||||
if (token = this.tokenizer.autolink(src)) {
|
||||
src = src.substring(token.raw.length);
|
||||
tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
// url (gfm)
|
||||
if (!this.state.inLink && (token = this.tokenizer.url(src, mangle))) {
|
||||
if (!this.state.inLink && (token = this.tokenizer.url(src))) {
|
||||
src = src.substring(token.raw.length);
|
||||
tokens.push(token);
|
||||
continue;
|
||||
@ -1728,7 +1589,7 @@ class _Lexer {
|
||||
cutSrc = src.substring(0, startIndex + 1);
|
||||
}
|
||||
}
|
||||
if (token = this.tokenizer.inlineText(cutSrc, smartypants)) {
|
||||
if (token = this.tokenizer.inlineText(cutSrc)) {
|
||||
src = src.substring(token.raw.length);
|
||||
if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
|
||||
prevChar = token.raw.slice(-1);
|
||||
@ -1769,21 +1630,13 @@ class _Renderer {
|
||||
}
|
||||
code(code, infostring, escaped) {
|
||||
const lang = (infostring || '').match(/^\S*/)?.[0];
|
||||
if (this.options.highlight) {
|
||||
const out = this.options.highlight(code, lang);
|
||||
if (out != null && out !== code) {
|
||||
escaped = true;
|
||||
code = out;
|
||||
}
|
||||
}
|
||||
code = code.replace(/\n$/, '') + '\n';
|
||||
if (!lang) {
|
||||
return '<pre><code>'
|
||||
+ (escaped ? code : escape(code, true))
|
||||
+ '</code></pre>\n';
|
||||
}
|
||||
return '<pre><code class="'
|
||||
+ this.options.langPrefix
|
||||
return '<pre><code class="language-'
|
||||
+ escape(lang)
|
||||
+ '">'
|
||||
+ (escaped ? code : escape(code, true))
|
||||
@ -1795,16 +1648,12 @@ class _Renderer {
|
||||
html(html, block) {
|
||||
return html;
|
||||
}
|
||||
heading(text, level, raw, slugger) {
|
||||
if (this.options.headerIds) {
|
||||
const id = this.options.headerPrefix + slugger.slug(raw);
|
||||
return `<h${level} id="${id}">${text}</h${level}>\n`;
|
||||
}
|
||||
heading(text, level, raw) {
|
||||
// ignore IDs
|
||||
return `<h${level}>${text}</h${level}>\n`;
|
||||
}
|
||||
hr() {
|
||||
return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
|
||||
return '<hr>\n';
|
||||
}
|
||||
list(body, ordered, start) {
|
||||
const type = ordered ? 'ol' : 'ul';
|
||||
@ -1817,9 +1666,7 @@ class _Renderer {
|
||||
checkbox(checked) {
|
||||
return '<input '
|
||||
+ (checked ? 'checked="" ' : '')
|
||||
+ 'disabled="" type="checkbox"'
|
||||
+ (this.options.xhtml ? ' /' : '')
|
||||
+ '> ';
|
||||
+ 'disabled="" type="checkbox">';
|
||||
}
|
||||
paragraph(text) {
|
||||
return `<p>${text}</p>\n`;
|
||||
@ -1857,13 +1704,13 @@ class _Renderer {
|
||||
return `<code>${text}</code>`;
|
||||
}
|
||||
br() {
|
||||
return this.options.xhtml ? '<br/>' : '<br>';
|
||||
return '<br>';
|
||||
}
|
||||
del(text) {
|
||||
return `<del>${text}</del>`;
|
||||
}
|
||||
link(href, title, text) {
|
||||
const cleanHref = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
const cleanHref = cleanUrl(href);
|
||||
if (cleanHref === null) {
|
||||
return text;
|
||||
}
|
||||
@ -1876,7 +1723,7 @@ class _Renderer {
|
||||
return out;
|
||||
}
|
||||
image(href, title, text) {
|
||||
const cleanHref = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
const cleanHref = cleanUrl(href);
|
||||
if (cleanHref === null) {
|
||||
return text;
|
||||
}
|
||||
@ -1885,7 +1732,7 @@ class _Renderer {
|
||||
if (title) {
|
||||
out += ` title="${title}"`;
|
||||
}
|
||||
out += this.options.xhtml ? '/>' : '>';
|
||||
out += '>';
|
||||
return out;
|
||||
}
|
||||
text(text) {
|
||||
@ -1928,52 +1775,6 @@ class _TextRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Slugger generates header id
|
||||
*/
|
||||
class _Slugger {
|
||||
seen;
|
||||
constructor() {
|
||||
this.seen = {};
|
||||
}
|
||||
serialize(value) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
// remove html tags
|
||||
.replace(/<[!\/a-z].*?>/ig, '')
|
||||
// remove unwanted chars
|
||||
.replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
|
||||
.replace(/\s/g, '-');
|
||||
}
|
||||
/**
|
||||
* Finds the next safe (unique) slug to use
|
||||
*/
|
||||
getNextSafeSlug(originalSlug, isDryRun) {
|
||||
let slug = originalSlug;
|
||||
let occurenceAccumulator = 0;
|
||||
if (this.seen.hasOwnProperty(slug)) {
|
||||
occurenceAccumulator = this.seen[originalSlug];
|
||||
do {
|
||||
occurenceAccumulator++;
|
||||
slug = originalSlug + '-' + occurenceAccumulator;
|
||||
} while (this.seen.hasOwnProperty(slug));
|
||||
}
|
||||
if (!isDryRun) {
|
||||
this.seen[originalSlug] = occurenceAccumulator;
|
||||
this.seen[slug] = 0;
|
||||
}
|
||||
return slug;
|
||||
}
|
||||
/**
|
||||
* Convert string to unique id
|
||||
*/
|
||||
slug(value, options = {}) {
|
||||
const slug = this.serialize(value);
|
||||
return this.getNextSafeSlug(slug, options.dryrun);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsing & Compiling
|
||||
*/
|
||||
@ -1981,14 +1782,12 @@ class _Parser {
|
||||
options;
|
||||
renderer;
|
||||
textRenderer;
|
||||
slugger;
|
||||
constructor(options) {
|
||||
this.options = options || exports.defaults;
|
||||
this.options.renderer = this.options.renderer || new _Renderer();
|
||||
this.renderer = this.options.renderer;
|
||||
this.renderer.options = this.options;
|
||||
this.textRenderer = new _TextRenderer();
|
||||
this.slugger = new _Slugger();
|
||||
}
|
||||
/**
|
||||
* Static Parse Method
|
||||
@ -2030,7 +1829,7 @@ class _Parser {
|
||||
}
|
||||
case 'heading': {
|
||||
const headingToken = token;
|
||||
out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)), this.slugger);
|
||||
out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)));
|
||||
continue;
|
||||
}
|
||||
case 'code': {
|
||||
@ -2253,7 +2052,6 @@ class Marked {
|
||||
Lexer = _Lexer;
|
||||
lexer = _Lexer.lex;
|
||||
Tokenizer = _Tokenizer;
|
||||
Slugger = _Slugger;
|
||||
Hooks = _Hooks;
|
||||
constructor(...args) {
|
||||
this.use(...args);
|
||||
@ -2450,12 +2248,8 @@ class Marked {
|
||||
return this;
|
||||
}
|
||||
#parseMarkdown(lexer, parser) {
|
||||
return (src, optOrCallback, callback) => {
|
||||
if (typeof optOrCallback === 'function') {
|
||||
callback = optOrCallback;
|
||||
optOrCallback = null;
|
||||
}
|
||||
const origOpt = { ...optOrCallback };
|
||||
return (src, options) => {
|
||||
const origOpt = { ...options };
|
||||
const opt = { ...this.defaults, ...origOpt };
|
||||
// Show warning if an extension set async to true but the parse was called with async: false
|
||||
if (this.defaults.async === true && origOpt.async === false) {
|
||||
@ -2464,7 +2258,7 @@ class Marked {
|
||||
}
|
||||
opt.async = true;
|
||||
}
|
||||
const throwError = this.#onError(!!opt.silent, !!opt.async, callback);
|
||||
const throwError = this.#onError(!!opt.silent, !!opt.async);
|
||||
// throw error in case of non string input
|
||||
if (typeof src === 'undefined' || src === null) {
|
||||
return throwError(new Error('marked(): input parameter is undefined or null'));
|
||||
@ -2473,76 +2267,9 @@ class Marked {
|
||||
return throwError(new Error('marked(): input parameter is of type '
|
||||
+ Object.prototype.toString.call(src) + ', string expected'));
|
||||
}
|
||||
checkDeprecations(opt, callback);
|
||||
if (opt.hooks) {
|
||||
opt.hooks.options = opt;
|
||||
}
|
||||
if (callback) {
|
||||
const resultCallback = callback;
|
||||
const highlight = opt.highlight;
|
||||
let tokens;
|
||||
try {
|
||||
if (opt.hooks) {
|
||||
src = opt.hooks.preprocess(src);
|
||||
}
|
||||
tokens = lexer(src, opt);
|
||||
}
|
||||
catch (e) {
|
||||
return throwError(e);
|
||||
}
|
||||
const done = (err) => {
|
||||
let out;
|
||||
if (!err) {
|
||||
try {
|
||||
if (opt.walkTokens) {
|
||||
this.walkTokens(tokens, opt.walkTokens);
|
||||
}
|
||||
out = parser(tokens, opt);
|
||||
if (opt.hooks) {
|
||||
out = opt.hooks.postprocess(out);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
err = e;
|
||||
}
|
||||
}
|
||||
opt.highlight = highlight;
|
||||
return err
|
||||
? throwError(err)
|
||||
: resultCallback(null, out);
|
||||
};
|
||||
if (!highlight || highlight.length < 3) {
|
||||
return done();
|
||||
}
|
||||
delete opt.highlight;
|
||||
if (!tokens.length)
|
||||
return done();
|
||||
let pending = 0;
|
||||
this.walkTokens(tokens, (token) => {
|
||||
if (token.type === 'code') {
|
||||
pending++;
|
||||
setTimeout(() => {
|
||||
highlight(token.text, token.lang, (err, code) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (code != null && code !== token.text) {
|
||||
token.text = code;
|
||||
token.escaped = true;
|
||||
}
|
||||
pending--;
|
||||
if (pending === 0) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
if (pending === 0) {
|
||||
done();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (opt.async) {
|
||||
return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
|
||||
.then(src => lexer(src, opt))
|
||||
@ -2570,7 +2297,7 @@ class Marked {
|
||||
}
|
||||
};
|
||||
}
|
||||
#onError(silent, async, callback) {
|
||||
#onError(silent, async) {
|
||||
return (e) => {
|
||||
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
|
||||
if (silent) {
|
||||
@ -2580,27 +2307,19 @@ class Marked {
|
||||
if (async) {
|
||||
return Promise.resolve(msg);
|
||||
}
|
||||
if (callback) {
|
||||
callback(null, msg);
|
||||
return;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
if (async) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
if (callback) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const markedInstance = new Marked();
|
||||
function marked(src, opt, callback) {
|
||||
return markedInstance.parse(src, opt, callback);
|
||||
function marked(src, opt) {
|
||||
return markedInstance.parse(src, opt);
|
||||
}
|
||||
/**
|
||||
* Sets the default options.
|
||||
@ -2652,7 +2371,6 @@ marked.TextRenderer = _TextRenderer;
|
||||
marked.Lexer = _Lexer;
|
||||
marked.lexer = _Lexer.lex;
|
||||
marked.Tokenizer = _Tokenizer;
|
||||
marked.Slugger = _Slugger;
|
||||
marked.Hooks = _Hooks;
|
||||
marked.parse = marked;
|
||||
const options = marked.options;
|
||||
@ -2669,7 +2387,6 @@ exports.Lexer = _Lexer;
|
||||
exports.Marked = Marked;
|
||||
exports.Parser = _Parser;
|
||||
exports.Renderer = _Renderer;
|
||||
exports.Slugger = _Slugger;
|
||||
exports.TextRenderer = _TextRenderer;
|
||||
exports.Tokenizer = _Tokenizer;
|
||||
exports.getDefaults = _getDefaults;
|
||||
|
2
lib/marked.cjs.map
generated
2
lib/marked.cjs.map
generated
File diff suppressed because one or more lines are too long
448
lib/marked.d.ts
generated
vendored
448
lib/marked.d.ts
generated
vendored
@ -152,6 +152,132 @@ declare module "Tokens" {
|
||||
links: Links;
|
||||
};
|
||||
}
|
||||
declare module "rules" {
|
||||
export type Rule = RegExp | string;
|
||||
export interface Rules {
|
||||
[ruleName: string]: Pick<RegExp, 'exec'> | Rule | Rules;
|
||||
}
|
||||
type BlockRuleNames = 'newline' | 'code' | 'fences' | 'hr' | 'heading' | 'blockquote' | 'list' | 'html' | 'def' | 'lheading' | '_paragraph' | 'text' | '_label' | '_title' | 'bullet' | 'listItemStart' | '_tag' | '_comment' | 'paragraph' | 'uote';
|
||||
type BlockSubRuleNames = 'normal' | 'gfm' | 'pedantic';
|
||||
type InlineRuleNames = 'escape' | 'autolink' | 'tag' | 'link' | 'reflink' | 'nolink' | 'reflinkSearch' | 'code' | 'br' | 'text' | '_punctuation' | 'punctuation' | 'blockSkip' | 'escapedEmSt' | '_comment' | '_escapes' | '_scheme' | '_email' | '_attribute' | '_label' | '_href' | '_title' | 'strong' | '_extended_email' | '_backpedal';
|
||||
type InlineSubRuleNames = 'gfm' | 'emStrong' | 'normal' | 'pedantic' | 'breaks';
|
||||
/**
|
||||
* Block-Level Grammar
|
||||
*/
|
||||
export const block: Record<BlockRuleNames, Rule> & Record<BlockSubRuleNames, Rules> & Rules;
|
||||
/**
|
||||
* Inline-Level Grammar
|
||||
*/
|
||||
export const inline: Record<InlineRuleNames, Rule> & Record<InlineSubRuleNames, Rules> & Rules;
|
||||
}
|
||||
declare module "helpers" {
|
||||
import type { Rule } from "rules";
|
||||
export function escape(html: string, encode?: boolean): string;
|
||||
export function unescape(html: string): string;
|
||||
export function edit(regex: Rule, opt?: string): {
|
||||
replace: (name: string | RegExp, val: string | RegExp) => any;
|
||||
getRegex: () => RegExp;
|
||||
};
|
||||
export function cleanUrl(href: string): string | null;
|
||||
export const noopTest: {
|
||||
exec: () => null;
|
||||
};
|
||||
export function splitCells(tableRow: string, count?: number): string[];
|
||||
/**
|
||||
* Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
|
||||
* /c*$/ is vulnerable to REDOS.
|
||||
*
|
||||
* @param str
|
||||
* @param c
|
||||
* @param invert Remove suffix of non-c chars instead. Default falsey.
|
||||
*/
|
||||
export function rtrim(str: string, c: string, invert?: boolean): string;
|
||||
export function findClosingBracket(str: string, b: string): number;
|
||||
}
|
||||
declare module "Renderer" {
|
||||
import type { MarkedOptions } from "MarkedOptions";
|
||||
/**
|
||||
* Renderer
|
||||
*/
|
||||
export class _Renderer {
|
||||
options: MarkedOptions;
|
||||
constructor(options?: MarkedOptions);
|
||||
code(code: string, infostring: string | undefined, escaped: boolean): string;
|
||||
blockquote(quote: string): string;
|
||||
html(html: string, block?: boolean): string;
|
||||
heading(text: string, level: number, raw: string): string;
|
||||
hr(): string;
|
||||
list(body: string, ordered: boolean, start: number | ''): string;
|
||||
listitem(text: string, task: boolean, checked: boolean): string;
|
||||
checkbox(checked: boolean): string;
|
||||
paragraph(text: string): string;
|
||||
table(header: string, body: string): string;
|
||||
tablerow(content: string): string;
|
||||
tablecell(content: string, flags: {
|
||||
header: boolean;
|
||||
align: 'center' | 'left' | 'right' | null;
|
||||
}): string;
|
||||
/**
|
||||
* span level renderer
|
||||
*/
|
||||
strong(text: string): string;
|
||||
em(text: string): string;
|
||||
codespan(text: string): string;
|
||||
br(): string;
|
||||
del(text: string): string;
|
||||
link(href: string, title: string | null | undefined, text: string): string;
|
||||
image(href: string, title: string | null, text: string): string;
|
||||
text(text: string): string;
|
||||
}
|
||||
}
|
||||
declare module "TextRenderer" {
|
||||
/**
|
||||
* TextRenderer
|
||||
* returns only the textual part of the token
|
||||
*/
|
||||
export class _TextRenderer {
|
||||
strong(text: string): string;
|
||||
em(text: string): string;
|
||||
codespan(text: string): string;
|
||||
del(text: string): string;
|
||||
html(text: string): string;
|
||||
text(text: string): string;
|
||||
link(href: string, title: string | null | undefined, text: string): string;
|
||||
image(href: string, title: string | null, text: string): string;
|
||||
br(): string;
|
||||
}
|
||||
}
|
||||
declare module "Parser" {
|
||||
import { _Renderer } from "Renderer";
|
||||
import { _TextRenderer } from "TextRenderer";
|
||||
import type { Token } from "Tokens";
|
||||
import type { MarkedOptions } from "MarkedOptions";
|
||||
/**
|
||||
* Parsing & Compiling
|
||||
*/
|
||||
export class _Parser {
|
||||
options: MarkedOptions;
|
||||
renderer: _Renderer;
|
||||
textRenderer: _TextRenderer;
|
||||
constructor(options?: MarkedOptions);
|
||||
/**
|
||||
* Static Parse Method
|
||||
*/
|
||||
static parse(tokens: Token[], options?: MarkedOptions): string;
|
||||
/**
|
||||
* Static Parse Inline Method
|
||||
*/
|
||||
static parseInline(tokens: Token[], options?: MarkedOptions): string;
|
||||
/**
|
||||
* Parse Loop
|
||||
*/
|
||||
parse(tokens: Token[], top?: boolean): string;
|
||||
/**
|
||||
* Parse Inline Tokens
|
||||
*/
|
||||
parseInline(tokens: Token[], renderer?: _Renderer | _TextRenderer): string;
|
||||
}
|
||||
}
|
||||
declare module "Tokenizer" {
|
||||
import type { _Lexer } from "Lexer";
|
||||
import type { Links, Tokens } from "Tokens";
|
||||
@ -171,7 +297,7 @@ declare module "Tokenizer" {
|
||||
hr(src: string): Tokens.Hr | undefined;
|
||||
blockquote(src: string): Tokens.Blockquote | undefined;
|
||||
list(src: string): Tokens.List | undefined;
|
||||
html(src: string): Tokens.HTML | Tokens.Paragraph | undefined;
|
||||
html(src: string): Tokens.HTML | undefined;
|
||||
def(src: string): Tokens.Def | undefined;
|
||||
table(src: string): Tokens.Table | undefined;
|
||||
lheading(src: string): Tokens.Heading | undefined;
|
||||
@ -185,29 +311,11 @@ declare module "Tokenizer" {
|
||||
codespan(src: string): Tokens.Codespan | undefined;
|
||||
br(src: string): Tokens.Br | undefined;
|
||||
del(src: string): Tokens.Del | undefined;
|
||||
autolink(src: string, mangle: (cap: string) => string): Tokens.Link | undefined;
|
||||
url(src: string, mangle: (cap: string) => string): Tokens.Link | undefined;
|
||||
inlineText(src: string, smartypants: (cap: string) => string): Tokens.Text | undefined;
|
||||
autolink(src: string): Tokens.Link | undefined;
|
||||
url(src: string): Tokens.Link | undefined;
|
||||
inlineText(src: string): Tokens.Text | undefined;
|
||||
}
|
||||
}
|
||||
declare module "rules" {
|
||||
export type Rule = RegExp | string;
|
||||
export interface Rules {
|
||||
[ruleName: string]: Pick<RegExp, 'exec'> | Rule | Rules;
|
||||
}
|
||||
type BlockRuleNames = 'newline' | 'code' | 'fences' | 'hr' | 'heading' | 'blockquote' | 'list' | 'html' | 'def' | 'lheading' | '_paragraph' | 'text' | '_label' | '_title' | 'bullet' | 'listItemStart' | '_tag' | '_comment' | 'paragraph' | 'uote';
|
||||
type BlockSubRuleNames = 'normal' | 'gfm' | 'pedantic';
|
||||
type InlineRuleNames = 'escape' | 'autolink' | 'tag' | 'link' | 'reflink' | 'nolink' | 'reflinkSearch' | 'code' | 'br' | 'text' | '_punctuation' | 'punctuation' | 'blockSkip' | 'escapedEmSt' | '_comment' | '_escapes' | '_scheme' | '_email' | '_attribute' | '_label' | '_href' | '_title' | 'strong' | '_extended_email' | '_backpedal';
|
||||
type InlineSubRuleNames = 'gfm' | 'emStrong' | 'normal' | 'pedantic' | 'breaks';
|
||||
/**
|
||||
* Block-Level Grammar
|
||||
*/
|
||||
export const block: Record<BlockRuleNames, Rule> & Record<BlockSubRuleNames, Rules> & Rules;
|
||||
/**
|
||||
* Inline-Level Grammar
|
||||
*/
|
||||
export const inline: Record<InlineRuleNames, Rule> & Record<InlineSubRuleNames, Rules> & Rules;
|
||||
}
|
||||
declare module "Lexer" {
|
||||
import type { Token, TokensList } from "Tokens";
|
||||
import type { MarkedOptions } from "MarkedOptions";
|
||||
@ -254,188 +362,12 @@ declare module "Lexer" {
|
||||
inlineTokens(src: string, tokens?: Token[]): Token[];
|
||||
}
|
||||
}
|
||||
declare module "TextRenderer" {
|
||||
/**
|
||||
* TextRenderer
|
||||
* returns only the textual part of the token
|
||||
*/
|
||||
export class _TextRenderer {
|
||||
strong(text: string): string;
|
||||
em(text: string): string;
|
||||
codespan(text: string): string;
|
||||
del(text: string): string;
|
||||
html(text: string): string;
|
||||
text(text: string): string;
|
||||
link(href: string, title: string | null | undefined, text: string): string;
|
||||
image(href: string, title: string | null, text: string): string;
|
||||
br(): string;
|
||||
}
|
||||
}
|
||||
declare module "Slugger" {
|
||||
import type { SluggerOptions } from "MarkedOptions";
|
||||
/**
|
||||
* Slugger generates header id
|
||||
*/
|
||||
export class _Slugger {
|
||||
seen: {
|
||||
[slugValue: string]: number;
|
||||
};
|
||||
constructor();
|
||||
serialize(value: string): string;
|
||||
/**
|
||||
* Finds the next safe (unique) slug to use
|
||||
*/
|
||||
getNextSafeSlug(originalSlug: string, isDryRun: boolean | undefined): string;
|
||||
/**
|
||||
* Convert string to unique id
|
||||
*/
|
||||
slug(value: string, options?: SluggerOptions): string;
|
||||
}
|
||||
}
|
||||
declare module "Instance" {
|
||||
import { _Lexer } from "Lexer";
|
||||
import { _Parser } from "Parser";
|
||||
import { _Hooks } from "Hooks";
|
||||
import { _Renderer } from "Renderer";
|
||||
import { _Tokenizer } from "Tokenizer";
|
||||
import { _TextRenderer } from "TextRenderer";
|
||||
import { _Slugger } from "Slugger";
|
||||
import type { MarkedExtension, MarkedOptions } from "MarkedOptions";
|
||||
import type { Token, TokensList } from "Tokens";
|
||||
export type ResultCallback = (error: Error | null, parseResult?: string) => undefined | void;
|
||||
export type MaybePromise = void | Promise<void>;
|
||||
export class Marked {
|
||||
#private;
|
||||
defaults: MarkedOptions;
|
||||
options: (opt: MarkedOptions) => this;
|
||||
parse: (src: string, optOrCallback?: MarkedOptions | ResultCallback | undefined | null, callback?: ResultCallback | undefined) => string | Promise<string | undefined> | undefined;
|
||||
parseInline: (src: string, optOrCallback?: MarkedOptions | ResultCallback | undefined | null, callback?: ResultCallback | undefined) => string | Promise<string | undefined> | undefined;
|
||||
Parser: typeof _Parser;
|
||||
parser: typeof _Parser.parse;
|
||||
Renderer: typeof _Renderer;
|
||||
TextRenderer: typeof _TextRenderer;
|
||||
Lexer: typeof _Lexer;
|
||||
lexer: typeof _Lexer.lex;
|
||||
Tokenizer: typeof _Tokenizer;
|
||||
Slugger: typeof _Slugger;
|
||||
Hooks: typeof _Hooks;
|
||||
constructor(...args: MarkedExtension[]);
|
||||
/**
|
||||
* Run callback for every token
|
||||
*/
|
||||
walkTokens(tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]): MaybePromise[];
|
||||
use(...args: MarkedExtension[]): this;
|
||||
setOptions(opt: MarkedOptions): this;
|
||||
}
|
||||
}
|
||||
declare module "helpers" {
|
||||
import type { MarkedOptions } from "MarkedOptions";
|
||||
import type { ResultCallback } from "Instance";
|
||||
import type { Rule } from "rules";
|
||||
export function escape(html: string, encode?: boolean): string;
|
||||
export function unescape(html: string): string;
|
||||
export function edit(regex: Rule, opt?: string): {
|
||||
replace: (name: string | RegExp, val: string | RegExp) => any;
|
||||
getRegex: () => RegExp;
|
||||
};
|
||||
export function cleanUrl(sanitize: boolean | undefined, base: string | undefined | null, href: string): string | null;
|
||||
export function resolveUrl(base: string, href: string): string;
|
||||
export const noopTest: {
|
||||
exec: () => null;
|
||||
};
|
||||
export function splitCells(tableRow: string, count?: number): string[];
|
||||
/**
|
||||
* Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
|
||||
* /c*$/ is vulnerable to REDOS.
|
||||
*
|
||||
* @param str
|
||||
* @param c
|
||||
* @param invert Remove suffix of non-c chars instead. Default falsey.
|
||||
*/
|
||||
export function rtrim(str: string, c: string, invert?: boolean): string;
|
||||
export function findClosingBracket(str: string, b: string): number;
|
||||
export function checkDeprecations(opt: MarkedOptions, callback?: ResultCallback): void;
|
||||
}
|
||||
declare module "Renderer" {
|
||||
import type { MarkedOptions } from "MarkedOptions";
|
||||
import type { _Slugger } from "Slugger";
|
||||
/**
|
||||
* Renderer
|
||||
*/
|
||||
export class _Renderer {
|
||||
options: MarkedOptions;
|
||||
constructor(options?: MarkedOptions);
|
||||
code(code: string, infostring: string | undefined, escaped: boolean): string;
|
||||
blockquote(quote: string): string;
|
||||
html(html: string, block?: boolean): string;
|
||||
heading(text: string, level: number, raw: string, slugger: _Slugger): string;
|
||||
hr(): string;
|
||||
list(body: string, ordered: boolean, start: number | ''): string;
|
||||
listitem(text: string, task: boolean, checked: boolean): string;
|
||||
checkbox(checked: boolean): string;
|
||||
paragraph(text: string): string;
|
||||
table(header: string, body: string): string;
|
||||
tablerow(content: string): string;
|
||||
tablecell(content: string, flags: {
|
||||
header: boolean;
|
||||
align: 'center' | 'left' | 'right' | null;
|
||||
}): string;
|
||||
/**
|
||||
* span level renderer
|
||||
*/
|
||||
strong(text: string): string;
|
||||
em(text: string): string;
|
||||
codespan(text: string): string;
|
||||
br(): string;
|
||||
del(text: string): string;
|
||||
link(href: string, title: string | null | undefined, text: string): string;
|
||||
image(href: string, title: string | null, text: string): string;
|
||||
text(text: string): string;
|
||||
}
|
||||
}
|
||||
declare module "Parser" {
|
||||
import { _Renderer } from "Renderer";
|
||||
import { _TextRenderer } from "TextRenderer";
|
||||
import { _Slugger } from "Slugger";
|
||||
import type { Token } from "Tokens";
|
||||
import type { MarkedOptions } from "MarkedOptions";
|
||||
/**
|
||||
* Parsing & Compiling
|
||||
*/
|
||||
export class _Parser {
|
||||
options: MarkedOptions;
|
||||
renderer: _Renderer;
|
||||
textRenderer: _TextRenderer;
|
||||
slugger: _Slugger;
|
||||
constructor(options?: MarkedOptions);
|
||||
/**
|
||||
* Static Parse Method
|
||||
*/
|
||||
static parse(tokens: Token[], options?: MarkedOptions): string;
|
||||
/**
|
||||
* Static Parse Inline Method
|
||||
*/
|
||||
static parseInline(tokens: Token[], options?: MarkedOptions): string;
|
||||
/**
|
||||
* Parse Loop
|
||||
*/
|
||||
parse(tokens: Token[], top?: boolean): string;
|
||||
/**
|
||||
* Parse Inline Tokens
|
||||
*/
|
||||
parseInline(tokens: Token[], renderer?: _Renderer | _TextRenderer): string;
|
||||
}
|
||||
}
|
||||
declare module "MarkedOptions" {
|
||||
import type { Token, Tokens, TokensList } from "Tokens";
|
||||
import type { _Parser } from "Parser";
|
||||
import type { _Lexer } from "Lexer";
|
||||
import type { _Renderer } from "Renderer";
|
||||
import type { _Tokenizer } from "Tokenizer";
|
||||
export interface SluggerOptions {
|
||||
/** Generates the next unique slug without updating the internal accumulator. */
|
||||
dryrun?: boolean;
|
||||
}
|
||||
export interface TokenizerThis {
|
||||
lexer: _Lexer;
|
||||
}
|
||||
@ -470,11 +402,6 @@ declare module "MarkedOptions" {
|
||||
* True will tell marked to await any walkTokens functions before parsing the tokens and returning an HTML string.
|
||||
*/
|
||||
async?: boolean;
|
||||
/**
|
||||
* A prefix URL for any relative link.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-base-url to prefix url for any relative link.
|
||||
*/
|
||||
baseUrl?: string | undefined | null;
|
||||
/**
|
||||
* Enable GFM line breaks. This option requires the gfm option to be true.
|
||||
*/
|
||||
@ -487,24 +414,6 @@ declare module "MarkedOptions" {
|
||||
* Enable GitHub flavored markdown.
|
||||
*/
|
||||
gfm?: boolean | undefined;
|
||||
/**
|
||||
* Include an id attribute when emitting headings.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-gfm-heading-id to include an id attribute when emitting headings (h1, h2, h3, etc).
|
||||
*/
|
||||
headerIds?: boolean | undefined;
|
||||
/**
|
||||
* Set the prefix for header tag ids.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-gfm-heading-id to add a string to prefix the id attribute when emitting headings (h1, h2, h3, etc).
|
||||
*/
|
||||
headerPrefix?: string | undefined;
|
||||
/**
|
||||
* A function to highlight code blocks. The function can either be
|
||||
* synchronous (returning a string) or asynchronous (callback invoked
|
||||
* with an error if any occurred during highlighting and a string
|
||||
* if highlighting was successful)
|
||||
* @deprecated Deprecated in v5.0.0 use marked-highlight to add highlighting to code blocks.
|
||||
*/
|
||||
highlight?: ((code: string, lang: string | undefined, callback?: (error: Error, code?: string) => void) => string | void) | null;
|
||||
/**
|
||||
* Hooks are methods that hook into some part of marked.
|
||||
* preprocess is called to process markdown before sending it to marked.
|
||||
@ -515,16 +424,6 @@ declare module "MarkedOptions" {
|
||||
postprocess: (html: string) => string | Promise<string>;
|
||||
options?: MarkedOptions;
|
||||
} | null;
|
||||
/**
|
||||
* Set the prefix for code block classes.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-highlight to prefix the className in a <code> block. Useful for syntax highlighting.
|
||||
*/
|
||||
langPrefix?: string | undefined;
|
||||
/**
|
||||
* Mangle autolinks (<email@domain.com>).
|
||||
* @deprecated Deprecated in v5.0.0 use marked-mangle to mangle email addresses.
|
||||
*/
|
||||
mangle?: boolean | undefined;
|
||||
/**
|
||||
* Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
|
||||
*/
|
||||
@ -535,29 +434,10 @@ declare module "MarkedOptions" {
|
||||
* An object containing functions to render tokens to HTML.
|
||||
*/
|
||||
renderer?: RendererObject | undefined | null;
|
||||
/**
|
||||
* Sanitize the output. Ignore any HTML that has been input. If true, sanitize the HTML passed into markdownString with the sanitizer function.
|
||||
* @deprecated Warning: This feature is deprecated and it should NOT be used as it cannot be considered secure. Instead use a sanitize library, like DOMPurify (recommended), sanitize-html or insane on the output HTML!
|
||||
*/
|
||||
sanitize?: boolean | undefined;
|
||||
/**
|
||||
* Optionally sanitize found HTML with a sanitizer function.
|
||||
* @deprecated A function to sanitize the HTML passed into markdownString.
|
||||
*/
|
||||
sanitizer?: ((html: string) => string) | null;
|
||||
/**
|
||||
* Shows an HTML error message when rendering fails.
|
||||
*/
|
||||
silent?: boolean | undefined;
|
||||
/**
|
||||
* Use smarter list behavior than the original markdown. May eventually be default with the old behavior moved into pedantic.
|
||||
*/
|
||||
smartLists?: boolean | undefined;
|
||||
/**
|
||||
* Use "smart" typograhic punctuation for things like quotes and dashes.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-smartypants to use "smart" typographic punctuation for things like quotes and dashes.
|
||||
*/
|
||||
smartypants?: boolean | undefined;
|
||||
/**
|
||||
* The tokenizer defines how to turn markdown text into tokens.
|
||||
*/
|
||||
@ -569,11 +449,6 @@ declare module "MarkedOptions" {
|
||||
* The return value of the function is ignored.
|
||||
*/
|
||||
walkTokens?: ((token: Token) => void | Promise<void>) | undefined | null;
|
||||
/**
|
||||
* Generate closing slash for self-closing tags (<br/> instead of <br>)
|
||||
* @deprecated Deprecated in v5.0.0 use marked-xhtml to emit self-closing HTML tags for void elements (<br/>, <img/>, etc.) with a "/" as required by XHTML.
|
||||
*/
|
||||
xhtml?: boolean | undefined;
|
||||
}
|
||||
export interface MarkedOptions extends Omit<MarkedExtension, 'renderer' | 'tokenizer' | 'extensions' | 'walkTokens'> {
|
||||
/**
|
||||
@ -632,18 +507,50 @@ declare module "Hooks" {
|
||||
postprocess(html: string): string;
|
||||
}
|
||||
}
|
||||
declare module "Instance" {
|
||||
import { _Lexer } from "Lexer";
|
||||
import { _Parser } from "Parser";
|
||||
import { _Hooks } from "Hooks";
|
||||
import { _Renderer } from "Renderer";
|
||||
import { _Tokenizer } from "Tokenizer";
|
||||
import { _TextRenderer } from "TextRenderer";
|
||||
import type { MarkedExtension, MarkedOptions } from "MarkedOptions";
|
||||
import type { Token, TokensList } from "Tokens";
|
||||
export type MaybePromise = void | Promise<void>;
|
||||
export class Marked {
|
||||
#private;
|
||||
defaults: MarkedOptions;
|
||||
options: (opt: MarkedOptions) => this;
|
||||
parse: (src: string, options?: MarkedOptions | undefined | null) => string | Promise<string>;
|
||||
parseInline: (src: string, options?: MarkedOptions | undefined | null) => string | Promise<string>;
|
||||
Parser: typeof _Parser;
|
||||
parser: typeof _Parser.parse;
|
||||
Renderer: typeof _Renderer;
|
||||
TextRenderer: typeof _TextRenderer;
|
||||
Lexer: typeof _Lexer;
|
||||
lexer: typeof _Lexer.lex;
|
||||
Tokenizer: typeof _Tokenizer;
|
||||
Hooks: typeof _Hooks;
|
||||
constructor(...args: MarkedExtension[]);
|
||||
/**
|
||||
* Run callback for every token
|
||||
*/
|
||||
walkTokens(tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]): MaybePromise[];
|
||||
use(...args: MarkedExtension[]): this;
|
||||
setOptions(opt: MarkedOptions): this;
|
||||
}
|
||||
}
|
||||
declare module "marked" {
|
||||
import { _Lexer } from "Lexer";
|
||||
import { _Parser } from "Parser";
|
||||
import { _Tokenizer } from "Tokenizer";
|
||||
import { _Renderer } from "Renderer";
|
||||
import { _TextRenderer } from "TextRenderer";
|
||||
import { _Slugger } from "Slugger";
|
||||
import { _Hooks } from "Hooks";
|
||||
import { _getDefaults } from "defaults";
|
||||
import type { MarkedExtension, MarkedOptions } from "MarkedOptions";
|
||||
import type { Token, TokensList } from "Tokens";
|
||||
import type { ResultCallback, MaybePromise } from "Instance";
|
||||
import type { MaybePromise } from "Instance";
|
||||
/**
|
||||
* Compiles markdown to HTML asynchronously.
|
||||
*
|
||||
@ -663,26 +570,11 @@ declare module "marked" {
|
||||
*/
|
||||
export function marked(src: string, options?: MarkedOptions): string;
|
||||
/**
|
||||
* Compiles markdown to HTML asynchronously with a callback.
|
||||
* Compiles markdown to HTML synchronously.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
*/
|
||||
export function marked(src: string, callback: ResultCallback): void;
|
||||
/**
|
||||
* Compiles markdown to HTML asynchronously with a callback.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param options Hash of options
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
*/
|
||||
export function marked(src: string, options: MarkedOptions, callback: ResultCallback): void;
|
||||
/**
|
||||
* Compiles markdown to HTML asynchronously with a callback.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param options Hash of options
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
* @param options Optional hash of options
|
||||
* @return String of compiled HTML
|
||||
*/
|
||||
export namespace marked {
|
||||
var options: (options: MarkedOptions) => typeof marked;
|
||||
@ -691,7 +583,7 @@ declare module "marked" {
|
||||
var defaults: MarkedOptions;
|
||||
var use: (...args: MarkedExtension[]) => typeof marked;
|
||||
var walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[];
|
||||
var parseInline: (src: string, optOrCallback?: MarkedOptions | ResultCallback | null | undefined, callback?: ResultCallback | undefined) => string | Promise<string | undefined> | undefined;
|
||||
var parseInline: (src: string, options?: MarkedOptions | null | undefined) => string | Promise<string>;
|
||||
var Parser: typeof _Parser;
|
||||
var parser: typeof _Parser.parse;
|
||||
var Renderer: typeof _Renderer;
|
||||
@ -699,7 +591,6 @@ declare module "marked" {
|
||||
var Lexer: typeof _Lexer;
|
||||
var lexer: typeof _Lexer.lex;
|
||||
var Tokenizer: typeof _Tokenizer;
|
||||
var Slugger: typeof _Slugger;
|
||||
var Hooks: typeof _Hooks;
|
||||
var parse: typeof marked;
|
||||
}
|
||||
@ -707,7 +598,7 @@ declare module "marked" {
|
||||
export const setOptions: (options: MarkedOptions) => typeof marked;
|
||||
export const use: (...args: MarkedExtension[]) => typeof marked;
|
||||
export const walkTokens: (tokens: Token[] | TokensList, callback: (token: Token) => MaybePromise | MaybePromise[]) => MaybePromise[];
|
||||
export const parseInline: (src: string, optOrCallback?: MarkedOptions | ResultCallback | null | undefined, callback?: ResultCallback | undefined) => string | Promise<string | undefined> | undefined;
|
||||
export const parseInline: (src: string, options?: MarkedOptions | null | undefined) => string | Promise<string>;
|
||||
export const parse: typeof marked;
|
||||
export const parser: typeof _Parser.parse;
|
||||
export const lexer: typeof _Lexer.lex;
|
||||
@ -717,7 +608,6 @@ declare module "marked" {
|
||||
export { _Tokenizer as Tokenizer } from "Tokenizer";
|
||||
export { _Renderer as Renderer } from "Renderer";
|
||||
export { _TextRenderer as TextRenderer } from "TextRenderer";
|
||||
export { _Slugger as Slugger } from "Slugger";
|
||||
export { _Hooks as Hooks } from "Hooks";
|
||||
export { Marked } from "Instance";
|
||||
export type * from "MarkedOptions";
|
||||
|
344
lib/marked.esm.js
generated
344
lib/marked.esm.js
generated
@ -15,25 +15,15 @@
|
||||
function _getDefaults() {
|
||||
return {
|
||||
async: false,
|
||||
baseUrl: null,
|
||||
breaks: false,
|
||||
extensions: null,
|
||||
gfm: true,
|
||||
headerIds: false,
|
||||
headerPrefix: '',
|
||||
highlight: null,
|
||||
hooks: null,
|
||||
langPrefix: 'language-',
|
||||
mangle: false,
|
||||
pedantic: false,
|
||||
renderer: null,
|
||||
sanitize: false,
|
||||
sanitizer: null,
|
||||
silent: false,
|
||||
smartypants: false,
|
||||
tokenizer: null,
|
||||
walkTokens: null,
|
||||
xhtml: false
|
||||
walkTokens: null
|
||||
};
|
||||
}
|
||||
let _defaults = _getDefaults();
|
||||
@ -101,26 +91,7 @@ function edit(regex, opt) {
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
const nonWordAndColonTest = /[^\w:]/g;
|
||||
const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
|
||||
function cleanUrl(sanitize, base, href) {
|
||||
if (sanitize) {
|
||||
let prot;
|
||||
try {
|
||||
prot = decodeURIComponent(unescape(href))
|
||||
.replace(nonWordAndColonTest, '')
|
||||
.toLowerCase();
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (base && !originIndependentUrl.test(href)) {
|
||||
href = resolveUrl(base, href);
|
||||
}
|
||||
function cleanUrl(href) {
|
||||
try {
|
||||
href = encodeURI(href).replace(/%25/g, '%');
|
||||
}
|
||||
@ -129,40 +100,6 @@ function cleanUrl(sanitize, base, href) {
|
||||
}
|
||||
return href;
|
||||
}
|
||||
const baseUrls = {};
|
||||
const justDomain = /^[^:]+:\/*[^/]*$/;
|
||||
const protocol = /^([^:]+:)[\s\S]*$/;
|
||||
const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
|
||||
function resolveUrl(base, href) {
|
||||
if (!baseUrls[' ' + base]) {
|
||||
// we can ignore everything in base after the last slash of its path component,
|
||||
// but we might need to add _that_
|
||||
// https://tools.ietf.org/html/rfc3986#section-3
|
||||
if (justDomain.test(base)) {
|
||||
baseUrls[' ' + base] = base + '/';
|
||||
}
|
||||
else {
|
||||
baseUrls[' ' + base] = rtrim(base, '/', true);
|
||||
}
|
||||
}
|
||||
base = baseUrls[' ' + base];
|
||||
const relativeBase = base.indexOf(':') === -1;
|
||||
if (href.substring(0, 2) === '//') {
|
||||
if (relativeBase) {
|
||||
return href;
|
||||
}
|
||||
return base.replace(protocol, '$1') + href;
|
||||
}
|
||||
else if (href.charAt(0) === '/') {
|
||||
if (relativeBase) {
|
||||
return href;
|
||||
}
|
||||
return base.replace(domain, '$1') + href;
|
||||
}
|
||||
else {
|
||||
return base + href;
|
||||
}
|
||||
}
|
||||
const noopTest = { exec: () => null };
|
||||
function splitCells(tableRow, count) {
|
||||
// ensure that every cell-delimiting pipe has a space
|
||||
@ -256,35 +193,6 @@ function findClosingBracket(str, b) {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function checkDeprecations(opt, callback) {
|
||||
if (!opt || opt.silent) {
|
||||
return;
|
||||
}
|
||||
if (callback) {
|
||||
console.warn('marked(): callback is deprecated since version 5.0.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/using_pro#async');
|
||||
}
|
||||
if (opt.sanitize || opt.sanitizer) {
|
||||
console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options');
|
||||
}
|
||||
if (opt.highlight || opt.langPrefix !== 'language-') {
|
||||
console.warn('marked(): highlight and langPrefix parameters are deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-highlight.');
|
||||
}
|
||||
if (opt.mangle) {
|
||||
console.warn('marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.');
|
||||
}
|
||||
if (opt.baseUrl) {
|
||||
console.warn('marked(): baseUrl parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-base-url.');
|
||||
}
|
||||
if (opt.smartypants) {
|
||||
console.warn('marked(): smartypants parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-smartypants.');
|
||||
}
|
||||
if (opt.xhtml) {
|
||||
console.warn('marked(): xhtml parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-xhtml.');
|
||||
}
|
||||
if (opt.headerIds || opt.headerPrefix) {
|
||||
console.warn('marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.');
|
||||
}
|
||||
}
|
||||
|
||||
function outputLink(cap, link, raw, lexer) {
|
||||
const href = link.href;
|
||||
@ -601,17 +509,9 @@ class _Tokenizer {
|
||||
type: 'html',
|
||||
block: true,
|
||||
raw: cap[0],
|
||||
pre: !this.options.sanitizer
|
||||
&& (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
|
||||
pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
|
||||
text: cap[0]
|
||||
};
|
||||
if (this.options.sanitize) {
|
||||
const text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]);
|
||||
const paragraph = token;
|
||||
paragraph.type = 'paragraph';
|
||||
paragraph.text = text;
|
||||
paragraph.tokens = this.lexer.inline(text);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@ -749,18 +649,12 @@ class _Tokenizer {
|
||||
this.lexer.state.inRawBlock = false;
|
||||
}
|
||||
return {
|
||||
type: this.options.sanitize
|
||||
? 'text'
|
||||
: 'html',
|
||||
type: 'html',
|
||||
raw: cap[0],
|
||||
inLink: this.lexer.state.inLink,
|
||||
inRawBlock: this.lexer.state.inRawBlock,
|
||||
block: false,
|
||||
text: this.options.sanitize
|
||||
? (this.options.sanitizer
|
||||
? this.options.sanitizer(cap[0])
|
||||
: escape(cap[0]))
|
||||
: cap[0]
|
||||
text: cap[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -931,12 +825,12 @@ class _Tokenizer {
|
||||
};
|
||||
}
|
||||
}
|
||||
autolink(src, mangle) {
|
||||
autolink(src) {
|
||||
const cap = this.rules.inline.autolink.exec(src);
|
||||
if (cap) {
|
||||
let text, href;
|
||||
if (cap[2] === '@') {
|
||||
text = escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
|
||||
text = escape(cap[1]);
|
||||
href = 'mailto:' + text;
|
||||
}
|
||||
else {
|
||||
@ -958,12 +852,12 @@ class _Tokenizer {
|
||||
};
|
||||
}
|
||||
}
|
||||
url(src, mangle) {
|
||||
url(src) {
|
||||
let cap;
|
||||
if (cap = this.rules.inline.url.exec(src)) {
|
||||
let text, href;
|
||||
if (cap[2] === '@') {
|
||||
text = escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
|
||||
text = escape(cap[0]);
|
||||
href = 'mailto:' + text;
|
||||
}
|
||||
else {
|
||||
@ -996,15 +890,15 @@ class _Tokenizer {
|
||||
};
|
||||
}
|
||||
}
|
||||
inlineText(src, smartypants) {
|
||||
inlineText(src) {
|
||||
const cap = this.rules.inline.text.exec(src);
|
||||
if (cap) {
|
||||
let text;
|
||||
if (this.lexer.state.inRawBlock) {
|
||||
text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0];
|
||||
text = cap[0];
|
||||
}
|
||||
else {
|
||||
text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
|
||||
text = escape(cap[0]);
|
||||
}
|
||||
return {
|
||||
type: 'text',
|
||||
@ -1292,39 +1186,6 @@ inline.breaks = {
|
||||
.getRegex()
|
||||
};
|
||||
|
||||
/**
|
||||
* smartypants text replacement
|
||||
*/
|
||||
function smartypants(text) {
|
||||
return text
|
||||
// em-dashes
|
||||
.replace(/---/g, '\u2014')
|
||||
// en-dashes
|
||||
.replace(/--/g, '\u2013')
|
||||
// opening singles
|
||||
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
|
||||
// closing singles & apostrophes
|
||||
.replace(/'/g, '\u2019')
|
||||
// opening doubles
|
||||
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
|
||||
// closing doubles
|
||||
.replace(/"/g, '\u201d')
|
||||
// ellipses
|
||||
.replace(/\.{3}/g, '\u2026');
|
||||
}
|
||||
/**
|
||||
* mangle email addresses
|
||||
*/
|
||||
function mangle(text) {
|
||||
let out = '';
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const ch = Math.random() > 0.5
|
||||
? 'x' + text.charCodeAt(i).toString(16)
|
||||
: text.charCodeAt(i).toString();
|
||||
out += '&#' + ch + ';';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Block Lexer
|
||||
*/
|
||||
@ -1698,13 +1559,13 @@ class _Lexer {
|
||||
continue;
|
||||
}
|
||||
// autolink
|
||||
if (token = this.tokenizer.autolink(src, mangle)) {
|
||||
if (token = this.tokenizer.autolink(src)) {
|
||||
src = src.substring(token.raw.length);
|
||||
tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
// url (gfm)
|
||||
if (!this.state.inLink && (token = this.tokenizer.url(src, mangle))) {
|
||||
if (!this.state.inLink && (token = this.tokenizer.url(src))) {
|
||||
src = src.substring(token.raw.length);
|
||||
tokens.push(token);
|
||||
continue;
|
||||
@ -1726,7 +1587,7 @@ class _Lexer {
|
||||
cutSrc = src.substring(0, startIndex + 1);
|
||||
}
|
||||
}
|
||||
if (token = this.tokenizer.inlineText(cutSrc, smartypants)) {
|
||||
if (token = this.tokenizer.inlineText(cutSrc)) {
|
||||
src = src.substring(token.raw.length);
|
||||
if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
|
||||
prevChar = token.raw.slice(-1);
|
||||
@ -1767,21 +1628,13 @@ class _Renderer {
|
||||
}
|
||||
code(code, infostring, escaped) {
|
||||
const lang = (infostring || '').match(/^\S*/)?.[0];
|
||||
if (this.options.highlight) {
|
||||
const out = this.options.highlight(code, lang);
|
||||
if (out != null && out !== code) {
|
||||
escaped = true;
|
||||
code = out;
|
||||
}
|
||||
}
|
||||
code = code.replace(/\n$/, '') + '\n';
|
||||
if (!lang) {
|
||||
return '<pre><code>'
|
||||
+ (escaped ? code : escape(code, true))
|
||||
+ '</code></pre>\n';
|
||||
}
|
||||
return '<pre><code class="'
|
||||
+ this.options.langPrefix
|
||||
return '<pre><code class="language-'
|
||||
+ escape(lang)
|
||||
+ '">'
|
||||
+ (escaped ? code : escape(code, true))
|
||||
@ -1793,16 +1646,12 @@ class _Renderer {
|
||||
html(html, block) {
|
||||
return html;
|
||||
}
|
||||
heading(text, level, raw, slugger) {
|
||||
if (this.options.headerIds) {
|
||||
const id = this.options.headerPrefix + slugger.slug(raw);
|
||||
return `<h${level} id="${id}">${text}</h${level}>\n`;
|
||||
}
|
||||
heading(text, level, raw) {
|
||||
// ignore IDs
|
||||
return `<h${level}>${text}</h${level}>\n`;
|
||||
}
|
||||
hr() {
|
||||
return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
|
||||
return '<hr>\n';
|
||||
}
|
||||
list(body, ordered, start) {
|
||||
const type = ordered ? 'ol' : 'ul';
|
||||
@ -1815,9 +1664,7 @@ class _Renderer {
|
||||
checkbox(checked) {
|
||||
return '<input '
|
||||
+ (checked ? 'checked="" ' : '')
|
||||
+ 'disabled="" type="checkbox"'
|
||||
+ (this.options.xhtml ? ' /' : '')
|
||||
+ '> ';
|
||||
+ 'disabled="" type="checkbox">';
|
||||
}
|
||||
paragraph(text) {
|
||||
return `<p>${text}</p>\n`;
|
||||
@ -1855,13 +1702,13 @@ class _Renderer {
|
||||
return `<code>${text}</code>`;
|
||||
}
|
||||
br() {
|
||||
return this.options.xhtml ? '<br/>' : '<br>';
|
||||
return '<br>';
|
||||
}
|
||||
del(text) {
|
||||
return `<del>${text}</del>`;
|
||||
}
|
||||
link(href, title, text) {
|
||||
const cleanHref = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
const cleanHref = cleanUrl(href);
|
||||
if (cleanHref === null) {
|
||||
return text;
|
||||
}
|
||||
@ -1874,7 +1721,7 @@ class _Renderer {
|
||||
return out;
|
||||
}
|
||||
image(href, title, text) {
|
||||
const cleanHref = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
const cleanHref = cleanUrl(href);
|
||||
if (cleanHref === null) {
|
||||
return text;
|
||||
}
|
||||
@ -1883,7 +1730,7 @@ class _Renderer {
|
||||
if (title) {
|
||||
out += ` title="${title}"`;
|
||||
}
|
||||
out += this.options.xhtml ? '/>' : '>';
|
||||
out += '>';
|
||||
return out;
|
||||
}
|
||||
text(text) {
|
||||
@ -1926,52 +1773,6 @@ class _TextRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Slugger generates header id
|
||||
*/
|
||||
class _Slugger {
|
||||
seen;
|
||||
constructor() {
|
||||
this.seen = {};
|
||||
}
|
||||
serialize(value) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
// remove html tags
|
||||
.replace(/<[!\/a-z].*?>/ig, '')
|
||||
// remove unwanted chars
|
||||
.replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
|
||||
.replace(/\s/g, '-');
|
||||
}
|
||||
/**
|
||||
* Finds the next safe (unique) slug to use
|
||||
*/
|
||||
getNextSafeSlug(originalSlug, isDryRun) {
|
||||
let slug = originalSlug;
|
||||
let occurenceAccumulator = 0;
|
||||
if (this.seen.hasOwnProperty(slug)) {
|
||||
occurenceAccumulator = this.seen[originalSlug];
|
||||
do {
|
||||
occurenceAccumulator++;
|
||||
slug = originalSlug + '-' + occurenceAccumulator;
|
||||
} while (this.seen.hasOwnProperty(slug));
|
||||
}
|
||||
if (!isDryRun) {
|
||||
this.seen[originalSlug] = occurenceAccumulator;
|
||||
this.seen[slug] = 0;
|
||||
}
|
||||
return slug;
|
||||
}
|
||||
/**
|
||||
* Convert string to unique id
|
||||
*/
|
||||
slug(value, options = {}) {
|
||||
const slug = this.serialize(value);
|
||||
return this.getNextSafeSlug(slug, options.dryrun);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsing & Compiling
|
||||
*/
|
||||
@ -1979,14 +1780,12 @@ class _Parser {
|
||||
options;
|
||||
renderer;
|
||||
textRenderer;
|
||||
slugger;
|
||||
constructor(options) {
|
||||
this.options = options || _defaults;
|
||||
this.options.renderer = this.options.renderer || new _Renderer();
|
||||
this.renderer = this.options.renderer;
|
||||
this.renderer.options = this.options;
|
||||
this.textRenderer = new _TextRenderer();
|
||||
this.slugger = new _Slugger();
|
||||
}
|
||||
/**
|
||||
* Static Parse Method
|
||||
@ -2028,7 +1827,7 @@ class _Parser {
|
||||
}
|
||||
case 'heading': {
|
||||
const headingToken = token;
|
||||
out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)), this.slugger);
|
||||
out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)));
|
||||
continue;
|
||||
}
|
||||
case 'code': {
|
||||
@ -2251,7 +2050,6 @@ class Marked {
|
||||
Lexer = _Lexer;
|
||||
lexer = _Lexer.lex;
|
||||
Tokenizer = _Tokenizer;
|
||||
Slugger = _Slugger;
|
||||
Hooks = _Hooks;
|
||||
constructor(...args) {
|
||||
this.use(...args);
|
||||
@ -2448,12 +2246,8 @@ class Marked {
|
||||
return this;
|
||||
}
|
||||
#parseMarkdown(lexer, parser) {
|
||||
return (src, optOrCallback, callback) => {
|
||||
if (typeof optOrCallback === 'function') {
|
||||
callback = optOrCallback;
|
||||
optOrCallback = null;
|
||||
}
|
||||
const origOpt = { ...optOrCallback };
|
||||
return (src, options) => {
|
||||
const origOpt = { ...options };
|
||||
const opt = { ...this.defaults, ...origOpt };
|
||||
// Show warning if an extension set async to true but the parse was called with async: false
|
||||
if (this.defaults.async === true && origOpt.async === false) {
|
||||
@ -2462,7 +2256,7 @@ class Marked {
|
||||
}
|
||||
opt.async = true;
|
||||
}
|
||||
const throwError = this.#onError(!!opt.silent, !!opt.async, callback);
|
||||
const throwError = this.#onError(!!opt.silent, !!opt.async);
|
||||
// throw error in case of non string input
|
||||
if (typeof src === 'undefined' || src === null) {
|
||||
return throwError(new Error('marked(): input parameter is undefined or null'));
|
||||
@ -2471,76 +2265,9 @@ class Marked {
|
||||
return throwError(new Error('marked(): input parameter is of type '
|
||||
+ Object.prototype.toString.call(src) + ', string expected'));
|
||||
}
|
||||
checkDeprecations(opt, callback);
|
||||
if (opt.hooks) {
|
||||
opt.hooks.options = opt;
|
||||
}
|
||||
if (callback) {
|
||||
const resultCallback = callback;
|
||||
const highlight = opt.highlight;
|
||||
let tokens;
|
||||
try {
|
||||
if (opt.hooks) {
|
||||
src = opt.hooks.preprocess(src);
|
||||
}
|
||||
tokens = lexer(src, opt);
|
||||
}
|
||||
catch (e) {
|
||||
return throwError(e);
|
||||
}
|
||||
const done = (err) => {
|
||||
let out;
|
||||
if (!err) {
|
||||
try {
|
||||
if (opt.walkTokens) {
|
||||
this.walkTokens(tokens, opt.walkTokens);
|
||||
}
|
||||
out = parser(tokens, opt);
|
||||
if (opt.hooks) {
|
||||
out = opt.hooks.postprocess(out);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
err = e;
|
||||
}
|
||||
}
|
||||
opt.highlight = highlight;
|
||||
return err
|
||||
? throwError(err)
|
||||
: resultCallback(null, out);
|
||||
};
|
||||
if (!highlight || highlight.length < 3) {
|
||||
return done();
|
||||
}
|
||||
delete opt.highlight;
|
||||
if (!tokens.length)
|
||||
return done();
|
||||
let pending = 0;
|
||||
this.walkTokens(tokens, (token) => {
|
||||
if (token.type === 'code') {
|
||||
pending++;
|
||||
setTimeout(() => {
|
||||
highlight(token.text, token.lang, (err, code) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (code != null && code !== token.text) {
|
||||
token.text = code;
|
||||
token.escaped = true;
|
||||
}
|
||||
pending--;
|
||||
if (pending === 0) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
if (pending === 0) {
|
||||
done();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (opt.async) {
|
||||
return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
|
||||
.then(src => lexer(src, opt))
|
||||
@ -2568,7 +2295,7 @@ class Marked {
|
||||
}
|
||||
};
|
||||
}
|
||||
#onError(silent, async, callback) {
|
||||
#onError(silent, async) {
|
||||
return (e) => {
|
||||
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
|
||||
if (silent) {
|
||||
@ -2578,27 +2305,19 @@ class Marked {
|
||||
if (async) {
|
||||
return Promise.resolve(msg);
|
||||
}
|
||||
if (callback) {
|
||||
callback(null, msg);
|
||||
return;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
if (async) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
if (callback) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const markedInstance = new Marked();
|
||||
function marked(src, opt, callback) {
|
||||
return markedInstance.parse(src, opt, callback);
|
||||
function marked(src, opt) {
|
||||
return markedInstance.parse(src, opt);
|
||||
}
|
||||
/**
|
||||
* Sets the default options.
|
||||
@ -2650,7 +2369,6 @@ marked.TextRenderer = _TextRenderer;
|
||||
marked.Lexer = _Lexer;
|
||||
marked.lexer = _Lexer.lex;
|
||||
marked.Tokenizer = _Tokenizer;
|
||||
marked.Slugger = _Slugger;
|
||||
marked.Hooks = _Hooks;
|
||||
marked.parse = marked;
|
||||
const options = marked.options;
|
||||
@ -2662,5 +2380,5 @@ const parse = marked;
|
||||
const parser = _Parser.parse;
|
||||
const lexer = _Lexer.lex;
|
||||
|
||||
export { _Hooks as Hooks, _Lexer as Lexer, Marked, _Parser as Parser, _Renderer as Renderer, _Slugger as Slugger, _TextRenderer as TextRenderer, _Tokenizer as Tokenizer, _defaults as defaults, _getDefaults as getDefaults, lexer, marked, options, parse, parseInline, parser, setOptions, use, walkTokens };
|
||||
export { _Hooks as Hooks, _Lexer as Lexer, Marked, _Parser as Parser, _Renderer as Renderer, _TextRenderer as TextRenderer, _Tokenizer as Tokenizer, _defaults as defaults, _getDefaults as getDefaults, lexer, marked, options, parse, parseInline, parser, setOptions, use, walkTokens };
|
||||
//# sourceMappingURL=marked.esm.js.map
|
||||
|
2
lib/marked.esm.js.map
generated
2
lib/marked.esm.js.map
generated
File diff suppressed because one or more lines are too long
343
lib/marked.umd.js
generated
343
lib/marked.umd.js
generated
@ -21,25 +21,15 @@
|
||||
function _getDefaults() {
|
||||
return {
|
||||
async: false,
|
||||
baseUrl: null,
|
||||
breaks: false,
|
||||
extensions: null,
|
||||
gfm: true,
|
||||
headerIds: false,
|
||||
headerPrefix: '',
|
||||
highlight: null,
|
||||
hooks: null,
|
||||
langPrefix: 'language-',
|
||||
mangle: false,
|
||||
pedantic: false,
|
||||
renderer: null,
|
||||
sanitize: false,
|
||||
sanitizer: null,
|
||||
silent: false,
|
||||
smartypants: false,
|
||||
tokenizer: null,
|
||||
walkTokens: null,
|
||||
xhtml: false
|
||||
walkTokens: null
|
||||
};
|
||||
}
|
||||
exports.defaults = _getDefaults();
|
||||
@ -107,26 +97,7 @@
|
||||
};
|
||||
return obj;
|
||||
}
|
||||
const nonWordAndColonTest = /[^\w:]/g;
|
||||
const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
|
||||
function cleanUrl(sanitize, base, href) {
|
||||
if (sanitize) {
|
||||
let prot;
|
||||
try {
|
||||
prot = decodeURIComponent(unescape(href))
|
||||
.replace(nonWordAndColonTest, '')
|
||||
.toLowerCase();
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (base && !originIndependentUrl.test(href)) {
|
||||
href = resolveUrl(base, href);
|
||||
}
|
||||
function cleanUrl(href) {
|
||||
try {
|
||||
href = encodeURI(href).replace(/%25/g, '%');
|
||||
}
|
||||
@ -135,40 +106,6 @@
|
||||
}
|
||||
return href;
|
||||
}
|
||||
const baseUrls = {};
|
||||
const justDomain = /^[^:]+:\/*[^/]*$/;
|
||||
const protocol = /^([^:]+:)[\s\S]*$/;
|
||||
const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
|
||||
function resolveUrl(base, href) {
|
||||
if (!baseUrls[' ' + base]) {
|
||||
// we can ignore everything in base after the last slash of its path component,
|
||||
// but we might need to add _that_
|
||||
// https://tools.ietf.org/html/rfc3986#section-3
|
||||
if (justDomain.test(base)) {
|
||||
baseUrls[' ' + base] = base + '/';
|
||||
}
|
||||
else {
|
||||
baseUrls[' ' + base] = rtrim(base, '/', true);
|
||||
}
|
||||
}
|
||||
base = baseUrls[' ' + base];
|
||||
const relativeBase = base.indexOf(':') === -1;
|
||||
if (href.substring(0, 2) === '//') {
|
||||
if (relativeBase) {
|
||||
return href;
|
||||
}
|
||||
return base.replace(protocol, '$1') + href;
|
||||
}
|
||||
else if (href.charAt(0) === '/') {
|
||||
if (relativeBase) {
|
||||
return href;
|
||||
}
|
||||
return base.replace(domain, '$1') + href;
|
||||
}
|
||||
else {
|
||||
return base + href;
|
||||
}
|
||||
}
|
||||
const noopTest = { exec: () => null };
|
||||
function splitCells(tableRow, count) {
|
||||
// ensure that every cell-delimiting pipe has a space
|
||||
@ -262,35 +199,6 @@
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
function checkDeprecations(opt, callback) {
|
||||
if (!opt || opt.silent) {
|
||||
return;
|
||||
}
|
||||
if (callback) {
|
||||
console.warn('marked(): callback is deprecated since version 5.0.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/using_pro#async');
|
||||
}
|
||||
if (opt.sanitize || opt.sanitizer) {
|
||||
console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options');
|
||||
}
|
||||
if (opt.highlight || opt.langPrefix !== 'language-') {
|
||||
console.warn('marked(): highlight and langPrefix parameters are deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-highlight.');
|
||||
}
|
||||
if (opt.mangle) {
|
||||
console.warn('marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.');
|
||||
}
|
||||
if (opt.baseUrl) {
|
||||
console.warn('marked(): baseUrl parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-base-url.');
|
||||
}
|
||||
if (opt.smartypants) {
|
||||
console.warn('marked(): smartypants parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-smartypants.');
|
||||
}
|
||||
if (opt.xhtml) {
|
||||
console.warn('marked(): xhtml parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-xhtml.');
|
||||
}
|
||||
if (opt.headerIds || opt.headerPrefix) {
|
||||
console.warn('marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.');
|
||||
}
|
||||
}
|
||||
|
||||
function outputLink(cap, link, raw, lexer) {
|
||||
const href = link.href;
|
||||
@ -607,17 +515,9 @@
|
||||
type: 'html',
|
||||
block: true,
|
||||
raw: cap[0],
|
||||
pre: !this.options.sanitizer
|
||||
&& (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
|
||||
pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
|
||||
text: cap[0]
|
||||
};
|
||||
if (this.options.sanitize) {
|
||||
const text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]);
|
||||
const paragraph = token;
|
||||
paragraph.type = 'paragraph';
|
||||
paragraph.text = text;
|
||||
paragraph.tokens = this.lexer.inline(text);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@ -755,18 +655,12 @@
|
||||
this.lexer.state.inRawBlock = false;
|
||||
}
|
||||
return {
|
||||
type: this.options.sanitize
|
||||
? 'text'
|
||||
: 'html',
|
||||
type: 'html',
|
||||
raw: cap[0],
|
||||
inLink: this.lexer.state.inLink,
|
||||
inRawBlock: this.lexer.state.inRawBlock,
|
||||
block: false,
|
||||
text: this.options.sanitize
|
||||
? (this.options.sanitizer
|
||||
? this.options.sanitizer(cap[0])
|
||||
: escape(cap[0]))
|
||||
: cap[0]
|
||||
text: cap[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -937,12 +831,12 @@
|
||||
};
|
||||
}
|
||||
}
|
||||
autolink(src, mangle) {
|
||||
autolink(src) {
|
||||
const cap = this.rules.inline.autolink.exec(src);
|
||||
if (cap) {
|
||||
let text, href;
|
||||
if (cap[2] === '@') {
|
||||
text = escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
|
||||
text = escape(cap[1]);
|
||||
href = 'mailto:' + text;
|
||||
}
|
||||
else {
|
||||
@ -964,12 +858,12 @@
|
||||
};
|
||||
}
|
||||
}
|
||||
url(src, mangle) {
|
||||
url(src) {
|
||||
let cap;
|
||||
if (cap = this.rules.inline.url.exec(src)) {
|
||||
let text, href;
|
||||
if (cap[2] === '@') {
|
||||
text = escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
|
||||
text = escape(cap[0]);
|
||||
href = 'mailto:' + text;
|
||||
}
|
||||
else {
|
||||
@ -1002,15 +896,15 @@
|
||||
};
|
||||
}
|
||||
}
|
||||
inlineText(src, smartypants) {
|
||||
inlineText(src) {
|
||||
const cap = this.rules.inline.text.exec(src);
|
||||
if (cap) {
|
||||
let text;
|
||||
if (this.lexer.state.inRawBlock) {
|
||||
text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0];
|
||||
text = cap[0];
|
||||
}
|
||||
else {
|
||||
text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
|
||||
text = escape(cap[0]);
|
||||
}
|
||||
return {
|
||||
type: 'text',
|
||||
@ -1298,39 +1192,6 @@
|
||||
.getRegex()
|
||||
};
|
||||
|
||||
/**
|
||||
* smartypants text replacement
|
||||
*/
|
||||
function smartypants(text) {
|
||||
return text
|
||||
// em-dashes
|
||||
.replace(/---/g, '\u2014')
|
||||
// en-dashes
|
||||
.replace(/--/g, '\u2013')
|
||||
// opening singles
|
||||
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
|
||||
// closing singles & apostrophes
|
||||
.replace(/'/g, '\u2019')
|
||||
// opening doubles
|
||||
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
|
||||
// closing doubles
|
||||
.replace(/"/g, '\u201d')
|
||||
// ellipses
|
||||
.replace(/\.{3}/g, '\u2026');
|
||||
}
|
||||
/**
|
||||
* mangle email addresses
|
||||
*/
|
||||
function mangle(text) {
|
||||
let out = '';
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const ch = Math.random() > 0.5
|
||||
? 'x' + text.charCodeAt(i).toString(16)
|
||||
: text.charCodeAt(i).toString();
|
||||
out += '&#' + ch + ';';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
/**
|
||||
* Block Lexer
|
||||
*/
|
||||
@ -1704,13 +1565,13 @@
|
||||
continue;
|
||||
}
|
||||
// autolink
|
||||
if (token = this.tokenizer.autolink(src, mangle)) {
|
||||
if (token = this.tokenizer.autolink(src)) {
|
||||
src = src.substring(token.raw.length);
|
||||
tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
// url (gfm)
|
||||
if (!this.state.inLink && (token = this.tokenizer.url(src, mangle))) {
|
||||
if (!this.state.inLink && (token = this.tokenizer.url(src))) {
|
||||
src = src.substring(token.raw.length);
|
||||
tokens.push(token);
|
||||
continue;
|
||||
@ -1732,7 +1593,7 @@
|
||||
cutSrc = src.substring(0, startIndex + 1);
|
||||
}
|
||||
}
|
||||
if (token = this.tokenizer.inlineText(cutSrc, smartypants)) {
|
||||
if (token = this.tokenizer.inlineText(cutSrc)) {
|
||||
src = src.substring(token.raw.length);
|
||||
if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
|
||||
prevChar = token.raw.slice(-1);
|
||||
@ -1773,21 +1634,13 @@
|
||||
}
|
||||
code(code, infostring, escaped) {
|
||||
const lang = (infostring || '').match(/^\S*/)?.[0];
|
||||
if (this.options.highlight) {
|
||||
const out = this.options.highlight(code, lang);
|
||||
if (out != null && out !== code) {
|
||||
escaped = true;
|
||||
code = out;
|
||||
}
|
||||
}
|
||||
code = code.replace(/\n$/, '') + '\n';
|
||||
if (!lang) {
|
||||
return '<pre><code>'
|
||||
+ (escaped ? code : escape(code, true))
|
||||
+ '</code></pre>\n';
|
||||
}
|
||||
return '<pre><code class="'
|
||||
+ this.options.langPrefix
|
||||
return '<pre><code class="language-'
|
||||
+ escape(lang)
|
||||
+ '">'
|
||||
+ (escaped ? code : escape(code, true))
|
||||
@ -1799,16 +1652,12 @@
|
||||
html(html, block) {
|
||||
return html;
|
||||
}
|
||||
heading(text, level, raw, slugger) {
|
||||
if (this.options.headerIds) {
|
||||
const id = this.options.headerPrefix + slugger.slug(raw);
|
||||
return `<h${level} id="${id}">${text}</h${level}>\n`;
|
||||
}
|
||||
heading(text, level, raw) {
|
||||
// ignore IDs
|
||||
return `<h${level}>${text}</h${level}>\n`;
|
||||
}
|
||||
hr() {
|
||||
return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
|
||||
return '<hr>\n';
|
||||
}
|
||||
list(body, ordered, start) {
|
||||
const type = ordered ? 'ol' : 'ul';
|
||||
@ -1821,9 +1670,7 @@
|
||||
checkbox(checked) {
|
||||
return '<input '
|
||||
+ (checked ? 'checked="" ' : '')
|
||||
+ 'disabled="" type="checkbox"'
|
||||
+ (this.options.xhtml ? ' /' : '')
|
||||
+ '> ';
|
||||
+ 'disabled="" type="checkbox">';
|
||||
}
|
||||
paragraph(text) {
|
||||
return `<p>${text}</p>\n`;
|
||||
@ -1861,13 +1708,13 @@
|
||||
return `<code>${text}</code>`;
|
||||
}
|
||||
br() {
|
||||
return this.options.xhtml ? '<br/>' : '<br>';
|
||||
return '<br>';
|
||||
}
|
||||
del(text) {
|
||||
return `<del>${text}</del>`;
|
||||
}
|
||||
link(href, title, text) {
|
||||
const cleanHref = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
const cleanHref = cleanUrl(href);
|
||||
if (cleanHref === null) {
|
||||
return text;
|
||||
}
|
||||
@ -1880,7 +1727,7 @@
|
||||
return out;
|
||||
}
|
||||
image(href, title, text) {
|
||||
const cleanHref = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
const cleanHref = cleanUrl(href);
|
||||
if (cleanHref === null) {
|
||||
return text;
|
||||
}
|
||||
@ -1889,7 +1736,7 @@
|
||||
if (title) {
|
||||
out += ` title="${title}"`;
|
||||
}
|
||||
out += this.options.xhtml ? '/>' : '>';
|
||||
out += '>';
|
||||
return out;
|
||||
}
|
||||
text(text) {
|
||||
@ -1932,52 +1779,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Slugger generates header id
|
||||
*/
|
||||
class _Slugger {
|
||||
seen;
|
||||
constructor() {
|
||||
this.seen = {};
|
||||
}
|
||||
serialize(value) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
// remove html tags
|
||||
.replace(/<[!\/a-z].*?>/ig, '')
|
||||
// remove unwanted chars
|
||||
.replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
|
||||
.replace(/\s/g, '-');
|
||||
}
|
||||
/**
|
||||
* Finds the next safe (unique) slug to use
|
||||
*/
|
||||
getNextSafeSlug(originalSlug, isDryRun) {
|
||||
let slug = originalSlug;
|
||||
let occurenceAccumulator = 0;
|
||||
if (this.seen.hasOwnProperty(slug)) {
|
||||
occurenceAccumulator = this.seen[originalSlug];
|
||||
do {
|
||||
occurenceAccumulator++;
|
||||
slug = originalSlug + '-' + occurenceAccumulator;
|
||||
} while (this.seen.hasOwnProperty(slug));
|
||||
}
|
||||
if (!isDryRun) {
|
||||
this.seen[originalSlug] = occurenceAccumulator;
|
||||
this.seen[slug] = 0;
|
||||
}
|
||||
return slug;
|
||||
}
|
||||
/**
|
||||
* Convert string to unique id
|
||||
*/
|
||||
slug(value, options = {}) {
|
||||
const slug = this.serialize(value);
|
||||
return this.getNextSafeSlug(slug, options.dryrun);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsing & Compiling
|
||||
*/
|
||||
@ -1985,14 +1786,12 @@
|
||||
options;
|
||||
renderer;
|
||||
textRenderer;
|
||||
slugger;
|
||||
constructor(options) {
|
||||
this.options = options || exports.defaults;
|
||||
this.options.renderer = this.options.renderer || new _Renderer();
|
||||
this.renderer = this.options.renderer;
|
||||
this.renderer.options = this.options;
|
||||
this.textRenderer = new _TextRenderer();
|
||||
this.slugger = new _Slugger();
|
||||
}
|
||||
/**
|
||||
* Static Parse Method
|
||||
@ -2034,7 +1833,7 @@
|
||||
}
|
||||
case 'heading': {
|
||||
const headingToken = token;
|
||||
out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)), this.slugger);
|
||||
out += this.renderer.heading(this.parseInline(headingToken.tokens), headingToken.depth, unescape(this.parseInline(headingToken.tokens, this.textRenderer)));
|
||||
continue;
|
||||
}
|
||||
case 'code': {
|
||||
@ -2257,7 +2056,6 @@
|
||||
Lexer = _Lexer;
|
||||
lexer = _Lexer.lex;
|
||||
Tokenizer = _Tokenizer;
|
||||
Slugger = _Slugger;
|
||||
Hooks = _Hooks;
|
||||
constructor(...args) {
|
||||
this.use(...args);
|
||||
@ -2454,12 +2252,8 @@
|
||||
return this;
|
||||
}
|
||||
#parseMarkdown(lexer, parser) {
|
||||
return (src, optOrCallback, callback) => {
|
||||
if (typeof optOrCallback === 'function') {
|
||||
callback = optOrCallback;
|
||||
optOrCallback = null;
|
||||
}
|
||||
const origOpt = { ...optOrCallback };
|
||||
return (src, options) => {
|
||||
const origOpt = { ...options };
|
||||
const opt = { ...this.defaults, ...origOpt };
|
||||
// Show warning if an extension set async to true but the parse was called with async: false
|
||||
if (this.defaults.async === true && origOpt.async === false) {
|
||||
@ -2468,7 +2262,7 @@
|
||||
}
|
||||
opt.async = true;
|
||||
}
|
||||
const throwError = this.#onError(!!opt.silent, !!opt.async, callback);
|
||||
const throwError = this.#onError(!!opt.silent, !!opt.async);
|
||||
// throw error in case of non string input
|
||||
if (typeof src === 'undefined' || src === null) {
|
||||
return throwError(new Error('marked(): input parameter is undefined or null'));
|
||||
@ -2477,76 +2271,9 @@
|
||||
return throwError(new Error('marked(): input parameter is of type '
|
||||
+ Object.prototype.toString.call(src) + ', string expected'));
|
||||
}
|
||||
checkDeprecations(opt, callback);
|
||||
if (opt.hooks) {
|
||||
opt.hooks.options = opt;
|
||||
}
|
||||
if (callback) {
|
||||
const resultCallback = callback;
|
||||
const highlight = opt.highlight;
|
||||
let tokens;
|
||||
try {
|
||||
if (opt.hooks) {
|
||||
src = opt.hooks.preprocess(src);
|
||||
}
|
||||
tokens = lexer(src, opt);
|
||||
}
|
||||
catch (e) {
|
||||
return throwError(e);
|
||||
}
|
||||
const done = (err) => {
|
||||
let out;
|
||||
if (!err) {
|
||||
try {
|
||||
if (opt.walkTokens) {
|
||||
this.walkTokens(tokens, opt.walkTokens);
|
||||
}
|
||||
out = parser(tokens, opt);
|
||||
if (opt.hooks) {
|
||||
out = opt.hooks.postprocess(out);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
err = e;
|
||||
}
|
||||
}
|
||||
opt.highlight = highlight;
|
||||
return err
|
||||
? throwError(err)
|
||||
: resultCallback(null, out);
|
||||
};
|
||||
if (!highlight || highlight.length < 3) {
|
||||
return done();
|
||||
}
|
||||
delete opt.highlight;
|
||||
if (!tokens.length)
|
||||
return done();
|
||||
let pending = 0;
|
||||
this.walkTokens(tokens, (token) => {
|
||||
if (token.type === 'code') {
|
||||
pending++;
|
||||
setTimeout(() => {
|
||||
highlight(token.text, token.lang, (err, code) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (code != null && code !== token.text) {
|
||||
token.text = code;
|
||||
token.escaped = true;
|
||||
}
|
||||
pending--;
|
||||
if (pending === 0) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
if (pending === 0) {
|
||||
done();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (opt.async) {
|
||||
return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
|
||||
.then(src => lexer(src, opt))
|
||||
@ -2574,7 +2301,7 @@
|
||||
}
|
||||
};
|
||||
}
|
||||
#onError(silent, async, callback) {
|
||||
#onError(silent, async) {
|
||||
return (e) => {
|
||||
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
|
||||
if (silent) {
|
||||
@ -2584,27 +2311,19 @@
|
||||
if (async) {
|
||||
return Promise.resolve(msg);
|
||||
}
|
||||
if (callback) {
|
||||
callback(null, msg);
|
||||
return;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
if (async) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
if (callback) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const markedInstance = new Marked();
|
||||
function marked(src, opt, callback) {
|
||||
return markedInstance.parse(src, opt, callback);
|
||||
function marked(src, opt) {
|
||||
return markedInstance.parse(src, opt);
|
||||
}
|
||||
/**
|
||||
* Sets the default options.
|
||||
@ -2656,7 +2375,6 @@
|
||||
marked.Lexer = _Lexer;
|
||||
marked.lexer = _Lexer.lex;
|
||||
marked.Tokenizer = _Tokenizer;
|
||||
marked.Slugger = _Slugger;
|
||||
marked.Hooks = _Hooks;
|
||||
marked.parse = marked;
|
||||
const options = marked.options;
|
||||
@ -2673,7 +2391,6 @@
|
||||
exports.Marked = Marked;
|
||||
exports.Parser = _Parser;
|
||||
exports.Renderer = _Renderer;
|
||||
exports.Slugger = _Slugger;
|
||||
exports.TextRenderer = _TextRenderer;
|
||||
exports.Tokenizer = _Tokenizer;
|
||||
exports.getDefaults = _getDefaults;
|
||||
|
2
lib/marked.umd.js.map
generated
2
lib/marked.umd.js.map
generated
File diff suppressed because one or more lines are too long
19
man/marked.1
19
man/marked.1
@ -7,9 +7,7 @@ marked \- a javascript markdown parser
|
||||
.SH SYNOPSIS
|
||||
.B marked
|
||||
[\-o \fI<output>\fP] [\-i \fI<input>\fP] [\-s \fI<string>\fP] [\-\-help]
|
||||
[\-c \fI<config>\fP] [\-\-tokens] [\-\-pedantic] [\-\-gfm]
|
||||
[\-\-breaks] [\-\-sanitize]
|
||||
[\-\-smart\-lists] [\-\-lang\-prefix \fI<prefix>\fP]
|
||||
[\-c \fI<config>\fP] [\-\-tokens] [\-\-pedantic] [\-\-gfm] [\-\-breaks]
|
||||
[\-\-no\-etc...] [\-\-silent] [\fIfilename\fP]
|
||||
|
||||
.SH DESCRIPTION
|
||||
@ -56,21 +54,6 @@ Enable github flavored markdown.
|
||||
.BI \-\-breaks
|
||||
Enable GFM line breaks. Only works with the gfm option.
|
||||
.TP
|
||||
.BI \-\-sanitize
|
||||
Sanitize output. Ignore any HTML input.
|
||||
.TP
|
||||
.BI \-\-smart\-lists
|
||||
Use smarter list behavior than the original markdown.
|
||||
.TP
|
||||
.BI \-\-lang\-prefix\ [\fIprefix\fP]
|
||||
Set the prefix for code block classes.
|
||||
.TP
|
||||
.BI \-\-mangle
|
||||
Mangle email addresses.
|
||||
.TP
|
||||
.BI \-\-no\-sanitize,\ \-no-etc...
|
||||
The inverse of any of the marked options above.
|
||||
.TP
|
||||
.BI \-\-silent
|
||||
Silence error output.
|
||||
.TP
|
||||
|
@ -5,8 +5,7 @@ NAME
|
||||
|
||||
SYNOPSIS
|
||||
marked [-o <output>] [-i <input>] [-s <string>] [--help] [--tokens]
|
||||
[--pedantic] [--gfm] [--breaks] [--sanitize] [--smart-lists]
|
||||
[--lang-prefix <prefix>] [--no-etc...] [--silent] [filename]
|
||||
[--pedantic] [--gfm] [--breaks] [--no-etc...] [--silent] [filename]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@ -45,19 +44,7 @@ OPTIONS
|
||||
--breaks
|
||||
Enable GFM line breaks. Only works with the gfm option.
|
||||
|
||||
--sanitize
|
||||
Sanitize output. Ignore any HTML input.
|
||||
|
||||
--smart-lists
|
||||
Use smarter list behavior than the original markdown.
|
||||
|
||||
--lang-prefix [prefix]
|
||||
Set the prefix for code block classes.
|
||||
|
||||
--mangle
|
||||
Mangle email addresses.
|
||||
|
||||
--no-sanitize, -no-etc...
|
||||
--no-breaks, -no-etc...
|
||||
The inverse of any of the marked options above.
|
||||
|
||||
--silent
|
||||
|
11
marked.min.js
generated
vendored
11
marked.min.js
generated
vendored
File diff suppressed because one or more lines are too long
105
src/Instance.ts
105
src/Instance.ts
@ -5,15 +5,12 @@ import { _Hooks } from './Hooks.ts';
|
||||
import { _Renderer } from './Renderer.ts';
|
||||
import { _Tokenizer } from './Tokenizer.ts';
|
||||
import { _TextRenderer } from './TextRenderer.ts';
|
||||
import { _Slugger } from './Slugger.ts';
|
||||
import {
|
||||
checkDeprecations,
|
||||
escape
|
||||
} from './helpers.ts';
|
||||
import type { MarkedExtension, MarkedOptions } from './MarkedOptions.ts';
|
||||
import type { Token, Tokens, TokensList } from './Tokens.ts';
|
||||
|
||||
export type ResultCallback = (error: Error | null, parseResult?: string) => undefined | void;
|
||||
export type MaybePromise = void | Promise<void>;
|
||||
|
||||
type UnknownFunction = (...args: unknown[]) => unknown;
|
||||
@ -33,7 +30,6 @@ export class Marked {
|
||||
Lexer = _Lexer;
|
||||
lexer = _Lexer.lex;
|
||||
Tokenizer = _Tokenizer;
|
||||
Slugger = _Slugger;
|
||||
Hooks = _Hooks;
|
||||
|
||||
constructor(...args: MarkedExtension[]) {
|
||||
@ -237,13 +233,8 @@ export class Marked {
|
||||
}
|
||||
|
||||
#parseMarkdown(lexer: (src: string, options?: MarkedOptions) => TokensList | Token[], parser: (tokens: Token[], options?: MarkedOptions) => string) {
|
||||
return (src: string, optOrCallback?: MarkedOptions | ResultCallback | undefined | null, callback?: ResultCallback | undefined): string | Promise<string | undefined> | undefined => {
|
||||
if (typeof optOrCallback === 'function') {
|
||||
callback = optOrCallback;
|
||||
optOrCallback = null;
|
||||
}
|
||||
|
||||
const origOpt = { ...optOrCallback };
|
||||
return (src: string, options?: MarkedOptions | undefined | null): string | Promise<string> => {
|
||||
const origOpt = { ...options };
|
||||
const opt = { ...this.defaults, ...origOpt };
|
||||
|
||||
// Show warning if an extension set async to true but the parse was called with async: false
|
||||
@ -255,7 +246,7 @@ export class Marked {
|
||||
opt.async = true;
|
||||
}
|
||||
|
||||
const throwError = this.#onError(!!opt.silent, !!opt.async, callback);
|
||||
const throwError = this.#onError(!!opt.silent, !!opt.async);
|
||||
|
||||
// throw error in case of non string input
|
||||
if (typeof src === 'undefined' || src === null) {
|
||||
@ -266,88 +257,10 @@ export class Marked {
|
||||
+ Object.prototype.toString.call(src) + ', string expected'));
|
||||
}
|
||||
|
||||
checkDeprecations(opt, callback);
|
||||
|
||||
if (opt.hooks) {
|
||||
opt.hooks.options = opt;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
const resultCallback = callback;
|
||||
const highlight = opt.highlight;
|
||||
let tokens: TokensList | Token[];
|
||||
|
||||
try {
|
||||
if (opt.hooks) {
|
||||
src = opt.hooks.preprocess(src) as string;
|
||||
}
|
||||
tokens = lexer(src, opt);
|
||||
} catch (e) {
|
||||
return throwError(e as Error);
|
||||
}
|
||||
|
||||
const done = (err?: Error) => {
|
||||
let out;
|
||||
|
||||
if (!err) {
|
||||
try {
|
||||
if (opt.walkTokens) {
|
||||
this.walkTokens(tokens, opt.walkTokens);
|
||||
}
|
||||
out = parser(tokens, opt);
|
||||
if (opt.hooks) {
|
||||
out = opt.hooks.postprocess(out) as string;
|
||||
}
|
||||
} catch (e) {
|
||||
err = e as Error;
|
||||
}
|
||||
}
|
||||
|
||||
opt.highlight = highlight;
|
||||
|
||||
return err
|
||||
? throwError(err)
|
||||
: resultCallback(null, out) as undefined;
|
||||
};
|
||||
|
||||
if (!highlight || highlight.length < 3) {
|
||||
return done();
|
||||
}
|
||||
|
||||
delete opt.highlight;
|
||||
|
||||
if (!tokens.length) return done();
|
||||
|
||||
let pending = 0;
|
||||
this.walkTokens(tokens, (token) => {
|
||||
if (token.type === 'code') {
|
||||
pending++;
|
||||
setTimeout(() => {
|
||||
highlight(token.text, token.lang, (err, code) => {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (code != null && code !== token.text) {
|
||||
token.text = code;
|
||||
token.escaped = true;
|
||||
}
|
||||
|
||||
pending--;
|
||||
if (pending === 0) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
if (pending === 0) {
|
||||
done();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt.async) {
|
||||
return Promise.resolve(opt.hooks ? opt.hooks.preprocess(src) : src)
|
||||
.then(src => lexer(src, opt))
|
||||
@ -376,8 +289,8 @@ export class Marked {
|
||||
};
|
||||
}
|
||||
|
||||
#onError(silent: boolean, async: boolean, callback?: ResultCallback) {
|
||||
return (e: Error): string | Promise<string> | undefined => {
|
||||
#onError(silent: boolean, async: boolean) {
|
||||
return (e: Error): string | Promise<string> => {
|
||||
e.message += '\nPlease report this to https://github.com/markedjs/marked.';
|
||||
|
||||
if (silent) {
|
||||
@ -387,20 +300,12 @@ export class Marked {
|
||||
if (async) {
|
||||
return Promise.resolve(msg);
|
||||
}
|
||||
if (callback) {
|
||||
callback(null, msg);
|
||||
return;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
if (async) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
if (callback) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
};
|
||||
}
|
||||
|
43
src/Lexer.ts
43
src/Lexer.ts
@ -5,43 +5,6 @@ import type { Token, TokensList, Tokens } from './Tokens.ts';
|
||||
import type { MarkedOptions, TokenizerExtension } from './MarkedOptions.ts';
|
||||
import type { Rules } from './rules.ts';
|
||||
|
||||
/**
|
||||
* smartypants text replacement
|
||||
*/
|
||||
function smartypants(text: string) {
|
||||
return text
|
||||
// em-dashes
|
||||
.replace(/---/g, '\u2014')
|
||||
// en-dashes
|
||||
.replace(/--/g, '\u2013')
|
||||
// opening singles
|
||||
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
|
||||
// closing singles & apostrophes
|
||||
.replace(/'/g, '\u2019')
|
||||
// opening doubles
|
||||
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
|
||||
// closing doubles
|
||||
.replace(/"/g, '\u201d')
|
||||
// ellipses
|
||||
.replace(/\.{3}/g, '\u2026');
|
||||
}
|
||||
|
||||
/**
|
||||
* mangle email addresses
|
||||
*/
|
||||
function mangle(text: string) {
|
||||
let out = '';
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const ch = Math.random() > 0.5
|
||||
? 'x' + text.charCodeAt(i).toString(16)
|
||||
: text.charCodeAt(i).toString();
|
||||
out += '&#' + ch + ';';
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block Lexer
|
||||
*/
|
||||
@ -456,14 +419,14 @@ export class _Lexer {
|
||||
}
|
||||
|
||||
// autolink
|
||||
if (token = this.tokenizer.autolink(src, mangle)) {
|
||||
if (token = this.tokenizer.autolink(src)) {
|
||||
src = src.substring(token.raw.length);
|
||||
tokens.push(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
// url (gfm)
|
||||
if (!this.state.inLink && (token = this.tokenizer.url(src, mangle))) {
|
||||
if (!this.state.inLink && (token = this.tokenizer.url(src))) {
|
||||
src = src.substring(token.raw.length);
|
||||
tokens.push(token);
|
||||
continue;
|
||||
@ -484,7 +447,7 @@ export class _Lexer {
|
||||
cutSrc = src.substring(0, startIndex + 1);
|
||||
}
|
||||
}
|
||||
if (token = this.tokenizer.inlineText(cutSrc, smartypants)) {
|
||||
if (token = this.tokenizer.inlineText(cutSrc)) {
|
||||
src = src.substring(token.raw.length);
|
||||
if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
|
||||
prevChar = token.raw.slice(-1);
|
||||
|
@ -4,11 +4,6 @@ import type { _Lexer } from './Lexer.ts';
|
||||
import type { _Renderer } from './Renderer.ts';
|
||||
import type { _Tokenizer } from './Tokenizer.ts';
|
||||
|
||||
export interface SluggerOptions {
|
||||
/** Generates the next unique slug without updating the internal accumulator. */
|
||||
dryrun?: boolean;
|
||||
}
|
||||
|
||||
export interface TokenizerThis {
|
||||
lexer: _Lexer;
|
||||
}
|
||||
@ -54,12 +49,6 @@ export interface MarkedExtension {
|
||||
*/
|
||||
async?: boolean;
|
||||
|
||||
/**
|
||||
* A prefix URL for any relative link.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-base-url to prefix url for any relative link.
|
||||
*/
|
||||
baseUrl?: string | undefined | null;
|
||||
|
||||
/**
|
||||
* Enable GFM line breaks. This option requires the gfm option to be true.
|
||||
*/
|
||||
@ -77,27 +66,6 @@ export interface MarkedExtension {
|
||||
*/
|
||||
gfm?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Include an id attribute when emitting headings.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-gfm-heading-id to include an id attribute when emitting headings (h1, h2, h3, etc).
|
||||
*/
|
||||
headerIds?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Set the prefix for header tag ids.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-gfm-heading-id to add a string to prefix the id attribute when emitting headings (h1, h2, h3, etc).
|
||||
*/
|
||||
headerPrefix?: string | undefined;
|
||||
|
||||
/**
|
||||
* A function to highlight code blocks. The function can either be
|
||||
* synchronous (returning a string) or asynchronous (callback invoked
|
||||
* with an error if any occurred during highlighting and a string
|
||||
* if highlighting was successful)
|
||||
* @deprecated Deprecated in v5.0.0 use marked-highlight to add highlighting to code blocks.
|
||||
*/
|
||||
highlight?: ((code: string, lang: string | undefined, callback?: (error: Error, code?: string) => void) => string | void) | null;
|
||||
|
||||
/**
|
||||
* Hooks are methods that hook into some part of marked.
|
||||
* preprocess is called to process markdown before sending it to marked.
|
||||
@ -110,18 +78,6 @@ export interface MarkedExtension {
|
||||
options?: MarkedOptions
|
||||
} | null;
|
||||
|
||||
/**
|
||||
* Set the prefix for code block classes.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-highlight to prefix the className in a <code> block. Useful for syntax highlighting.
|
||||
*/
|
||||
langPrefix?: string | undefined;
|
||||
|
||||
/**
|
||||
* Mangle autolinks (<email@domain.com>).
|
||||
* @deprecated Deprecated in v5.0.0 use marked-mangle to mangle email addresses.
|
||||
*/
|
||||
mangle?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
|
||||
*/
|
||||
@ -134,34 +90,11 @@ export interface MarkedExtension {
|
||||
*/
|
||||
renderer?: RendererObject | undefined | null;
|
||||
|
||||
/**
|
||||
* Sanitize the output. Ignore any HTML that has been input. If true, sanitize the HTML passed into markdownString with the sanitizer function.
|
||||
* @deprecated Warning: This feature is deprecated and it should NOT be used as it cannot be considered secure. Instead use a sanitize library, like DOMPurify (recommended), sanitize-html or insane on the output HTML!
|
||||
*/
|
||||
sanitize?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Optionally sanitize found HTML with a sanitizer function.
|
||||
* @deprecated A function to sanitize the HTML passed into markdownString.
|
||||
*/
|
||||
sanitizer?: ((html: string) => string) | null;
|
||||
|
||||
/**
|
||||
* Shows an HTML error message when rendering fails.
|
||||
*/
|
||||
silent?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Use smarter list behavior than the original markdown. May eventually be default with the old behavior moved into pedantic.
|
||||
*/
|
||||
smartLists?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* Use "smart" typograhic punctuation for things like quotes and dashes.
|
||||
* @deprecated Deprecated in v5.0.0 use marked-smartypants to use "smart" typographic punctuation for things like quotes and dashes.
|
||||
*/
|
||||
smartypants?: boolean | undefined;
|
||||
|
||||
/**
|
||||
* The tokenizer defines how to turn markdown text into tokens.
|
||||
*/
|
||||
@ -174,11 +107,6 @@ export interface MarkedExtension {
|
||||
* The return value of the function is ignored.
|
||||
*/
|
||||
walkTokens?: ((token: Token) => void | Promise<void>) | undefined | null;
|
||||
/**
|
||||
* Generate closing slash for self-closing tags (<br/> instead of <br>)
|
||||
* @deprecated Deprecated in v5.0.0 use marked-xhtml to emit self-closing HTML tags for void elements (<br/>, <img/>, etc.) with a "/" as required by XHTML.
|
||||
*/
|
||||
xhtml?: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface MarkedOptions extends Omit<MarkedExtension, 'renderer' | 'tokenizer' | 'extensions' | 'walkTokens'> {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { _Renderer } from './Renderer.ts';
|
||||
import { _TextRenderer } from './TextRenderer.ts';
|
||||
import { _Slugger } from './Slugger.ts';
|
||||
import { _defaults } from './defaults.ts';
|
||||
import {
|
||||
unescape
|
||||
@ -15,14 +14,12 @@ export class _Parser {
|
||||
options: MarkedOptions;
|
||||
renderer: _Renderer;
|
||||
textRenderer: _TextRenderer;
|
||||
slugger: _Slugger;
|
||||
constructor(options?: MarkedOptions) {
|
||||
this.options = options || _defaults;
|
||||
this.options.renderer = this.options.renderer || new _Renderer();
|
||||
this.renderer = this.options.renderer;
|
||||
this.renderer.options = this.options;
|
||||
this.textRenderer = new _TextRenderer();
|
||||
this.slugger = new _Slugger();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,8 +70,7 @@ export class _Parser {
|
||||
out += this.renderer.heading(
|
||||
this.parseInline(headingToken.tokens),
|
||||
headingToken.depth,
|
||||
unescape(this.parseInline(headingToken.tokens, this.textRenderer)),
|
||||
this.slugger);
|
||||
unescape(this.parseInline(headingToken.tokens, this.textRenderer)));
|
||||
continue;
|
||||
}
|
||||
case 'code': {
|
||||
|
@ -4,7 +4,6 @@ import {
|
||||
escape
|
||||
} from './helpers.ts';
|
||||
import type { MarkedOptions } from './MarkedOptions.ts';
|
||||
import type { _Slugger } from './Slugger.ts';
|
||||
|
||||
/**
|
||||
* Renderer
|
||||
@ -17,13 +16,6 @@ export class _Renderer {
|
||||
|
||||
code(code: string, infostring: string | undefined, escaped: boolean): string {
|
||||
const lang = (infostring || '').match(/^\S*/)?.[0];
|
||||
if (this.options.highlight) {
|
||||
const out = this.options.highlight(code, lang);
|
||||
if (out != null && out !== code) {
|
||||
escaped = true;
|
||||
code = out;
|
||||
}
|
||||
}
|
||||
|
||||
code = code.replace(/\n$/, '') + '\n';
|
||||
|
||||
@ -33,8 +25,7 @@ export class _Renderer {
|
||||
+ '</code></pre>\n';
|
||||
}
|
||||
|
||||
return '<pre><code class="'
|
||||
+ this.options.langPrefix
|
||||
return '<pre><code class="language-'
|
||||
+ escape(lang)
|
||||
+ '">'
|
||||
+ (escaped ? code : escape(code, true))
|
||||
@ -49,18 +40,13 @@ export class _Renderer {
|
||||
return html;
|
||||
}
|
||||
|
||||
heading(text: string, level: number, raw: string, slugger: _Slugger): string {
|
||||
if (this.options.headerIds) {
|
||||
const id = this.options.headerPrefix + slugger.slug(raw);
|
||||
return `<h${level} id="${id}">${text}</h${level}>\n`;
|
||||
}
|
||||
|
||||
heading(text: string, level: number, raw: string): string {
|
||||
// ignore IDs
|
||||
return `<h${level}>${text}</h${level}>\n`;
|
||||
}
|
||||
|
||||
hr(): string {
|
||||
return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
|
||||
return '<hr>\n';
|
||||
}
|
||||
|
||||
list(body: string, ordered: boolean, start: number | ''): string {
|
||||
@ -76,9 +62,7 @@ export class _Renderer {
|
||||
checkbox(checked: boolean): string {
|
||||
return '<input '
|
||||
+ (checked ? 'checked="" ' : '')
|
||||
+ 'disabled="" type="checkbox"'
|
||||
+ (this.options.xhtml ? ' /' : '')
|
||||
+ '> ';
|
||||
+ 'disabled="" type="checkbox">';
|
||||
}
|
||||
|
||||
paragraph(text: string): string {
|
||||
@ -127,7 +111,7 @@ export class _Renderer {
|
||||
}
|
||||
|
||||
br(): string {
|
||||
return this.options.xhtml ? '<br/>' : '<br>';
|
||||
return '<br>';
|
||||
}
|
||||
|
||||
del(text: string): string {
|
||||
@ -135,7 +119,7 @@ export class _Renderer {
|
||||
}
|
||||
|
||||
link(href: string, title: string | null | undefined, text: string): string {
|
||||
const cleanHref = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
const cleanHref = cleanUrl(href);
|
||||
if (cleanHref === null) {
|
||||
return text;
|
||||
}
|
||||
@ -149,7 +133,7 @@ export class _Renderer {
|
||||
}
|
||||
|
||||
image(href: string, title: string | null, text: string): string {
|
||||
const cleanHref = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
|
||||
const cleanHref = cleanUrl(href);
|
||||
if (cleanHref === null) {
|
||||
return text;
|
||||
}
|
||||
@ -159,7 +143,7 @@ export class _Renderer {
|
||||
if (title) {
|
||||
out += ` title="${title}"`;
|
||||
}
|
||||
out += this.options.xhtml ? '/>' : '>';
|
||||
out += '>';
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -1,51 +0,0 @@
|
||||
import type { SluggerOptions } from './MarkedOptions.ts';
|
||||
|
||||
/**
|
||||
* Slugger generates header id
|
||||
*/
|
||||
export class _Slugger {
|
||||
seen: { [slugValue: string]: number };
|
||||
|
||||
constructor() {
|
||||
this.seen = {};
|
||||
}
|
||||
|
||||
serialize(value: string) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
// remove html tags
|
||||
.replace(/<[!\/a-z].*?>/ig, '')
|
||||
// remove unwanted chars
|
||||
.replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
|
||||
.replace(/\s/g, '-');
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the next safe (unique) slug to use
|
||||
*/
|
||||
getNextSafeSlug(originalSlug: string, isDryRun: boolean | undefined) {
|
||||
let slug = originalSlug;
|
||||
let occurenceAccumulator = 0;
|
||||
if (this.seen.hasOwnProperty(slug)) {
|
||||
occurenceAccumulator = this.seen[originalSlug];
|
||||
do {
|
||||
occurenceAccumulator++;
|
||||
slug = originalSlug + '-' + occurenceAccumulator;
|
||||
} while (this.seen.hasOwnProperty(slug));
|
||||
}
|
||||
if (!isDryRun) {
|
||||
this.seen[originalSlug] = occurenceAccumulator;
|
||||
this.seen[slug] = 0;
|
||||
}
|
||||
return slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to unique id
|
||||
*/
|
||||
slug(value: string, options: SluggerOptions = {}) {
|
||||
const slug = this.serialize(value);
|
||||
return this.getNextSafeSlug(slug, options.dryrun);
|
||||
}
|
||||
}
|
@ -366,24 +366,16 @@ export class _Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
html(src: string): Tokens.HTML | Tokens.Paragraph | undefined {
|
||||
html(src: string): Tokens.HTML | undefined {
|
||||
const cap = this.rules.block.html.exec(src);
|
||||
if (cap) {
|
||||
const token: Tokens.HTML | Tokens.Paragraph = {
|
||||
const token: Tokens.HTML = {
|
||||
type: 'html',
|
||||
block: true,
|
||||
raw: cap[0],
|
||||
pre: !this.options.sanitizer
|
||||
&& (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
|
||||
pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
|
||||
text: cap[0]
|
||||
};
|
||||
if (this.options.sanitize) {
|
||||
const text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]);
|
||||
const paragraph = token as unknown as Tokens.Paragraph;
|
||||
paragraph.type = 'paragraph';
|
||||
paragraph.text = text;
|
||||
paragraph.tokens = this.lexer.inline(text);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@ -530,18 +522,12 @@ export class _Tokenizer {
|
||||
}
|
||||
|
||||
return {
|
||||
type: this.options.sanitize
|
||||
? 'text'
|
||||
: 'html',
|
||||
type: 'html',
|
||||
raw: cap[0],
|
||||
inLink: this.lexer.state.inLink,
|
||||
inRawBlock: this.lexer.state.inRawBlock,
|
||||
block: false,
|
||||
text: this.options.sanitize
|
||||
? (this.options.sanitizer
|
||||
? this.options.sanitizer(cap[0])
|
||||
: escape(cap[0]))
|
||||
: cap[0]
|
||||
text: cap[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -729,12 +715,12 @@ export class _Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
autolink(src: string, mangle: (cap: string) => string): Tokens.Link | undefined {
|
||||
autolink(src: string): Tokens.Link | undefined {
|
||||
const cap = this.rules.inline.autolink.exec(src);
|
||||
if (cap) {
|
||||
let text, href;
|
||||
if (cap[2] === '@') {
|
||||
text = escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
|
||||
text = escape(cap[1]);
|
||||
href = 'mailto:' + text;
|
||||
} else {
|
||||
text = escape(cap[1]);
|
||||
@ -757,12 +743,12 @@ export class _Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
url(src: string, mangle: (cap: string) => string): Tokens.Link | undefined {
|
||||
url(src: string): Tokens.Link | undefined {
|
||||
let cap;
|
||||
if (cap = this.rules.inline.url.exec(src)) {
|
||||
let text, href;
|
||||
if (cap[2] === '@') {
|
||||
text = escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
|
||||
text = escape(cap[0]);
|
||||
href = 'mailto:' + text;
|
||||
} else {
|
||||
// do extended autolink path validation
|
||||
@ -794,14 +780,14 @@ export class _Tokenizer {
|
||||
}
|
||||
}
|
||||
|
||||
inlineText(src: string, smartypants: (cap: string) => string): Tokens.Text | undefined {
|
||||
inlineText(src: string): Tokens.Text | undefined {
|
||||
const cap = this.rules.inline.text.exec(src);
|
||||
if (cap) {
|
||||
let text;
|
||||
if (this.lexer.state.inRawBlock) {
|
||||
text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0];
|
||||
text = cap[0];
|
||||
} else {
|
||||
text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
|
||||
text = escape(cap[0]);
|
||||
}
|
||||
return {
|
||||
type: 'text',
|
||||
|
@ -6,25 +6,15 @@ import type { MarkedOptions } from './MarkedOptions.ts';
|
||||
export function _getDefaults(): MarkedOptions {
|
||||
return {
|
||||
async: false,
|
||||
baseUrl: null,
|
||||
breaks: false,
|
||||
extensions: null,
|
||||
gfm: true,
|
||||
headerIds: false,
|
||||
headerPrefix: '',
|
||||
highlight: null,
|
||||
hooks: null,
|
||||
langPrefix: 'language-',
|
||||
mangle: false,
|
||||
pedantic: false,
|
||||
renderer: null,
|
||||
sanitize: false,
|
||||
sanitizer: null,
|
||||
silent: false,
|
||||
smartypants: false,
|
||||
tokenizer: null,
|
||||
walkTokens: null,
|
||||
xhtml: false
|
||||
walkTokens: null
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import type { MarkedOptions } from './MarkedOptions.ts';
|
||||
import type { ResultCallback } from './Instance.ts';
|
||||
import type { Rule } from './rules.ts';
|
||||
|
||||
/**
|
||||
@ -67,26 +65,7 @@ export function edit(regex: Rule, opt?: string) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const nonWordAndColonTest = /[^\w:]/g;
|
||||
const originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
|
||||
|
||||
export function cleanUrl(sanitize: boolean | undefined, base: string | undefined | null, href: string) {
|
||||
if (sanitize) {
|
||||
let prot;
|
||||
try {
|
||||
prot = decodeURIComponent(unescape(href))
|
||||
.replace(nonWordAndColonTest, '')
|
||||
.toLowerCase();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (base && !originIndependentUrl.test(href)) {
|
||||
href = resolveUrl(base, href);
|
||||
}
|
||||
export function cleanUrl(href: string) {
|
||||
try {
|
||||
href = encodeURI(href).replace(/%25/g, '%');
|
||||
} catch (e) {
|
||||
@ -95,40 +74,6 @@ export function cleanUrl(sanitize: boolean | undefined, base: string | undefined
|
||||
return href;
|
||||
}
|
||||
|
||||
const baseUrls: Record<string, string> = {};
|
||||
const justDomain = /^[^:]+:\/*[^/]*$/;
|
||||
const protocol = /^([^:]+:)[\s\S]*$/;
|
||||
const domain = /^([^:]+:\/*[^/]*)[\s\S]*$/;
|
||||
|
||||
export function resolveUrl(base: string, href: string) {
|
||||
if (!baseUrls[' ' + base]) {
|
||||
// we can ignore everything in base after the last slash of its path component,
|
||||
// but we might need to add _that_
|
||||
// https://tools.ietf.org/html/rfc3986#section-3
|
||||
if (justDomain.test(base)) {
|
||||
baseUrls[' ' + base] = base + '/';
|
||||
} else {
|
||||
baseUrls[' ' + base] = rtrim(base, '/', true);
|
||||
}
|
||||
}
|
||||
base = baseUrls[' ' + base];
|
||||
const relativeBase = base.indexOf(':') === -1;
|
||||
|
||||
if (href.substring(0, 2) === '//') {
|
||||
if (relativeBase) {
|
||||
return href;
|
||||
}
|
||||
return base.replace(protocol, '$1') + href;
|
||||
} else if (href.charAt(0) === '/') {
|
||||
if (relativeBase) {
|
||||
return href;
|
||||
}
|
||||
return base.replace(domain, '$1') + href;
|
||||
} else {
|
||||
return base + href;
|
||||
}
|
||||
}
|
||||
|
||||
export const noopTest = { exec: () => null };
|
||||
|
||||
export function splitCells(tableRow: string, count?: number) {
|
||||
@ -225,41 +170,3 @@ export function findClosingBracket(str: string, b: string) {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function checkDeprecations(opt: MarkedOptions, callback?: ResultCallback) {
|
||||
if (!opt || opt.silent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
console.warn('marked(): callback is deprecated since version 5.0.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/using_pro#async');
|
||||
}
|
||||
|
||||
if (opt.sanitize || opt.sanitizer) {
|
||||
console.warn('marked(): sanitize and sanitizer parameters are deprecated since version 0.7.0, should not be used and will be removed in the future. Read more here: https://marked.js.org/#/USING_ADVANCED.md#options');
|
||||
}
|
||||
|
||||
if (opt.highlight || opt.langPrefix !== 'language-') {
|
||||
console.warn('marked(): highlight and langPrefix parameters are deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-highlight.');
|
||||
}
|
||||
|
||||
if (opt.mangle) {
|
||||
console.warn('marked(): mangle parameter is enabled by default, but is deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-mangle, or disable by setting `{mangle: false}`.');
|
||||
}
|
||||
|
||||
if (opt.baseUrl) {
|
||||
console.warn('marked(): baseUrl parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-base-url.');
|
||||
}
|
||||
|
||||
if (opt.smartypants) {
|
||||
console.warn('marked(): smartypants parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-smartypants.');
|
||||
}
|
||||
|
||||
if (opt.xhtml) {
|
||||
console.warn('marked(): xhtml parameter is deprecated since version 5.0.0, should not be used and will be removed in the future. Instead use https://www.npmjs.com/package/marked-xhtml.');
|
||||
}
|
||||
|
||||
if (opt.headerIds || opt.headerPrefix) {
|
||||
console.warn('marked(): headerIds and headerPrefix parameters enabled by default, but are deprecated since version 5.0.0, and will be removed in the future. To clear this warning, install https://www.npmjs.com/package/marked-gfm-heading-id, or disable by setting `{headerIds: false}`.');
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import { _Parser } from './Parser.ts';
|
||||
import { _Tokenizer } from './Tokenizer.ts';
|
||||
import { _Renderer } from './Renderer.ts';
|
||||
import { _TextRenderer } from './TextRenderer.ts';
|
||||
import { _Slugger } from './Slugger.ts';
|
||||
import { _Hooks } from './Hooks.ts';
|
||||
import { Marked } from './Instance.ts';
|
||||
import {
|
||||
@ -13,7 +12,7 @@ import {
|
||||
} from './defaults.ts';
|
||||
import type { MarkedExtension, MarkedOptions } from './MarkedOptions.ts';
|
||||
import type { Token, TokensList } from './Tokens.ts';
|
||||
import type { ResultCallback, MaybePromise } from './Instance.ts';
|
||||
import type { MaybePromise } from './Instance.ts';
|
||||
|
||||
const markedInstance = new Marked();
|
||||
|
||||
@ -34,29 +33,8 @@ export function marked(src: string, options: MarkedOptions & { async: true }): P
|
||||
* @return String of compiled HTML
|
||||
*/
|
||||
export function marked(src: string, options?: MarkedOptions): string;
|
||||
|
||||
/**
|
||||
* Compiles markdown to HTML asynchronously with a callback.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
*/
|
||||
export function marked(src: string, callback: ResultCallback): void;
|
||||
|
||||
/**
|
||||
* Compiles markdown to HTML asynchronously with a callback.
|
||||
*
|
||||
* @param src String of markdown source to be compiled
|
||||
* @param options Hash of options
|
||||
* @param callback Function called when the markdownString has been fully parsed when using async highlighting
|
||||
*/
|
||||
export function marked(
|
||||
src: string,
|
||||
options: MarkedOptions,
|
||||
callback: ResultCallback,
|
||||
): void;
|
||||
export function marked(src: string, opt?: MarkedOptions | ResultCallback, callback?: ResultCallback): string | Promise<string | undefined> | undefined {
|
||||
return markedInstance.parse(src, opt, callback);
|
||||
export function marked(src: string, opt?: MarkedOptions): string | Promise<string> {
|
||||
return markedInstance.parse(src, opt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +95,6 @@ marked.TextRenderer = _TextRenderer;
|
||||
marked.Lexer = _Lexer;
|
||||
marked.lexer = _Lexer.lex;
|
||||
marked.Tokenizer = _Tokenizer;
|
||||
marked.Slugger = _Slugger;
|
||||
marked.Hooks = _Hooks;
|
||||
marked.parse = marked;
|
||||
|
||||
@ -135,7 +112,6 @@ export { _Parser as Parser } from './Parser.ts';
|
||||
export { _Tokenizer as Tokenizer } from './Tokenizer.ts';
|
||||
export { _Renderer as Renderer } from './Renderer.ts';
|
||||
export { _TextRenderer as TextRenderer } from './TextRenderer.ts';
|
||||
export { _Slugger as Slugger } from './Slugger.ts';
|
||||
export { _Hooks as Hooks } from './Hooks.ts';
|
||||
export { Marked } from './Instance.ts';
|
||||
export type * from './MarkedOptions.ts';
|
||||
|
21
test/bench.js
vendored
21
test/bench.js
vendored
@ -35,12 +35,9 @@ export async function runBench(options) {
|
||||
|
||||
// Non-GFM, Non-pedantic
|
||||
cjsMarked.setOptions({
|
||||
headerIds: false,
|
||||
mangle: false,
|
||||
gfm: false,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: false
|
||||
pedantic: false
|
||||
});
|
||||
if (options.marked) {
|
||||
cjsMarked.setOptions(options.marked);
|
||||
@ -48,29 +45,15 @@ export async function runBench(options) {
|
||||
tests['cjs marked'] = cjsMarked.parse;
|
||||
|
||||
esmMarked.setOptions({
|
||||
headerIds: false,
|
||||
mangle: false,
|
||||
gfm: false,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: false
|
||||
pedantic: false
|
||||
});
|
||||
if (options.marked) {
|
||||
esmMarked.setOptions(options.marked);
|
||||
}
|
||||
tests['esm marked'] = esmMarked.parse;
|
||||
|
||||
// esmMarked.setOptions({
|
||||
// gfm: true,
|
||||
// breaks: false,
|
||||
// pedantic: false,
|
||||
// sanitize: false
|
||||
// });
|
||||
// if (options.marked) {
|
||||
// esmMarked.setOptions(options.marked);
|
||||
// }
|
||||
// tests['esm marked (gfm)'] = esmMarked.parse;
|
||||
|
||||
try {
|
||||
tests.commonmark = (await (async() => {
|
||||
const { Parser, HtmlRenderer } = await import('commonmark');
|
||||
|
5
test/recheck.js
vendored
5
test/recheck.js
vendored
@ -28,11 +28,6 @@ function checkRegexp(obj, name) {
|
||||
console.log(`
|
||||
import { marked } from './src/marked.js';
|
||||
|
||||
marked.use({
|
||||
mangle: false,
|
||||
headerIds: false
|
||||
});
|
||||
|
||||
const start = Date.now();
|
||||
`);
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
<h3 id="heading-with-html">Heading with <em>html</em></h3>
|
||||
|
||||
<h3 id="heading-with-a-link">Heading with a <a href="http://github.com/">link</a></h3>
|
||||
|
||||
<h3 id="heading-with-some-italic-text">Heading with some <em>italic text</em></h3>
|
||||
|
||||
<h3 id="or-some-strong">Or some <strong>strong</strong></h3>
|
||||
|
||||
<p>(which doesn't really make any difference, here)</p>
|
||||
|
||||
<h3 id="or-even-code">Or even <code>code</code></h3>
|
||||
|
||||
<h3 id="what-about-strikethrough">What about <del>strikethrough</del></h3>
|
||||
|
||||
<h2 id="and-a-ref-link">And a ref <a href="/some/url" title="link to nowhere">link</a></h2>
|
@ -1,19 +0,0 @@
|
||||
---
|
||||
headerIds: true
|
||||
---
|
||||
### Heading with <em>html</em>
|
||||
|
||||
### Heading with a [link](http://github.com/)
|
||||
|
||||
### Heading with some _italic text_
|
||||
|
||||
### Or some **strong**
|
||||
(which doesn't really make any difference, here)
|
||||
|
||||
### Or even `code`
|
||||
|
||||
### What about ~~strikethrough~~
|
||||
|
||||
## And a ref [link][destination]
|
||||
|
||||
[destination]: /some/url "link to nowhere"
|
@ -1,5 +0,0 @@
|
||||
<p>Image</p>
|
||||
<p>Image</p>
|
||||
<p>Image</p>
|
||||
<p>Image</p>
|
||||
<p>Image</p>
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
sanitize: true
|
||||
---
|
||||

|
||||
|
||||

|
||||
|
||||
)
|
||||
|
||||
)
|
||||
|
||||

|
@ -1,6 +1,3 @@
|
||||
---
|
||||
headerIds: false
|
||||
---
|
||||
## *list
|
||||
|
||||
* list1
|
||||
@ -19,7 +16,7 @@ headerIds: false
|
||||
-Not list(without space)
|
||||
- list2
|
||||
|
||||
## number(1.)list
|
||||
## number(1.)list
|
||||
1. list
|
||||
1.Notlist(without space)
|
||||
1. list
|
||||
|
@ -1,3 +0,0 @@
|
||||
<p><<svg/onload="alert(1)"//@x></p>
|
||||
|
||||
<p><bar"onclick="alert('XSS')"@foo></p>
|
@ -1,7 +0,0 @@
|
||||
---
|
||||
sanatize: true
|
||||
mangle: false
|
||||
---
|
||||
<<svg/onload="alert(1)"//@x>
|
||||
|
||||
<bar"onclick="alert('XSS')"@foo>
|
@ -1,35 +0,0 @@
|
||||
<h1>Absolutization of RFC 3986 URIs</h1>
|
||||
|
||||
<h2>Absolute URI</h2>
|
||||
|
||||
<p><a href="http://example.com/"><img src="http://example.com/logo" alt="section 4.3"></a></p>
|
||||
|
||||
<h2>Network-path reference</h2>
|
||||
|
||||
<p><a href="//example.com/"><img src="//example.com/logo" alt="section 4.2"></a></p>
|
||||
|
||||
<h2>Absolute path</h2>
|
||||
|
||||
<p><a href="/path/to/content"><img src="/path/to/img" alt="section 4.2"></a></p>
|
||||
|
||||
<h2>Relative path</h2>
|
||||
|
||||
<p><a href="/base/content"><img src="/base/img" alt="section 4.2"></a></p>
|
||||
|
||||
<h2>Dot-relative path</h2>
|
||||
|
||||
<p><a href="/base/./content"><img src="/base/./img" alt="section 3.3"></a></p>
|
||||
|
||||
<p><a href="/base/../content"><img src="/base/../img" alt="section 3.3"></a></p>
|
||||
|
||||
<h2>Same-document query</h2>
|
||||
|
||||
<p><a href="?"><img src="?type=image" alt="section 4.4"></a></p>
|
||||
|
||||
<h2>Same-document fragment</h2>
|
||||
|
||||
<p><a href="#"><img src="#img" alt="section 4.4"></a></p>
|
||||
|
||||
<h2>Empty</h2>
|
||||
|
||||
<p><a href="">section 4.2</a></p>
|
@ -1,30 +0,0 @@
|
||||
---
|
||||
baseUrl: "/base/"
|
||||
---
|
||||
# Absolutization of RFC 3986 URIs
|
||||
|
||||
## Absolute URI
|
||||
[](http://example.com/)
|
||||
|
||||
## Network-path reference
|
||||
[](//example.com/)
|
||||
|
||||
## Absolute path
|
||||
[](/path/to/content)
|
||||
|
||||
## Relative path
|
||||
[](content)
|
||||
|
||||
## Dot-relative path
|
||||
[](./content)
|
||||
|
||||
[](../content)
|
||||
|
||||
## Same-document query
|
||||
[](?)
|
||||
|
||||
## Same-document fragment
|
||||
[](#)
|
||||
|
||||
## Empty
|
||||
[section 4.2]()
|
@ -1,35 +0,0 @@
|
||||
<h1>Absolutization of RFC 3986 URIs</h1>
|
||||
|
||||
<h2>Absolute URI</h2>
|
||||
|
||||
<p><a href="http://example.com/"><img src="http://example.com/logo" alt="section 4.3"></a></p>
|
||||
|
||||
<h2>Network-path reference</h2>
|
||||
|
||||
<p><a href="http://example.com/"><img src="http://example.com/logo" alt="section 4.2"></a></p>
|
||||
|
||||
<h2>Absolute path</h2>
|
||||
|
||||
<p><a href="http://example.com/path/to/content"><img src="http://example.com/path/to/img" alt="section 4.2"></a></p>
|
||||
|
||||
<h2>Relative path</h2>
|
||||
|
||||
<p><a href="http://example.com/base/content"><img src="http://example.com/base/img" alt="section 4.2"></a></p>
|
||||
|
||||
<h2>Dot-relative path</h2>
|
||||
|
||||
<p><a href="http://example.com/base/./content"><img src="http://example.com/base/./img" alt="section 3.3"></a></p>
|
||||
|
||||
<p><a href="http://example.com/base/../content"><img src="http://example.com/base/../img" alt="section 3.3"></a></p>
|
||||
|
||||
<h2>Same-document query</h2>
|
||||
|
||||
<p><a href="?"><img src="?type=image" alt="section 4.4"></a></p>
|
||||
|
||||
<h2>Same-document fragment</h2>
|
||||
|
||||
<p><a href="#"><img src="#img" alt="section 4.4"></a></p>
|
||||
|
||||
<h2>Empty</h2>
|
||||
|
||||
<p><a href="">section 4.2</a></p>
|
@ -1,30 +0,0 @@
|
||||
---
|
||||
baseUrl: "http://example.com/base/"
|
||||
---
|
||||
# Absolutization of RFC 3986 URIs
|
||||
|
||||
## Absolute URI
|
||||
[](http://example.com/)
|
||||
|
||||
## Network-path reference
|
||||
[](//example.com/)
|
||||
|
||||
## Absolute path
|
||||
[](/path/to/content)
|
||||
|
||||
## Relative path
|
||||
[](content)
|
||||
|
||||
## Dot-relative path
|
||||
[](./content)
|
||||
|
||||
[](../content)
|
||||
|
||||
## Same-document query
|
||||
[](?)
|
||||
|
||||
## Same-document fragment
|
||||
[](#)
|
||||
|
||||
## Empty
|
||||
[section 4.2]()
|
@ -1,5 +0,0 @@
|
||||
<p>URL</p>
|
||||
<p>URL</p>
|
||||
<p>URL</p>
|
||||
<p>URL</p>
|
||||
<p>URL</p>
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
sanitize: true
|
||||
---
|
||||
[URL](javascript:alert)
|
||||
|
||||
[URL](vbscript:alert)
|
||||
|
||||
[URL](javascript:alert(1))
|
||||
|
||||
[URL](javascript:document;alert(1))
|
||||
|
||||
[URL](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)
|
@ -1,6 +0,0 @@
|
||||
<p>Hello world ‘how’ “are” you – today…</p>
|
||||
|
||||
<p>“It’s a more ‘challenging’ smartypants test…”</p>
|
||||
|
||||
<p>‘And,’ as a bonus — “one
|
||||
multiline” test!</p>
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
smartypants: true
|
||||
---
|
||||
Hello world 'how' "are" you -- today...
|
||||
|
||||
"It's a more 'challenging' smartypants test..."
|
||||
|
||||
'And,' as a bonus --- "one
|
||||
multiline" test!
|
@ -1,11 +0,0 @@
|
||||
<pre>&</pre>
|
||||
<p><code>--foo</code>
|
||||
<kbd>---foo</kbd></p>
|
||||
<script>--foo</script>
|
||||
|
||||
<p>Ensure that text such as custom tags that happen to
|
||||
begin with the same letters as the above tags don’t
|
||||
match and thus benefit from Smartypants-ing.</p>
|
||||
|
||||
<p><script-custom>–foo</script-custom>
|
||||
<code>--foo</code> <codebar –foo codebar></p>
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
smartypants: true
|
||||
description: SmartyPants does not modify characters within <pre>, <code>, <kbd>, or <script> tag blocks.
|
||||
spec: https://daringfireball.net/projects/smartypants/
|
||||
---
|
||||
<pre>&</pre>
|
||||
<code>--foo</code>
|
||||
<kbd>---foo</kbd>
|
||||
<script>--foo</script>
|
||||
|
||||
Ensure that text such as custom tags that happen to
|
||||
begin with the same letters as the above tags don't
|
||||
match and thus benefit from Smartypants-ing.
|
||||
<script-custom>--foo</script-custom>
|
||||
`--foo` <codebar --foo codebar>
|
@ -1,2 +0,0 @@
|
||||
<p>lowerclick melower
|
||||
upperclick meupper</p>
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
sanitize: true
|
||||
---
|
||||
lower[click me](javascript:...)lower
|
||||
upper[click me](javascript:...)upper
|
@ -1,4 +1,4 @@
|
||||
<h1 id="markdown-basics">Markdown: Basics</h1>
|
||||
<h1>Markdown: Basics</h1>
|
||||
|
||||
<ul id="ProjectSubmenu">
|
||||
<li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
|
||||
@ -8,7 +8,7 @@
|
||||
<li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="getting-the-gist-of-markdowns-formatting-syntax">Getting the Gist of Markdown's Formatting Syntax</h2>
|
||||
<h2>Getting the Gist of Markdown's Formatting Syntax</h2>
|
||||
|
||||
<p>This page offers a brief overview of what it's like to use Markdown.
|
||||
The <a href="/projects/markdown/syntax" title="Markdown Syntax">syntax page</a> provides complete, detailed documentation for
|
||||
@ -24,7 +24,7 @@ and translate it to XHTML.</p>
|
||||
<p><strong>Note:</strong> This document is itself written using Markdown; you
|
||||
can <a href="/projects/markdown/basics.text">see the source for it by adding '.text' to the URL</a>.</p>
|
||||
|
||||
<h2 id="paragraphs-headers-blockquotes">Paragraphs, Headers, Blockquotes</h2>
|
||||
<h2>Paragraphs, Headers, Blockquotes</h2>
|
||||
|
||||
<p>A paragraph is simply one or more consecutive lines of text, separated
|
||||
by one or more blank lines. (A blank line is any line that looks like a
|
||||
@ -58,7 +58,7 @@ dog's back.
|
||||
### Header 3
|
||||
|
||||
> This is a blockquote.
|
||||
>
|
||||
>
|
||||
> This is the second paragraph in the blockquote.
|
||||
>
|
||||
> ## This is an H2 in a blockquote
|
||||
@ -88,7 +88,7 @@ dog's back.</p>
|
||||
</blockquote>
|
||||
</code></pre>
|
||||
|
||||
<h3 id="phrase-emphasis">Phrase Emphasis</h3>
|
||||
<h3>Phrase Emphasis</h3>
|
||||
|
||||
<p>Markdown uses asterisks and underscores to indicate spans of emphasis.</p>
|
||||
|
||||
@ -110,7 +110,7 @@ Some of these words <em>are emphasized also</em>.</p>
|
||||
Or, if you prefer, <strong>use two underscores instead</strong>.</p>
|
||||
</code></pre>
|
||||
|
||||
<h2 id="lists">Lists</h2>
|
||||
<h2>Lists</h2>
|
||||
|
||||
<p>Unordered (bulleted) lists use asterisks, pluses, and hyphens (<code>*</code>,
|
||||
<code>+</code>, and <code>-</code>) as list markers. These three markers are
|
||||
@ -181,7 +181,7 @@ the paragraphs by 4 spaces or 1 tab:</p>
|
||||
</ul>
|
||||
</code></pre>
|
||||
|
||||
<h3 id="links">Links</h3>
|
||||
<h3>Links</h3>
|
||||
|
||||
<p>Markdown supports two styles for creating links: <em>inline</em> and
|
||||
<em>reference</em>. With both styles, you use square brackets to delimit the
|
||||
@ -244,7 +244,7 @@ numbers and spaces, but are <em>not</em> case sensitive:</p>
|
||||
<a href="http://www.nytimes.com/">The New York Times</a>.</p>
|
||||
</code></pre>
|
||||
|
||||
<h3 id="images">Images</h3>
|
||||
<h3>Images</h3>
|
||||
|
||||
<p>Image syntax is very much like link syntax.</p>
|
||||
|
||||
@ -265,7 +265,7 @@ numbers and spaces, but are <em>not</em> case sensitive:</p>
|
||||
<pre><code><img src="/path/to/img.jpg" alt="alt text" title="Title" />
|
||||
</code></pre>
|
||||
|
||||
<h3 id="code">Code</h3>
|
||||
<h3>Code</h3>
|
||||
|
||||
<p>In a regular paragraph, you can create code span by wrapping text in
|
||||
backtick quotes. Any ampersands (<code>&</code>) and angle brackets (<code><</code> or
|
||||
|
@ -1,6 +1,5 @@
|
||||
---
|
||||
pedantic: true
|
||||
headerIds: true
|
||||
---
|
||||
|
||||
Markdown: Basics
|
||||
|
@ -1,6 +1,5 @@
|
||||
---
|
||||
pedantic: true
|
||||
headerIds: false
|
||||
---
|
||||
|
||||
Markdown: Syntax
|
||||
|
@ -1,6 +1,3 @@
|
||||
---
|
||||
headerIds: false
|
||||
---
|
||||
## Unordered
|
||||
|
||||
Asterisks tight:
|
||||
|
@ -1,7 +1,4 @@
|
||||
module.exports = {
|
||||
markdown: `# #${' '.repeat(50000)}a`,
|
||||
html: '<h1># a</h1>',
|
||||
options: {
|
||||
headerIds: false
|
||||
}
|
||||
html: '<h1># a</h1>'
|
||||
};
|
||||
|
@ -24,11 +24,6 @@ function runSpecs(title, dir, showCompletionTable, options) {
|
||||
spec.options.silent = true;
|
||||
}
|
||||
|
||||
if (spec.options.sanitizer) {
|
||||
// eslint-disable-next-line no-eval
|
||||
spec.options.sanitizer = eval(spec.options.sanitizer);
|
||||
}
|
||||
|
||||
(spec.only ? fit : (spec.skip ? xit : it))('should ' + passFail + example, async() => {
|
||||
const before = process.hrtime();
|
||||
if (spec.shouldFail) {
|
||||
@ -50,9 +45,8 @@ function runSpecs(title, dir, showCompletionTable, options) {
|
||||
});
|
||||
}
|
||||
|
||||
runSpecs('GFM', './gfm', true, { gfm: true, pedantic: false, headerIds: false });
|
||||
runSpecs('CommonMark', './commonmark', true, { gfm: false, pedantic: false, headerIds: false });
|
||||
runSpecs('GFM', './gfm', true, { gfm: true, pedantic: false });
|
||||
runSpecs('CommonMark', './commonmark', true, { gfm: false, pedantic: false });
|
||||
runSpecs('Original', './original', false, { gfm: false, pedantic: true });
|
||||
runSpecs('New', './new');
|
||||
runSpecs('ReDOS', './redos');
|
||||
runSpecs('Security', './security');
|
||||
|
@ -1,6 +0,0 @@
|
||||
<p>AAA<script> <img <script> src=x onerror=alert(1) />BBB</p>
|
||||
|
||||
<p>AAA<sometag> <img <sometag> src=x onerror=alert(1)BBB</p>
|
||||
|
||||
<p><a>a2<a2t>a2</a> b <c>c</c> d</p>
|
||||
<h1><img src="URL" alt="text"></h1>
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
sanitize: true
|
||||
---
|
||||
AAA<script> <img <script> src=x onerror=alert(1) />BBB
|
||||
|
||||
AAA<sometag> <img <sometag> src=x onerror=alert(1)BBB
|
||||
|
||||
<a>a2<a2t>a2</a> b <c>c</c> d
|
||||
# 
|
@ -1,2 +0,0 @@
|
||||
<p>a2a2 b c d</p>
|
||||
<h1><img src="URL" alt="text"></h1>
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
sanitize: true
|
||||
sanitizer: () => ''
|
||||
---
|
||||
<a>a2<a2t>a2</a> b <c>c</c> d
|
||||
# 
|
@ -1 +0,0 @@
|
||||
<p>AAA</p>
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
sanitize: true
|
||||
sanitizer: () => ''
|
||||
---
|
||||
AAA<script> <img <script> src=x onerror=alert(1) />BBB
|
@ -1 +0,0 @@
|
||||
<p>AAA <img src=x onerror=alert(1)BBB</p>
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
sanitize: true
|
||||
sanitizer: () => ''
|
||||
---
|
||||
AAA<sometag> <img <sometag> src=x onerror=alert(1)BBB
|
@ -4,7 +4,7 @@ import { expectType } from 'ts-expect';
|
||||
|
||||
// other exports
|
||||
|
||||
import { Lexer, Parser, Tokenizer, Renderer, TextRenderer, Slugger } from 'marked';
|
||||
import { Lexer, Parser, Tokenizer, Renderer, TextRenderer } from 'marked';
|
||||
import type { Tokens, MarkedExtension, TokenizerAndRendererExtension, Token ,TokenizerExtension, MarkedOptions, TokensList, Rules, RendererExtension } from 'marked';
|
||||
|
||||
const tokenizer = new marked.Tokenizer();
|
||||
@ -31,18 +31,10 @@ tokenizer.inlineText = function inlineText(...args: Parameters<Tokenizer['inline
|
||||
};
|
||||
|
||||
let options: MarkedOptions = {
|
||||
baseUrl: '',
|
||||
gfm: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: true,
|
||||
smartLists: true,
|
||||
silent: false,
|
||||
highlight(code: string, lang: string | undefined) {
|
||||
return '';
|
||||
},
|
||||
langPrefix: 'lang-',
|
||||
smartypants: false,
|
||||
tokenizer,
|
||||
renderer: new marked.Renderer(),
|
||||
walkTokens: token => {
|
||||
@ -52,11 +44,6 @@ let options: MarkedOptions = {
|
||||
}
|
||||
};
|
||||
|
||||
options.highlight = (code: string, lang: string | undefined, callback?: (error: any, code?: string) => string | void) => {
|
||||
callback?.(new Error());
|
||||
callback?.(null, '');
|
||||
};
|
||||
|
||||
options = marked.getDefaults();
|
||||
options = marked.defaults;
|
||||
|
||||
@ -70,13 +57,9 @@ myOldMarked = marked.setOptions(options);
|
||||
|
||||
console.log(marked('1) I am using __markdown__.'));
|
||||
console.log(marked('2) I am using __markdown__.', options));
|
||||
marked('3) I am using __markdown__.', callback);
|
||||
marked('4) I am using __markdown__.', options, callback);
|
||||
|
||||
console.log(marked.parse('5) I am using __markdown__.'));
|
||||
console.log(marked.parse('6) I am using __markdown__.', options));
|
||||
marked.parse('7) I am using __markdown__.', callback);
|
||||
marked.parse('8) I am using __markdown__.', options, callback);
|
||||
|
||||
console.log(marked.parseInline('9) I am using __markdown__.'));
|
||||
console.log(marked.parseInline('10) I am using __markdown__.', options));
|
||||
@ -97,11 +80,11 @@ const re: Rules = marked.Lexer.rules;
|
||||
const lexerOptions: MarkedOptions = lexer.options;
|
||||
|
||||
const renderer = new marked.Renderer();
|
||||
renderer.heading = (text, level, raw, slugger) => {
|
||||
return text + level.toString() + slugger.slug(raw);
|
||||
renderer.heading = (text, level, raw) => {
|
||||
return text + level.toString();
|
||||
};
|
||||
renderer.hr = () => {
|
||||
return `<hr${renderer.options.xhtml ? '/' : ''}>\n`;
|
||||
return `<hr>\n`;
|
||||
};
|
||||
renderer.checkbox = checked => {
|
||||
return checked ? 'CHECKED' : 'UNCHECKED';
|
||||
@ -111,7 +94,7 @@ class ExtendedRenderer extends marked.Renderer {
|
||||
code = (code: string, language: string | undefined, isEscaped: boolean): string => super.code(code, language, isEscaped);
|
||||
blockquote = (quote: string): string => super.blockquote(quote);
|
||||
html = (html: string): string => super.html(html);
|
||||
heading = (text: string, level: 1 | 2 | 3 | 4 | 5 | 6, raw: string, slugger: Slugger): string => super.heading(text, level, raw, slugger);
|
||||
heading = (text: string, level: 1 | 2 | 3 | 4 | 5 | 6, raw: string): string => super.heading(text, level, raw);
|
||||
hr = (): string => super.hr();
|
||||
list = (body: string, ordered: boolean, start: number): string => super.list(body, ordered, start);
|
||||
listitem = (text: string, task: boolean, checked: boolean): string => super.listitem(text, task, checked);
|
||||
@ -149,10 +132,6 @@ console.log(parser.parse(parseTestTokens));
|
||||
console.log(marked.Parser.parse(parseTestTokens));
|
||||
const parserOptions: MarkedOptions = parser.options;
|
||||
|
||||
const slugger = new marked.Slugger();
|
||||
console.log(slugger.slug('Test Slug'));
|
||||
console.log(slugger.slug('Test Slug', { dryrun: true }));
|
||||
|
||||
marked.use({ renderer }, { tokenizer });
|
||||
|
||||
marked.use({
|
||||
@ -270,17 +249,11 @@ const asyncMarked: string = await marked(md, { async: true });
|
||||
const promiseMarked: Promise<string> = marked(md, { async: true });
|
||||
const notAsyncMarked: string = marked(md, { async: false });
|
||||
const defaultMarked: string = marked(md);
|
||||
expectType<void>(marked(md, (_: any, res: string | undefined) => { res; }));
|
||||
expectType<void>(marked(md, { async: true }, (_: any, res: string | undefined) => { res; }));
|
||||
expectType<void>(marked(md, { async: false }, (_: any, res: string | undefined) => { res; }));
|
||||
|
||||
const asyncMarkedParse: string = await marked.parse(md, { async: true });
|
||||
const promiseMarkedParse: Promise<string> = marked.parse(md, { async: true, headerIds: false });
|
||||
const promiseMarkedParse: Promise<string> = marked.parse(md, { async: true });
|
||||
const notAsyncMarkedParse: string = marked.parse(md, { async: false });
|
||||
const defaultMarkedParse: string = marked.parse(md);
|
||||
expectType<void>(marked.parse(md, (_: any, res: string | undefined) => { res; }));
|
||||
expectType<void>(marked(md, { async: true }, (_: any, res: string | undefined) => { res; }));
|
||||
expectType<void>(marked(md, { async: false }, (_: any, res: string | undefined) => { res; }));
|
||||
})();
|
||||
|
||||
// Tests for List and ListItem
|
||||
@ -330,9 +303,6 @@ const tokens4 = lexer2.lex('# test');
|
||||
const parser2 = new Parser();
|
||||
console.log(parser2.parse(tokens4));
|
||||
|
||||
const slugger2 = new Slugger();
|
||||
console.log(slugger2.slug('Test Slug'));
|
||||
|
||||
marked.use({ renderer: new Renderer() });
|
||||
marked.use({ renderer: new TextRenderer() });
|
||||
marked.use({ tokenizer: new Tokenizer() });
|
||||
|
@ -34,13 +34,13 @@ describe('Hooks', () => {
|
||||
marked.use({
|
||||
hooks: {
|
||||
preprocess(markdown) {
|
||||
this.options.headerIds = false;
|
||||
this.options.breaks = true;
|
||||
return markdown;
|
||||
}
|
||||
}
|
||||
});
|
||||
const html = marked('# test');
|
||||
expect(html.trim()).toBe('<h1>test</h1>');
|
||||
const html = marked('line1\nline2');
|
||||
expect(html.trim()).toBe('<p>line1<br>line2</p>');
|
||||
});
|
||||
|
||||
it('should preprocess options async', async() => {
|
||||
@ -49,13 +49,13 @@ describe('Hooks', () => {
|
||||
hooks: {
|
||||
async preprocess(markdown) {
|
||||
await timeout();
|
||||
this.options.headerIds = false;
|
||||
this.options.breaks = true;
|
||||
return markdown;
|
||||
}
|
||||
}
|
||||
});
|
||||
const html = await marked('# test');
|
||||
expect(html.trim()).toBe('<h1>test</h1>');
|
||||
const html = await marked('line1\nline2');
|
||||
expect(html.trim()).toBe('<p>line1<br>line2</p>');
|
||||
});
|
||||
|
||||
it('should postprocess html', () => {
|
||||
|
@ -794,27 +794,6 @@ paragraph
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('sanitize', () => {
|
||||
expectTokens({
|
||||
md: '<div>html</div>',
|
||||
options: { sanitize: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
raw: '<div>html</div>',
|
||||
pre: false,
|
||||
block: true,
|
||||
text: '<div>html</div>',
|
||||
tokens: [{
|
||||
type: 'text',
|
||||
raw: '<div>html</div>',
|
||||
text: '<div>html</div>'
|
||||
}]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('def', () => {
|
||||
@ -894,23 +873,6 @@ paragraph
|
||||
});
|
||||
});
|
||||
|
||||
it('html sanitize', () => {
|
||||
expectInlineTokens({
|
||||
md: '<div>html</div>',
|
||||
options: { sanitize: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'text',
|
||||
raw: '<div>html</div>',
|
||||
inLink: false,
|
||||
inRawBlock: false,
|
||||
block: false,
|
||||
text: '<div>html</div>'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('link', () => {
|
||||
expectInlineTokens({
|
||||
md: '[link](https://example.com)',
|
||||
@ -1205,7 +1167,7 @@ paragraph
|
||||
it('autolink email', () => {
|
||||
expectInlineTokens({
|
||||
md: '<test@example.com>',
|
||||
options: { mangle: false },
|
||||
options: {},
|
||||
tokens: [
|
||||
{
|
||||
type: 'link',
|
||||
@ -1220,28 +1182,6 @@ paragraph
|
||||
});
|
||||
});
|
||||
|
||||
it('autolink mangle email', () => {
|
||||
expectInlineTokens({
|
||||
md: '<test@example.com>',
|
||||
options: { mangle: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'link',
|
||||
raw: '<test@example.com>',
|
||||
text: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/),
|
||||
href: jasmine.stringMatching(/^mailto:(&#x?[0-9a-f]+;)+$/),
|
||||
tokens: [
|
||||
{
|
||||
type: 'text',
|
||||
raw: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/),
|
||||
text: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/)
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('url', () => {
|
||||
expectInlineTokens({
|
||||
md: 'https://example.com',
|
||||
@ -1262,7 +1202,7 @@ paragraph
|
||||
it('url email', () => {
|
||||
expectInlineTokens({
|
||||
md: 'test@example.com',
|
||||
options: { gfm: true, mangle: false },
|
||||
options: { gfm: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'link',
|
||||
@ -1276,28 +1216,6 @@ paragraph
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('url mangle email', () => {
|
||||
expectInlineTokens({
|
||||
md: 'test@example.com',
|
||||
options: { gfm: true, mangle: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'link',
|
||||
raw: 'test@example.com',
|
||||
text: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/),
|
||||
href: jasmine.stringMatching(/^mailto:(&#x?[0-9a-f]+;)+$/),
|
||||
tokens: [
|
||||
{
|
||||
type: 'text',
|
||||
raw: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/),
|
||||
text: jasmine.stringMatching(/^(&#x?[0-9a-f]+;)+$/)
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('text', () => {
|
||||
@ -1312,78 +1230,6 @@ paragraph
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
describe('smartypants', () => {
|
||||
it('single quotes', () => {
|
||||
expectInlineTokens({
|
||||
md: "'single quotes'",
|
||||
options: { smartypants: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'text',
|
||||
raw: "'single quotes'",
|
||||
text: '‘single quotes’'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('double quotes', () => {
|
||||
expectInlineTokens({
|
||||
md: '"double quotes"',
|
||||
options: { smartypants: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'text',
|
||||
raw: '"double quotes"',
|
||||
text: '“double quotes”'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('ellipses', () => {
|
||||
expectInlineTokens({
|
||||
md: 'ellipses...',
|
||||
options: { smartypants: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'text',
|
||||
raw: 'ellipses...',
|
||||
text: 'ellipses…'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('en-dash', () => {
|
||||
expectInlineTokens({
|
||||
md: 'en--dash',
|
||||
options: { smartypants: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'text',
|
||||
raw: 'en--dash',
|
||||
text: 'en–dash'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('em-dash', () => {
|
||||
expectInlineTokens({
|
||||
md: 'em---dash',
|
||||
options: { smartypants: true },
|
||||
tokens: [
|
||||
{
|
||||
type: 'text',
|
||||
raw: 'em---dash',
|
||||
text: 'em—dash'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,74 +0,0 @@
|
||||
import { _Slugger } from '../../src/Slugger.js';
|
||||
|
||||
describe('Test slugger functionality', () => {
|
||||
it('should use lowercase slug', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('Test')).toBe('test');
|
||||
});
|
||||
|
||||
it('should be unique to avoid collisions 1280', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('test')).toBe('test');
|
||||
expect(slugger.slug('test')).toBe('test-1');
|
||||
expect(slugger.slug('test')).toBe('test-2');
|
||||
});
|
||||
|
||||
it('should be unique when slug ends with number', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('test 1')).toBe('test-1');
|
||||
expect(slugger.slug('test')).toBe('test');
|
||||
expect(slugger.slug('test')).toBe('test-2');
|
||||
});
|
||||
|
||||
it('should be unique when slug ends with hyphen number', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('foo')).toBe('foo');
|
||||
expect(slugger.slug('foo')).toBe('foo-1');
|
||||
expect(slugger.slug('foo 1')).toBe('foo-1-1');
|
||||
expect(slugger.slug('foo-1')).toBe('foo-1-2');
|
||||
expect(slugger.slug('foo')).toBe('foo-2');
|
||||
});
|
||||
|
||||
it('should allow non-latin chars', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('привет')).toBe('привет');
|
||||
});
|
||||
|
||||
it('should remove ampersands 857', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('This & That Section')).toBe('this--that-section');
|
||||
});
|
||||
|
||||
it('should remove periods', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('file.txt')).toBe('filetxt');
|
||||
});
|
||||
|
||||
it('should remove html tags', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('<em>html</em>')).toBe('html');
|
||||
});
|
||||
|
||||
it('should not increment seen when using dryrun option', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('<h1>This Section</h1>', { dryrun: true })).toBe('this-section');
|
||||
expect(slugger.slug('<h1>This Section</h1>')).toBe('this-section');
|
||||
});
|
||||
|
||||
it('should still return the next unique id when using dryrun', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('<h1>This Section</h1>')).toBe('this-section');
|
||||
expect(slugger.slug('<h1>This Section</h1>', { dryrun: true })).toBe('this-section-1');
|
||||
});
|
||||
|
||||
it('should be repeatable in a sequence', () => {
|
||||
const slugger = new _Slugger();
|
||||
expect(slugger.slug('foo')).toBe('foo');
|
||||
expect(slugger.slug('foo')).toBe('foo-1');
|
||||
expect(slugger.slug('foo')).toBe('foo-2');
|
||||
expect(slugger.slug('foo', { dryrun: true })).toBe('foo-3');
|
||||
expect(slugger.slug('foo', { dryrun: true })).toBe('foo-3');
|
||||
expect(slugger.slug('foo')).toBe('foo-3');
|
||||
expect(slugger.slug('foo')).toBe('foo-4');
|
||||
});
|
||||
});
|
@ -1,21 +1,6 @@
|
||||
import { marked, Renderer, Slugger, lexer, parseInline, use, getDefaults, walkTokens, defaults, setOptions } from '../../src/marked.js';
|
||||
import { marked, Renderer, lexer, parseInline, use, getDefaults, walkTokens, defaults, setOptions } from '../../src/marked.js';
|
||||
import { timeout } from './utils.js';
|
||||
|
||||
describe('Test heading ID functionality', () => {
|
||||
it('should add id attribute', () => {
|
||||
const renderer = new Renderer({ ...defaults, headerIds: true });
|
||||
const slugger = new Slugger();
|
||||
const header = renderer.heading('test', 1, 'test', slugger);
|
||||
expect(header).toBe('<h1 id="test">test</h1>\n');
|
||||
});
|
||||
|
||||
it('should NOT add id attribute when options set false', () => {
|
||||
const renderer = new Renderer({ headerIds: false });
|
||||
const header = renderer.heading('test', 1, 'test');
|
||||
expect(header).toBe('<h1>test</h1>\n');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test paragraph token type', () => {
|
||||
it('should use the "paragraph" type on top level', () => {
|
||||
const md = 'A Paragraph.\n\n> A blockquote\n\n- list item\n';
|
||||
@ -227,9 +212,9 @@ describe('use extension', () => {
|
||||
return `<u>${token.text}</u>\n`;
|
||||
}
|
||||
};
|
||||
use({ sanitize: true, silent: true, extensions: [extension] });
|
||||
use({ silent: true, extensions: [extension] });
|
||||
const html = marked(':test:\ntest\n<div></div>');
|
||||
expect(html).toBe('<u>test</u>\n<p>test</p>\n<p><div></div></p>\n');
|
||||
expect(html).toBe('<u>test</u>\n<p>test</p>\n<div></div>');
|
||||
});
|
||||
|
||||
it('should handle renderers that return false', () => {
|
||||
@ -456,8 +441,7 @@ describe('use extension', () => {
|
||||
if (token.text === `used ${name}`) {
|
||||
token.text += ' walked';
|
||||
}
|
||||
},
|
||||
headerIds: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -483,8 +467,7 @@ describe('use extension', () => {
|
||||
renderer(token) {
|
||||
return false;
|
||||
}
|
||||
}],
|
||||
headerIds: false
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
@ -636,8 +619,7 @@ used extension2 walked</p>
|
||||
token.tokens.pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
headerIds: false
|
||||
}
|
||||
};
|
||||
use(styleTags);
|
||||
const html = marked('This is a *paragraph* with blue text. {blue}\n'
|
||||
@ -695,28 +677,13 @@ used extension2 walked</p>
|
||||
expect(walked).toBe(2);
|
||||
});
|
||||
|
||||
it('should use walkTokens in async', (done) => {
|
||||
let walked = 0;
|
||||
const extension = {
|
||||
walkTokens(token) {
|
||||
walked++;
|
||||
}
|
||||
};
|
||||
use({ silent: true });
|
||||
use(extension);
|
||||
marked('text', () => {
|
||||
expect(walked).toBe(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use options from extension', () => {
|
||||
const extension = {
|
||||
headerIds: false
|
||||
breaks: true
|
||||
};
|
||||
use(extension);
|
||||
const html = marked('# heading');
|
||||
expect(html).toBe('<h1>heading</h1>\n');
|
||||
const html = marked('line1\nline2');
|
||||
expect(html).toBe('<p>line1<br>line2</p>\n');
|
||||
});
|
||||
|
||||
it('should call all walkTokens in reverse order', () => {
|
||||
@ -831,100 +798,6 @@ paragraph
|
||||
});
|
||||
});
|
||||
|
||||
describe('async highlight', () => {
|
||||
let highlight, markdown;
|
||||
beforeEach(() => {
|
||||
marked.use({ silent: true });
|
||||
highlight = jasmine.createSpy('highlight', (text, lang, callback) => {
|
||||
setImmediate(() => {
|
||||
callback(null, `async ${text || ''}`);
|
||||
});
|
||||
});
|
||||
markdown = `
|
||||
\`\`\`lang1
|
||||
text 1
|
||||
\`\`\`
|
||||
|
||||
> \`\`\`lang2
|
||||
> text 2
|
||||
> \`\`\`
|
||||
|
||||
- \`\`\`lang3
|
||||
text 3
|
||||
\`\`\`
|
||||
`;
|
||||
});
|
||||
|
||||
it('should highlight codeblocks async', (done) => {
|
||||
highlight.and.callThrough();
|
||||
|
||||
marked(markdown, { highlight }, (err, html) => {
|
||||
if (err) {
|
||||
fail(err);
|
||||
}
|
||||
|
||||
expect(html).toBe(`<pre><code class="language-lang1">async text 1
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<pre><code class="language-lang2">async text 2
|
||||
</code></pre>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li><pre><code class="language-lang3">async text 3
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call callback for each error in highlight', (done) => {
|
||||
highlight.and.callFake((text, lang, callback) => {
|
||||
callback(new Error('highlight error'));
|
||||
});
|
||||
|
||||
let numErrors = 0;
|
||||
marked(markdown, { highlight }, (err, html) => {
|
||||
expect(html).toContain('An error occurred:');
|
||||
|
||||
if (err || html.includes('An error occurred:')) {
|
||||
numErrors++;
|
||||
}
|
||||
|
||||
if (numErrors === 3) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should highlight codeblocks when not async', (done) => {
|
||||
highlight.and.callFake((text, lang, callback) => {
|
||||
callback(null, `async ${text || ''}`);
|
||||
});
|
||||
|
||||
marked(markdown, { highlight }, (err, html) => {
|
||||
if (err) {
|
||||
fail(err);
|
||||
}
|
||||
|
||||
expect(html).toBe(`<pre><code class="language-lang1">async text 1
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<pre><code class="language-lang2">async text 2
|
||||
</code></pre>
|
||||
</blockquote>
|
||||
<ul>
|
||||
<li><pre><code class="language-lang3">async text 3
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('walkTokens', () => {
|
||||
it('should walk over every token', () => {
|
||||
const markdown = `
|
||||
|
4
test/update-specs.js
vendored
4
test/update-specs.js
vendored
@ -73,6 +73,6 @@ const commonmarkDir = resolve(__dirname, './specs/commonmark');
|
||||
const gfmDir = resolve(__dirname, './specs/gfm');
|
||||
removeFiles(commonmarkDir);
|
||||
removeFiles(gfmDir);
|
||||
updateCommonmark(commonmarkDir, { gfm: false, pedantic: false, headerIds: false });
|
||||
updateCommonmark(gfmDir, { gfm: true, pedantic: false, headerIds: false });
|
||||
updateCommonmark(commonmarkDir, { gfm: false, pedantic: false });
|
||||
updateCommonmark(gfmDir, { gfm: true, pedantic: false });
|
||||
updateGfm(gfmDir);
|
||||
|
Loading…
x
Reference in New Issue
Block a user