├── docs ├── CNAME ├── demo │ ├── preview.html │ ├── demo.css │ ├── initial.md │ ├── index.html │ └── worker.js ├── .eslintrc.json ├── PUBLISHING.md ├── CODE_OF_CONDUCT.md ├── README.md ├── USING_PRO.md ├── img │ └── logo-black.svg └── CONTRIBUTING.md ├── .eslintignore ├── .nowignore ├── test ├── specs │ ├── new │ │ ├── escaped_angles.md │ │ ├── link_lt.md │ │ ├── breaks.html │ │ ├── escaped_angles.html │ │ ├── html_no_new_line.html │ │ ├── html_no_new_line.md │ │ ├── not_a_link.md │ │ ├── lazy_blockquotes.md │ │ ├── not_a_link.html │ │ ├── same_bullet.md │ │ ├── substitutions.md │ │ ├── case_insensitive_refs.md │ │ ├── code_spans.md │ │ ├── link_lt.html │ │ ├── ref_paren.md │ │ ├── autolink_lines.md │ │ ├── case_insensitive_refs.html │ │ ├── emphasis_extra tests.md │ │ ├── ref_paren.html │ │ ├── substitutions.html │ │ ├── breaks.md │ │ ├── list_item_text.md │ │ ├── nested_em.md │ │ ├── uppercase_hex.html │ │ ├── hr_list_break.md │ │ ├── list_loose_tasks.md │ │ ├── code_spans.html │ │ ├── lazy_blockquotes.html │ │ ├── nested_square_link.md │ │ ├── hr_following_nptables.md │ │ ├── list_item_text.html │ │ ├── same_bullet.html │ │ ├── blockquote_list_item.md │ │ ├── images.html │ │ ├── sanitize_links.html │ │ ├── text_following_nptables.md │ │ ├── autolink_lines.html │ │ ├── heading_following_nptable.md │ │ ├── hr_following_tables.md │ │ ├── image_paren.md │ │ ├── strong_following_nptables.md │ │ ├── emphasis_extra tests.html │ │ ├── inlinecode_following_nptables.md │ │ ├── text_following_tables.md │ │ ├── heading_following_table.md │ │ ├── html_following_nptable.md │ │ ├── lheading_following_nptable.md │ │ ├── strong_following_tables.md │ │ ├── adjacent_lists.md │ │ ├── fences_following_nptable.md │ │ ├── inlinecode_following_tables.md │ │ ├── list_following_nptable.md │ │ ├── html_following_table.md │ │ ├── lheading_following_table.md │ │ ├── nested_em.html │ │ ├── fences_following_table.md │ │ ├── list_following_table.md │ │ ├── nogfm_hashtag.html │ │ ├── nogfm_hashtag.md │ │ ├── nested_square_link.html │ │ ├── hr_list_break.html │ │ ├── mangle_xss.md │ │ ├── uppercase_hex.md │ │ ├── blockquote_list_item.html │ │ ├── mangle_xss.html │ │ ├── image_paren.html │ │ ├── links.md │ │ ├── smartypants.html │ │ ├── adjacent_lists.html │ │ ├── blockquote_following_nptable.md │ │ ├── em_2char.md │ │ ├── links.html │ │ ├── nested_code.md │ │ ├── smartypants.md │ │ ├── links_paren.md │ │ ├── pedantic_heading_interrupts_paragraph.md │ │ ├── tricky_list.md │ │ ├── blockquote_following_table.md │ │ ├── pedantic_heading_interrupts_paragraph.html │ │ ├── def_blocks.md │ │ ├── double_link.md │ │ ├── autolinks.md │ │ ├── list_loose_tasks.html │ │ ├── code_following_nptable.md │ │ ├── double_link.html │ │ ├── code_following_table.md │ │ ├── list_table.md │ │ ├── nested_code.html │ │ ├── sanitize_links.md │ │ ├── images.md │ │ ├── links_paren.html │ │ ├── hr_following_nptables.html │ │ ├── hr_following_tables.html │ │ ├── em_2char.html │ │ ├── html_following_table.html │ │ ├── html_following_nptable.html │ │ ├── heading_following_nptable.html │ │ ├── heading_following_table.html │ │ ├── fences_following_nptable.html │ │ ├── fences_following_table.html │ │ ├── smartypants_code.html │ │ ├── def_blocks.html │ │ ├── headings_id.md │ │ ├── text_following_tables.html │ │ ├── list_following_table.html │ │ ├── text_following_nptables.html │ │ ├── list_following_nptable.html │ │ ├── fences_breaking_paragraphs.md │ │ ├── inlinecode_following_tables.html │ │ ├── strong_following_tables.html │ │ ├── inlinecode_following_nptables.html │ │ ├── strong_following_nptables.html │ │ ├── tricky_list.html │ │ ├── lheading_following_table.html │ │ ├── lheading_following_nptable.html │ │ ├── fences_breaking_paragraphs.html │ │ ├── table_cells.md │ │ ├── smartypants_code.md │ │ ├── toplevel_paragraphs.md │ │ ├── autolinks.html │ │ ├── headings_id.html │ │ ├── toplevel_paragraphs.html │ │ ├── relative_base_urls.md │ │ ├── relative_urls.md │ │ ├── blockquote_following_table.html │ │ ├── blockquote_following_nptable.html │ │ ├── html_comments.md │ │ ├── code_following_nptable.html │ │ ├── code_following_table.html │ │ ├── list_table.html │ │ ├── html_comments.html │ │ ├── main.md │ │ ├── relative_base_urls.html │ │ ├── em_list_links.md │ │ ├── main.html │ │ ├── relative_urls.html │ │ ├── table_cells.html │ │ ├── link_tick_redos.md │ │ ├── link_tick_redos.html │ │ └── em_list_links.html │ ├── security │ │ ├── sanitizer_bypass_remove_script.html │ │ ├── sanitizer_bypass_remove_tag.html │ │ ├── sanitizer_bypass_remove_generic.html │ │ ├── sanitizer_bypass_remove_generic.md │ │ ├── sanitizer_bypass_remove_tag.md │ │ ├── sanitizer_bypass_remove_script.md │ │ ├── sanitizer_bypass.md │ │ └── sanitizer_bypass.html │ ├── original │ │ ├── nested_blockquotes.md │ │ ├── tidyness.md │ │ ├── strong_and_em_together.md │ │ ├── nested_blockquotes.html │ │ ├── tidyness.html │ │ ├── code_spans.md │ │ ├── literal_quotes_in_titles.html │ │ ├── literal_quotes_in_titles.md │ │ ├── blockquotes_with_code_blocks.md │ │ ├── inline_html_advanced.md │ │ ├── inline_html_comments.md │ │ ├── code_spans.html │ │ ├── inline_html_advanced.html │ │ ├── strong_and_em_together.html │ │ ├── hard_wrapped_paragraphs_with_list_like_lines.md │ │ ├── inline_html_comments.html │ │ ├── code_blocks.md │ │ ├── hard_wrapped_paragraphs_with_list_like_lines.html │ │ ├── blockquotes_with_code_blocks.html │ │ ├── links_shortcut_references.html │ │ ├── auto_links.md │ │ ├── links_shortcut_references.md │ │ ├── code_blocks.html │ │ ├── tabs.md │ │ ├── links_inline_style.md │ │ ├── amps_and_angles_encoding.md │ │ ├── tabs.html │ │ ├── links_inline_style.html │ │ ├── amps_and_angles_encoding.html │ │ ├── auto_links.html │ │ ├── horizontal_rules.md │ │ ├── inline_html_simple.md │ │ ├── horizontal_rules.html │ │ ├── inline_html_simple.html │ │ ├── links_reference_style.md │ │ ├── links_reference_style.html │ │ ├── ordered_and_unordered_lists.md │ │ ├── backslash_escapes.md │ │ ├── backslash_escapes.html │ │ └── ordered_and_unordered_lists.html │ ├── redos │ │ ├── quadratic_email.js │ │ ├── quadratic_br.js │ │ ├── redos_nolink.html │ │ ├── redos_nolink.md │ │ ├── redos_html_closing.md │ │ ├── link_code.md │ │ ├── redos_html_closing.html │ │ ├── link_code.html │ │ ├── link_redos.md │ │ └── link_redos.html │ └── run-spec.js ├── .eslintrc.json ├── helpers │ ├── helpers.js │ ├── html-differ.js │ └── load.js ├── vuln-regex.js ├── update-specs.js └── unit │ └── marked-spec.js ├── .gitattributes ├── index.js ├── .gitignore ├── jasmine.json ├── component.json ├── .editorconfig ├── Makefile ├── .github ├── ISSUE_TEMPLATE │ ├── Proposal.md │ ├── Feature_request.md │ └── Bug_report.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE │ ├── release.md │ └── badges.md └── PULL_REQUEST_TEMPLATE.md ├── now.json ├── bower.json ├── SECURITY.md ├── rollup.config.esm.js ├── src ├── defaults.js ├── TextRenderer.js ├── Slugger.js ├── marked.js ├── Renderer.js └── Parser.js ├── .eslintrc.json ├── rollup.config.js ├── .travis.yml ├── package.json ├── man ├── marked.1 └── marked.1.txt ├── README.md ├── LICENSE.md └── bin └── marked /docs/CNAME: -------------------------------------------------------------------------------- 1 | marked.js.org 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib 2 | *.min.js 3 | -------------------------------------------------------------------------------- /.nowignore: -------------------------------------------------------------------------------- 1 | * 2 | !docs 3 | !docs/** 4 | -------------------------------------------------------------------------------- /test/specs/new/escaped_angles.md: -------------------------------------------------------------------------------- 1 | \> 2 | -------------------------------------------------------------------------------- /test/specs/new/link_lt.md: -------------------------------------------------------------------------------- 1 | [URL](A
B

2 | -------------------------------------------------------------------------------- /test/specs/new/escaped_angles.html: -------------------------------------------------------------------------------- 1 |

>

2 | -------------------------------------------------------------------------------- /test/specs/new/html_no_new_line.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/specs/new/html_no_new_line.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/specs/new/not_a_link.md: -------------------------------------------------------------------------------- 1 | \[test](not a link) 2 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/marked'); 2 | -------------------------------------------------------------------------------- /test/specs/new/lazy_blockquotes.md: -------------------------------------------------------------------------------- 1 | > hi there 2 | bud 3 | -------------------------------------------------------------------------------- /test/specs/new/not_a_link.html: -------------------------------------------------------------------------------- 1 |

[test](not a link)

2 | -------------------------------------------------------------------------------- /test/specs/new/same_bullet.md: -------------------------------------------------------------------------------- 1 | * test 2 | + test 3 | - test 4 | -------------------------------------------------------------------------------- /test/specs/new/substitutions.md: -------------------------------------------------------------------------------- 1 | foo␤␤bar 2 | 3 | * a * 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | test/compiled_tests 4 | -------------------------------------------------------------------------------- /test/specs/new/case_insensitive_refs.md: -------------------------------------------------------------------------------- 1 | [hi] 2 | 3 | [HI]: /url 4 | -------------------------------------------------------------------------------- /test/specs/new/code_spans.md: -------------------------------------------------------------------------------- 1 | `someone@example.com` 2 | 3 | ``*test`* -------------------------------------------------------------------------------- /test/specs/new/link_lt.html: -------------------------------------------------------------------------------- 1 |

URL

2 | -------------------------------------------------------------------------------- /test/specs/new/ref_paren.md: -------------------------------------------------------------------------------- 1 | [hi] 2 | 3 | [hi]: /url (there) 4 | -------------------------------------------------------------------------------- /test/specs/new/autolink_lines.md: -------------------------------------------------------------------------------- 1 | hello world 2 | 3 | -------------------------------------------------------------------------------- /test/specs/security/sanitizer_bypass_remove_script.html: -------------------------------------------------------------------------------- 1 |

AAA

2 | -------------------------------------------------------------------------------- /test/specs/new/case_insensitive_refs.html: -------------------------------------------------------------------------------- 1 |

hi

2 | -------------------------------------------------------------------------------- /test/specs/new/emphasis_extra tests.md: -------------------------------------------------------------------------------- 1 | _test_. _test_: _test_! _test_? _test_- -------------------------------------------------------------------------------- /test/specs/new/ref_paren.html: -------------------------------------------------------------------------------- 1 |

hi

2 | -------------------------------------------------------------------------------- /test/specs/new/substitutions.html: -------------------------------------------------------------------------------- 1 |

foo␤␤bar

2 | 3 |

* a *

4 | -------------------------------------------------------------------------------- /test/specs/new/breaks.md: -------------------------------------------------------------------------------- 1 | --- 2 | breaks: true 3 | gfm: true 4 | --- 5 | A 6 | B 7 | -------------------------------------------------------------------------------- /test/specs/new/list_item_text.md: -------------------------------------------------------------------------------- 1 | * item1 2 | 3 | * item2 4 | 5 | text 6 | -------------------------------------------------------------------------------- /test/specs/new/nested_em.md: -------------------------------------------------------------------------------- 1 | *test **test** test* 2 | 3 | _test __test__ test_ 4 | -------------------------------------------------------------------------------- /test/specs/new/uppercase_hex.html: -------------------------------------------------------------------------------- 1 |

lowerclick melower 2 | upperclick meupper

3 | -------------------------------------------------------------------------------- /test/specs/original/nested_blockquotes.md: -------------------------------------------------------------------------------- 1 | > foo 2 | > 3 | > > bar 4 | > 5 | > foo 6 | -------------------------------------------------------------------------------- /test/specs/new/hr_list_break.md: -------------------------------------------------------------------------------- 1 | * hello 2 | world 3 | * how 4 | are 5 | * * * 6 | you today? 7 | -------------------------------------------------------------------------------- /test/specs/new/list_loose_tasks.md: -------------------------------------------------------------------------------- 1 | - Tasks 2 | - [x] Task1 3 | 4 | - [ ]
Task2
5 | -------------------------------------------------------------------------------- /test/specs/new/code_spans.html: -------------------------------------------------------------------------------- 1 |

someone@example.com

2 | 3 |

``test`

-------------------------------------------------------------------------------- /test/specs/new/lazy_blockquotes.html: -------------------------------------------------------------------------------- 1 |
2 |

hi there 3 | bud

4 |
5 | -------------------------------------------------------------------------------- /test/specs/new/nested_square_link.md: -------------------------------------------------------------------------------- 1 | [the `]` character](/url) 2 | 3 | [the \` character](/url) 4 | -------------------------------------------------------------------------------- /test/specs/security/sanitizer_bypass_remove_tag.html: -------------------------------------------------------------------------------- 1 |

AAA <img src=x onerror=alert(1)BBB

2 | -------------------------------------------------------------------------------- /test/specs/new/hr_following_nptables.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 | ___ 6 | -------------------------------------------------------------------------------- /test/specs/new/list_item_text.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/specs/new/same_bullet.html: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /test/specs/new/blockquote_list_item.md: -------------------------------------------------------------------------------- 1 | This fails in markdown.pl and upskirt: 2 | 3 | * hello 4 | > world 5 | -------------------------------------------------------------------------------- /test/specs/new/images.html: -------------------------------------------------------------------------------- 1 |

Image

2 |

Image

3 |

Image

4 |

Image

5 |

Image

6 | -------------------------------------------------------------------------------- /test/specs/new/sanitize_links.html: -------------------------------------------------------------------------------- 1 |

URL

2 |

URL

3 |

URL

4 |

URL

5 |

URL

6 | -------------------------------------------------------------------------------- /test/specs/new/text_following_nptables.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 | hello 6 | -------------------------------------------------------------------------------- /test/specs/new/autolink_lines.html: -------------------------------------------------------------------------------- 1 |

hello world 2 | http://example.com 3 |

4 | -------------------------------------------------------------------------------- /test/specs/new/heading_following_nptable.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 | # title 6 | -------------------------------------------------------------------------------- /test/specs/new/hr_following_tables.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 | ___ 6 | -------------------------------------------------------------------------------- /test/specs/new/image_paren.md: -------------------------------------------------------------------------------- 1 | ![](img1.svg) (or ![](img2.svg)) 2 | 3 | ![one](img1.svg) (or ![two](img2.svg)) 4 | -------------------------------------------------------------------------------- /test/specs/new/strong_following_nptables.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 | **strong** 6 | -------------------------------------------------------------------------------- /test/specs/new/emphasis_extra tests.html: -------------------------------------------------------------------------------- 1 |

test. test: test! test? test-

-------------------------------------------------------------------------------- /test/specs/new/inlinecode_following_nptables.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 | `hello` 6 | -------------------------------------------------------------------------------- /test/specs/new/text_following_tables.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 | hello 6 | -------------------------------------------------------------------------------- /test/specs/new/heading_following_table.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 | # title 6 | -------------------------------------------------------------------------------- /test/specs/new/html_following_nptable.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 |
Some HTML
6 | -------------------------------------------------------------------------------- /test/specs/new/lheading_following_nptable.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 | title 6 | ===== 7 | -------------------------------------------------------------------------------- /test/specs/new/strong_following_tables.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 | **strong** 6 | -------------------------------------------------------------------------------- /test/specs/security/sanitizer_bypass_remove_generic.html: -------------------------------------------------------------------------------- 1 |

a2a2 b c d

2 |

text

3 | -------------------------------------------------------------------------------- /test/specs/new/adjacent_lists.md: -------------------------------------------------------------------------------- 1 | * This should be 2 | * An unordered list 3 | 4 | 1. This should be 5 | 2. An unordered list 6 | -------------------------------------------------------------------------------- /test/specs/new/fences_following_nptable.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 | ``` 6 | foobar() 7 | ``` 8 | -------------------------------------------------------------------------------- /test/specs/new/inlinecode_following_tables.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 | `hello` 6 | -------------------------------------------------------------------------------- /test/specs/new/list_following_nptable.md: -------------------------------------------------------------------------------- 1 | abc | def 2 | --- | --- 3 | bar | foo 4 | baz | boo 5 | - foo 6 | - bar 7 | - baz 8 | -------------------------------------------------------------------------------- /test/specs/original/tidyness.md: -------------------------------------------------------------------------------- 1 | > A list within a blockquote: 2 | > 3 | > * asterisk 1 4 | > * asterisk 2 5 | > * asterisk 3 6 | -------------------------------------------------------------------------------- /test/specs/new/html_following_table.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 |
Some HTML
6 | -------------------------------------------------------------------------------- /test/specs/new/lheading_following_table.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 | title 6 | ===== 7 | -------------------------------------------------------------------------------- /test/specs/new/nested_em.html: -------------------------------------------------------------------------------- 1 |

test test test

2 | 3 |

test test test

4 | -------------------------------------------------------------------------------- /test/specs/new/fences_following_table.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 | ``` 6 | foobar() 7 | ``` 8 | -------------------------------------------------------------------------------- /test/specs/new/list_following_table.md: -------------------------------------------------------------------------------- 1 | | abc | def | 2 | | --- | --- | 3 | | bar | foo | 4 | | baz | boo | 5 | - foo 6 | - bar 7 | - baz 8 | -------------------------------------------------------------------------------- /test/specs/new/nogfm_hashtag.html: -------------------------------------------------------------------------------- 1 |

header

2 | 3 |

header1

4 | 5 |

header2

6 | -------------------------------------------------------------------------------- /test/specs/new/nogfm_hashtag.md: -------------------------------------------------------------------------------- 1 | --- 2 | gfm: false 3 | pedantic: true 4 | --- 5 | #header 6 | 7 | # header1 8 | 9 | # header2 10 | -------------------------------------------------------------------------------- /test/specs/redos/quadratic_email.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | markdown: 'a'.repeat(50000), 3 | html: `

${'a'.repeat(50000)}

` 4 | }; 5 | -------------------------------------------------------------------------------- /test/specs/new/nested_square_link.html: -------------------------------------------------------------------------------- 1 |

the ] character

2 | 3 |

the ` character

4 | -------------------------------------------------------------------------------- /test/specs/redos/quadratic_br.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | markdown: `a${' '.repeat(50000)}`, 3 | html: `

a${' '.repeat(50000)}

` 4 | }; 5 | -------------------------------------------------------------------------------- /test/specs/new/hr_list_break.html: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 | 10 |

you today?

11 | -------------------------------------------------------------------------------- /test/specs/new/mangle_xss.md: -------------------------------------------------------------------------------- 1 | --- 2 | sanatize: true 3 | mangle: false 4 | --- 5 | < 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/specs/new/uppercase_hex.md: -------------------------------------------------------------------------------- 1 | --- 2 | sanitize: true 3 | --- 4 | lower[click me](javascript:...)lower 5 | upper[click me](javascript:...)upper 6 | -------------------------------------------------------------------------------- /test/specs/new/blockquote_list_item.html: -------------------------------------------------------------------------------- 1 |

This fails in markdown.pl and upskirt:

2 | 3 | 4 | -------------------------------------------------------------------------------- /test/specs/security/sanitizer_bypass_remove_generic.md: -------------------------------------------------------------------------------- 1 | --- 2 | sanitize: true 3 | sanitizer: () => '' 4 | --- 5 | a2a2 b c d 6 | # ![text](URL) -------------------------------------------------------------------------------- /test/specs/new/mangle_xss.html: -------------------------------------------------------------------------------- 1 |

<<svg/onload="alert(1)"//@x>

2 | 3 |

<bar"onclick="alert('XSS')"@foo>

4 | -------------------------------------------------------------------------------- /test/specs/security/sanitizer_bypass_remove_tag.md: -------------------------------------------------------------------------------- 1 | --- 2 | sanitize: true 3 | sanitizer: () => '' 4 | --- 5 | AAA src=x onerror=alert(1)BBB 6 | -------------------------------------------------------------------------------- /test/specs/security/sanitizer_bypass_remove_script.md: -------------------------------------------------------------------------------- 1 | --- 2 | sanitize: true 3 | sanitizer: () => '' 4 | --- 5 | AAA 5 | 6 |

Ensure that text such as custom tags that happen to 7 | begin with the same letters as the above tags don’t 8 | match and thus benefit from Smartypants-ing.

9 | 10 |

–foo 11 | --foo <codebar –foo codebar>

12 | -------------------------------------------------------------------------------- /test/specs/new/def_blocks.html: -------------------------------------------------------------------------------- 1 |
2 |

hello 3 | [1]: hello

4 |
5 | 6 |
7 | 8 |
9 |

hello 10 | [2]: hello

11 |
12 | 13 | 14 |
    15 |
  • hello
  • 16 |
  • [3]: hello
  • 17 |
18 | 19 | 20 |
    21 |
  • hello
  • 22 |
23 | 24 | 25 |
26 |

foo 27 | bar 28 | [5]: foo 29 | bar

30 |
31 | -------------------------------------------------------------------------------- /test/specs/new/headings_id.md: -------------------------------------------------------------------------------- 1 | ### Heading with html 2 | 3 | ### Heading with a [link](http://github.com/) 4 | 5 | ### Heading with some _italic text_ 6 | 7 | ### Or some **strong** 8 | (which doesn't really make any difference, here) 9 | 10 | ### Or even `code` 11 | 12 | ### What about ~~strikethrough~~ 13 | 14 | ## And a ref [link][destination] 15 | 16 | [destination]: /some/url "link to nowhere" 17 | -------------------------------------------------------------------------------- /test/specs/original/links_shortcut_references.md: -------------------------------------------------------------------------------- 1 | --- 2 | pedantic: true 3 | --- 4 | 5 | This is the [simple case]. 6 | 7 | [simple case]: /simple 8 | 9 | 10 | 11 | This one has a [line 12 | break]. 13 | 14 | This one has a [line 15 | break] with a line-ending space. 16 | 17 | [line break]: /foo 18 | 19 | 20 | [this] [that] and the [other] 21 | 22 | [this]: /this 23 | [that]: /that 24 | [other]: /other 25 | -------------------------------------------------------------------------------- /test/specs/new/text_following_tables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
abcdef
barfoo
bazboo
hello
23 | -------------------------------------------------------------------------------- /test/specs/new/list_following_table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
abcdef
barfoo
bazboo
19 |
    20 |
  • foo
  • 21 |
  • bar
  • 22 |
  • baz
  • 23 |
24 | -------------------------------------------------------------------------------- /test/specs/new/text_following_nptables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
abcdef
barfoo
bazboo
hello
23 | -------------------------------------------------------------------------------- /test/specs/original/code_blocks.html: -------------------------------------------------------------------------------- 1 |
code block on the first line
 2 | 
3 | 4 |

Regular text.

5 | 6 |
code block indented by spaces
 7 | 
8 | 9 |

Regular text.

10 | 11 |
the lines in this block  
12 | all contain trailing spaces  
13 | 
14 | 15 |

Regular Text.

16 | 17 |
code block on the last line
18 | 
19 | -------------------------------------------------------------------------------- /test/specs/new/list_following_nptable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
abcdef
barfoo
bazboo
19 |
    20 |
  • foo
  • 21 |
  • bar
  • 22 |
  • baz
  • 23 |
24 | -------------------------------------------------------------------------------- /test/specs/original/tabs.md: -------------------------------------------------------------------------------- 1 | + this is a list item 2 | indented with tabs 3 | 4 | + this is a list item 5 | indented with spaces 6 | 7 | Code: 8 | 9 | this code block is indented by one tab 10 | 11 | And: 12 | 13 | this code block is indented by two tabs 14 | 15 | And: 16 | 17 | + this is an example list item 18 | indented with tabs 19 | 20 | + this is an example list item 21 | indented with spaces 22 | -------------------------------------------------------------------------------- /test/specs/new/fences_breaking_paragraphs.md: -------------------------------------------------------------------------------- 1 | A paragraph 2 | ```A 3 | Here is code in 4 | backtick fences 5 | ``` 6 | 7 | B paragraph 8 | ~~~B 9 | Here is code in 10 | tilde fences 11 | ~~~ 12 | 13 | C paragraph 14 | ~~~`C~ 15 | Alternative 16 | tilde fences 17 | ~~~ 18 | 19 | D paragraph 20 | ```~D` 21 | Invalid use of 22 | backtick fences 23 | ``` 24 | 25 | This will be read as 26 | part of a codeblock 27 | that ends with the file 28 | -------------------------------------------------------------------------------- /test/specs/new/inlinecode_following_tables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
abcdef
barfoo
bazboo
hello
23 | -------------------------------------------------------------------------------- /test/specs/new/strong_following_tables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
abcdef
barfoo
bazboo
strong
23 | -------------------------------------------------------------------------------- /test/specs/redos/redos_html_closing.md: -------------------------------------------------------------------------------- 1 | a'a 2 | -------------------------------------------------------------------------------- /test/specs/new/inlinecode_following_nptables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
abcdef
barfoo
bazboo
hello
23 | -------------------------------------------------------------------------------- /test/specs/new/strong_following_nptables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
abcdef
barfoo
bazboo
strong
23 | -------------------------------------------------------------------------------- /test/specs/new/tricky_list.html: -------------------------------------------------------------------------------- 1 |

hello world

2 | 3 |
    4 |
  • hello world
  • 5 |
6 | 7 |

hello world

8 | 9 |
    10 |
  • hello world
  • 11 |
12 | 13 |

hello world

14 | 15 |
    16 |
  • Hello world
  • 17 |
18 | 19 |

hello world

20 | 21 |
    22 |
  • hello world
  • 23 |
24 | -------------------------------------------------------------------------------- /test/specs/original/links_inline_style.md: -------------------------------------------------------------------------------- 1 | --- 2 | pedantic: true 3 | --- 4 | 5 | Just a [URL](/url/). 6 | 7 | [URL and title](/url/ "title"). 8 | 9 | [URL and title](/url/ "title preceded by two spaces"). 10 | 11 | [URL and title](/url/ "title preceded by a tab"). 12 | 13 | [URL and title](/url/ "title has spaces afterward" ). 14 | 15 | [URL and title]( /url/has space ). 16 | 17 | [URL and title]( /url/has space/ "url has space and title"). 18 | 19 | [Empty](). 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Marked doesn't do this thing and I think it should 4 | 5 | --- 6 | 7 | **Describe the feature** 8 | A clear and concise description of what you would like. 9 | 10 | **Why is this feature necessary?** 11 | A clear and concise description of why. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | -------------------------------------------------------------------------------- /test/specs/new/lheading_following_table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
abcdef
barfoo
bazboo
title
=====
27 | -------------------------------------------------------------------------------- /test/specs/new/lheading_following_nptable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
abcdef
barfoo
bazboo
title
=====
27 | -------------------------------------------------------------------------------- /test/specs/new/fences_breaking_paragraphs.html: -------------------------------------------------------------------------------- 1 |

A paragraph

2 |
Here is code in
 3 | backtick fences
4 |

B paragraph

5 |
Here is code in
 6 | tilde fences
7 |

C paragraph

8 |
Alternative
 9 | tilde fences
10 |

D paragraph ```~D` Invalid use of backtick fences

11 |

12 | This will be read as
13 | part of a codeblock
14 | that ends with the file
15 | -------------------------------------------------------------------------------- /test/specs/new/table_cells.md: -------------------------------------------------------------------------------- 1 | |1| 2 | |-| 3 | |1| 4 | 5 | |1| 6 | |-| 7 | |\|| 8 | 9 | |1| 10 | |-| 11 | |1\\1| 12 | 13 | |1| 14 | |-| 15 | |\\\\|| 16 | 17 | |1| 18 | |-| 19 | |\\\\\|| 20 | 21 | |1|2| 22 | |-|-| 23 | ||2| 24 | 25 | |1|2| 26 | |-|-| 27 | |1\|\\|2\|\\| 28 | 29 | |1|2| 30 | |-|-| 31 | | |2| 32 | 33 | 1|2 34 | -|- 35 | 1\|\\|2\|\\ 36 | 37 | 1|2 38 | -|- 39 | |2 40 | 41 | 1|2 42 | -|- 43 | 1|2\| 44 | 45 | 1|2 46 | -|- 47 | 1|2\| 48 | 49 | |1|2| 50 | |-|-| 51 | |1|2\|| 52 | 53 | |1|2| 54 | |-|-| 55 | |1|2\|| 56 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marked", 3 | "homepage": "https://github.com/markedjs/marked", 4 | "authors": [ 5 | "Christopher Jeffrey " 6 | ], 7 | "description": "A markdown parser built for speed", 8 | "keywords": [ 9 | "markdown", 10 | "markup", 11 | "html" 12 | ], 13 | "main": "lib/marked.js", 14 | "license": "MIT", 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "app/bower_components", 20 | "test", 21 | "tests" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /test/specs/original/amps_and_angles_encoding.md: -------------------------------------------------------------------------------- 1 | --- 2 | pedantic: true 3 | --- 4 | 5 | AT&T has an ampersand in their name. 6 | 7 | AT&T is another way to write it. 8 | 9 | This & that. 10 | 11 | 4 < 5. 12 | 13 | 6 > 5. 14 | 15 | Here's a [link] [1] with an ampersand in the URL. 16 | 17 | Here's a link with an amersand in the link text: [AT&T] [2]. 18 | 19 | Here's an inline [link](/script?foo=1&bar=2). 20 | 21 | Here's an inline [link](). 22 | 23 | 24 | [1]: http://example.com/?foo=1&bar=2 25 | [2]: http://att.com/ "AT&T" 26 | -------------------------------------------------------------------------------- /test/specs/new/smartypants_code.md: -------------------------------------------------------------------------------- 1 | --- 2 | smartypants: true 3 | description: SmartyPants does not modify characters within
, , , or 
10 | 
11 | Ensure that text such as custom tags that happen to
12 | begin with the same letters as the above tags don't
13 | match and thus benefit from Smartypants-ing.
14 | --foo
15 | `--foo` 
16 | 


--------------------------------------------------------------------------------
/test/specs/original/tabs.html:
--------------------------------------------------------------------------------
 1 | 
    2 |
  • this is a list item 3 | indented with tabs

  • 4 |
  • this is a list item 5 | indented with spaces

  • 6 |
7 | 8 |

Code:

9 | 10 |
this code block is indented by one tab
11 | 
12 | 13 |

And:

14 | 15 |
    this code block is indented by two tabs
16 | 
17 | 18 |

And:

19 | 20 |
+   this is an example list item
21 |     indented with tabs
22 | 
23 | +   this is an example list item
24 |     indented with spaces
25 | 
26 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | The only completely secure system is the one that doesn't exist in the first place. 4 | Having said that, we take the security of Marked very seriously. 5 | 6 | ## Reporting a Vulnerability 7 | 8 | Please disclose potential security issues by email to the project [committers](https://marked.js.org/#/AUTHORS.md) as well as the [listed owners within NPM](https://docs.npmjs.com/cli/owner). 9 | We will provide an initial assessment of security reports within 48 hours and should apply patches within 2 weeks 10 | (also, feel free to contribute a fix for the issue). 11 | -------------------------------------------------------------------------------- /test/specs/original/links_inline_style.html: -------------------------------------------------------------------------------- 1 |

Just a URL.

2 | 3 |

URL and title.

4 | 5 |

URL and title.

6 | 7 |

URL and title.

8 | 9 |

URL and title.

10 | 11 |

URL and title.

12 | 13 |

URL and title.

14 | 15 |

Empty.

16 | -------------------------------------------------------------------------------- /test/specs/new/toplevel_paragraphs.md: -------------------------------------------------------------------------------- 1 | --- 2 | gfm: true 3 | --- 4 | hello world 5 | text after spaces 6 | text after spaces 7 | 8 | paragraph before code 9 | ``` 10 | text inside block code 11 | ``` 12 | 13 | paragraph before hr 14 | * * * 15 | 16 | paragraph before blockquote 17 | > text for blockquote 18 | 19 | paragraph before list 20 | * text inside list 21 | 22 | paragraph before div 23 |
text inside div
24 | 25 | paragraph with span 26 | text inside span 27 | 28 | hello [world][how] 29 | 30 | [how]: /are/you 31 | 32 |
hello
33 | 34 | hello 35 | -------------------------------------------------------------------------------- /test/specs/new/autolinks.html: -------------------------------------------------------------------------------- 1 |

(See https://www.example.com/fhqwhgads.)

2 | 3 |

((http://foo.com))

4 | 5 |

((http://foo.com.))

6 | 7 |

HTTP://FOO.COM

8 | 9 |

hTtP://fOo.CoM

10 | 11 |

hello@email.com

12 | 13 |

me@example.com

14 | 15 |

test@test.com

-------------------------------------------------------------------------------- /test/specs/original/amps_and_angles_encoding.html: -------------------------------------------------------------------------------- 1 |

AT&T has an ampersand in their name.

2 | 3 |

AT&T is another way to write it.

4 | 5 |

This & that.

6 | 7 |

4 < 5.

8 | 9 |

6 > 5.

10 | 11 |

Here's a link with an ampersand in the URL.

12 | 13 |

Here's a link with an amersand in the link text: AT&T.

14 | 15 |

Here's an inline link.

16 | 17 |

Here's an inline link.

18 | -------------------------------------------------------------------------------- /test/specs/original/auto_links.html: -------------------------------------------------------------------------------- 1 |

Link: http://example.com/.

2 | 3 |

With an ampersand: http://example.com/?foo=1&bar=2

4 | 5 | 10 | 11 |
12 |

Blockquoted: http://example.com/

13 |
14 | 15 |

Auto-links should not occur here: <http://example.com/>

16 | 17 |
or here: <http://example.com/>
18 | 
19 | -------------------------------------------------------------------------------- /test/specs/new/headings_id.html: -------------------------------------------------------------------------------- 1 |

Heading with html

2 | 3 | 4 | 5 |

Heading with some italic text

6 | 7 |

Or some strong

8 | 9 |

(which doesn't really make any difference, here)

10 | 11 |

Or even code

12 | 13 |

What about strikethrough

14 | 15 | 16 | -------------------------------------------------------------------------------- /rollup.config.esm.js: -------------------------------------------------------------------------------- 1 | const commonjs = require('rollup-plugin-commonjs'); 2 | const license = require('rollup-plugin-license'); 3 | 4 | module.exports = { 5 | input: 'src/marked.js', 6 | output: { 7 | file: 'lib/marked.esm.js', 8 | format: 'esm' 9 | }, 10 | plugins: [ 11 | license({ 12 | banner: ` 13 | DO NOT EDIT THIS FILE 14 | The code in this file is generated from files in ./src/ 15 | ` 16 | }), 17 | license({ 18 | banner: ` 19 | marked - a markdown parser 20 | Copyright (c) 2011-${new Date().getFullYear()}, Christopher Jeffrey. (MIT Licensed) 21 | https://github.com/markedjs/marked 22 | ` 23 | }), 24 | commonjs() 25 | ] 26 | }; 27 | -------------------------------------------------------------------------------- /src/defaults.js: -------------------------------------------------------------------------------- 1 | function getDefaults() { 2 | return { 3 | baseUrl: null, 4 | breaks: false, 5 | gfm: true, 6 | headerIds: true, 7 | headerPrefix: '', 8 | highlight: null, 9 | langPrefix: 'language-', 10 | mangle: true, 11 | pedantic: false, 12 | renderer: null, 13 | sanitize: false, 14 | sanitizer: null, 15 | silent: false, 16 | smartLists: false, 17 | smartypants: false, 18 | xhtml: false 19 | }; 20 | } 21 | 22 | function changeDefaults(newDefaults) { 23 | module.exports.defaults = newDefaults; 24 | } 25 | 26 | module.exports = { 27 | defaults: getDefaults(), 28 | getDefaults, 29 | changeDefaults 30 | }; 31 | -------------------------------------------------------------------------------- /test/specs/new/toplevel_paragraphs.html: -------------------------------------------------------------------------------- 1 |

hello world 2 | text after spaces 3 | text after spaces

4 | 5 |

paragraph before code

6 |
text inside block code
7 | 8 |

paragraph before hr

9 |
10 | 11 |

paragraph before blockquote

12 |

text for blockquote

13 | 14 |

paragraph before list

15 |
  • text inside list
16 | 17 |

paragraph before div

18 |
text inside div
19 | 20 |

paragraph with span 21 | text inside span

22 | 23 |

hello world 24 |

25 | 26 |
hello
27 | 28 |

hello

29 | -------------------------------------------------------------------------------- /src/TextRenderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * TextRenderer 3 | * returns only the textual part of the token 4 | */ 5 | module.exports = class TextRenderer { 6 | // no need for block level renderers 7 | strong(text) { 8 | return text; 9 | } 10 | 11 | em(text) { 12 | return text; 13 | } 14 | 15 | codespan(text) { 16 | return text; 17 | } 18 | 19 | del(text) { 20 | return text; 21 | } 22 | 23 | html(text) { 24 | return text; 25 | } 26 | 27 | text(text) { 28 | return text; 29 | } 30 | 31 | link(href, title, text) { 32 | return '' + text; 33 | } 34 | 35 | image(href, title, text) { 36 | return '' + text; 37 | } 38 | 39 | br() { 40 | return ''; 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /test/specs/new/relative_base_urls.md: -------------------------------------------------------------------------------- 1 | --- 2 | baseUrl: "/base/" 3 | --- 4 | # Absolutization of RFC 3986 URIs 5 | 6 | ## Absolute URI 7 | [![section 4.3](http://example.com/logo)](http://example.com/) 8 | 9 | ## Network-path reference 10 | [![section 4.2](//example.com/logo)](//example.com/) 11 | 12 | ## Absolute path 13 | [![section 4.2](/path/to/img)](/path/to/content) 14 | 15 | ## Relative path 16 | [![section 4.2](img)](content) 17 | 18 | ## Dot-relative path 19 | [![section 3.3](./img)](./content) 20 | 21 | [![section 3.3](../img)](../content) 22 | 23 | ## Same-document query 24 | [![section 4.4](?type=image)](?) 25 | 26 | ## Same-document fragment 27 | [![section 4.4](#img)](#) 28 | 29 | ## Empty 30 | [section 4.2]() 31 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "plugins": [ 4 | "standard" 5 | ], 6 | "rules": { 7 | "semi": ["error", "always"], 8 | "indent": ["error", 2, { 9 | "SwitchCase": 1, 10 | "VariableDeclarator": { "var": 2 }, 11 | "outerIIFEBody": 0 12 | }], 13 | "operator-linebreak": ["error", "before", { "overrides": { "=": "after" } }], 14 | "space-before-function-paren": ["error", "never"], 15 | "no-cond-assign": "off", 16 | "no-useless-escape": "off", 17 | "one-var": "off", 18 | "no-control-regex": "off", 19 | "no-prototype-builtins": "off", 20 | 21 | "prefer-const": "error", 22 | "no-var": "error" 23 | }, 24 | "env": { 25 | "node": true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/specs/new/relative_urls.md: -------------------------------------------------------------------------------- 1 | --- 2 | baseUrl: "http://example.com/base/" 3 | --- 4 | # Absolutization of RFC 3986 URIs 5 | 6 | ## Absolute URI 7 | [![section 4.3](http://example.com/logo)](http://example.com/) 8 | 9 | ## Network-path reference 10 | [![section 4.2](//example.com/logo)](//example.com/) 11 | 12 | ## Absolute path 13 | [![section 4.2](/path/to/img)](/path/to/content) 14 | 15 | ## Relative path 16 | [![section 4.2](img)](content) 17 | 18 | ## Dot-relative path 19 | [![section 3.3](./img)](./content) 20 | 21 | [![section 3.3](../img)](../content) 22 | 23 | ## Same-document query 24 | [![section 4.4](?type=image)](?) 25 | 26 | ## Same-document fragment 27 | [![section 4.4](#img)](#) 28 | 29 | ## Empty 30 | [section 4.2]() 31 | -------------------------------------------------------------------------------- /test/specs/new/blockquote_following_table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
abcdef
barfoo
bazboo
19 |

a blockquote

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
abcdef
barfoo
bazboo
38 |

a blockquote

39 | -------------------------------------------------------------------------------- /test/specs/new/blockquote_following_nptable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
abcdef
barfoo
bazboo
19 |

a blockquote

20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
abcdef
barfoo
bazboo
38 |

a blockquote

39 | -------------------------------------------------------------------------------- /test/specs/new/html_comments.md: -------------------------------------------------------------------------------- 1 | ### Example 1 2 | 3 | 4 | 5 | ### Example 2 6 | 7 | 8 | 9 | ### Example 3 10 | 11 | 12 | 13 | ### Example 4 14 | 15 | 16 | 17 | ### Example 5 18 | 19 | 20 | 21 | ### Example 6 22 | 23 | 24 | 25 | ### Example 7 26 | 27 | 28 | 29 | ### Example 8 30 | 31 | 32 | 33 | ### Example 9 34 | 35 | 36 | 37 | ### Example 10 38 | 39 | 43 | 44 | ### Example 11 45 | 46 | 47 | 48 | 49 | 50 | ### Example 12 51 | 52 | not a comment --> 53 | 54 | not a comment --> 55 | 56 | --> -------------------------------------------------------------------------------- /test/specs/new/code_following_nptable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
abcdef
barfoo
bazboo
19 |
a simple
20 |  *indented* code block
21 | 
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
abcdef
barfoo
bazboo
41 |
a simple
42 |  *indented* code block
43 | 
44 | -------------------------------------------------------------------------------- /test/specs/new/code_following_table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
abcdef
barfoo
bazboo
19 |
a simple
20 |  *indented* code block
21 | 
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |
abcdef
barfoo
bazboo
41 |
a simple
42 |  *indented* code block
43 | 
44 | -------------------------------------------------------------------------------- /test/specs/new/list_table.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • 3 |

    Table in list:

    4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    column1column2
    value1value2
    value3value4
    22 |
  • 23 |
  • 24 |

    No leading pipe table in list:

    25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
    column1column2
    value1value2
    value3value4
    43 |
  • 44 |
45 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | const commonjs = require('rollup-plugin-commonjs'); 2 | const babel = require('rollup-plugin-babel'); 3 | const license = require('rollup-plugin-license'); 4 | 5 | module.exports = { 6 | input: 'src/marked.js', 7 | output: { 8 | file: 'lib/marked.js', 9 | format: 'umd', 10 | name: 'marked' 11 | }, 12 | plugins: [ 13 | license({ 14 | banner: ` 15 | DO NOT EDIT THIS FILE 16 | The code in this file is generated from files in ./src/ 17 | ` 18 | }), 19 | license({ 20 | banner: ` 21 | marked - a markdown parser 22 | Copyright (c) 2011-${new Date().getFullYear()}, Christopher Jeffrey. (MIT Licensed) 23 | https://github.com/markedjs/marked 24 | ` 25 | }), 26 | commonjs(), 27 | babel({ 28 | presets: [['@babel/preset-env', { loose: true }]] 29 | }) 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /test/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "plugins": [ 4 | "standard" 5 | ], 6 | "globals": { 7 | "expectAsync": "readonly" 8 | }, 9 | "rules": { 10 | "semi": ["error", "always"], 11 | "indent": ["error", 2, { 12 | "SwitchCase": 1, 13 | "VariableDeclarator": { "var": 2 }, 14 | "outerIIFEBody": 0 15 | }], 16 | "operator-linebreak": ["error", "before", { "overrides": { "=": "after" } }], 17 | "space-before-function-paren": ["error", "never"], 18 | "no-cond-assign": "off", 19 | "no-useless-escape": "off", 20 | "one-var": "off", 21 | "no-control-regex": "off", 22 | "no-prototype-builtins": "off", 23 | 24 | "prefer-const": "error", 25 | "no-var": "error" 26 | }, 27 | "env": { 28 | "node": true, 29 | "jasmine": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/specs/original/horizontal_rules.md: -------------------------------------------------------------------------------- 1 | Dashes: 2 | 3 | --- 4 | 5 | --- 6 | 7 | --- 8 | 9 | --- 10 | 11 | --- 12 | 13 | - - - 14 | 15 | - - - 16 | 17 | - - - 18 | 19 | - - - 20 | 21 | - - - 22 | 23 | 24 | Asterisks: 25 | 26 | *** 27 | 28 | *** 29 | 30 | *** 31 | 32 | *** 33 | 34 | *** 35 | 36 | * * * 37 | 38 | * * * 39 | 40 | * * * 41 | 42 | * * * 43 | 44 | * * * 45 | 46 | 47 | Underscores: 48 | 49 | ___ 50 | 51 | ___ 52 | 53 | ___ 54 | 55 | ___ 56 | 57 | ___ 58 | 59 | _ _ _ 60 | 61 | _ _ _ 62 | 63 | _ _ _ 64 | 65 | _ _ _ 66 | 67 | _ _ _ 68 | 69 | 70 | 71 | Not horizontal rules: 72 | 73 | --* 74 | 75 | -*- 76 | 77 | *-- 78 | 79 | -_- 80 | 81 | __- 82 | 83 | -__ 84 | 85 | _-_ 86 | 87 | 88 | Long rules: 89 | 90 | ----------- 91 | 92 | ___________ 93 | 94 | *********** 95 | -------------------------------------------------------------------------------- /docs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "standard", 3 | "plugins": [ 4 | "standard" 5 | ], 6 | "parserOptions": { 7 | "ecmaVersion": 5, 8 | "sourceType": "script" 9 | }, 10 | "rules": { 11 | "semi": ["error", "always"], 12 | "indent": ["error", 2, { 13 | "SwitchCase": 1, 14 | "VariableDeclarator": { "var": 2 }, 15 | "outerIIFEBody": 0 16 | }], 17 | "operator-linebreak": ["error", "before", { "overrides": { "=": "after" } }], 18 | "space-before-function-paren": ["error", "never"], 19 | "no-cond-assign": "off", 20 | "no-useless-escape": "off", 21 | "one-var": "off", 22 | "no-control-regex": "off", 23 | "no-prototype-builtins": "off", 24 | 25 | "prefer-const": "off", 26 | "no-var": "off" 27 | }, 28 | "env": { 29 | "node": true, 30 | "browser": true, 31 | "amd": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Slugger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Slugger generates header id 3 | */ 4 | module.exports = class Slugger { 5 | constructor() { 6 | this.seen = {}; 7 | } 8 | 9 | /** 10 | * Convert string to unique id 11 | */ 12 | slug(value) { 13 | let slug = value 14 | .toLowerCase() 15 | .trim() 16 | // remove html tags 17 | .replace(/<[!\/a-z].*?>/ig, '') 18 | // remove unwanted chars 19 | .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '') 20 | .replace(/\s/g, '-'); 21 | 22 | if (this.seen.hasOwnProperty(slug)) { 23 | const originalSlug = slug; 24 | do { 25 | this.seen[originalSlug]++; 26 | slug = originalSlug + '-' + this.seen[originalSlug]; 27 | } while (this.seen.hasOwnProperty(slug)); 28 | } 29 | this.seen[slug] = 0; 30 | 31 | return slug; 32 | }; 33 | }; 34 | -------------------------------------------------------------------------------- /test/helpers/helpers.js: -------------------------------------------------------------------------------- 1 | const marked = require('../../src/marked.js'); 2 | const htmlDiffer = require('./html-differ.js'); 3 | 4 | beforeEach(() => { 5 | marked.setOptions(marked.getDefaults()); 6 | 7 | jasmine.addAsyncMatchers({ 8 | toRender: () => { 9 | return { 10 | compare: async(spec, expected) => { 11 | const result = {}; 12 | const actual = marked(spec.markdown, spec.options); 13 | result.pass = await htmlDiffer.isEqual(expected, actual); 14 | 15 | if (result.pass) { 16 | result.message = `${spec.markdown}\n------\n\nExpected: Should Fail`; 17 | } else { 18 | const diff = await htmlDiffer.firstDiff(actual, expected); 19 | result.message = `Expected: ${diff.expected}\n Actual: ${diff.actual}`; 20 | } 21 | return result; 22 | } 23 | }; 24 | } 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/specs/original/inline_html_simple.md: -------------------------------------------------------------------------------- 1 | Here's a simple block: 2 | 3 |
4 | foo 5 |
6 | 7 | This should be a code block, though: 8 | 9 |
10 | foo 11 |
12 | 13 | As should this: 14 | 15 |
foo
16 | 17 | Now, nested: 18 | 19 |
20 |
21 |
22 | foo 23 |
24 |
25 |
26 | 27 | This should just be an HTML comment: 28 | 29 | 30 | 31 | Multiline: 32 | 33 | 37 | 38 | Code block: 39 | 40 | 41 | 42 | Just plain comment, with trailing spaces on the line: 43 | 44 | 45 | 46 | Code: 47 | 48 |
49 | 50 | Hr's: 51 | 52 |
53 | 54 |
55 | 56 |
57 | 58 |
59 | 60 |
61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 |
69 | 70 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Marked says it does this thing but does not 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 13 | 14 | 18 | 19 | 20 | 25 | 26 | **Expected behavior** 27 | A clear and concise description of what you expected to happen. 28 | -------------------------------------------------------------------------------- /test/specs/original/horizontal_rules.html: -------------------------------------------------------------------------------- 1 |

Dashes:

2 | 3 |
4 | 5 |
6 | 7 |
8 | 9 |
10 | 11 |
---
12 | 
13 | 14 |
15 | 16 |
17 | 18 |
19 | 20 |
21 | 22 |
- - -
23 | 
24 | 25 |

Asterisks:

26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 |
34 | 35 |
***
36 | 
37 | 38 |
39 | 40 |
41 | 42 |
43 | 44 |
45 | 46 |
* * *
47 | 
48 | 49 |

Underscores:

50 | 51 |
52 | 53 |
54 | 55 |
56 | 57 |
58 | 59 |
___
60 | 
61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 |
69 | 70 |
_ _ _
71 | 
72 | 73 |

Not horizontal rules:

74 |

--*

75 |

-*-

76 |

*--

77 |

-_-

78 |

__-

79 |

-__

80 |
_-_
81 | 
82 |

Long rules:

83 |
84 |
85 |
86 | -------------------------------------------------------------------------------- /test/specs/new/html_comments.html: -------------------------------------------------------------------------------- 1 |

Example 1

2 | 3 | 4 | 5 |

Example 2

6 | 7 | 8 | 9 |

Example 3

10 | 11 | 12 | 13 |

Example 4

14 | 15 | 16 | 17 |

Example 5

18 | 19 | 20 | 21 |

Example 6

22 | 23 | 24 | 25 |

Example 7

26 | 27 | 28 | 29 |

Example 8

30 | 31 | 32 | 33 |

Example 9

34 | 35 | 36 | 37 |

Example 10

38 | 39 | 43 | 44 |

Example 11

45 | 46 | 47 | 48 |
<!-- too much indentation -->
49 | 
50 | 51 |

Example 12

52 | 53 |

<!--> not a comment -->

54 | 55 |

<!---> not a comment -->

56 | 57 | --> 58 | -------------------------------------------------------------------------------- /test/specs/original/inline_html_simple.html: -------------------------------------------------------------------------------- 1 |

Here's a simple block:

2 | 3 |
4 | foo 5 |
6 | 7 |

This should be a code block, though:

8 | 9 |
<div>
10 |     foo
11 | </div>
12 | 
13 | 14 |

As should this:

15 | 16 |
<div>foo</div>
17 | 
18 | 19 |

Now, nested:

20 | 21 |
22 |
23 |
24 | foo 25 |
26 |
27 |
28 | 29 |

This should just be an HTML comment:

30 | 31 | 32 | 33 |

Multiline:

34 | 35 | 39 | 40 |

Code block:

41 | 42 |
<!-- Comment -->
43 | 
44 | 45 |

Just plain comment, with trailing spaces on the line:

46 | 47 | 48 | 49 |

Code:

50 | 51 |
<hr />
52 | 
53 | 54 |

Hr's:

55 | 56 |
57 | 58 |
59 | 60 |
61 | 62 |
63 | 64 |
65 | 66 |
67 | 68 |
69 | 70 |
71 | 72 |
73 | -------------------------------------------------------------------------------- /test/specs/original/links_reference_style.md: -------------------------------------------------------------------------------- 1 | --- 2 | pedantic: true 3 | --- 4 | 5 | Foo [bar] [1]. 6 | 7 | Foo [bar][1]. 8 | 9 | Foo [bar] 10 | [1]. 11 | 12 | [1]: /url/ "Title" 13 | 14 | 15 | With [embedded [brackets]] [b]. 16 | 17 | 18 | Indented [once][]. 19 | 20 | Indented [twice][]. 21 | 22 | Indented [thrice][]. 23 | 24 | Indented [four][] times. 25 | 26 | [once]: /url 27 | 28 | [twice]: /url 29 | 30 | [thrice]: /url 31 | 32 | [four]: /url 33 | 34 | 35 | [b]: /url/ 36 | 37 | * * * 38 | 39 | [this] [this] should work 40 | 41 | So should [this][this]. 42 | 43 | And [this] []. 44 | 45 | And [this][]. 46 | 47 | And [this]. 48 | 49 | But not [that] []. 50 | 51 | Nor [that][]. 52 | 53 | Nor [that]. 54 | 55 | [Something in brackets like [this][] should work] 56 | 57 | [Same with [this].] 58 | 59 | In this case, [this](/somethingelse/) points to something else. 60 | 61 | Backslashing should suppress \[this] and [this\]. 62 | 63 | [this]: foo 64 | 65 | 66 | * * * 67 | 68 | Here's one where the [link 69 | breaks] across lines. 70 | 71 | Here's another where the [link 72 | breaks] across lines, but with a line-ending space. 73 | 74 | 75 | [link breaks]: /url/ 76 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Marked version:** 2 | 3 | **Markdown flavor:** Markdown.pl|CommonMark|GitHub Flavored Markdown|n/a 4 | 5 | 6 | 7 | 18 | 19 | ## Expectation 20 | 21 | **CommonMark Demo:** [demo](https://spec.commonmark.org/dingus/) 22 | 23 | 24 | 25 | 26 | ## Result 27 | 28 | **Marked Demo:** [demo](https://marked.js.org/demo/) 29 | 30 | 31 | 32 | 33 | ## What was attempted 34 | 35 | 36 | 37 | 43 | -------------------------------------------------------------------------------- /test/specs/redos/link_code.md: -------------------------------------------------------------------------------- 1 | INDEX(string, pattern[, start)` : searches for the first occurrence of pattern in string, starting from start: `INDEX("123123", "23", 3)` == `5` 2 | `INSERT(new, old[, start][, length][, pad])` : inserts the new string into the old string after the specified position (default is 0), new string is truncated or padded (default is " ") to the specified length, if start is beyond the end of old old will be padded 3 | `LASTPOS(pattern, string[, start])` : searches backwards for the last occurrence of pattern in string, starting from start: `LASTPOS("123123", "23", 4)` == `2` 4 | `LINES(file)` : returns the number of lines typed ahead at the interactive stream: `push("a line"); push("second line"); lines(STDIN); /* == 2 */` 5 | `MAX(number, number[, number,...])` : obvious 6 | `MIN(number, number[, number,...])` : obvious 7 | `OPEN(filehandle, filename[, "APPEND"|"READ"|"WRITE"])` : opens file, returns boolean for success: `OPEN("MyCon", "CON:160/50/320/100/MyCon/CDS")` == `1` 8 | `OVERLAY(new, old[, start][, length][, pad])` : overlays new string onto old one at start for length chars padding with pad if necessary: `OVERLAY("4", "123", 5, 5)` == `"123-4----"` 9 | `POS(pattern, string[, start])` : same as index 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | jobs: 4 | fast_finish: true 5 | allow_failures: 6 | - stage: security scan 🔐 7 | 8 | include: 9 | - stage: unit tests 👩🏽‍💻 10 | script: npm run test:unit 11 | node_js: lts/* 12 | 13 | - stage: spec tests 👩🏽‍💻 14 | script: npm run test:specs 15 | node_js: lts/* 16 | - node_js: node 17 | 18 | - stage: lint ✨ 19 | script: npm run test:lint 20 | node_js: lts/* 21 | 22 | - stage: build 🗜️ 23 | script: | 24 | npm run build 25 | if ! git diff --quiet; then 26 | git config --global user.email "<>" 27 | git config --global user.name "MarkedJS bot" 28 | git config credential.helper "store --file=.git/credentials" 29 | echo "https://${GITHUB_TOKEN}:@github.com" > .git/credentials 30 | git commit -am '🗜️ build [skip ci]' 31 | git push origin HEAD:${TRAVIS_BRANCH} 32 | fi 33 | node_js: lts/* 34 | if: branch = master AND type = push 35 | 36 | # - stage: security scan 🔐 37 | # script: npm run test:redos 38 | # node_js: lts/* 39 | 40 | cache: 41 | directories: 42 | - node_modules 43 | 44 | git: 45 | depth: 3 46 | -------------------------------------------------------------------------------- /docs/demo/demo.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | color: #333; 6 | background-color: #fbfbfb; 7 | height: 100%; 8 | } 9 | 10 | textarea { 11 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 12 | font-size: 12px; 13 | resize: none; 14 | } 15 | 16 | header { 17 | padding-top: 10px; 18 | display: flex; 19 | height: 58px; 20 | } 21 | 22 | header h1 { 23 | margin: 0; 24 | } 25 | 26 | .github-ribbon { 27 | position: absolute; 28 | top: 0; 29 | right: 0; 30 | border: 0; 31 | z-index: 1000; 32 | } 33 | 34 | .containers { 35 | display: flex; 36 | height: calc(100vh - 68px); 37 | } 38 | 39 | .container { 40 | flex-basis: 50%; 41 | padding: 5px; 42 | display: flex; 43 | flex-direction: column; 44 | height: 100%; 45 | box-sizing: border-box; 46 | } 47 | 48 | .pane, .inputPane { 49 | margin-top: 5px; 50 | padding: 0.6em; 51 | border: 1px solid #ccc; 52 | overflow: auto; 53 | flex-grow: 1; 54 | flex-shrink: 1; 55 | } 56 | 57 | #preview { 58 | display: flex; 59 | } 60 | 61 | #preview iframe { 62 | flex-grow: 1; 63 | } 64 | 65 | #main { 66 | display: none; 67 | } 68 | 69 | .error { 70 | border-color: red; 71 | background-color: #FEE 72 | } 73 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/release.md: -------------------------------------------------------------------------------- 1 | ## Publisher 2 | 3 | - [ ] `$ npm version` has been run. 4 | - [ ] Release notes in [draft GitHub release](https://github.com/markedjs/marked/releases) are up to date 5 | - [ ] Release notes include which flavors and versions of Markdown are supported by this release 6 | - [ ] Committer checklist is complete. 7 | - [ ] Merge PR. 8 | - [ ] Publish GitHub release using `master` with correct version number. 9 | - [ ] `$ npm publish` has been run. 10 | - [ ] Create draft GitHub release to prepare next release. 11 | 12 | Note: If merges to `master` occur after submitting this PR and before running `$ npm pubish` you should be able to 13 | 14 | 1. pull from `upstream/master` (`git pull upstream master`) into the branch holding this version, 15 | 2. run `$ npm run build` to regenerate the `min` file, and 16 | 3. commit and push the updated changes. 17 | 18 | ## Committer 19 | 20 | In most cases, this should be someone different than the publisher. 21 | 22 | - [ ] Version in `package.json` has been updated (see [PUBLISHING.md](https://github.com/markedjs/marked/blob/master/docs/PUBLISHING.md)). 23 | - [ ] The `marked.min.js` has been updated; or, 24 | - [ ] release does not change library. 25 | - [ ] CI is green (no forced merge required). 26 | -------------------------------------------------------------------------------- /test/specs/new/main.md: -------------------------------------------------------------------------------- 1 | [test]: http://google.com/ "Google" 2 | 3 | # A heading 4 | 5 | Just a note, I've found that I can't test my markdown parser vs others. 6 | For example, both markdown.js and showdown code blocks in lists wrong. They're 7 | also completely [inconsistent][test] with regards to paragraphs in list items. 8 | 9 | A link. Not anymore. 10 | 11 | 13 | 14 | * List Item 1 15 | 16 | * List Item 2 17 | * New List Item 1 18 | Hi, this is a list item. 19 | * New List Item 2 20 | Another item 21 | Code goes here. 22 | Lots of it... 23 | * New List Item 3 24 | The last item 25 | 26 | * List Item 3 27 | The final item. 28 | 29 | * List Item 4 30 | The real final item. 31 | 32 | Paragraph. 33 | 34 | > * bq Item 1 35 | > * bq Item 2 36 | > * New bq Item 1 37 | > * New bq Item 2 38 | > Text here 39 | 40 | * * * 41 | 42 | > Another blockquote! 43 | > I really need to get 44 | > more creative with 45 | > mockup text.. 46 | > markdown.js breaks here again 47 | 48 | Another Heading 49 | ------------- 50 | 51 | Hello *world*. Here is a [link](//hello). 52 | And an image ![alt](src). 53 | 54 | Code goes here. 55 | Lots of it... 56 | -------------------------------------------------------------------------------- /test/specs/new/relative_base_urls.html: -------------------------------------------------------------------------------- 1 |

Absolutization of RFC 3986 URIs

2 | 3 |

Absolute URI

4 | 5 |

section 4.3

6 | 7 |

Network-path reference

8 | 9 |

section 4.2

10 | 11 |

Absolute path

12 | 13 |

section 4.2

14 | 15 |

Relative path

16 | 17 |

section 4.2

18 | 19 |

Dot-relative path

20 | 21 |

section 3.3

22 | 23 |

section 3.3

24 | 25 |

Same-document query

26 | 27 |

section 4.4

28 | 29 |

Same-document fragment

30 | 31 |

section 4.4

32 | 33 |

Empty

34 | 35 |

section 4.2

36 | -------------------------------------------------------------------------------- /test/helpers/html-differ.js: -------------------------------------------------------------------------------- 1 | const HtmlDiffer = require('@markedjs/html-differ').HtmlDiffer; 2 | const htmlDiffer = new HtmlDiffer({ ignoreSelfClosingSlash: true }); 3 | 4 | module.exports = { 5 | isEqual: htmlDiffer.isEqual.bind(htmlDiffer), 6 | firstDiff: async(actual, expected, padding) => { 7 | padding = padding || 30; 8 | const diffHtml = await htmlDiffer.diffHtml(actual, expected); 9 | const result = diffHtml.reduce((obj, diff) => { 10 | if (diff.added) { 11 | if (obj.firstIndex === null) { 12 | obj.firstIndex = obj.expected.length; 13 | } 14 | obj.expected += diff.value; 15 | } else if (diff.removed) { 16 | if (obj.firstIndex === null) { 17 | obj.firstIndex = obj.actual.length; 18 | } 19 | obj.actual += diff.value; 20 | } else { 21 | obj.actual += diff.value; 22 | obj.expected += diff.value; 23 | } 24 | 25 | return obj; 26 | }, { 27 | firstIndex: null, 28 | actual: '', 29 | expected: '' 30 | }); 31 | 32 | return { 33 | actual: result.actual.substring(result.firstIndex - padding, result.firstIndex + padding), 34 | expected: result.expected.substring(result.firstIndex - padding, result.firstIndex + padding) 35 | }; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /test/specs/new/em_list_links.md: -------------------------------------------------------------------------------- 1 | - italic 2 | - [*named link*][some-url] 3 | - *[named link][some-url]* 4 | - [_named link_][some-url] 5 | - _[named link][some-url]_ 6 | - bold 7 | - [**named link**][some-url] 8 | - **[named link][some-url]** 9 | - [__named link__][some-url] 10 | - __[named link][some-url]__ 11 | - bold italic 12 | - [***named link***][some-url] 13 | - ***[named link][some-url]*** 14 | - [___named link___][some-url] 15 | - ___[named link][some-url]___ 16 | - [*__named link__*][some-url] 17 | - [__*named link*__][some-url] 18 | - __*[named link][some-url]*__ 19 | - code 20 | - [`named link`][some-url] 21 | - code italic 22 | - *[`named link`][some-url]* 23 | - [*`named link`*][some-url] 24 | - _[`named link`][some-url]_ 25 | - [_`named link`_][some-url] 26 | - code bold 27 | - **[`named link`][some-url]** 28 | - [**`named link`**][some-url] 29 | - __[`named link`][some-url]__ 30 | - [__`named link`__][some-url] 31 | - code bold italic 32 | - [***`named link`***][some-url] 33 | - ***[`named link`][some-url]*** 34 | - [___`named link`___][some-url] 35 | - ___[`named link`][some-url]___ 36 | - [*__`named link`__*][some-url] 37 | - [__*`named link`*__][some-url] 38 | - __*[`named link`][some-url]*__ 39 | 40 | [some-url]: https://www.google.com -------------------------------------------------------------------------------- /test/specs/new/main.html: -------------------------------------------------------------------------------- 1 |

A heading

Just a note, I've found that I can't test my markdown parser vs others. For example, both markdown.js and showdown code blocks in lists wrong. They're also completely inconsistent with regards to paragraphs in list items.

A link. Not anymore.

  • List Item 1

  • List Item 2

    • New List Item 1 Hi, this is a list item.
    • New List Item 2 Another item
      Code goes here.
      3 | Lots of it...
    • New List Item 3 The last item
  • List Item 3 The final item.

  • List Item 4 The real final item.

Paragraph.

  • bq Item 1
  • bq Item 2
    • New bq Item 1
    • New bq Item 2 Text here

Another blockquote! I really need to get more creative with mockup text.. markdown.js breaks here again

Another Heading

Hello world. Here is a link. And an image alt.

Code goes here.
4 | Lots of it...
5 | -------------------------------------------------------------------------------- /docs/PUBLISHING.md: -------------------------------------------------------------------------------- 1 | # Releasing Marked 2 | 3 | - [ ] See [contributing](#/CONTRIBUTING.md) 4 | - [ ] Create release branch from `master` (`release-x.y.z`) 5 | - [ ] Submit PR with minimal name: Release x.y.z 6 | - [ ] Complete PR checklists 7 | 8 | ## Overall strategy 9 | 10 | **Master is always shippable:** We try to merge PRs in such a way that `master` is the only branch to really be concerned about *and* `master` can always be released. This allows smoother flow between new features, bug fixes, and so on. (Almost a continuous deployment setup, without automation.) 11 | 12 | ## Versioning 13 | 14 | We follow [semantic versioning](https://semver.org) where the following sequence is true `[major].[minor].[patch]`; therefore, consider the following implications of the release you are preparing: 15 | 16 | 1. **Major:** There is at least one change not deemed backward compatible. 17 | 2. **Minor:** There is at least one new feature added to the release. 18 | 3. **Patch:** No breaking changes, no new features. 19 | 20 | What to expect while Marked is a zero-major (0.x.y): 21 | 22 | 1. The major will remain at zero; thereby, alerting consumers to the potentially volatile nature of the package. 23 | 2. The minor will tend to be more analogous to a `major` release. 24 | 3. The patch will tend to be more analogous to a `minor` release or a collection of bug fixes (patches). 25 | -------------------------------------------------------------------------------- /test/specs/original/links_reference_style.html: -------------------------------------------------------------------------------- 1 |

Foo bar.

2 | 3 |

Foo bar.

4 | 5 |

Foo bar.

6 | 7 |

With embedded [brackets].

8 | 9 |

Indented once.

10 | 11 |

Indented twice.

12 | 13 |

Indented thrice.

14 | 15 |

Indented [four][] times.

16 | 17 |
[four]: /url
18 | 
19 | 20 |
21 | 22 |

this should work

23 | 24 |

So should this.

25 | 26 |

And this.

27 | 28 |

And this.

29 | 30 |

And this.

31 | 32 |

But not [that] [].

33 | 34 |

Nor [that][].

35 | 36 |

Nor [that].

37 | 38 |

[Something in brackets like this should work]

39 | 40 |

[Same with this.]

41 | 42 |

In this case, this points to something else.

43 | 44 |

Backslashing should suppress [this] and [this].

45 | 46 |
47 | 48 |

Here's one where the link 49 | breaks across lines.

50 | 51 |

Here's another where the link 52 | breaks across lines, but with a line-ending space.

53 | -------------------------------------------------------------------------------- /test/vuln-regex.js: -------------------------------------------------------------------------------- 1 | const regexp = require('../src/rules.js'); 2 | const vulnRegexDetector = require('vuln-regex-detector'); 3 | 4 | const promises = []; 5 | function findRegexps(name, obj) { 6 | if (typeof obj === 'string') { 7 | promises.push(testRegexp(name, obj)); 8 | } if (obj instanceof RegExp || obj.exec) { 9 | if (obj.source) { 10 | promises.push(testRegexp(name, obj.source)); 11 | } 12 | } else if (typeof obj === 'object') { 13 | for (const prop in obj) { 14 | findRegexps(name + (name ? '.' : '') + prop, obj[prop]); 15 | } 16 | } 17 | } 18 | 19 | async function testRegexp(name, source) { 20 | try { 21 | const result = await vulnRegexDetector.test(source); 22 | 23 | if (result === vulnRegexDetector.responses.safe) { 24 | console.log(`${name} is safe`); 25 | return true; 26 | } else if (result === vulnRegexDetector.responses.vulnerable) { 27 | console.error(`${name} is vulnerable`); 28 | } else { 29 | console.error(`${name} might be vulnerable: ` + result.toString()); 30 | } 31 | } catch (ex) { 32 | console.error(`${name} failed with error: ` + ex.toString()); 33 | } 34 | return false; 35 | } 36 | 37 | findRegexps('', regexp); 38 | // promises.push(testRegexp('a', /(a+)+$/.source)); 39 | Promise.allSettled(promises).then(results => { 40 | const code = results.every(r => r.value) ? 0 : 1; 41 | process.exit(code); 42 | }); 43 | -------------------------------------------------------------------------------- /test/specs/redos/redos_html_closing.html: -------------------------------------------------------------------------------- 1 |

<tag "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""<" />a'a

2 | -------------------------------------------------------------------------------- /test/specs/new/relative_urls.html: -------------------------------------------------------------------------------- 1 |

Absolutization of RFC 3986 URIs

2 | 3 |

Absolute URI

4 | 5 |

section 4.3

6 | 7 |

Network-path reference

8 | 9 |

section 4.2

10 | 11 |

Absolute path

12 | 13 |

section 4.2

14 | 15 |

Relative path

16 | 17 |

section 4.2

18 | 19 |

Dot-relative path

20 | 21 |

section 3.3

22 | 23 |

section 3.3

24 | 25 |

Same-document query

26 | 27 |

section 4.4

28 | 29 |

Same-document fragment

30 | 31 |

section 4.4

32 | 33 |

Empty

34 | 35 |

section 4.2

36 | -------------------------------------------------------------------------------- /test/specs/redos/link_code.html: -------------------------------------------------------------------------------- 1 |

INDEX(string, pattern[, start): searches for the first occurrence of pattern in string, starting from start:INDEX("123123", "23", 3)==5INSERT(new, old[, start][, length][, pad]): inserts the new string into the old string after the specified position (default is 0), new string is truncated or padded (default is " ") to the specified length, if start is beyond the end of old old will be paddedLASTPOS(pattern, string[, start]): searches backwards for the last occurrence of pattern in string, starting from start:LASTPOS("123123", "23", 4)==2LINES(file): returns the number of lines typed ahead at the interactive stream:push("a line"); push("second line"); lines(STDIN); /* == 2 */MAX(number, number[, number,...]): obviousMIN(number, number[, number,...]): obviousOPEN(filehandle, filename[, "APPEND"|"READ"|"WRITE"]): opens file, returns boolean for success:OPEN("MyCon", "CON:160/50/320/100/MyCon/CDS")==1OVERLAY(new, old[, start][, length][, pad]): overlays new string onto old one at start for length chars padding with pad if necessary:OVERLAY("4", "123", 5, 5)=="123-4----"POS(pattern, string[, start])` : same as index

2 | -------------------------------------------------------------------------------- /test/specs/new/table_cells.html: -------------------------------------------------------------------------------- 1 |
1
1
2 | 3 |
1
|
4 | 5 |
1
1\1
6 | 7 |
1
\\
8 | 9 |
1
\\|
10 | 11 |
12
2
12 | 13 |
12
1|\2|\
14 | 15 |
12
2
16 | 17 |
12
1|\2|\
18 | 19 |
12
2
20 | 21 |
12
12|
22 | 23 |
12
12|
24 | 25 |
12
12|
26 | 27 |
12
12|
28 | -------------------------------------------------------------------------------- /test/specs/new/link_tick_redos.md: -------------------------------------------------------------------------------- 1 | dash_capstyle: ['butt' | 'round' | 'projecting'] 2 | dash_joinstyle: ['miter' | 'round' | 'bevel'] 3 | dashes: sequence of on/off ink in points 4 | drawstyle: ['default' | 'steps' | 'steps-pre' | 'steps-mid' | 'steps-post'] 5 | figure: a `~.Figure` instance 6 | fillstyle: ['full' | 'left' | 'right' | 'bottom' | 'top' | 'none'] 7 | gid: an id string 8 | label: object 9 | linestyle or ls: ['solid' | 'dashed', 'dashdot', 'dotted' | (offset, on-off-dash-seq) | ``'-'`` | ``'--'`` | ``'-.'`` | ``':'`` | ``'None'`` | ``' '`` | ``''``] 10 | linewidth or lw: float value in points 11 | marker: :mod:`A valid marker style ` 12 | markeredgecolor or mec: any matplotlib color 13 | markeredgewidth or mew: float value in points 14 | markerfacecolor or mfc: any matplotlib color 15 | markerfacecoloralt or mfcalt: any matplotlib color 16 | markersize or ms: float 17 | markevery: [None | int | length-2 tuple of int | slice | list/array of int | float | length-2 tuple of float] 18 | path_effects: `~.AbstractPathEffect` 19 | picker: float distance in points or callable pick function ``fn(artist, event)`` 20 | pickradius: float distance in points 21 | rasterized: bool or None 22 | sketch_params: (scale: float, length: float, randomness: float) 23 | snap: bool or None 24 | solid_capstyle: ['butt' | 'round' | 'projecting'] 25 | solid_joinstyle: ['miter' | 'round' | 'bevel'] 26 | transform: a :class:`matplotlib.transforms.Transform` instance 27 | url: a url string 28 | visible: bool 29 | xdata: 1D array 30 | ydata: 1D array 31 | zorder: float 32 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | **Marked version:** 14 | 15 | 16 | 17 | **Markdown flavor:** Markdown.pl|CommonMark|GitHub Flavored Markdown|n/a 18 | 19 | ## Description 20 | 21 | - Fixes #### (if fixing a known issue; otherwise, describe issue using the following format) 22 | 23 | 40 | 41 | ## Contributor 42 | 43 | - [ ] Test(s) exist to ensure functionality and minimize regression (if no tests added, list tests covering this PR); or, 44 | - [ ] no tests required for this PR. 45 | - [ ] If submitting new feature, it has been documented in the appropriate places. 46 | 47 | ## Committer 48 | 49 | In most cases, this should be a different person than the contributor. 50 | 51 | - [ ] Draft GitHub release notes have been updated. 52 | - [ ] CI is green (no forced merge required). 53 | - [ ] Merge PR 54 | -------------------------------------------------------------------------------- /docs/demo/initial.md: -------------------------------------------------------------------------------- 1 | Marked - Markdown Parser 2 | ======================== 3 | 4 | [Marked] lets you convert [Markdown] into HTML. Markdown is a simple text format whose goal is to be very easy to read and write, even when not converted to HTML. This demo page will let you type anything you like and see how it gets converted. Live. No more waiting around. 5 | 6 | How To Use The Demo 7 | ------------------- 8 | 9 | 1. Type in stuff on the left. 10 | 2. See the live updates on the right. 11 | 12 | That's it. Pretty simple. There's also a drop-down option in the upper right to switch between various views: 13 | 14 | - **Preview:** A live display of the generated HTML as it would render in a browser. 15 | - **HTML Source:** The generated HTML before your browser makes it pretty. 16 | - **Lexer Data:** What [marked] uses internally, in case you like gory stuff like this. 17 | - **Quick Reference:** A brief run-down of how to format things using markdown. 18 | 19 | Why Markdown? 20 | ------------- 21 | 22 | It's easy. It's not overly bloated, unlike HTML. Also, as the creator of [markdown] says, 23 | 24 | > The overriding design goal for Markdown's 25 | > formatting syntax is to make it as readable 26 | > as possible. The idea is that a 27 | > Markdown-formatted document should be 28 | > publishable as-is, as plain text, without 29 | > looking like it's been marked up with tags 30 | > or formatting instructions. 31 | 32 | Ready to start writing? Either start changing stuff on the left or 33 | [clear everything](/demo/?text=) with a simple click. 34 | 35 | [Marked]: https://github.com/markedjs/marked/ 36 | [Markdown]: http://daringfireball.net/projects/markdown/ 37 | -------------------------------------------------------------------------------- /test/specs/original/ordered_and_unordered_lists.md: -------------------------------------------------------------------------------- 1 | --- 2 | headerIds: false 3 | --- 4 | ## Unordered 5 | 6 | Asterisks tight: 7 | 8 | * asterisk 1 9 | * asterisk 2 10 | * asterisk 3 11 | 12 | 13 | Asterisks loose: 14 | 15 | * asterisk 1 16 | 17 | * asterisk 2 18 | 19 | * asterisk 3 20 | 21 | * * * 22 | 23 | Pluses tight: 24 | 25 | + Plus 1 26 | + Plus 2 27 | + Plus 3 28 | 29 | 30 | Pluses loose: 31 | 32 | + Plus 1 33 | 34 | + Plus 2 35 | 36 | + Plus 3 37 | 38 | * * * 39 | 40 | 41 | Minuses tight: 42 | 43 | - Minus 1 44 | - Minus 2 45 | - Minus 3 46 | 47 | 48 | Minuses loose: 49 | 50 | - Minus 1 51 | 52 | - Minus 2 53 | 54 | - Minus 3 55 | 56 | 57 | ## Ordered 58 | 59 | Tight: 60 | 61 | 1. First 62 | 2. Second 63 | 3. Third 64 | 65 | and: 66 | 67 | 1. One 68 | 2. Two 69 | 3. Three 70 | 71 | 72 | Loose using tabs: 73 | 74 | 1. First 75 | 76 | 2. Second 77 | 78 | 3. Third 79 | 80 | and using spaces: 81 | 82 | 1. One 83 | 84 | 2. Two 85 | 86 | 3. Three 87 | 88 | Multiple paragraphs: 89 | 90 | 1. Item 1, graf one. 91 | 92 | Item 2. graf two. The quick brown fox jumped over the lazy dog's 93 | back. 94 | 95 | 2. Item 2. 96 | 97 | 3. Item 3. 98 | 99 | 100 | 101 | ## Nested 102 | 103 | * Tab 104 | * Tab 105 | * Tab 106 | 107 | Here's another: 108 | 109 | 1. First 110 | 2. Second: 111 | * Fee 112 | * Fie 113 | * Foe 114 | 3. Third 115 | 116 | Same thing but with paragraphs: 117 | 118 | 1. First 119 | 120 | 2. Second: 121 | * Fee 122 | * Fie 123 | * Foe 124 | 125 | 3. Third 126 | 127 | 128 | This was an error in Markdown 1.0.1: 129 | 130 | * this 131 | 132 | * sub 133 | 134 | that 135 | 136 | Ordered lists start from initial number: 137 | 138 | 3. Three 139 | 1. Four 140 | 141 | Ordered lists start from initial zero: 142 | 143 | 0. Zero 144 | 1. One 145 | -------------------------------------------------------------------------------- /test/specs/new/link_tick_redos.html: -------------------------------------------------------------------------------- 1 |

dash_capstyle: ['butt' | 'round' | 'projecting'] 2 | dash_joinstyle: ['miter' | 'round' | 'bevel'] 3 | dashes: sequence of on/off ink in points 4 | drawstyle: ['default' | 'steps' | 'steps-pre' | 'steps-mid' | 'steps-post'] 5 | figure: a ~.Figure instance 6 | fillstyle: ['full' | 'left' | 'right' | 'bottom' | 'top' | 'none'] 7 | gid: an id string 8 | label: object 9 | linestyle or ls: ['solid' | 'dashed', 'dashdot', 'dotted' | (offset, on-off-dash-seq) | '-' | '--' | '-.' | ':' | 'None' | ' ' | ''] 10 | linewidth or lw: float value in points 11 | marker: :mod:A valid marker style <matplotlib.markers> 12 | markeredgecolor or mec: any matplotlib color 13 | markeredgewidth or mew: float value in points 14 | markerfacecolor or mfc: any matplotlib color 15 | markerfacecoloralt or mfcalt: any matplotlib color 16 | markersize or ms: float 17 | markevery: [None | int | length-2 tuple of int | slice | list/array of int | float | length-2 tuple of float] 18 | path_effects: ~.AbstractPathEffect 19 | picker: float distance in points or callable pick function fn(artist, event) 20 | pickradius: float distance in points 21 | rasterized: bool or None 22 | sketch_params: (scale: float, length: float, randomness: float) 23 | snap: bool or None 24 | solid_capstyle: ['butt' | 'round' | 'projecting'] 25 | solid_joinstyle: ['miter' | 'round' | 'bevel'] 26 | transform: a :class:matplotlib.transforms.Transform instance 27 | url: a url string 28 | visible: bool 29 | xdata: 1D array 30 | ydata: 1D array 31 | zorder: float

32 | -------------------------------------------------------------------------------- /test/specs/original/backslash_escapes.md: -------------------------------------------------------------------------------- 1 | These should all get escaped: 2 | 3 | Backslash: \\ 4 | 5 | Backtick: \` 6 | 7 | Asterisk: \* 8 | 9 | Underscore: \_ 10 | 11 | Left brace: \{ 12 | 13 | Right brace: \} 14 | 15 | Left bracket: \[ 16 | 17 | Right bracket: \] 18 | 19 | Left paren: \( 20 | 21 | Right paren: \) 22 | 23 | Greater-than: \> 24 | 25 | Hash: \# 26 | 27 | Period: \. 28 | 29 | Bang: \! 30 | 31 | Plus: \+ 32 | 33 | Minus: \- 34 | 35 | 36 | 37 | These should not, because they occur within a code block: 38 | 39 | Backslash: \\ 40 | 41 | Backtick: \` 42 | 43 | Asterisk: \* 44 | 45 | Underscore: \_ 46 | 47 | Left brace: \{ 48 | 49 | Right brace: \} 50 | 51 | Left bracket: \[ 52 | 53 | Right bracket: \] 54 | 55 | Left paren: \( 56 | 57 | Right paren: \) 58 | 59 | Greater-than: \> 60 | 61 | Hash: \# 62 | 63 | Period: \. 64 | 65 | Bang: \! 66 | 67 | Plus: \+ 68 | 69 | Minus: \- 70 | 71 | 72 | Nor should these, which occur in code spans: 73 | 74 | Backslash: `\\` 75 | 76 | Backtick: `` \` `` 77 | 78 | Asterisk: `\*` 79 | 80 | Underscore: `\_` 81 | 82 | Left brace: `\{` 83 | 84 | Right brace: `\}` 85 | 86 | Left bracket: `\[` 87 | 88 | Right bracket: `\]` 89 | 90 | Left paren: `\(` 91 | 92 | Right paren: `\)` 93 | 94 | Greater-than: `\>` 95 | 96 | Hash: `\#` 97 | 98 | Period: `\.` 99 | 100 | Bang: `\!` 101 | 102 | Plus: `\+` 103 | 104 | Minus: `\-` 105 | 106 | 107 | These should get escaped, even though they're matching pairs for 108 | other Markdown constructs: 109 | 110 | \*asterisks\* 111 | 112 | \_underscores\_ 113 | 114 | \`backticks\` 115 | 116 | This is a code span with a literal backslash-backtick sequence: `` \` `` 117 | 118 | This is a tag with unescaped backticks bar. 119 | 120 | This is a tag with backslashes bar. 121 | -------------------------------------------------------------------------------- /test/specs/run-spec.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const load = require('../helpers/load.js'); 3 | 4 | function runSpecs(title, dir, showCompletionTable, options) { 5 | options = options || {}; 6 | const specs = load.loadFiles(path.resolve(__dirname, dir)); 7 | 8 | if (showCompletionTable) { 9 | load.outputCompletionTable(title, specs); 10 | } 11 | 12 | describe(title, () => { 13 | Object.keys(specs).forEach(section => { 14 | describe(section, () => { 15 | specs[section].specs.forEach((spec) => { 16 | spec.options = Object.assign({}, options, (spec.options || {})); 17 | const example = (spec.example ? ' example ' + spec.example : ''); 18 | const passFail = (spec.shouldFail ? 'fail' : 'pass'); 19 | if (typeof spec.options.silent === 'undefined') { 20 | spec.options.silent = true; 21 | } 22 | if (spec.options.sanitizer) { 23 | // eslint-disable-next-line no-eval 24 | spec.options.sanitizer = eval(spec.options.sanitizer); 25 | } 26 | (spec.only ? fit : (spec.skip ? xit : it))('should ' + passFail + example, async() => { 27 | const before = process.hrtime(); 28 | if (spec.shouldFail) { 29 | await expectAsync(spec).not.toRender(spec.html); 30 | } else { 31 | await expectAsync(spec).toRender(spec.html); 32 | } 33 | const elapsed = process.hrtime(before); 34 | if (elapsed[0] > 0) { 35 | const s = (elapsed[0] + elapsed[1] * 1e-9).toFixed(3); 36 | fail(`took too long: ${s}s`); 37 | } 38 | }); 39 | }); 40 | }); 41 | }); 42 | }); 43 | } 44 | 45 | runSpecs('GFM', './gfm', true, { gfm: true, pedantic: false, headerIds: false }); 46 | runSpecs('CommonMark', './commonmark', true, { gfm: false, pedantic: false, headerIds: false }); 47 | runSpecs('Original', './original', false, { gfm: false, pedantic: true }); 48 | runSpecs('New', './new'); 49 | runSpecs('ReDOS', './redos'); 50 | runSpecs('Security', './security', false, { silent: true }); // silent - do not show deprecation warning 51 | -------------------------------------------------------------------------------- /test/specs/original/backslash_escapes.html: -------------------------------------------------------------------------------- 1 |

These should all get escaped:

2 | 3 |

Backslash: \

4 | 5 |

Backtick: `

6 | 7 |

Asterisk: *

8 | 9 |

Underscore: _

10 | 11 |

Left brace: {

12 | 13 |

Right brace: }

14 | 15 |

Left bracket: [

16 | 17 |

Right bracket: ]

18 | 19 |

Left paren: (

20 | 21 |

Right paren: )

22 | 23 |

Greater-than: >

24 | 25 |

Hash: #

26 | 27 |

Period: .

28 | 29 |

Bang: !

30 | 31 |

Plus: +

32 | 33 |

Minus: -

34 | 35 |

These should not, because they occur within a code block:

36 | 37 |
Backslash: \\
 38 | 
 39 | Backtick: \`
 40 | 
 41 | Asterisk: \*
 42 | 
 43 | Underscore: \_
 44 | 
 45 | Left brace: \{
 46 | 
 47 | Right brace: \}
 48 | 
 49 | Left bracket: \[
 50 | 
 51 | Right bracket: \]
 52 | 
 53 | Left paren: \(
 54 | 
 55 | Right paren: \)
 56 | 
 57 | Greater-than: \>
 58 | 
 59 | Hash: \#
 60 | 
 61 | Period: \.
 62 | 
 63 | Bang: \!
 64 | 
 65 | Plus: \+
 66 | 
 67 | Minus: \-
 68 | 
69 | 70 |

Nor should these, which occur in code spans:

71 | 72 |

Backslash: \\

73 | 74 |

Backtick: \`

75 | 76 |

Asterisk: \*

77 | 78 |

Underscore: \_

79 | 80 |

Left brace: \{

81 | 82 |

Right brace: \}

83 | 84 |

Left bracket: \[

85 | 86 |

Right bracket: \]

87 | 88 |

Left paren: \(

89 | 90 |

Right paren: \)

91 | 92 |

Greater-than: \>

93 | 94 |

Hash: \#

95 | 96 |

Period: \.

97 | 98 |

Bang: \!

99 | 100 |

Plus: \+

101 | 102 |

Minus: \-

103 | 104 | 105 |

These should get escaped, even though they're matching pairs for 106 | other Markdown constructs:

107 | 108 |

*asterisks*

109 | 110 |

_underscores_

111 | 112 |

`backticks`

113 | 114 |

This is a code span with a literal backslash-backtick sequence: \`

115 | 116 |

This is a tag with unescaped backticks bar.

117 | 118 |

This is a tag with backslashes bar.

119 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "marked", 3 | "description": "A markdown parser built for speed", 4 | "author": "Christopher Jeffrey", 5 | "version": "0.8.2", 6 | "main": "./src/marked.js", 7 | "bin": "./bin/marked", 8 | "man": "./man/marked.1", 9 | "files": [ 10 | "bin/", 11 | "lib/", 12 | "src/", 13 | "man/", 14 | "marked.min.js" 15 | ], 16 | "repository": "git://github.com/markedjs/marked.git", 17 | "homepage": "https://marked.js.org", 18 | "bugs": { 19 | "url": "http://github.com/markedjs/marked/issues" 20 | }, 21 | "license": "MIT", 22 | "keywords": [ 23 | "markdown", 24 | "markup", 25 | "html" 26 | ], 27 | "tags": [ 28 | "markdown", 29 | "markup", 30 | "html" 31 | ], 32 | "devDependencies": { 33 | "@babel/core": "^7.9.0", 34 | "@babel/preset-env": "^7.9.0", 35 | "@markedjs/html-differ": "^3.0.0", 36 | "cheerio": "^1.0.0-rc.3", 37 | "commonmark": "0.29.x", 38 | "eslint": "^6.8.0", 39 | "eslint-config-standard": "^14.1.1", 40 | "eslint-plugin-import": "^2.20.1", 41 | "eslint-plugin-node": "^11.0.0", 42 | "eslint-plugin-promise": "^4.2.1", 43 | "eslint-plugin-standard": "^4.0.1", 44 | "front-matter": "^3.1.0", 45 | "jasmine": "^3.5.0", 46 | "markdown": "0.5.x", 47 | "markdown-it": "10.x", 48 | "node-fetch": "^2.6.0", 49 | "rollup": "^2.1.0", 50 | "rollup-plugin-babel": "^4.4.0", 51 | "rollup-plugin-commonjs": "^10.1.0", 52 | "rollup-plugin-license": "^0.13.0", 53 | "uglify-js": "^3.8.0", 54 | "vuln-regex-detector": "^1.3.0" 55 | }, 56 | "scripts": { 57 | "test": "jasmine --config=jasmine.json", 58 | "test:all": "npm test && npm run test:lint", 59 | "test:unit": "npm test -- test/unit/**/*-spec.js", 60 | "test:specs": "npm test -- test/specs/**/*-spec.js", 61 | "test:lint": "eslint bin/marked .", 62 | "test:redos": "node test/vuln-regex.js", 63 | "test:update": "node test/update-specs.js", 64 | "bench": "npm run rollup && node test/bench.js", 65 | "lint": "eslint --fix bin/marked .", 66 | "build:reset": "git checkout upstream/master lib/marked.js lib/marked.esm.js marked.min.js", 67 | "build": "npm run rollup && npm run minify", 68 | "rollup": "npm run rollup:umd && npm run rollup:esm", 69 | "rollup:umd": "rollup -c rollup.config.js", 70 | "rollup:esm": "rollup -c rollup.config.esm.js", 71 | "minify": "uglifyjs lib/marked.js -cm --comments /Copyright/ -o marked.min.js", 72 | "preversion": "npm run build && (git diff --quiet || git commit -am build)" 73 | }, 74 | "engines": { 75 | "node": ">= 8.16.2" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /test/specs/original/ordered_and_unordered_lists.html: -------------------------------------------------------------------------------- 1 |

Unordered

2 | 3 |

Asterisks tight:

4 | 5 |
    6 |
  • asterisk 1
  • 7 |
  • asterisk 2
  • 8 |
  • asterisk 3
  • 9 |
10 | 11 |

Asterisks loose:

12 | 13 |
    14 |
  • asterisk 1

  • 15 |
  • asterisk 2

  • 16 |
  • asterisk 3

  • 17 |
18 | 19 |
20 | 21 |

Pluses tight:

22 | 23 |
    24 |
  • Plus 1
  • 25 |
  • Plus 2
  • 26 |
  • Plus 3
  • 27 |
28 | 29 |

Pluses loose:

30 | 31 |
    32 |
  • Plus 1

  • 33 |
  • Plus 2

  • 34 |
  • Plus 3

  • 35 |
36 | 37 |
38 | 39 |

Minuses tight:

40 | 41 |
    42 |
  • Minus 1
  • 43 |
  • Minus 2
  • 44 |
  • Minus 3
  • 45 |
46 | 47 |

Minuses loose:

48 | 49 |
    50 |
  • Minus 1

  • 51 |
  • Minus 2

  • 52 |
  • Minus 3

  • 53 |
54 | 55 |

Ordered

56 | 57 |

Tight:

58 | 59 |
    60 |
  1. First
  2. 61 |
  3. Second
  4. 62 |
  5. Third
  6. 63 |
64 | 65 |

and:

66 | 67 |
    68 |
  1. One
  2. 69 |
  3. Two
  4. 70 |
  5. Three
  6. 71 |
72 | 73 |

Loose using tabs:

74 | 75 |
    76 |
  1. First

  2. 77 |
  3. Second

  4. 78 |
  5. Third

  6. 79 |
80 | 81 |

and using spaces:

82 | 83 |
    84 |
  1. One

  2. 85 |
  3. Two

  4. 86 |
  5. Three

  6. 87 |
88 | 89 |

Multiple paragraphs:

90 | 91 |
    92 |
  1. Item 1, graf one.

    93 | 94 |

    Item 2. graf two. The quick brown fox jumped over the lazy dog's 95 | back.

  2. 96 |
  3. Item 2.

  4. 97 |
  5. Item 3.

  6. 98 |
99 | 100 |

Nested

101 | 102 |
    103 |
  • Tab 104 |
      105 |
    • Tab 106 |
        107 |
      • Tab
      • 108 |
    • 109 |
  • 110 |
111 | 112 |

Here's another:

113 | 114 |
    115 |
  1. First
  2. 116 |
  3. Second: 117 |
      118 |
    • Fee
    • 119 |
    • Fie
    • 120 |
    • Foe
    • 121 |
  4. 122 |
  5. Third
  6. 123 |
124 | 125 |

Same thing but with paragraphs:

126 | 127 |
    128 |
  1. First

  2. 129 |
  3. Second:

    130 | 131 |
      132 |
    • Fee
    • 133 |
    • Fie
    • 134 |
    • Foe
    • 135 |
  4. 136 |
  5. Third

  6. 137 |
138 | 139 | 140 |

This was an error in Markdown 1.0.1:

141 | 142 |
    143 |
  • this

    144 | 145 |
    • sub
    146 | 147 |

    that

  • 148 |
149 | 150 | 151 |

Ordered lists start from initial number:

152 | 153 |
    154 |
  1. Three
  2. 155 |
  3. Four
  4. 156 |
157 | 158 | 159 |

Ordered lists start from initial zero:

160 | 161 |
    162 |
  1. Zero
  2. 163 |
  3. One
  4. 164 |
165 | -------------------------------------------------------------------------------- /man/marked.1: -------------------------------------------------------------------------------- 1 | .ds q \N'34' 2 | .TH marked 1 3 | 4 | .SH NAME 5 | marked \- a javascript markdown parser 6 | 7 | .SH SYNOPSIS 8 | .B marked 9 | [\-o \fI\fP] [\-i \fI\fP] [\-\-help] 10 | [\-\-tokens] [\-\-pedantic] [\-\-gfm] 11 | [\-\-breaks] [\-\-sanitize] 12 | [\-\-smart\-lists] [\-\-lang\-prefix \fI\fP] 13 | [\-\-no\-etc...] [\-\-silent] [\fIfilename\fP] 14 | 15 | .SH DESCRIPTION 16 | .B marked 17 | is a full-featured javascript markdown parser, built for speed. 18 | It also includes multiple GFM features. 19 | 20 | .SH EXAMPLES 21 | .TP 22 | cat in.md | marked > out.html 23 | .TP 24 | echo "hello *world*" | marked 25 | .TP 26 | marked \-o out.html \-i in.md \-\-gfm 27 | .TP 28 | marked \-\-output="hello world.html" \-i in.md \-\-no-breaks 29 | 30 | .SH OPTIONS 31 | .TP 32 | .BI \-o,\ \-\-output\ [\fIoutput\fP] 33 | Specify file output. If none is specified, write to stdout. 34 | .TP 35 | .BI \-i,\ \-\-input\ [\fIinput\fP] 36 | Specify file input, otherwise use last argument as input file. 37 | If no input file is specified, read from stdin. 38 | .TP 39 | .BI \-\-test 40 | Makes sure the test(s) pass. 41 | .RS 42 | .PP 43 | .B \-\-glob [\fIfile\fP] 44 | Specify which test to use. 45 | .PP 46 | .B \-\-fix 47 | Fixes tests. 48 | .PP 49 | .B \-\-bench 50 | Benchmarks the test(s). 51 | .PP 52 | .B \-\-time 53 | Times The test(s). 54 | .PP 55 | .B \-\-minified 56 | Runs test file(s) as minified. 57 | .PP 58 | .B \-\-stop 59 | Stop process if a test fails. 60 | .RE 61 | .TP 62 | .BI \-t,\ \-\-tokens 63 | Output a token stream instead of html. 64 | .TP 65 | .BI \-\-pedantic 66 | Conform to obscure parts of markdown.pl as much as possible. 67 | Don't fix original markdown bugs. 68 | .TP 69 | .BI \-\-gfm 70 | Enable github flavored markdown. 71 | .TP 72 | .BI \-\-breaks 73 | Enable GFM line breaks. Only works with the gfm option. 74 | .TP 75 | .BI \-\-sanitize 76 | Sanitize output. Ignore any HTML input. 77 | .TP 78 | .BI \-\-smart\-lists 79 | Use smarter list behavior than the original markdown. 80 | .TP 81 | .BI \-\-lang\-prefix\ [\fIprefix\fP] 82 | Set the prefix for code block classes. 83 | .TP 84 | .BI \-\-mangle 85 | Mangle email addresses. 86 | .TP 87 | .BI \-\-no\-sanitize,\ \-no-etc... 88 | The inverse of any of the marked options above. 89 | .TP 90 | .BI \-\-silent 91 | Silence error output. 92 | .TP 93 | .BI \-h,\ \-\-help 94 | Display help information. 95 | 96 | .SH CONFIGURATION 97 | For configuring and running programmatically. 98 | 99 | .B Example 100 | 101 | require('marked')('*foo*', { gfm: true }); 102 | 103 | .SH BUGS 104 | Please report any bugs to https://github.com/markedjs/marked. 105 | 106 | .SH LICENSE 107 | Copyright (c) 2011-2014, Christopher Jeffrey (MIT License). 108 | 109 | .SH "SEE ALSO" 110 | .BR markdown(1), 111 | .BR node.js(1) 112 | -------------------------------------------------------------------------------- /man/marked.1.txt: -------------------------------------------------------------------------------- 1 | marked(1) General Commands Manual marked(1) 2 | 3 | NAME 4 | marked - a javascript markdown parser 5 | 6 | SYNOPSIS 7 | marked [-o ] [-i ] [--help] [--tokens] [--pedantic] 8 | [--gfm] [--breaks] [--sanitize] [--smart-lists] [--lang-prefix ] [--no-etc...] [--silent] [filename] 10 | 11 | 12 | DESCRIPTION 13 | marked is a full-featured javascript markdown parser, built for speed. 14 | It also includes multiple GFM features. 15 | 16 | EXAMPLES 17 | cat in.md | marked > out.html 18 | 19 | echo "hello *world*" | marked 20 | 21 | marked -o out.html -i in.md --gfm 22 | 23 | marked --output="hello world.html" -i in.md --no-breaks 24 | 25 | OPTIONS 26 | -o, --output [output] 27 | Specify file output. If none is specified, write to stdout. 28 | 29 | -i, --input [input] 30 | Specify file input, otherwise use last argument as input file. 31 | If no input file is specified, read from stdin. 32 | 33 | --test Makes sure the test(s) pass. 34 | 35 | --glob [file] Specify which test to use. 36 | 37 | --fix Fixes tests. 38 | 39 | --bench Benchmarks the test(s). 40 | 41 | --time Times The test(s). 42 | 43 | --minified Runs test file(s) as minified. 44 | 45 | --stop Stop process if a test fails. 46 | 47 | -t, --tokens 48 | Output a token stream instead of html. 49 | 50 | --pedantic 51 | Conform to obscure parts of markdown.pl as much as possible. 52 | Don't fix original markdown bugs. 53 | 54 | --gfm Enable github flavored markdown. 55 | 56 | --breaks 57 | Enable GFM line breaks. Only works with the gfm option. 58 | 59 | --sanitize 60 | Sanitize output. Ignore any HTML input. 61 | 62 | --smart-lists 63 | Use smarter list behavior than the original markdown. 64 | 65 | --lang-prefix [prefix] 66 | Set the prefix for code block classes. 67 | 68 | --mangle 69 | Mangle email addresses. 70 | 71 | --no-sanitize, -no-etc... 72 | The inverse of any of the marked options above. 73 | 74 | --silent 75 | Silence error output. 76 | 77 | -h, --help 78 | Display help information. 79 | 80 | CONFIGURATION 81 | For configuring and running programmatically. 82 | 83 | Example 84 | 85 | require('marked')('*foo*', { gfm: true }); 86 | 87 | BUGS 88 | Please report any bugs to https://github.com/markedjs/marked. 89 | 90 | LICENSE 91 | Copyright (c) 2011-2014, Christopher Jeffrey (MIT License). 92 | 93 | SEE ALSO 94 | markdown(1), node.js(1) 95 | 96 | marked(1) 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | # Marked 6 | 7 | [![npm](https://badgen.net/npm/v/marked)](https://www.npmjs.com/package/marked) 8 | [![gzip size](https://badgen.net/badgesize/gzip/https://cdn.jsdelivr.net/npm/marked/marked.min.js)](https://cdn.jsdelivr.net/npm/marked/marked.min.js) 9 | [![install size](https://badgen.net/packagephobia/install/marked)](https://packagephobia.now.sh/result?p=marked) 10 | [![downloads](https://badgen.net/npm/dt/marked)](https://www.npmjs.com/package/marked) 11 | [![dep](https://badgen.net/david/dep/markedjs/marked?label=deps)](https://david-dm.org/markedjs/marked) 12 | [![dev dep](https://badgen.net/david/dev/markedjs/marked?label=devDeps)](https://david-dm.org/markedjs/marked?type=dev) 13 | [![travis](https://badgen.net/travis/markedjs/marked)](https://travis-ci.org/markedjs/marked) 14 | [![snyk](https://snyk.io/test/npm/marked/badge.svg)](https://snyk.io/test/npm/marked) 15 | 16 | - ⚡ built for speed 17 | - ⬇️ low-level compiler for parsing markdown without caching or blocking for long periods of time 18 | - ⚖️ light-weight while implementing all markdown features from the supported flavors & specifications 19 | - 🌐 works in a browser, on a server, or from a command line interface (CLI) 20 | 21 | ## Demo 22 | 23 | Checkout the [demo page](https://marked.js.org/demo/) to see marked in action ⛹️ 24 | 25 | ## Docs 26 | 27 | Our [documentation pages](https://marked.js.org) are also rendered using marked 💯 28 | 29 | Also read about: 30 | 31 | * [Options](https://marked.js.org/#/USING_ADVANCED.md) 32 | * [Extensibility](https://marked.js.org/#/USING_PRO.md) 33 | 34 | ## Installation 35 | 36 | **CLI:** `npm install -g marked` 37 | 38 | **In-browser:** `npm install marked` 39 | 40 | ## Usage 41 | 42 | ### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML. Please 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! 🚨 43 | 44 | **CLI** 45 | 46 | ``` bash 47 | $ marked -o hello.html 48 | hello world 49 | ^D 50 | $ cat hello.html 51 |

hello world

52 | ``` 53 | 54 | **Browser** 55 | 56 | ```html 57 | 58 | 59 | 60 | 61 | Marked in the browser 62 | 63 | 64 |
65 | 66 | 70 | 71 | 72 | ``` 73 | 74 | ## License 75 | 76 | Copyright (c) 2011-2018, Christopher Jeffrey. (MIT License) 77 | -------------------------------------------------------------------------------- /test/specs/new/em_list_links.html: -------------------------------------------------------------------------------- 1 | 55 | 56 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License information 2 | 3 | ## Contribution License Agreement 4 | 5 | If you contribute code to this project, you are implicitly allowing your code 6 | to be distributed under the MIT license. You are also implicitly verifying that 7 | all code is your original work. `` 8 | 9 | ## Marked 10 | 11 | Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/) 12 | 13 | Permission is hereby granted, free of charge, to any person obtaining a copy 14 | of this software and associated documentation files (the "Software"), to deal 15 | in the Software without restriction, including without limitation the rights 16 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be included in 21 | all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 | THE SOFTWARE. 30 | 31 | ## Markdown 32 | 33 | Copyright © 2004, John Gruber 34 | http://daringfireball.net/ 35 | All rights reserved. 36 | 37 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 38 | 39 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 40 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 41 | * Neither the name “Markdown” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 42 | 43 | This software is provided by the copyright holders and contributors “as is” and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. 44 | -------------------------------------------------------------------------------- /test/update-specs.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const cheerio = require('cheerio'); 3 | const marked = require('../'); 4 | const htmlDiffer = require('./helpers/html-differ.js'); 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | 8 | function removeFiles(dir) { 9 | fs.readdirSync(dir).forEach(file => { 10 | fs.unlinkSync(path.join(dir, file)); 11 | }); 12 | } 13 | 14 | async function updateCommonmark(dir, options) { 15 | try { 16 | const res = await fetch('https://raw.githubusercontent.com/commonmark/commonmark.js/master/package.json'); 17 | const pkg = await res.json(); 18 | const version = pkg.version.replace(/^(\d+\.\d+).*$/, '$1'); 19 | const res2 = await fetch(`https://spec.commonmark.org/${version}/spec.json`); 20 | const specs = await res2.json(); 21 | specs.forEach(spec => { 22 | const html = marked(spec.markdown, options); 23 | if (!htmlDiffer.isEqual(html, spec.html)) { 24 | spec.shouldFail = true; 25 | } 26 | }); 27 | fs.writeFileSync(path.resolve(dir, `./commonmark.${version}.json`), JSON.stringify(specs, null, 2) + '\n'); 28 | console.log(`Saved CommonMark v${version} specs`); 29 | } catch (ex) { 30 | console.log(ex); 31 | } 32 | } 33 | 34 | async function updateGfm(dir) { 35 | try { 36 | const res = await fetch('https://github.github.com/gfm/'); 37 | const html = await res.text(); 38 | const $ = cheerio.load(html); 39 | const version = $('.version').text().match(/\d+\.\d+/)[0]; 40 | if (!version) { 41 | throw new Error('No version found'); 42 | } 43 | const specs = []; 44 | $('.extension').each((i, ext) => { 45 | const section = $('.definition', ext).text().trim().replace(/^\d+\.\d+(.*?) \(extension\)[\s\S]*$/, '$1'); 46 | $('.example', ext).each((j, exa) => { 47 | const example = +$(exa).attr('id').replace(/\D/g, ''); 48 | const markdown = $('.language-markdown', exa).text().trim(); 49 | const html = $('.language-html', exa).text().trim(); 50 | specs.push({ 51 | section: `[extension] ${section}`, 52 | html, 53 | markdown, 54 | example 55 | }); 56 | }); 57 | }); 58 | 59 | specs.forEach(spec => { 60 | const html = marked(spec.markdown, { gfm: true, pedantic: false }); 61 | if (!htmlDiffer.isEqual(html, spec.html)) { 62 | spec.shouldFail = true; 63 | } 64 | }); 65 | fs.writeFileSync(path.resolve(dir, `./gfm.${version}.json`), JSON.stringify(specs, null, 2) + '\n'); 66 | console.log(`Saved GFM v${version} specs.`); 67 | } catch (ex) { 68 | console.log(ex); 69 | } 70 | } 71 | 72 | const commonmarkDir = path.resolve(__dirname, './specs/commonmark'); 73 | const gfmDir = path.resolve(__dirname, './specs/gfm'); 74 | removeFiles(commonmarkDir); 75 | removeFiles(gfmDir); 76 | updateCommonmark(commonmarkDir, { gfm: false, pedantic: false, headerIds: false }); 77 | updateCommonmark(gfmDir, { gfm: true, pedantic: false, headerIds: false }); 78 | updateGfm(gfmDir); 79 | -------------------------------------------------------------------------------- /docs/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Marked Demo 6 | 7 | 8 | 9 | 10 | 11 | Fork me on GitHub 12 | 13 | 14 |
15 | 16 | 17 | 18 |

Marked Demo

19 |
20 | 21 |
Loading...
22 |
23 |
24 |
25 |
26 | Input · 27 | · 28 | Version: 29 | 34 | · 35 | 36 | 40 |
41 | 42 | 43 |
44 | 45 |
46 |
47 | · 53 | Response Time: 54 | 55 |
56 | 57 |
58 | 61 | 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 |
71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /docs/demo/worker.js: -------------------------------------------------------------------------------- 1 | /* globals marked, unfetch, ES6Promise, Promise */ // eslint-disable-line no-redeclare 2 | if (!self.Promise) { 3 | self.importScripts('https://cdn.jsdelivr.net/npm/es6-promise/dist/es6-promise.js'); 4 | self.Promise = ES6Promise; 5 | } 6 | if (!self.fetch) { 7 | self.importScripts('https://cdn.jsdelivr.net/npm/unfetch/dist/unfetch.umd.js'); 8 | self.fetch = unfetch; 9 | } 10 | 11 | var versionCache = {}; 12 | var currentVersion; 13 | 14 | onunhandledrejection = function(e) { 15 | throw e.reason; 16 | }; 17 | 18 | onmessage = function(e) { 19 | if (e.data.version === currentVersion) { 20 | parse(e); 21 | } else { 22 | loadVersion(e.data.version).then(function() { 23 | parse(e); 24 | }); 25 | } 26 | }; 27 | 28 | function parse(e) { 29 | switch (e.data.task) { 30 | case 'defaults': 31 | 32 | var defaults = {}; 33 | if (typeof marked.getDefaults === 'function') { 34 | defaults = marked.getDefaults(); 35 | delete defaults.renderer; 36 | } else if ('defaults' in marked) { 37 | for (var prop in marked.defaults) { 38 | if (prop !== 'renderer') { 39 | defaults[prop] = marked.defaults[prop]; 40 | } 41 | } 42 | } 43 | postMessage({ 44 | task: e.data.task, 45 | defaults: defaults 46 | }); 47 | break; 48 | case 'parse': 49 | var startTime = new Date(); 50 | var lexed = marked.lexer(e.data.markdown, e.data.options); 51 | var lexedList = []; 52 | for (var i = 0; i < lexed.length; i++) { 53 | var lexedLine = []; 54 | for (var j in lexed[i]) { 55 | lexedLine.push(j + ':' + jsonString(lexed[i][j])); 56 | } 57 | lexedList.push('{' + lexedLine.join(', ') + '}'); 58 | } 59 | var parsed = marked.parser(lexed, e.data.options); 60 | var endTime = new Date(); 61 | // setTimeout(function () { 62 | postMessage({ 63 | task: e.data.task, 64 | lexed: lexedList.join('\n'), 65 | parsed: parsed, 66 | time: endTime - startTime 67 | }); 68 | // }, 10000); 69 | break; 70 | } 71 | } 72 | 73 | function jsonString(input) { 74 | var output = (input + '') 75 | .replace(/\n/g, '\\n') 76 | .replace(/\r/g, '\\r') 77 | .replace(/\t/g, '\\t') 78 | .replace(/\f/g, '\\f') 79 | .replace(/[\\"']/g, '\\$&') 80 | .replace(/\u0000/g, '\\0'); 81 | return '"' + output + '"'; 82 | }; 83 | 84 | function loadVersion(ver) { 85 | var promise; 86 | if (versionCache[ver]) { 87 | promise = Promise.resolve(versionCache[ver]); 88 | } else { 89 | promise = fetch(ver) 90 | .then(function(res) { return res.text(); }) 91 | .then(function(text) { 92 | versionCache[ver] = text; 93 | return text; 94 | }); 95 | } 96 | return promise.then(function(text) { 97 | try { 98 | // eslint-disable-next-line no-new-func 99 | Function(text)(); 100 | } catch (err) { 101 | throw new Error('Cannot load that version of marked'); 102 | } 103 | currentVersion = ver; 104 | }); 105 | } 106 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/badges.md: -------------------------------------------------------------------------------- 1 | **@mention the contributor:** 2 | 3 | ## Recommendation to: 4 | 5 | - [ ] Change user group 6 | - [ ] Add a badge 7 | - [ ] Remove a badge 8 | 9 | 16 | 17 | ## As the one mentioned, I would like to: 18 | 19 | - [ ] accept the recommendation; or, 20 | - [ ] graciously decline; or, 21 | - [ ] dispute the recommendation 22 | 23 | within 30 days, if you have not indicated which option you are taking one of the following will happen: 24 | 25 | 1. If adding a badge, we will assume you are graciously declining. 26 | 2. If removing a badge, we will assume you do not want to dispute the recommendation; therefore, the badge will be removed. 27 | 28 | 49 | 50 | Note: All committers must approve via review before merging, the disapproving committer can simply close the PR. -------------------------------------------------------------------------------- /test/unit/marked-spec.js: -------------------------------------------------------------------------------- 1 | const marked = require('../../src/marked.js'); 2 | 3 | describe('Test heading ID functionality', () => { 4 | it('should add id attribute by default', () => { 5 | const renderer = new marked.Renderer(); 6 | const slugger = new marked.Slugger(); 7 | const header = renderer.heading('test', 1, 'test', slugger); 8 | expect(header).toBe('

test

\n'); 9 | }); 10 | 11 | it('should NOT add id attribute when options set false', () => { 12 | const renderer = new marked.Renderer({ headerIds: false }); 13 | const header = renderer.heading('test', 1, 'test'); 14 | expect(header).toBe('

test

\n'); 15 | }); 16 | }); 17 | 18 | describe('Test slugger functionality', () => { 19 | it('should use lowercase slug', () => { 20 | const slugger = new marked.Slugger(); 21 | expect(slugger.slug('Test')).toBe('test'); 22 | }); 23 | 24 | it('should be unique to avoid collisions 1280', () => { 25 | const slugger = new marked.Slugger(); 26 | expect(slugger.slug('test')).toBe('test'); 27 | expect(slugger.slug('test')).toBe('test-1'); 28 | expect(slugger.slug('test')).toBe('test-2'); 29 | }); 30 | 31 | it('should be unique when slug ends with number', () => { 32 | const slugger = new marked.Slugger(); 33 | expect(slugger.slug('test 1')).toBe('test-1'); 34 | expect(slugger.slug('test')).toBe('test'); 35 | expect(slugger.slug('test')).toBe('test-2'); 36 | }); 37 | 38 | it('should be unique when slug ends with hyphen number', () => { 39 | const slugger = new marked.Slugger(); 40 | expect(slugger.slug('foo')).toBe('foo'); 41 | expect(slugger.slug('foo')).toBe('foo-1'); 42 | expect(slugger.slug('foo 1')).toBe('foo-1-1'); 43 | expect(slugger.slug('foo-1')).toBe('foo-1-2'); 44 | expect(slugger.slug('foo')).toBe('foo-2'); 45 | }); 46 | 47 | it('should allow non-latin chars', () => { 48 | const slugger = new marked.Slugger(); 49 | expect(slugger.slug('привет')).toBe('привет'); 50 | }); 51 | 52 | it('should remove ampersands 857', () => { 53 | const slugger = new marked.Slugger(); 54 | expect(slugger.slug('This & That Section')).toBe('this--that-section'); 55 | }); 56 | 57 | it('should remove periods', () => { 58 | const slugger = new marked.Slugger(); 59 | expect(slugger.slug('file.txt')).toBe('filetxt'); 60 | }); 61 | }); 62 | 63 | describe('Test paragraph token type', () => { 64 | it('should use the "paragraph" type on top level', () => { 65 | const md = 'A Paragraph.\n\n> A blockquote\n\n- list item\n'; 66 | 67 | const tokens = marked.lexer(md); 68 | 69 | expect(tokens[0].type).toBe('paragraph'); 70 | expect(tokens[3].type).toBe('paragraph'); 71 | expect(tokens[7].type).toBe('text'); 72 | }); 73 | }); 74 | 75 | describe('changeDefaults', () => { 76 | it('should change global defaults', () => { 77 | const { defaults, changeDefaults } = require('../../src/defaults'); 78 | expect(defaults.test).toBeUndefined(); 79 | changeDefaults({ test: true }); 80 | expect(require('../../src/defaults').defaults.test).toBe(true); 81 | }); 82 | }); 83 | 84 | describe('inlineLexer', () => { 85 | it('should send html to renderer.html', () => { 86 | const renderer = new marked.Renderer(); 87 | spyOn(renderer, 'html').and.callThrough(); 88 | const md = 'HTML Image: MY IMAGE'; 89 | marked(md, { renderer }); 90 | 91 | expect(renderer.html).toHaveBeenCalledWith('MY IMAGE'); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to block temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team by submitting a PR with changes to the [AUTHORS](#/AUTHORS.md) page (or emailing josh@8fold.com). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 71 | version [1.4][version]. 72 | 73 | [homepage]: https://www.contributor-covenant.org/ 74 | [version]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 75 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | Marked is 2 | 3 | 1. built for speed.* 4 | 2. a low-level markdown compiler for parsing markdown without caching or blocking for long periods of time.** 5 | 3. light-weight while implementing all markdown features from the supported flavors & specifications.*** 6 | 4. available as a command line interface (CLI) and running in client- or server-side JavaScript projects. 7 | 8 |

* Still working on metrics for comparative analysis and definition.
9 | ** As few dependencies as possible.
10 | *** Strict compliance could result in slower processing when running comparative benchmarking.

11 | 12 | 13 |

Demo

14 | 15 | Checkout the [demo page](./demo/) to see marked in action ⛹️ 16 | 17 | These documentation pages are also rendered using marked 💯 18 | 19 | 20 |

Installation

21 | 22 | **CLI:** `npm install -g marked` 23 | 24 | **In-browser:** `npm install marked` 25 | 26 |

Usage

27 | 28 | ### Warning: 🚨 Marked does not [sanitize](https://marked.js.org/#/USING_ADVANCED.md#options) the output HTML. Please 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! 🚨 29 | 30 | **CLI** 31 | 32 | ``` bash 33 | $ marked -o hello.html 34 | hello world 35 | ^D 36 | $ cat hello.html 37 |

hello world

38 | ``` 39 | 40 | ``` bash 41 | $ marked -s "*hello world*" 42 |

hello world

43 | ``` 44 | 45 | **Browser** 46 | 47 | ```html 48 | 49 | 50 | 51 | 52 | Marked in the browser 53 | 54 | 55 |
56 | 57 | 61 | 62 | 63 | ``` 64 | 65 | 66 | Marked offers [advanced configurations](#/USING_ADVANCED.md) and [extensibility](#/USING_PRO.md) as well. 67 | 68 |

Supported Markdown specifications

69 | 70 | We actively support the features of the following [Markdown flavors](https://github.com/commonmark/CommonMark/wiki/Markdown-Flavors). 71 | 72 | |Flavor |Version | 73 | |:----------------------------------------------------------|:----------| 74 | |The original markdown.pl |-- | 75 | |[CommonMark](http://spec.commonmark.org/0.29/) |0.29 | 76 | |[GitHub Flavored Markdown](https://github.github.com/gfm/) |0.29 | 77 | 78 | By supporting the above Markdown flavors, it's possible that Marked can help you use other flavors as well; however, these are not actively supported by the community. 79 | 80 |

Security

81 | 82 | The only completely secure system is the one that doesn't exist in the first place. Having said that, we take the security of Marked very seriously. 83 | 84 | Therefore, please disclose potential security issues by email to the project [committers](#/AUTHORS.md) as well as the [listed owners within NPM](https://docs.npmjs.com/cli/owner). We will provide an initial assessment of security reports within 48 hours and should apply patches within 2 weeks (also, feel free to contribute a fix for the issue). 85 | 86 | -------------------------------------------------------------------------------- /src/marked.js: -------------------------------------------------------------------------------- 1 | const Lexer = require('./Lexer.js'); 2 | const Parser = require('./Parser.js'); 3 | const Renderer = require('./Renderer.js'); 4 | const TextRenderer = require('./TextRenderer.js'); 5 | const InlineLexer = require('./InlineLexer.js'); 6 | const Slugger = require('./Slugger.js'); 7 | const { 8 | merge, 9 | checkSanitizeDeprecation, 10 | escape 11 | } = require('./helpers.js'); 12 | const { 13 | getDefaults, 14 | changeDefaults, 15 | defaults 16 | } = require('./defaults.js'); 17 | 18 | /** 19 | * Marked 20 | */ 21 | function marked(src, opt, callback) { 22 | // throw error in case of non string input 23 | if (typeof src === 'undefined' || src === null) { 24 | throw new Error('marked(): input parameter is undefined or null'); 25 | } 26 | if (typeof src !== 'string') { 27 | throw new Error('marked(): input parameter is of type ' 28 | + Object.prototype.toString.call(src) + ', string expected'); 29 | } 30 | 31 | if (callback || typeof opt === 'function') { 32 | if (!callback) { 33 | callback = opt; 34 | opt = null; 35 | } 36 | 37 | opt = merge({}, marked.defaults, opt || {}); 38 | checkSanitizeDeprecation(opt); 39 | const highlight = opt.highlight; 40 | let tokens, 41 | pending, 42 | i = 0; 43 | 44 | try { 45 | tokens = Lexer.lex(src, opt); 46 | } catch (e) { 47 | return callback(e); 48 | } 49 | 50 | pending = tokens.length; 51 | 52 | const done = function(err) { 53 | if (err) { 54 | opt.highlight = highlight; 55 | return callback(err); 56 | } 57 | 58 | let out; 59 | 60 | try { 61 | out = Parser.parse(tokens, opt); 62 | } catch (e) { 63 | err = e; 64 | } 65 | 66 | opt.highlight = highlight; 67 | 68 | return err 69 | ? callback(err) 70 | : callback(null, out); 71 | }; 72 | 73 | if (!highlight || highlight.length < 3) { 74 | return done(); 75 | } 76 | 77 | delete opt.highlight; 78 | 79 | if (!pending) return done(); 80 | 81 | for (; i < tokens.length; i++) { 82 | (function(token) { 83 | if (token.type !== 'code') { 84 | return --pending || done(); 85 | } 86 | return highlight(token.text, token.lang, function(err, code) { 87 | if (err) return done(err); 88 | if (code == null || code === token.text) { 89 | return --pending || done(); 90 | } 91 | token.text = code; 92 | token.escaped = true; 93 | --pending || done(); 94 | }); 95 | })(tokens[i]); 96 | } 97 | 98 | return; 99 | } 100 | try { 101 | opt = merge({}, marked.defaults, opt || {}); 102 | checkSanitizeDeprecation(opt); 103 | return Parser.parse(Lexer.lex(src, opt), opt); 104 | } catch (e) { 105 | e.message += '\nPlease report this to https://github.com/markedjs/marked.'; 106 | if ((opt || marked.defaults).silent) { 107 | return '

An error occurred:

'
108 |         + escape(e.message + '', true)
109 |         + '
'; 110 | } 111 | throw e; 112 | } 113 | } 114 | 115 | /** 116 | * Options 117 | */ 118 | 119 | marked.options = 120 | marked.setOptions = function(opt) { 121 | merge(marked.defaults, opt); 122 | changeDefaults(marked.defaults); 123 | return marked; 124 | }; 125 | 126 | marked.getDefaults = getDefaults; 127 | 128 | marked.defaults = defaults; 129 | 130 | /** 131 | * Expose 132 | */ 133 | 134 | marked.Parser = Parser; 135 | marked.parser = Parser.parse; 136 | 137 | marked.Renderer = Renderer; 138 | marked.TextRenderer = TextRenderer; 139 | 140 | marked.Lexer = Lexer; 141 | marked.lexer = Lexer.lex; 142 | 143 | marked.InlineLexer = InlineLexer; 144 | marked.inlineLexer = InlineLexer.output; 145 | 146 | marked.Slugger = Slugger; 147 | 148 | marked.parse = marked; 149 | 150 | module.exports = marked; 151 | -------------------------------------------------------------------------------- /src/Renderer.js: -------------------------------------------------------------------------------- 1 | const { defaults } = require('./defaults.js'); 2 | const { 3 | cleanUrl, 4 | escape 5 | } = require('./helpers.js'); 6 | 7 | /** 8 | * Renderer 9 | */ 10 | module.exports = class Renderer { 11 | constructor(options) { 12 | this.options = options || defaults; 13 | } 14 | 15 | code(code, infostring, escaped) { 16 | const lang = (infostring || '').match(/\S*/)[0]; 17 | if (this.options.highlight) { 18 | const out = this.options.highlight(code, lang); 19 | if (out != null && out !== code) { 20 | escaped = true; 21 | code = out; 22 | } 23 | } 24 | 25 | if (!lang) { 26 | return '
'
 27 |         + (escaped ? code : escape(code, true))
 28 |         + '
'; 29 | } 30 | 31 | return '
'
 35 |       + (escaped ? code : escape(code, true))
 36 |       + '
\n'; 37 | }; 38 | 39 | blockquote(quote) { 40 | return '
\n' + quote + '
\n'; 41 | }; 42 | 43 | html(html) { 44 | return html; 45 | }; 46 | 47 | heading(text, level, raw, slugger) { 48 | if (this.options.headerIds) { 49 | return '' 55 | + text 56 | + '\n'; 59 | } 60 | // ignore IDs 61 | return '' + text + '\n'; 62 | }; 63 | 64 | hr() { 65 | return this.options.xhtml ? '
\n' : '
\n'; 66 | }; 67 | 68 | list(body, ordered, start) { 69 | const type = ordered ? 'ol' : 'ul', 70 | startatt = (ordered && start !== 1) ? (' start="' + start + '"') : ''; 71 | return '<' + type + startatt + '>\n' + body + '\n'; 72 | }; 73 | 74 | listitem(text) { 75 | return '
  • ' + text + '
  • \n'; 76 | }; 77 | 78 | checkbox(checked) { 79 | return ' '; 84 | }; 85 | 86 | paragraph(text) { 87 | return '

    ' + text + '

    \n'; 88 | }; 89 | 90 | table(header, body) { 91 | if (body) body = '' + body + ''; 92 | 93 | return '\n' 94 | + '\n' 95 | + header 96 | + '\n' 97 | + body 98 | + '
    \n'; 99 | }; 100 | 101 | tablerow(content) { 102 | return '\n' + content + '\n'; 103 | }; 104 | 105 | tablecell(content, flags) { 106 | const type = flags.header ? 'th' : 'td'; 107 | const tag = flags.align 108 | ? '<' + type + ' align="' + flags.align + '">' 109 | : '<' + type + '>'; 110 | return tag + content + '\n'; 111 | }; 112 | 113 | // span level renderer 114 | strong(text) { 115 | return '' + text + ''; 116 | }; 117 | 118 | em(text) { 119 | return '' + text + ''; 120 | }; 121 | 122 | codespan(text) { 123 | return '' + text + ''; 124 | }; 125 | 126 | br() { 127 | return this.options.xhtml ? '
    ' : '
    '; 128 | }; 129 | 130 | del(text) { 131 | return '' + text + ''; 132 | }; 133 | 134 | link(href, title, text) { 135 | href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); 136 | if (href === null) { 137 | return text; 138 | } 139 | let out = ''; 144 | return out; 145 | }; 146 | 147 | image(href, title, text) { 148 | href = cleanUrl(this.options.sanitize, this.options.baseUrl, href); 149 | if (href === null) { 150 | return text; 151 | } 152 | 153 | let out = '' + text + '' : '>'; 158 | return out; 159 | }; 160 | 161 | text(text) { 162 | return text; 163 | }; 164 | }; 165 | -------------------------------------------------------------------------------- /docs/USING_PRO.md: -------------------------------------------------------------------------------- 1 | ## Extending Marked 2 | 3 | To champion the single-responsibility and open/closed principles, we have tried to make it relatively painless to extend marked. If you are looking to add custom functionality, this is the place to start. 4 | 5 |

    The renderer

    6 | 7 | The renderer is... 8 | 9 | **Example:** Overriding default heading token by adding an embedded anchor tag like on GitHub. 10 | 11 | ```js 12 | // Create reference instance 13 | const marked = require('marked'); 14 | 15 | // Get reference 16 | const renderer = new marked.Renderer(); 17 | 18 | // Override function 19 | renderer.heading = function (text, level) { 20 | const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-'); 21 | 22 | return ` 23 | 24 |
    25 | 26 | 27 | ${text} 28 | `; 29 | }; 30 | 31 | // Run marked 32 | console.log(marked('# heading+', { renderer: renderer })); 33 | ``` 34 | 35 | **Output:** 36 | 37 | ```html 38 |

    39 | 40 | 41 | 42 | heading+ 43 |

    44 | ``` 45 | 46 | ### Block level renderer methods 47 | 48 | - code(*string* code, *string* infostring, *boolean* escaped) 49 | - blockquote(*string* quote) 50 | - html(*string* html) 51 | - heading(*string* text, *number* level, *string* raw, *Slugger* slugger) 52 | - hr() 53 | - list(*string* body, *boolean* ordered, *number* start) 54 | - listitem(*string* text, *boolean* task, *boolean* checked) 55 | - checkbox(*boolean* checked) 56 | - paragraph(*string* text) 57 | - table(*string* header, *string* body) 58 | - tablerow(*string* content) 59 | - tablecell(*string* content, *object* flags) 60 | 61 | `slugger` has the `slug` method to create an unique id from value: 62 | 63 | ```js 64 | slugger.slug('foo') // foo 65 | slugger.slug('foo') // foo-1 66 | slugger.slug('foo') // foo-2 67 | slugger.slug('foo 1') // foo-1-1 68 | slugger.slug('foo-1') // foo-1-2 69 | ... 70 | ``` 71 | 72 | `flags` has the following properties: 73 | 74 | ```js 75 | { 76 | header: true || false, 77 | align: 'center' || 'left' || 'right' 78 | } 79 | ``` 80 | 81 | ### Inline level renderer methods 82 | 83 | - strong(*string* text) 84 | - em(*string* text) 85 | - codespan(*string* code) 86 | - br() 87 | - del(*string* text) 88 | - link(*string* href, *string* title, *string* text) 89 | - image(*string* href, *string* title, *string* text) 90 | - text(*string* text) 91 | 92 |

    The lexer

    93 | 94 | The lexer is... 95 | 96 | 97 |

    The parser

    98 | 99 | The parser is... 100 | 101 | *** 102 | 103 |

    Access to lexer and parser

    104 | 105 | You also have direct access to the lexer and parser if you so desire. 106 | 107 | ``` js 108 | const tokens = marked.lexer(text, options); 109 | console.log(marked.parser(tokens, options)); 110 | ``` 111 | 112 | ``` js 113 | const lexer = new marked.Lexer(options); 114 | const tokens = lexer.lex(text); 115 | console.log(tokens); 116 | console.log(lexer.rules); 117 | ``` 118 | 119 | ``` bash 120 | $ node 121 | > require('marked').lexer('> i am using marked.') 122 | [ { type: 'blockquote_start' }, 123 | { type: 'paragraph', 124 | text: 'i am using marked.' }, 125 | { type: 'blockquote_end' }, 126 | links: {} ] 127 | ``` 128 | 129 | The Lexers build an array of tokens, which will be passed to their respective 130 | Parsers. The Parsers process each token in the token arrays, 131 | which are removed from the array of tokens: 132 | 133 | ``` js 134 | const marked = require('marked'); 135 | 136 | const md = ` 137 | # heading 138 | 139 | [link][1] 140 | 141 | [1]: #heading "heading" 142 | `; 143 | 144 | const tokens = marked.lexer(md); 145 | console.log(tokens); 146 | 147 | const html = marked.parser(tokens); 148 | console.log(html); 149 | 150 | console.log(tokens); 151 | ``` 152 | 153 | ``` bash 154 | [ { type: 'heading', depth: 1, text: 'heading' }, 155 | { type: 'paragraph', text: ' [link][1]' }, 156 | { type: 'space' }, 157 | links: { '1': { href: '#heading', title: 'heading' } } ] 158 | 159 |

    heading

    160 |

    link

    161 | 162 | [ links: { '1': { href: '#heading', title: 'heading' } } ] 163 | ``` 164 | -------------------------------------------------------------------------------- /test/helpers/load.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const fm = require('front-matter'); 6 | 7 | function node4Polyfills() { 8 | // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js 9 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd 10 | if (!String.prototype.padEnd) { 11 | // eslint-disable-next-line no-extend-native 12 | String.prototype.padEnd = function padEnd(targetLength, padString) { 13 | targetLength = targetLength >> 0; // floor if number or convert non-number to 0; 14 | padString = String((typeof padString !== 'undefined' ? padString : ' ')); 15 | if (this.length > targetLength) { 16 | return String(this); 17 | } else { 18 | targetLength = targetLength - this.length; 19 | if (targetLength > padString.length) { 20 | padString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed 21 | } 22 | return String(this) + padString.slice(0, targetLength); 23 | } 24 | }; 25 | } 26 | 27 | // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js 28 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart 29 | if (!String.prototype.padStart) { 30 | // eslint-disable-next-line no-extend-native 31 | String.prototype.padStart = function padStart(targetLength, padString) { 32 | targetLength = targetLength >> 0; // truncate if number, or convert non-number to 0; 33 | padString = String(typeof padString !== 'undefined' ? padString : ' '); 34 | if (this.length >= targetLength) { 35 | return String(this); 36 | } else { 37 | targetLength = targetLength - this.length; 38 | if (targetLength > padString.length) { 39 | padString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed 40 | } 41 | return padString.slice(0, targetLength) + String(this); 42 | } 43 | }; 44 | } 45 | } 46 | node4Polyfills(); 47 | 48 | function outputCompletionTable(title, specs) { 49 | let longestName = 0; 50 | let maxSpecs = 0; 51 | 52 | for (const section in specs) { 53 | longestName = Math.max(section.length, longestName); 54 | maxSpecs = Math.max(specs[section].total, maxSpecs); 55 | } 56 | 57 | const maxSpecsLen = ('' + maxSpecs).length; 58 | const spaces = maxSpecsLen * 2 + longestName + 11; 59 | 60 | console.log('-'.padEnd(spaces + 4, '-')); 61 | console.log(`| ${title.padStart(Math.ceil((spaces + title.length) / 2)).padEnd(spaces)} |`); 62 | console.log(`| ${' '.padEnd(spaces)} |`); 63 | for (const section in specs) { 64 | console.log(`| ${section.padEnd(longestName)} ${('' + specs[section].pass).padStart(maxSpecsLen)} of ${('' + specs[section].total).padStart(maxSpecsLen)} ${(100 * specs[section].pass / specs[section].total).toFixed().padStart(4)}% |`); 65 | } 66 | console.log('-'.padEnd(spaces + 4, '-')); 67 | console.log(); 68 | } 69 | 70 | function loadFiles(dir) { 71 | const files = fs.readdirSync(dir); 72 | 73 | return files.reduce((obj, file) => { 74 | const ext = path.extname(file); 75 | const name = path.basename(file, ext); 76 | const absFile = path.join(dir, file); 77 | let specs; 78 | 79 | switch (ext) { 80 | case '.md': { 81 | const content = fm(fs.readFileSync(absFile, 'utf8')); 82 | const skip = content.attributes.skip; 83 | delete content.attributes.skip; 84 | const only = content.attributes.only; 85 | delete content.attributes.only; 86 | specs = [{ 87 | section: name, 88 | markdown: content.body, 89 | html: fs.readFileSync(absFile.replace(/[^.]+$/, 'html'), 'utf8'), 90 | options: content.attributes, 91 | only, 92 | skip 93 | }]; 94 | break; 95 | } 96 | case '.js': 97 | case '.json': { 98 | specs = require(absFile); 99 | if (!Array.isArray(specs)) { 100 | specs = [specs]; 101 | } 102 | break; 103 | } 104 | default: 105 | return obj; 106 | } 107 | 108 | for (const spec of specs) { 109 | if (!spec.section) { 110 | spec.section = name; 111 | } 112 | if (!obj[spec.section]) { 113 | obj[spec.section] = { 114 | total: 0, 115 | pass: 0, 116 | specs: [] 117 | }; 118 | } 119 | 120 | obj[spec.section].total++; 121 | obj[spec.section].pass += spec.shouldFail ? 0 : 1; 122 | obj[spec.section].specs.push(spec); 123 | } 124 | 125 | return obj; 126 | }, {}); 127 | } 128 | 129 | module.exports = { 130 | outputCompletionTable, 131 | loadFiles 132 | }; 133 | -------------------------------------------------------------------------------- /test/specs/redos/link_redos.md: -------------------------------------------------------------------------------- 1 | * 伪类:[:active](https://developer.mozilla.org/en-US/docs/Web/CSS/:active)、[:any-link](https://developer.mozilla.org/en-US/docs/Web/CSS/:any-link)、[:blank](https://developer.mozilla.org/en-US/docs/Web/CSS/:blank)、[:checked](https://developer.mozilla.org/en-US/docs/Web/CSS/:checked)、[:current](https://developer.mozilla.org/en-US/docs/Web/CSS/:current)、[:default](https://developer.mozilla.org/en-US/docs/Web/CSS/:default)、[:defined](https://developer.mozilla.org/en-US/docs/Web/CSS/:defined)、[:dir()](https://developer.mozilla.org/en-US/docs/Web/CSS/:dir)、[:disabled](https://developer.mozilla.org/en-US/docs/Web/CSS/:disabled)、[:drop](https://developer.mozilla.org/en-US/docs/Web/CSS/:drop)、[:empty](https://developer.mozilla.org/en-US/docs/Web/CSS/:empty)、[:enabled](https://developer.mozilla.org/en-US/docs/Web/CSS/:enabled)、[:first](https://developer.mozilla.org/en-US/docs/Web/CSS/:first)、[:first-child](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-child)、[:first-of-type](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-of-type)、[:fullscreen](https://developer.mozilla.org/en-US/docs/Web/CSS/:fullscreen)、[:future](https://developer.mozilla.org/en-US/docs/Web/CSS/:future)、[:focus](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus)、[:focus-visible](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible)、[:focus-within](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-within)、[:has()](https://developer.mozilla.org/en-US/docs/Web/CSS/:has)、[:host](https://developer.mozilla.org/en-US/docs/Web/CSS/:host)、[:host()](https://developer.mozilla.org/en-US/docs/Web/CSS/:host())、[:host-context()](https://developer.mozilla.org/en-US/docs/Web/CSS/:host-context())、[:hover](https://developer.mozilla.org/en-US/docs/Web/CSS/:hover)、[:indeterminate](https://developer.mozilla.org/en-US/docs/Web/CSS/:indeterminate)、[:in-range](https://developer.mozilla.org/en-US/docs/Web/CSS/:in-range)、[:invalid](https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid)、[:is()](https://developer.mozilla.org/en-US/docs/Web/CSS/:is)、[:lang()](https://developer.mozilla.org/en-US/docs/Web/CSS/:lang)、[:last-child](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-child)、[:last-of-type](https://developer.mozilla.org/en-US/docs/Web/CSS/:last-of-type)、[:left](https://developer.mozilla.org/en-US/docs/Web/CSS/:left)、[:link](https://developer.mozilla.org/en-US/docs/Web/CSS/:link)、[:local-link](https://developer.mozilla.org/en-US/docs/Web/CSS/:local-link)、[:not()](https://developer.mozilla.org/en-US/docs/Web/CSS/:not)、[:nth-child()](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child)、[:nth-col()](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-col)、[:nth-last-child()](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-child)、[:nth-last-col()](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-col)、[:nth-last-of-type()](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-last-of-type)、[:nth-of-type()](https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-of-type)、[:only-child](https://developer.mozilla.org/en-US/docs/Web/CSS/:only-child)、[:only-of-type](https://developer.mozilla.org/en-US/docs/Web/CSS/:only-of-type)、[:optional](https://developer.mozilla.org/en-US/docs/Web/CSS/:optional)、[:out-of-range](https://developer.mozilla.org/en-US/docs/Web/CSS/:out-of-range)、[:past](https://developer.mozilla.org/en-US/docs/Web/CSS/:past)、[:placeholder-shown](https://developer.mozilla.org/en-US/docs/Web/CSS/:placeholder-shown)、[:read-only](https://developer.mozilla.org/en-US/docs/Web/CSS/:read-only)、[:read-write](https://developer.mozilla.org/en-US/docs/Web/CSS/:read-write)、[:required](https://developer.mozilla.org/en-US/docs/Web/CSS/:required)、[:right](https://developer.mozilla.org/en-US/docs/Web/CSS/:right)、[:root](https://developer.mozilla.org/en-US/docs/Web/CSS/:root)、[:scope](https://developer.mozilla.org/en-US/docs/Web/CSS/:scope)、[:target](https://developer.mozilla.org/en-US/docs/Web/CSS/:target)、[:target-within](https://developer.mozilla.org/en-US/docs/Web/CSS/:target-within)、[:user-invalid](https://developer.mozilla.org/en-US/docs/Web/CSS/:user-invalid)、[:valid](https://developer.mozilla.org/en-US/docs/Web/CSS/:valid)、[:visited](https://developer.mozilla.org/en-US/docs/Web/CSS/:visited)、[:where()](https://developer.mozilla.org/en-US/docs/Web/CSS/:where) 2 | * 伪元素:[::after (:after)](https://developer.mozilla.org/en-US/docs/Web/CSS/::after)、[::backdrop](https://developer.mozilla.org/en-US/docs/Web/CSS/::backdrop)、[::before (:before)](https://developer.mozilla.org/en-US/docs/Web/CSS/::before)、[::cue (:cue)](https://developer.mozilla.org/en-US/docs/Web/CSS/::cue)、[::first-letter (:first-letter)](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-letter)、[::first-line (:first-line)](https://developer.mozilla.org/en-US/docs/Web/CSS/::first-line)、[::grammar-error](https://developer.mozilla.org/en-US/docs/Web/CSS/::grammar-error)、[::marker](https://developer.mozilla.org/en-US/docs/Web/CSS/::marker)、[::placeholder](https://developer.mozilla.org/en-US/docs/Web/CSS/::placeholder)、[::selection](https://developer.mozilla.org/en-US/docs/Web/CSS/::selection)、[::slotted()](https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted)、[::spelling-error](https://developer.mozilla.org/en-US/docs/Web/CSS/::spelling-error) -------------------------------------------------------------------------------- /bin/marked: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Marked CLI 5 | * Copyright (c) 2011-2013, Christopher Jeffrey (MIT License) 6 | */ 7 | 8 | const fs = require('fs'), 9 | path = require('path'), 10 | marked = require('../'); 11 | 12 | /** 13 | * Man Page 14 | */ 15 | 16 | function help() { 17 | const spawn = require('child_process').spawn; 18 | 19 | const options = { 20 | cwd: process.cwd(), 21 | env: process.env, 22 | setsid: false, 23 | stdio: 'inherit' 24 | }; 25 | 26 | spawn('man', [path.resolve(__dirname, '../man/marked.1')], options) 27 | .on('error', function() { 28 | fs.readFile(path.resolve(__dirname, '../man/marked.1.txt'), 'utf8', function(err, data) { 29 | if (err) throw err; 30 | console.log(data); 31 | }); 32 | }); 33 | } 34 | 35 | function version() { 36 | const pkg = require('../package.json'); 37 | console.log(pkg.version); 38 | } 39 | 40 | /** 41 | * Main 42 | */ 43 | 44 | function main(argv, callback) { 45 | const files = [], 46 | options = {}; 47 | let input, 48 | output, 49 | string, 50 | arg, 51 | tokens, 52 | opt; 53 | 54 | function getarg() { 55 | let arg = argv.shift(); 56 | 57 | if (arg.indexOf('--') === 0) { 58 | // e.g. --opt 59 | arg = arg.split('='); 60 | if (arg.length > 1) { 61 | // e.g. --opt=val 62 | argv.unshift(arg.slice(1).join('=')); 63 | } 64 | arg = arg[0]; 65 | } else if (arg[0] === '-') { 66 | if (arg.length > 2) { 67 | // e.g. -abc 68 | argv = arg.substring(1).split('').map(function(ch) { 69 | return '-' + ch; 70 | }).concat(argv); 71 | arg = argv.shift(); 72 | } else { 73 | // e.g. -a 74 | } 75 | } else { 76 | // e.g. foo 77 | } 78 | 79 | return arg; 80 | } 81 | 82 | while (argv.length) { 83 | arg = getarg(); 84 | switch (arg) { 85 | case '--test': 86 | return require('../test').main(process.argv.slice()); 87 | case '-o': 88 | case '--output': 89 | output = argv.shift(); 90 | break; 91 | case '-i': 92 | case '--input': 93 | input = argv.shift(); 94 | break; 95 | case '-s': 96 | case '--string': 97 | string = argv.shift(); 98 | break; 99 | case '-t': 100 | case '--tokens': 101 | tokens = true; 102 | break; 103 | case '-h': 104 | case '--help': 105 | return help(); 106 | case '-v': 107 | case '--version': 108 | return version(); 109 | default: 110 | if (arg.indexOf('--') === 0) { 111 | opt = camelize(arg.replace(/^--(no-)?/, '')); 112 | if (!marked.defaults.hasOwnProperty(opt)) { 113 | continue; 114 | } 115 | if (arg.indexOf('--no-') === 0) { 116 | options[opt] = typeof marked.defaults[opt] !== 'boolean' 117 | ? null 118 | : false; 119 | } else { 120 | options[opt] = typeof marked.defaults[opt] !== 'boolean' 121 | ? argv.shift() 122 | : true; 123 | } 124 | } else { 125 | files.push(arg); 126 | } 127 | break; 128 | } 129 | } 130 | 131 | function getData(callback) { 132 | if (!input) { 133 | if (files.length <= 2) { 134 | if (string) { 135 | return callback(null, string); 136 | } 137 | return getStdin(callback); 138 | } 139 | input = files.pop(); 140 | } 141 | return fs.readFile(input, 'utf8', callback); 142 | } 143 | 144 | return getData(function(err, data) { 145 | if (err) return callback(err); 146 | 147 | data = tokens 148 | ? JSON.stringify(marked.lexer(data, options), null, 2) 149 | : marked(data, options); 150 | 151 | if (!output) { 152 | process.stdout.write(data + '\n'); 153 | return callback(); 154 | } 155 | 156 | return fs.writeFile(output, data, callback); 157 | }); 158 | } 159 | 160 | /** 161 | * Helpers 162 | */ 163 | 164 | function getStdin(callback) { 165 | const stdin = process.stdin; 166 | let buff = ''; 167 | 168 | stdin.setEncoding('utf8'); 169 | 170 | stdin.on('data', function(data) { 171 | buff += data; 172 | }); 173 | 174 | stdin.on('error', function(err) { 175 | return callback(err); 176 | }); 177 | 178 | stdin.on('end', function() { 179 | return callback(null, buff); 180 | }); 181 | 182 | try { 183 | stdin.resume(); 184 | } catch (e) { 185 | callback(e); 186 | } 187 | } 188 | 189 | function camelize(text) { 190 | return text.replace(/(\w)-(\w)/g, function(_, a, b) { 191 | return a + b.toUpperCase(); 192 | }); 193 | } 194 | 195 | function handleError(err) { 196 | if (err.code === 'ENOENT') { 197 | console.error('marked: output to ' + err.path + ': No such directory'); 198 | return process.exit(1); 199 | } 200 | throw err; 201 | } 202 | 203 | /** 204 | * Expose / Entry Point 205 | */ 206 | 207 | if (!module.parent) { 208 | process.title = 'marked'; 209 | main(process.argv.slice(), function(err, code) { 210 | if (err) return handleError(err); 211 | return process.exit(code || 0); 212 | }); 213 | } else { 214 | module.exports = main; 215 | } 216 | -------------------------------------------------------------------------------- /docs/img/logo-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /test/specs/redos/link_redos.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Marked 2 | 3 | - [ ] Fork `markedjs/marked`. 4 | - [ ] Clone the library locally using GitHub Desktop or the command line. 5 | - [ ] Make sure you are on the `master` branch. 6 | - [ ] Be sure to run `npm install` or `npm update`. 7 | - [ ] Create a branch. 8 | - [ ] Update code in `src` folder. (`lib` folder is for auto compiled code) 9 | - [ ] Run `npm run test:all`, fix any broken things (for linting, you can run `npm run lint` to have the linter fix them for you). 10 | - [ ] Run `npm run build:reset` to remove changes to compiled files. 11 | - [ ] Submit a Pull Request. 12 | 13 | ## Design principles 14 | 15 | Marked tends to favor following the SOLID set of software design and development principles; mainly the [single responsibility](https://en.wikipedia.org/wiki/Single_responsibility_principle) and [open/closed principles](https://en.wikipedia.org/wiki/Open/closed_principle): 16 | 17 | - **Single responsibility:** Marked, and the components of Marked, have the single responsibility of converting Markdown strings into HTML. 18 | - **Open/closed:** Marked favors giving developers the means to easily extend the library and its components over changing Marked's behavior through configuration options. 19 | 20 | ## Priorities 21 | 22 | We think we have our priorities sorted to build quality in. 23 | 24 | The following table lists the ticket type labels we use when there is work to be done on the code either through an Issue or a PR; in priority order. 25 | 26 | |Ticket type label |Description | 27 | |:----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 28 | |L0 - security |A security vulnerability within the Marked library is discovered. | 29 | |L1 - broken |Valid usage results in incorrect output compared to [supported specifications](#/README.md#specifications) OR causes marked to crash AND there is no known workaround for the issue. | 30 | |L2 - annoying |Similar to L1 - broken only there is a known workaround available for the issue. | 31 | |RR - refactor and re-engineer |Results in an improvement to developers using Marked (improved readability) or end-users (faster performance) or both. | 32 | |NFS - new feature (spec related) |A capability Marked does not currently provide but is in one of the [supported specifications](#/README.md#specifications) | 33 | |NFU - new feature (user requested) |A capability Marked does not currently provide but has been requested by users of Marked. | 34 | |NFE - new feature (should be an extension) |A capability Marked does not currently provide and is not part of a spec. | 35 | 36 | ## Test early, often, and everything 37 | 38 | We try to write test cases to validate output (writing tests based on the [supported specifications](#/README.md#specifications)) and minimize regression (writing tests for issues fixed). Therefore, if you would like to contribute, some things you should know regarding the test harness. 39 | 40 | |Location |Description | 41 | |:---------------------|:--------------------------------------------------------------------------------------------------------------| 42 | |/test/specs/commonmark|Tests for [CommonMark](https://spec.commonmark.org/current/) compliance | 43 | |/test/specs/gfm |Tests for [GFM](https://github.github.com/gfm/) compliance | 44 | |/test/specs/new |Tests not related to the original `markdown.pl`. | 45 | |/test/specs/original |Tests validating against the original `markdown.pl`. | 46 | |/test/specs/redos |Tests for [ReDOS](https://www.owasp.org/index.php/Regular_expression_Denial_of_Service_-_ReDoS) vulnerabilities| 47 | 48 | If your test uses features or options, assuming `gfm` is set to `false`, for example, you can add [front-matter](https://www.npmjs.com/package/front-matter) to the top of 49 | your `.md` file 50 | 51 | ``` yml 52 | --- 53 | gfm: false 54 | --- 55 | ``` 56 | 57 | ## Submitting PRs and Issues 58 | 59 | Marked provides templates for submitting both pull requests and issues. When you begin creating a new PR or issue, you will see instructions on using the template. 60 | 61 | The PR templates include checklists for both the submitter and the reviewer, which, in most cases, will not be the same person. 62 | 63 | ## Scripts 64 | 65 | When it comes to NPM commands, we try to use the native scripts provided by the NPM framework. 66 | 67 | To run the tests: 68 | 69 | ``` bash 70 | npm test 71 | ``` 72 | 73 | To test whether you are using the standard syntax rules for the project: 74 | 75 | ```bash 76 | npm run test:lint 77 | ``` 78 | 79 | To see time comparisons between Marked and other popular Markdown libraries: 80 | 81 | ```bash 82 | npm run bench 83 | ``` 84 | 85 | To check for (and fix) standardized syntax (lint): 86 | 87 | ```bash 88 | npm run lint 89 | ``` 90 | 91 | To build your own es5, esm, and minified versions of Marked: 92 | 93 | ```bash 94 | npm run build 95 | ``` 96 | -------------------------------------------------------------------------------- /src/Parser.js: -------------------------------------------------------------------------------- 1 | const Renderer = require('./Renderer.js'); 2 | const Slugger = require('./Slugger.js'); 3 | const InlineLexer = require('./InlineLexer.js'); 4 | const TextRenderer = require('./TextRenderer.js'); 5 | const { defaults } = require('./defaults.js'); 6 | const { 7 | merge, 8 | unescape 9 | } = require('./helpers.js'); 10 | 11 | /** 12 | * Parsing & Compiling 13 | */ 14 | module.exports = class Parser { 15 | constructor(options) { 16 | this.tokens = []; 17 | this.token = null; 18 | this.options = options || defaults; 19 | this.options.renderer = this.options.renderer || new Renderer(); 20 | this.renderer = this.options.renderer; 21 | this.renderer.options = this.options; 22 | this.slugger = new Slugger(); 23 | } 24 | 25 | /** 26 | * Static Parse Method 27 | */ 28 | static parse(tokens, options) { 29 | const parser = new Parser(options); 30 | return parser.parse(tokens); 31 | }; 32 | 33 | /** 34 | * Parse Loop 35 | */ 36 | parse(tokens) { 37 | this.inline = new InlineLexer(tokens.links, this.options); 38 | // use an InlineLexer with a TextRenderer to extract pure text 39 | this.inlineText = new InlineLexer( 40 | tokens.links, 41 | merge({}, this.options, { renderer: new TextRenderer() }) 42 | ); 43 | this.tokens = tokens.reverse(); 44 | 45 | let out = ''; 46 | while (this.next()) { 47 | out += this.tok(); 48 | } 49 | 50 | return out; 51 | }; 52 | 53 | /** 54 | * Next Token 55 | */ 56 | next() { 57 | this.token = this.tokens.pop(); 58 | return this.token; 59 | }; 60 | 61 | /** 62 | * Preview Next Token 63 | */ 64 | peek() { 65 | return this.tokens[this.tokens.length - 1] || 0; 66 | }; 67 | 68 | /** 69 | * Parse Text Tokens 70 | */ 71 | parseText() { 72 | let body = this.token.text; 73 | 74 | while (this.peek().type === 'text') { 75 | body += '\n' + this.next().text; 76 | } 77 | 78 | return this.inline.output(body); 79 | }; 80 | 81 | /** 82 | * Parse Current Token 83 | */ 84 | tok() { 85 | let body = ''; 86 | switch (this.token.type) { 87 | case 'space': { 88 | return ''; 89 | } 90 | case 'hr': { 91 | return this.renderer.hr(); 92 | } 93 | case 'heading': { 94 | return this.renderer.heading( 95 | this.inline.output(this.token.text), 96 | this.token.depth, 97 | unescape(this.inlineText.output(this.token.text)), 98 | this.slugger); 99 | } 100 | case 'code': { 101 | return this.renderer.code(this.token.text, 102 | this.token.lang, 103 | this.token.escaped); 104 | } 105 | case 'table': { 106 | let header = '', 107 | i, 108 | row, 109 | cell, 110 | j; 111 | 112 | // header 113 | cell = ''; 114 | for (i = 0; i < this.token.header.length; i++) { 115 | cell += this.renderer.tablecell( 116 | this.inline.output(this.token.header[i]), 117 | { header: true, align: this.token.align[i] } 118 | ); 119 | } 120 | header += this.renderer.tablerow(cell); 121 | 122 | for (i = 0; i < this.token.cells.length; i++) { 123 | row = this.token.cells[i]; 124 | 125 | cell = ''; 126 | for (j = 0; j < row.length; j++) { 127 | cell += this.renderer.tablecell( 128 | this.inline.output(row[j]), 129 | { header: false, align: this.token.align[j] } 130 | ); 131 | } 132 | 133 | body += this.renderer.tablerow(cell); 134 | } 135 | return this.renderer.table(header, body); 136 | } 137 | case 'blockquote_start': { 138 | body = ''; 139 | 140 | while (this.next().type !== 'blockquote_end') { 141 | body += this.tok(); 142 | } 143 | 144 | return this.renderer.blockquote(body); 145 | } 146 | case 'list_start': { 147 | body = ''; 148 | const ordered = this.token.ordered, 149 | start = this.token.start; 150 | 151 | while (this.next().type !== 'list_end') { 152 | body += this.tok(); 153 | } 154 | 155 | return this.renderer.list(body, ordered, start); 156 | } 157 | case 'list_item_start': { 158 | body = ''; 159 | const loose = this.token.loose; 160 | const checked = this.token.checked; 161 | const task = this.token.task; 162 | 163 | if (this.token.task) { 164 | if (loose) { 165 | if (this.peek().type === 'text') { 166 | const nextToken = this.peek(); 167 | nextToken.text = this.renderer.checkbox(checked) + ' ' + nextToken.text; 168 | } else { 169 | this.tokens.push({ 170 | type: 'text', 171 | text: this.renderer.checkbox(checked) 172 | }); 173 | } 174 | } else { 175 | body += this.renderer.checkbox(checked); 176 | } 177 | } 178 | 179 | while (this.next().type !== 'list_item_end') { 180 | body += !loose && this.token.type === 'text' 181 | ? this.parseText() 182 | : this.tok(); 183 | } 184 | return this.renderer.listitem(body, task, checked); 185 | } 186 | case 'html': { 187 | // TODO parse inline content if parameter markdown=1 188 | return this.renderer.html(this.token.text); 189 | } 190 | case 'paragraph': { 191 | return this.renderer.paragraph(this.inline.output(this.token.text)); 192 | } 193 | case 'text': { 194 | return this.renderer.paragraph(this.parseText()); 195 | } 196 | default: { 197 | const errMsg = 'Token with "' + this.token.type + '" type was not found.'; 198 | if (this.options.silent) { 199 | console.log(errMsg); 200 | } else { 201 | throw new Error(errMsg); 202 | } 203 | } 204 | } 205 | }; 206 | }; 207 | --------------------------------------------------------------------------------