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:
Tony Brix 2023-09-02 22:02:24 -06:00 committed by GitHub
parent fc643a2625
commit 22ebdb2507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 368 additions and 2501 deletions

View File

@ -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 (&lt;br/&gt;, &lt;img/&gt;, 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 (&lt;br/&gt;, &lt;img/&gt;, etc.) with a "/" as required by XHTML.|
<h2 id="extensions">Known Extensions</h2>

View File

@ -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')
// "&#x74;&#101;&#x73;&#116;&#x40;&#101;&#120;&#x61;&#x6d;&#112;&#108;&#101;&#46;&#x63;&#111;&#x6d;"
```
`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
View File

@ -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

File diff suppressed because one or more lines are too long

448
lib/marked.d.ts generated vendored
View File

@ -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
View File

@ -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

File diff suppressed because one or more lines are too long

343
lib/marked.umd.js generated
View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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'> {

View File

@ -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': {

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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',

View File

@ -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
};
}

View File

@ -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}`.');
}
}

View File

@ -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
View File

@ -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
View File

@ -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();
`);

View File

@ -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&#39;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>

View File

@ -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"

View File

@ -1,5 +0,0 @@
<p>Image</p>
<p>Image</p>
<p>Image</p>
<p>Image</p>
<p>Image</p>

View File

@ -1,12 +0,0 @@
---
sanitize: true
---
![Image](javascript:alert)
![Image](vbscript:alert)
![Image](javascript&colon;alert&#40;1&#41;)
![Image](javascript&#58document;alert&#40;1&#41;)
![Image](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)

View File

@ -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

View File

@ -1,3 +0,0 @@
<p>&lt;&lt;svg/onload=&quot;alert(1)&quot;//@x&gt;</p>
<p>&lt;bar&quot;onclick=&quot;alert(&#39;XSS&#39;)&quot;@foo&gt;</p>

View File

@ -1,7 +0,0 @@
---
sanatize: true
mangle: false
---
<<svg/onload="alert(1)"//@x>
<bar"onclick="alert('XSS')"@foo>

View File

@ -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>

View File

@ -1,30 +0,0 @@
---
baseUrl: "/base/"
---
# Absolutization of RFC 3986 URIs
## Absolute URI
[![section 4.3](http://example.com/logo)](http://example.com/)
## Network-path reference
[![section 4.2](//example.com/logo)](//example.com/)
## Absolute path
[![section 4.2](/path/to/img)](/path/to/content)
## Relative path
[![section 4.2](img)](content)
## Dot-relative path
[![section 3.3](./img)](./content)
[![section 3.3](../img)](../content)
## Same-document query
[![section 4.4](?type=image)](?)
## Same-document fragment
[![section 4.4](#img)](#)
## Empty
[section 4.2]()

View File

@ -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>

View File

@ -1,30 +0,0 @@
---
baseUrl: "http://example.com/base/"
---
# Absolutization of RFC 3986 URIs
## Absolute URI
[![section 4.3](http://example.com/logo)](http://example.com/)
## Network-path reference
[![section 4.2](//example.com/logo)](//example.com/)
## Absolute path
[![section 4.2](/path/to/img)](/path/to/content)
## Relative path
[![section 4.2](img)](content)
## Dot-relative path
[![section 3.3](./img)](./content)
[![section 3.3](../img)](../content)
## Same-document query
[![section 4.4](?type=image)](?)
## Same-document fragment
[![section 4.4](#img)](#)
## Empty
[section 4.2]()

View File

@ -1,5 +0,0 @@
<p>URL</p>
<p>URL</p>
<p>URL</p>
<p>URL</p>
<p>URL</p>

View File

@ -1,12 +0,0 @@
---
sanitize: true
---
[URL](javascript:alert)
[URL](vbscript:alert)
[URL](javascript&colon;alert&#40;1&#41;)
[URL](javascript&#58document;alert&#40;1&#41;)
[URL](data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K)

View File

@ -1,6 +0,0 @@
<p>Hello world how “are” you today…</p>
<p>“Its a more challenging smartypants test…”</p>
<p>And, as a bonus — “one
multiline” test!</p>

View File

@ -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!

View File

@ -1,11 +0,0 @@
<pre>&amp;</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 dont
match and thus benefit from Smartypants-ing.</p>
<p><script-custom>foo</script-custom>
<code>--foo</code> &lt;codebar foo codebar&gt;</p>

View File

@ -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>&amp;</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>

View File

@ -1,2 +0,0 @@
<p>lowerclick melower
upperclick meupper</p>

View File

@ -1,5 +0,0 @@
---
sanitize: true
---
lower[click me](javascript&#x3a;...)lower
upper[click me](javascript&#X3a;...)upper

View File

@ -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
&gt; This is a blockquote.
&gt;
&gt;
&gt; This is the second paragraph in the blockquote.
&gt;
&gt; ## This is an H2 in a blockquote
@ -88,7 +88,7 @@ dog's back.&lt;/p&gt;
&lt;/blockquote&gt;
</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 &lt;em&gt;are emphasized also&lt;/em&gt;.&lt;/p&gt;
Or, if you prefer, &lt;strong&gt;use two underscores instead&lt;/strong&gt;.&lt;/p&gt;
</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>
&lt;/ul&gt;
</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>
&lt;a href="http://www.nytimes.com/"&gt;The New York Times&lt;/a&gt;.&lt;/p&gt;
</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>&lt;img src="/path/to/img.jpg" alt="alt text" title="Title" /&gt;
</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>&amp;</code>) and angle brackets (<code>&lt;</code> or

View File

@ -1,6 +1,5 @@
---
pedantic: true
headerIds: true
---
Markdown: Basics

View File

@ -1,6 +1,5 @@
---
pedantic: true
headerIds: false
---
Markdown: Syntax

View File

@ -1,6 +1,3 @@
---
headerIds: false
---
## Unordered
Asterisks tight:

View File

@ -1,7 +1,4 @@
module.exports = {
markdown: `# #${' '.repeat(50000)}a`,
html: '<h1># a</h1>',
options: {
headerIds: false
}
html: '<h1># a</h1>'
};

View File

@ -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');

View File

@ -1,6 +0,0 @@
<p>AAA&lt;script&gt; &lt;img &lt;script&gt; src=x onerror=alert(1) /&gt;BBB</p>
<p>AAA&lt;sometag&gt; &lt;img &lt;sometag&gt; src=x onerror=alert(1)BBB</p>
<p>&lt;a&gt;a2&lt;a2t&gt;a2&lt;/a&gt; b &lt;c&gt;c&lt;/c&gt; d</p>
<h1><img src="URL" alt="text"></h1>

View File

@ -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
# ![text](URL)

View File

@ -1,2 +0,0 @@
<p>a2a2 b c d</p>
<h1><img src="URL" alt="text"></h1>

View File

@ -1,6 +0,0 @@
---
sanitize: true
sanitizer: () => ''
---
<a>a2<a2t>a2</a> b <c>c</c> d
# ![text](URL)

View File

@ -1 +0,0 @@
<p>AAA</p>

View File

@ -1,5 +0,0 @@
---
sanitize: true
sanitizer: () => ''
---
AAA<script> <img <script> src=x onerror=alert(1) />BBB

View File

@ -1 +0,0 @@
<p>AAA &lt;img src=x onerror=alert(1)BBB</p>

View File

@ -1,5 +0,0 @@
---
sanitize: true
sanitizer: () => ''
---
AAA<sometag> <img <sometag> src=x onerror=alert(1)BBB

View File

@ -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() });

View File

@ -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', () => {

View File

@ -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: '&lt;div&gt;html&lt;/div&gt;',
tokens: [{
type: 'text',
raw: '&lt;div&gt;html&lt;/div&gt;',
text: '&lt;div&gt;html&lt;/div&gt;'
}]
}
]
});
});
});
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: '&lt;div&gt;html&lt;/div&gt;'
}
]
});
});
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: 'endash'
}
]
});
});
it('em-dash', () => {
expectInlineTokens({
md: 'em---dash',
options: { smartypants: true },
tokens: [
{
type: 'text',
raw: 'em---dash',
text: 'em—dash'
}
]
});
});
});
});
});
});

View File

@ -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');
});
});

View File

@ -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>&lt;div&gt;&lt;/div&gt;</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 = `

View File

@ -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);